@code-yeongyu/senpi 2026.9.7 → 2026.9.8

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 (323) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/core/agent-session.d.ts +40 -3
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +159 -40
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/compaction.d.ts.map +1 -1
  7. package/dist/core/compaction/compaction.js +2 -1
  8. package/dist/core/compaction/compaction.js.map +1 -1
  9. package/dist/core/dynamic-prompt/working-task.js +1 -1
  10. package/dist/core/dynamic-prompt/working-task.js.map +1 -1
  11. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
  12. package/dist/core/extensions/builtin/cache-keepalive/index.js +4 -21
  13. package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
  14. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.d.ts +2 -0
  15. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.d.ts.map +1 -1
  16. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js +10 -25
  17. package/dist/core/extensions/builtin/claude-sdk-oauth/account-command.js.map +1 -1
  18. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
  19. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +36 -19
  20. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
  21. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +21 -10
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -17
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +23 -33
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +5 -3
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +8 -3
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts +6 -5
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts.map +1 -1
  39. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js +6 -5
  40. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js.map +1 -1
  41. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts +8 -0
  42. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
  43. package/dist/core/extensions/builtin/compaction/circuit-breaker.js +8 -0
  44. package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
  45. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts +8 -0
  46. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  47. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +125 -20
  48. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  49. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts +18 -0
  50. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts.map +1 -0
  51. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js +20 -0
  52. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js.map +1 -0
  53. package/dist/core/extensions/builtin/compaction/index.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/compaction/index.js +54 -11
  55. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  56. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +3 -0
  57. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
  58. package/dist/core/extensions/builtin/compaction/lane-policy.js +47 -23
  59. package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
  60. package/dist/core/extensions/builtin/goal/agent-end-continuation.d.ts.map +1 -1
  61. package/dist/core/extensions/builtin/goal/agent-end-continuation.js +11 -0
  62. package/dist/core/extensions/builtin/goal/agent-end-continuation.js.map +1 -1
  63. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +6 -4
  64. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  65. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +25 -1
  66. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  67. package/dist/core/extensions/builtin/goal/cache-warm.js +29 -10
  68. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  69. package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
  70. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
  71. package/dist/core/extensions/builtin/goal/continuation.js +17 -2
  72. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
  73. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  74. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +1 -0
  75. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  76. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +1 -1
  77. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  78. package/dist/core/extensions/builtin/goal/monitor-continuation.js +8 -3
  79. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  80. package/dist/core/extensions/builtin/gpt-account.d.ts +5 -1
  81. package/dist/core/extensions/builtin/gpt-account.d.ts.map +1 -1
  82. package/dist/core/extensions/builtin/gpt-account.js +9 -26
  83. package/dist/core/extensions/builtin/gpt-account.js.map +1 -1
  84. package/dist/core/extensions/builtin/hooks/plugin-manifest.js +2 -2
  85. package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -1
  86. package/dist/core/extensions/builtin/hooks/safety.js +2 -2
  87. package/dist/core/extensions/builtin/hooks/safety.js.map +1 -1
  88. package/dist/core/extensions/builtin/monitor-state-event.d.ts +18 -0
  89. package/dist/core/extensions/builtin/monitor-state-event.d.ts.map +1 -1
  90. package/dist/core/extensions/builtin/monitor-state-event.js +23 -0
  91. package/dist/core/extensions/builtin/monitor-state-event.js.map +1 -1
  92. package/dist/core/extensions/builtin/oauth-login-interaction.d.ts +24 -0
  93. package/dist/core/extensions/builtin/oauth-login-interaction.d.ts.map +1 -0
  94. package/dist/core/extensions/builtin/oauth-login-interaction.js +77 -0
  95. package/dist/core/extensions/builtin/oauth-login-interaction.js.map +1 -0
  96. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  97. package/dist/core/extensions/builtin/permission-system/external-dir.js +5 -51
  98. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  99. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  100. package/dist/core/extensions/builtin/permission-system/parsers.js +5 -7
  101. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  102. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js +1 -1
  103. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5-1.js.map +1 -1
  104. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js +1 -1
  105. package/dist/core/extensions/builtin/prompt-preset/claude-fable-5.js.map +1 -1
  106. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts +14 -7
  107. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.d.ts.map +1 -1
  108. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js +34 -13
  109. package/dist/core/extensions/builtin/prompt-preset/execution-tooling.js.map +1 -1
  110. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts +6 -10
  111. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.d.ts.map +1 -1
  112. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js +14 -9
  113. package/dist/core/extensions/builtin/prompt-preset/gpt-5.6.js.map +1 -1
  114. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts +15 -15
  115. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.d.ts.map +1 -1
  116. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js +49 -21
  117. package/dist/core/extensions/builtin/prompt-preset/gpt-6-astra.js.map +1 -1
  118. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js +1 -1
  119. package/dist/core/extensions/builtin/prompt-preset/kimi-k3.js.map +1 -1
  120. package/dist/core/extensions/builtin/service-tier.d.ts +2 -0
  121. package/dist/core/extensions/builtin/service-tier.d.ts.map +1 -1
  122. package/dist/core/extensions/builtin/service-tier.js +17 -1
  123. package/dist/core/extensions/builtin/service-tier.js.map +1 -1
  124. package/dist/core/extensions/builtin/terminal/durable-command.d.ts.map +1 -1
  125. package/dist/core/extensions/builtin/terminal/durable-command.js +3 -0
  126. package/dist/core/extensions/builtin/terminal/durable-command.js.map +1 -1
  127. package/dist/core/extensions/builtin/terminal/durable-file.d.ts.map +1 -1
  128. package/dist/core/extensions/builtin/terminal/durable-file.js +2 -0
  129. package/dist/core/extensions/builtin/terminal/durable-file.js.map +1 -1
  130. package/dist/core/extensions/builtin/terminal/extension.d.ts.map +1 -1
  131. package/dist/core/extensions/builtin/terminal/extension.js +14 -2
  132. package/dist/core/extensions/builtin/terminal/extension.js.map +1 -1
  133. package/dist/core/extensions/builtin/terminal/monitor-notify.d.ts.map +1 -1
  134. package/dist/core/extensions/builtin/terminal/monitor-notify.js +27 -5
  135. package/dist/core/extensions/builtin/terminal/monitor-notify.js.map +1 -1
  136. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts +17 -0
  137. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  138. package/dist/core/extensions/builtin/terminal/monitor-registry.js +76 -14
  139. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  140. package/dist/core/extensions/builtin/terminal/notify.d.ts +2 -0
  141. package/dist/core/extensions/builtin/terminal/notify.d.ts.map +1 -1
  142. package/dist/core/extensions/builtin/terminal/notify.js +6 -1
  143. package/dist/core/extensions/builtin/terminal/notify.js.map +1 -1
  144. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts +3 -1
  145. package/dist/core/extensions/builtin/terminal/session-bundle.d.ts.map +1 -1
  146. package/dist/core/extensions/builtin/terminal/session-bundle.js +13 -0
  147. package/dist/core/extensions/builtin/terminal/session-bundle.js.map +1 -1
  148. package/dist/core/extensions/builtin/terminal/shared.d.ts +13 -2
  149. package/dist/core/extensions/builtin/terminal/shared.d.ts.map +1 -1
  150. package/dist/core/extensions/builtin/terminal/shared.js +15 -2
  151. package/dist/core/extensions/builtin/terminal/shared.js.map +1 -1
  152. package/dist/core/extensions/builtin/terminal/tools/monitor.d.ts.map +1 -1
  153. package/dist/core/extensions/builtin/terminal/tools/monitor.js +8 -1
  154. package/dist/core/extensions/builtin/terminal/tools/monitor.js.map +1 -1
  155. package/dist/core/extensions/builtin/tool-search/index.d.ts.map +1 -1
  156. package/dist/core/extensions/builtin/tool-search/index.js +1 -0
  157. package/dist/core/extensions/builtin/tool-search/index.js.map +1 -1
  158. package/dist/core/extensions/builtin/tool-search/service.d.ts +4 -0
  159. package/dist/core/extensions/builtin/tool-search/service.d.ts.map +1 -1
  160. package/dist/core/extensions/builtin/tool-search/service.js +11 -0
  161. package/dist/core/extensions/builtin/tool-search/service.js.map +1 -1
  162. package/dist/core/extensions/runner.d.ts +1 -0
  163. package/dist/core/extensions/runner.d.ts.map +1 -1
  164. package/dist/core/extensions/runner.js +8 -1
  165. package/dist/core/extensions/runner.js.map +1 -1
  166. package/dist/core/extensions/types.d.ts +9 -0
  167. package/dist/core/extensions/types.d.ts.map +1 -1
  168. package/dist/core/extensions/types.js.map +1 -1
  169. package/dist/core/high-reasoning-warning.d.ts.map +1 -1
  170. package/dist/core/high-reasoning-warning.js +4 -6
  171. package/dist/core/high-reasoning-warning.js.map +1 -1
  172. package/dist/core/sdk.d.ts +1 -1
  173. package/dist/core/sdk.d.ts.map +1 -1
  174. package/dist/core/sdk.js +25 -6
  175. package/dist/core/sdk.js.map +1 -1
  176. package/dist/core/settings-manager.js +1 -1
  177. package/dist/core/settings-manager.js.map +1 -1
  178. package/dist/core/settings-shapes.js.map +1 -1
  179. package/dist/core/tools/bounded-realpath.d.ts +28 -0
  180. package/dist/core/tools/bounded-realpath.d.ts.map +1 -0
  181. package/dist/core/tools/bounded-realpath.js +53 -0
  182. package/dist/core/tools/bounded-realpath.js.map +1 -0
  183. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  184. package/dist/core/tools/file-mutation-queue.js +15 -13
  185. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  186. package/dist/core/tools/filesystem-policy.d.ts +7 -0
  187. package/dist/core/tools/filesystem-policy.d.ts.map +1 -1
  188. package/dist/core/tools/filesystem-policy.js +25 -15
  189. package/dist/core/tools/filesystem-policy.js.map +1 -1
  190. package/dist/core/trust-manager.d.ts.map +1 -1
  191. package/dist/core/trust-manager.js +13 -2
  192. package/dist/core/trust-manager.js.map +1 -1
  193. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  194. package/dist/modes/interactive/interactive-mode.js +1 -0
  195. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  196. package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -1
  197. package/dist/modes/interactive/tips/catalog/memory-tips.js +6 -0
  198. package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -1
  199. package/dist/modes/interactive/tips/catalog/subagent-tips.d.ts.map +1 -1
  200. package/dist/modes/interactive/tips/catalog/subagent-tips.js +6 -0
  201. package/dist/modes/interactive/tips/catalog/subagent-tips.js.map +1 -1
  202. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  203. package/dist/modes/rpc/host-lifecycle.js +31 -5
  204. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  205. package/dist/modes/rpc/host-watchdog.d.ts +11 -0
  206. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  207. package/dist/modes/rpc/host-watchdog.js +16 -2
  208. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  209. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  210. package/dist/modes/rpc/multi-session-host.js +54 -16
  211. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  212. package/dist/modes/rpc/socket-ownership.d.ts +46 -0
  213. package/dist/modes/rpc/socket-ownership.d.ts.map +1 -0
  214. package/dist/modes/rpc/socket-ownership.js +178 -0
  215. package/dist/modes/rpc/socket-ownership.js.map +1 -0
  216. package/dist/utils/fs-watch.d.ts +17 -0
  217. package/dist/utils/fs-watch.d.ts.map +1 -1
  218. package/dist/utils/fs-watch.js +38 -10
  219. package/dist/utils/fs-watch.js.map +1 -1
  220. package/dist/utils/paths.d.ts +33 -0
  221. package/dist/utils/paths.d.ts.map +1 -1
  222. package/dist/utils/paths.js +109 -3
  223. package/dist/utils/paths.js.map +1 -1
  224. package/docs/development.md +3 -2
  225. package/docs/providers.md +5 -1
  226. package/docs/settings.md +1 -0
  227. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +31 -0
  228. package/node_modules/@code-yeongyu/senpi-codemode/README.md +14 -1
  229. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  230. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +1 -0
  231. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +3 -0
  232. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +7 -0
  233. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +7 -0
  234. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +4 -0
  235. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +3 -0
  236. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +29 -0
  237. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +10 -6
  238. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.d.ts +16 -0
  239. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.js +44 -8
  240. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +1 -0
  241. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +3 -0
  242. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +9 -1
  243. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +4 -0
  244. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/session-env.ts +56 -0
  245. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +3 -0
  246. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +9 -1
  247. package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +26 -2
  248. package/node_modules/@code-yeongyu/senpi-codemode/src/prompt/eval-prompt.ts +17 -24
  249. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +12 -3
  250. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
  251. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js +2 -2
  252. package/node_modules/@earendil-works/pi-agent-core/dist/agent-loop.js.map +1 -1
  253. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts +7 -0
  254. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.d.ts.map +1 -1
  255. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js +19 -0
  256. package/node_modules/@earendil-works/pi-agent-core/dist/assistant-terminal-state.js.map +1 -1
  257. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.d.ts.map +1 -1
  258. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js +24 -16
  259. package/node_modules/@earendil-works/pi-agent-core/dist/empty-assistant-recovery.js.map +1 -1
  260. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts +1 -1
  261. package/node_modules/@earendil-works/pi-agent-core/dist/index.d.ts.map +1 -1
  262. package/node_modules/@earendil-works/pi-agent-core/dist/index.js +1 -1
  263. package/node_modules/@earendil-works/pi-agent-core/dist/index.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  265. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.d.ts.map +1 -1
  266. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js +47 -178
  267. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-messages.js.map +1 -1
  268. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.d.ts +3 -0
  269. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.d.ts.map +1 -0
  270. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js +260 -0
  271. package/node_modules/@earendil-works/pi-ai/dist/api/anthropic-tool-references.js.map +1 -0
  272. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  273. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +1 -0
  274. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  275. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  276. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +1 -0
  277. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  278. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.d.ts +40 -0
  279. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.d.ts.map +1 -0
  280. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.js +138 -0
  281. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic-callback-listener.js.map +1 -0
  282. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts +2 -3
  283. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.d.ts.map +1 -1
  284. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js +42 -139
  285. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/anthropic.js.map +1 -1
  286. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.d.ts +6 -0
  287. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.d.ts.map +1 -0
  288. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.js +29 -0
  289. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/authorization-input.js.map +1 -0
  290. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.d.ts +3 -0
  291. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.d.ts.map +1 -0
  292. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.js +20 -0
  293. package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/error-details.js.map +1 -0
  294. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  295. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  296. package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
  297. package/node_modules/@earendil-works/pi-ai/dist/providers/data/fireworks.json +1 -1
  298. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  299. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  300. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  301. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  302. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  303. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  304. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  305. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.d.ts.map +1 -1
  306. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.js +16 -1
  307. package/node_modules/@earendil-works/pi-ai/dist/tool-call-middleware/recovery-stream-terminal.js.map +1 -1
  308. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts +7 -0
  309. package/node_modules/@earendil-works/pi-ai/dist/types.d.ts.map +1 -1
  310. package/node_modules/@earendil-works/pi-ai/dist/types.js.map +1 -1
  311. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
  312. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  313. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +14 -3
  314. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  315. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts +2 -1
  316. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.d.ts.map +1 -1
  317. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js +16 -0
  318. package/node_modules/@earendil-works/pi-ai/dist/utils/server-fallback-receipt.js.map +1 -1
  319. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  320. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  321. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  322. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  323. package/package.json +7 -7
@@ -29,13 +29,15 @@ function validIteration(value) {
29
29
  function whyLine(data) {
30
30
  switch (data.phase) {
31
31
  case "scheduled": {
32
- const expected = `Continuation expected ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;
32
+ // The wait is not a cache-warm timer: it lets the live wake sources
33
+ // deliver, and the timed wake is only the stall backstop.
34
+ const backstop = `Stall backstop ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;
33
35
  if (data.cache?.ttlSeconds === undefined) {
34
- return `${expected} - the monitor wakes the goal the moment decisive output lands.`;
36
+ return `${backstop} - the goal resumes as soon as a wake source delivers.`;
35
37
  }
36
38
  return data.delayMs < data.cache.ttlSeconds * 1000
37
- ? `${expected} - the timed wake stays inside the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL.`
38
- : `${expected} - the prompt-cache TTL may elapse before the timed wake.`;
39
+ ? `${backstop} - the goal resumes as soon as a wake source delivers, inside the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL.`
40
+ : `${backstop} - the goal resumes as soon as a wake source delivers; the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL may elapse first.`;
39
41
  }
40
42
  case "resumed":
41
43
  return "Woke on schedule to keep pursuing the goal.";
@@ -1 +1 @@
1
- {"version":3,"file":"cache-warm-renderer.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { noticeEntryRenderer } from \"../../notice/index.ts\";\nimport type { EntryRenderer } from \"../../types.ts\";\nimport {\n\tformatCacheTtl,\n\tformatSavedUsd,\n\tformatWakeDuration,\n\tformatWakeTimestamp,\n\tformatWarmTokenCount,\n\ttype GoalCacheWarmupEntryData,\n} from \"./cache-warm.ts\";\n\nexport const renderGoalCacheWarmupEntry: EntryRenderer<GoalCacheWarmupEntryData> = noticeEntryRenderer((entry) => {\n\tconst data = entry.data;\n\tif (data === undefined) return undefined;\n\tconst warm = warmLine(data);\n\treturn {\n\t\ttitle: titleLine(data),\n\t\twhy: whyLine(data),\n\t\textra: warm === undefined ? [] : [{ text: warm, tone: \"success\" }],\n\t\texpandedLine: expandedLine(data),\n\t};\n});\n\nfunction titleLine(data: GoalCacheWarmupEntryData): string {\n\tconst wakeSources =\n\t\tdata.activeMonitorCount === 1 ? \"1 wake source on duty\" : `${data.activeMonitorCount} wake sources on duty`;\n\tconst iteration = validIteration(data.iteration);\n\tconst iterationText = iteration === undefined ? \"\" : ` · iteration ${iteration}`;\n\tswitch (data.phase) {\n\t\tcase \"scheduled\":\n\t\t\treturn `⚡ Cache-warm wait${iterationText} · ${wakeSources}`;\n\t\tcase \"resumed\":\n\t\t\treturn `⚡ Cache-warm wake${iterationText} · ${formatExpectedWake(data.dueAtMs, data.waitedMs ?? data.delayMs)} · ${wakeSources}`;\n\t}\n}\n\nfunction validIteration(value: number | undefined): number | undefined {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\nfunction whyLine(data: GoalCacheWarmupEntryData): string {\n\tswitch (data.phase) {\n\t\tcase \"scheduled\": {\n\t\t\tconst expected = `Continuation expected ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;\n\t\t\tif (data.cache?.ttlSeconds === undefined) {\n\t\t\t\treturn `${expected} - the monitor wakes the goal the moment decisive output lands.`;\n\t\t\t}\n\t\t\treturn data.delayMs < data.cache.ttlSeconds * 1000\n\t\t\t\t? `${expected} - the timed wake stays inside the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL.`\n\t\t\t\t: `${expected} - the prompt-cache TTL may elapse before the timed wake.`;\n\t\t}\n\t\tcase \"resumed\":\n\t\t\treturn \"Woke on schedule to keep pursuing the goal.\";\n\t}\n}\n\nfunction formatExpectedWake(dueAtMs: number | undefined, elapsedMs: number): string {\n\tif (dueAtMs === undefined || !Number.isFinite(dueAtMs)) return `waited ${formatWakeDuration(elapsedMs)}`;\n\treturn `ready ${formatWakeTimestamp(dueAtMs)} (${formatWakeDuration(elapsedMs)})`;\n}\n\nfunction expandedLine(data: GoalCacheWarmupEntryData): string {\n\tconst planned = `goal ${data.goalId} · planned delay ${formatWakeDuration(data.delayMs)}`;\n\tif (data.phase === \"scheduled\") return `${planned} · ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;\n\treturn `${planned} · ${formatExpectedWake(data.dueAtMs, data.waitedMs ?? data.delayMs)}`;\n}\n\nfunction warmLine(data: GoalCacheWarmupEntryData): string | undefined {\n\tconst cache = data.cache;\n\tif (cache === undefined || cache.cachedTokens <= 0) return undefined;\n\tconst tokens = `~${formatWarmTokenCount(cache.cachedTokens)} tokens`;\n\tconst ttlMayHaveElapsed =\n\t\tcache.ttlSeconds !== undefined && (data.waitedMs ?? data.delayMs) >= cache.ttlSeconds * 1000;\n\tif (ttlMayHaveElapsed) {\n\t\treturn `${tokens} were cached after the prior turn · prompt-cache TTL may have elapsed before this wake`;\n\t}\n\tconst body = data.phase === \"scheduled\" ? `${tokens} kept warm` : `${tokens} stayed warm in the prompt cache`;\n\tconst saved =\n\t\tcache.estimatedSavedUsd !== undefined && cache.estimatedSavedUsd > 0\n\t\t\t? ` · est. ${formatSavedUsd(cache.estimatedSavedUsd)} saved vs a cold re-read`\n\t\t\t: \"\";\n\treturn `${body}${saved}`;\n}\n"]}
1
+ {"version":3,"file":"cache-warm-renderer.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm-renderer.ts"],"names":[],"mappings":"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","sourcesContent":["import { noticeEntryRenderer } from \"../../notice/index.ts\";\nimport type { EntryRenderer } from \"../../types.ts\";\nimport {\n\tformatCacheTtl,\n\tformatSavedUsd,\n\tformatWakeDuration,\n\tformatWakeTimestamp,\n\tformatWarmTokenCount,\n\ttype GoalCacheWarmupEntryData,\n} from \"./cache-warm.ts\";\n\nexport const renderGoalCacheWarmupEntry: EntryRenderer<GoalCacheWarmupEntryData> = noticeEntryRenderer((entry) => {\n\tconst data = entry.data;\n\tif (data === undefined) return undefined;\n\tconst warm = warmLine(data);\n\treturn {\n\t\ttitle: titleLine(data),\n\t\twhy: whyLine(data),\n\t\textra: warm === undefined ? [] : [{ text: warm, tone: \"success\" }],\n\t\texpandedLine: expandedLine(data),\n\t};\n});\n\nfunction titleLine(data: GoalCacheWarmupEntryData): string {\n\tconst wakeSources =\n\t\tdata.activeMonitorCount === 1 ? \"1 wake source on duty\" : `${data.activeMonitorCount} wake sources on duty`;\n\tconst iteration = validIteration(data.iteration);\n\tconst iterationText = iteration === undefined ? \"\" : ` · iteration ${iteration}`;\n\tswitch (data.phase) {\n\t\tcase \"scheduled\":\n\t\t\treturn `⚡ Cache-warm wait${iterationText} · ${wakeSources}`;\n\t\tcase \"resumed\":\n\t\t\treturn `⚡ Cache-warm wake${iterationText} · ${formatExpectedWake(data.dueAtMs, data.waitedMs ?? data.delayMs)} · ${wakeSources}`;\n\t}\n}\n\nfunction validIteration(value: number | undefined): number | undefined {\n\treturn typeof value === \"number\" && Number.isInteger(value) && value > 0 ? value : undefined;\n}\n\nfunction whyLine(data: GoalCacheWarmupEntryData): string {\n\tswitch (data.phase) {\n\t\tcase \"scheduled\": {\n\t\t\t// The wait is not a cache-warm timer: it lets the live wake sources\n\t\t\t// deliver, and the timed wake is only the stall backstop.\n\t\t\tconst backstop = `Stall backstop ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;\n\t\t\tif (data.cache?.ttlSeconds === undefined) {\n\t\t\t\treturn `${backstop} - the goal resumes as soon as a wake source delivers.`;\n\t\t\t}\n\t\t\treturn data.delayMs < data.cache.ttlSeconds * 1000\n\t\t\t\t? `${backstop} - the goal resumes as soon as a wake source delivers, inside the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL.`\n\t\t\t\t: `${backstop} - the goal resumes as soon as a wake source delivers; the ${formatCacheTtl(data.cache.ttlSeconds)} prompt-cache TTL may elapse first.`;\n\t\t}\n\t\tcase \"resumed\":\n\t\t\treturn \"Woke on schedule to keep pursuing the goal.\";\n\t}\n}\n\nfunction formatExpectedWake(dueAtMs: number | undefined, elapsedMs: number): string {\n\tif (dueAtMs === undefined || !Number.isFinite(dueAtMs)) return `waited ${formatWakeDuration(elapsedMs)}`;\n\treturn `ready ${formatWakeTimestamp(dueAtMs)} (${formatWakeDuration(elapsedMs)})`;\n}\n\nfunction expandedLine(data: GoalCacheWarmupEntryData): string {\n\tconst planned = `goal ${data.goalId} · planned delay ${formatWakeDuration(data.delayMs)}`;\n\tif (data.phase === \"scheduled\") return `${planned} · ${formatExpectedWake(data.dueAtMs, data.delayMs)}`;\n\treturn `${planned} · ${formatExpectedWake(data.dueAtMs, data.waitedMs ?? data.delayMs)}`;\n}\n\nfunction warmLine(data: GoalCacheWarmupEntryData): string | undefined {\n\tconst cache = data.cache;\n\tif (cache === undefined || cache.cachedTokens <= 0) return undefined;\n\tconst tokens = `~${formatWarmTokenCount(cache.cachedTokens)} tokens`;\n\tconst ttlMayHaveElapsed =\n\t\tcache.ttlSeconds !== undefined && (data.waitedMs ?? data.delayMs) >= cache.ttlSeconds * 1000;\n\tif (ttlMayHaveElapsed) {\n\t\treturn `${tokens} were cached after the prior turn · prompt-cache TTL may have elapsed before this wake`;\n\t}\n\tconst body = data.phase === \"scheduled\" ? `${tokens} kept warm` : `${tokens} stayed warm in the prompt cache`;\n\tconst saved =\n\t\tcache.estimatedSavedUsd !== undefined && cache.estimatedSavedUsd > 0\n\t\t\t? ` · est. ${formatSavedUsd(cache.estimatedSavedUsd)} saved vs a cold re-read`\n\t\t\t: \"\";\n\treturn `${body}${saved}`;\n}\n"]}
@@ -2,8 +2,32 @@ import type { Api, Model } from "@earendil-works/pi-ai";
2
2
  import type { TokenUsageSnapshot } from "./types.ts";
3
3
  /** Custom session-entry type carrying the cache-warm continuation story. */
4
4
  export declare const GOAL_CACHE_WARMUP_ENTRY_TYPE = "goal-cache-warmup";
5
+ /**
6
+ * Accounting fallback for a monitor continuation whose scheduled delay is no
7
+ * longer known (a held timer restored across a reload). It is deliberately NOT
8
+ * the armed delay: while a wake source is live the monitor arms the configured
9
+ * backstop below, never a timer derived from the cache-safe wait.
10
+ */
5
11
  export declare const GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS = 240000;
6
- export declare function resolveGoalMonitorContinuationDelayMs(cacheSafeWaitSeconds: number | undefined, goalBackstopMaxSeconds?: number): number;
12
+ /**
13
+ * Default `promptCache.goalBackstopMaxSeconds` (settings-manager.ts) expressed in ms:
14
+ * the 5-minute Anthropic prompt-cache TTL minus the 30s safety buffer, so the
15
+ * periodic re-check lands inside the cache.
16
+ */
17
+ export declare const GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS = 270000;
18
+ /**
19
+ * Delay of the goal-monitor backstop, in milliseconds.
20
+ *
21
+ * A live wake source normally resumes the goal when it delivers (the drain
22
+ * fire), but a wake source can be misconfigured - a filter that never matches,
23
+ * a stream that never ends - so the backstop is the floor underneath it: the
24
+ * goal re-checks at least this often instead of trusting the source for an
25
+ * hour. It is configured, never derived from the prompt-cache safe wait, through
26
+ * `promptCache.goalBackstopMaxSeconds`; a missing, non-finite, or non-positive
27
+ * setting falls back to the 270s default, and the result is clamped into
28
+ * [1s, 1h].
29
+ */
30
+ export declare function resolveGoalMonitorContinuationDelayMs(goalBackstopMaxSeconds: number | undefined): number;
7
31
  /** Cache context captured when a monitor-wait continuation is scheduled. */
8
32
  export interface GoalCacheWarmMetrics {
9
33
  /** Prompt-cache TTL of the active model in seconds, when known. */
@@ -1 +1 @@
1
- {"version":3,"file":"cache-warm.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cache-warm.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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"}
@@ -1,21 +1,40 @@
1
1
  import { resolvePromptCacheTtlSeconds } from "@earendil-works/pi-ai";
2
2
  /** Custom session-entry type carrying the cache-warm continuation story. */
3
3
  export const GOAL_CACHE_WARMUP_ENTRY_TYPE = "goal-cache-warmup";
4
+ /**
5
+ * Accounting fallback for a monitor continuation whose scheduled delay is no
6
+ * longer known (a held timer restored across a reload). It is deliberately NOT
7
+ * the armed delay: while a wake source is live the monitor arms the configured
8
+ * backstop below, never a timer derived from the cache-safe wait.
9
+ */
4
10
  export const GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS = 240_000;
11
+ /**
12
+ * Default `promptCache.goalBackstopMaxSeconds` (settings-manager.ts) expressed in ms:
13
+ * the 5-minute Anthropic prompt-cache TTL minus the 30s safety buffer, so the
14
+ * periodic re-check lands inside the cache.
15
+ */
16
+ export const GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS = 270_000;
5
17
  const GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS = 1_000;
6
18
  const GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS = 3_600_000;
7
- export function resolveGoalMonitorContinuationDelayMs(cacheSafeWaitSeconds, goalBackstopMaxSeconds) {
8
- if (typeof cacheSafeWaitSeconds !== "number" ||
9
- !Number.isFinite(cacheSafeWaitSeconds) ||
10
- cacheSafeWaitSeconds <= 0) {
11
- return GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS;
12
- }
13
- const configuredCeilingMs = typeof goalBackstopMaxSeconds === "number" &&
19
+ /**
20
+ * Delay of the goal-monitor backstop, in milliseconds.
21
+ *
22
+ * A live wake source normally resumes the goal when it delivers (the drain
23
+ * fire), but a wake source can be misconfigured - a filter that never matches,
24
+ * a stream that never ends - so the backstop is the floor underneath it: the
25
+ * goal re-checks at least this often instead of trusting the source for an
26
+ * hour. It is configured, never derived from the prompt-cache safe wait, through
27
+ * `promptCache.goalBackstopMaxSeconds`; a missing, non-finite, or non-positive
28
+ * setting falls back to the 270s default, and the result is clamped into
29
+ * [1s, 1h].
30
+ */
31
+ export function resolveGoalMonitorContinuationDelayMs(goalBackstopMaxSeconds) {
32
+ const configuredMs = typeof goalBackstopMaxSeconds === "number" &&
14
33
  Number.isFinite(goalBackstopMaxSeconds) &&
15
34
  goalBackstopMaxSeconds > 0
16
- ? Math.min(goalBackstopMaxSeconds * 1000, GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS)
17
- : GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS;
18
- return Math.max(GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS, Math.min(cacheSafeWaitSeconds * 1000, configuredCeilingMs));
35
+ ? goalBackstopMaxSeconds * 1000
36
+ : GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS;
37
+ return Math.max(GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS, Math.min(configuredMs, GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS));
19
38
  }
20
39
  export function createGoalCacheWarmScheduleData(options) {
21
40
  return {
@@ -1 +1 @@
1
- {"version":3,"file":"cache-warm.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, ProviderEnv } from \"@earendil-works/pi-ai\";\nimport { resolvePromptCacheTtlSeconds } from \"@earendil-works/pi-ai\";\nimport type { TokenUsageSnapshot } from \"./types.ts\";\n\n/** Custom session-entry type carrying the cache-warm continuation story. */\nexport const GOAL_CACHE_WARMUP_ENTRY_TYPE = \"goal-cache-warmup\";\n\nexport const GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS = 240_000;\nconst GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS = 1_000;\nconst GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS = 3_600_000;\n\nexport function resolveGoalMonitorContinuationDelayMs(\n\tcacheSafeWaitSeconds: number | undefined,\n\tgoalBackstopMaxSeconds?: number,\n): number {\n\tif (\n\t\ttypeof cacheSafeWaitSeconds !== \"number\" ||\n\t\t!Number.isFinite(cacheSafeWaitSeconds) ||\n\t\tcacheSafeWaitSeconds <= 0\n\t) {\n\t\treturn GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS;\n\t}\n\tconst configuredCeilingMs =\n\t\ttypeof goalBackstopMaxSeconds === \"number\" &&\n\t\tNumber.isFinite(goalBackstopMaxSeconds) &&\n\t\tgoalBackstopMaxSeconds > 0\n\t\t\t? Math.min(goalBackstopMaxSeconds * 1000, GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS)\n\t\t\t: GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS;\n\treturn Math.max(GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS, Math.min(cacheSafeWaitSeconds * 1000, configuredCeilingMs));\n}\n\n/** Cache context captured when a monitor-wait continuation is scheduled. */\nexport interface GoalCacheWarmMetrics {\n\t/** Prompt-cache TTL of the active model in seconds, when known. */\n\treadonly ttlSeconds?: number;\n\t/** Tokens sitting warm in the provider prompt cache after the last turn. */\n\treadonly cachedTokens: number;\n\t/** Estimated USD saved by re-reading those tokens from cache instead of paying a cold input read. */\n\treadonly estimatedSavedUsd?: number;\n}\n\nexport type GoalCacheWarmupPhase = \"scheduled\" | \"resumed\";\n\n/**\n * Durable payload appended as a `goal-cache-warmup` custom entry and carried by\n * the `goal_continuation_scheduled` / `goal_continuation_resumed` pi-events, so\n * external consumers (for example omo-desktop-app) can render the story later.\n */\nexport interface GoalCacheWarmupEntryData {\n\treadonly phase: GoalCacheWarmupPhase;\n\treadonly goalId: string;\n\t/** Display ordinal within the current in-memory Goal/wake epoch; absent on legacy persisted entries. */\n\treadonly iteration?: number;\n\t/** Planned continuation delay in milliseconds. */\n\treadonly delayMs: number;\n\t/** Epoch milliseconds when the scheduled continuation is expected to resume. */\n\treadonly dueAtMs?: number;\n\t/** Actual wait in milliseconds; present on the `resumed` phase only. */\n\treadonly waitedMs?: number;\n\t/** Backward-compatible field containing the total active wake-source count. */\n\treadonly activeMonitorCount: number;\n\t/** Full source-keyed snapshot; absent on entries written before wake sources were generalized. */\n\treadonly wakeSources?: Readonly<Record<string, number>>;\n\treadonly cache?: GoalCacheWarmMetrics;\n}\n\n/** Live entries always carry an iteration; the ordinal is intentionally not persisted into Goal state. */\nexport type LiveGoalCacheWarmupEntryData = GoalCacheWarmupEntryData & { readonly iteration: number };\n\nexport type GoalCacheWarmScheduleData = Omit<LiveGoalCacheWarmupEntryData, \"phase\" | \"waitedMs\">;\n\nexport function createGoalCacheWarmScheduleData(options: {\n\treadonly goalId: string;\n\treadonly delayMs: number;\n\treadonly scheduledAtMs: number;\n\treadonly iteration: number;\n\treadonly activeMonitorCount: number;\n\treadonly wakeSources: Readonly<Record<string, number>>;\n\treadonly cache?: GoalCacheWarmMetrics;\n}): GoalCacheWarmScheduleData {\n\treturn {\n\t\tgoalId: options.goalId,\n\t\tdelayMs: options.delayMs,\n\t\tdueAtMs: options.scheduledAtMs + options.delayMs,\n\t\titeration: options.iteration,\n\t\tactiveMonitorCount: options.activeMonitorCount,\n\t\twakeSources: options.wakeSources,\n\t\t...(options.cache !== undefined ? { cache: options.cache } : {}),\n\t};\n}\n\nconst TOKENS_PER_PRICE_UNIT = 1_000_000;\n\nexport function estimateCacheWarmMetrics(\n\tmodel: Model<Api> | undefined,\n\tenv: NodeJS.ProcessEnv,\n\tlastTurnUsage: Pick<TokenUsageSnapshot, \"cacheRead\" | \"cacheWrite\"> | undefined,\n): GoalCacheWarmMetrics | undefined {\n\tconst cachedTokens = clampTokens(lastTurnUsage?.cacheRead) + clampTokens(lastTurnUsage?.cacheWrite);\n\tconst ttlSeconds = model === undefined ? undefined : resolvePromptCacheTtlSeconds(model, toProviderEnv(env));\n\tif (ttlSeconds === undefined && cachedTokens === 0) return undefined;\n\tconst estimatedSavedUsd =\n\t\tmodel !== undefined && cachedTokens > 0\n\t\t\t? (Math.max(0, model.cost.input - model.cost.cacheRead) * cachedTokens) / TOKENS_PER_PRICE_UNIT\n\t\t\t: undefined;\n\treturn {\n\t\tcachedTokens,\n\t\t...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n\t\t...(estimatedSavedUsd !== undefined ? { estimatedSavedUsd } : {}),\n\t};\n}\n\nexport function formatWarmTokenCount(tokens: number): string {\n\tif (tokens >= 1_000_000) return `${trimTrailingZero((tokens / 1_000_000).toFixed(1))}M`;\n\tif (tokens >= 1000) return `${trimTrailingZero((tokens / 1000).toFixed(1))}K`;\n\treturn String(Math.max(0, Math.trunc(tokens)));\n}\n\nexport function formatWakeDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tconst restSeconds = seconds % 60;\n\tif (minutes < 60) return restSeconds === 0 ? `${minutes}m` : `${minutes}m ${restSeconds}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tconst restMinutes = minutes % 60;\n\treturn restMinutes === 0 ? `${hours}h` : `${hours}h ${restMinutes}m`;\n}\n\n/**\n * Wake timestamp shown in cache-warm notices. Rendered in the user's local\n * system timezone with its short zone label (for example `GMT+9`); falls back\n * to the legacy UTC shape when local timezone formatting is unavailable.\n */\nexport function formatWakeTimestamp(dueAtMs: number): string {\n\tconst date = new Date(dueAtMs);\n\tconst local = formatLocalWakeTimestamp(date);\n\tif (local !== undefined) return local;\n\treturn `${date.toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\nfunction formatLocalWakeTimestamp(date: Date): string | undefined {\n\ttry {\n\t\tconst parts = new Intl.DateTimeFormat(\"en-CA\", {\n\t\t\tyear: \"numeric\",\n\t\t\tmonth: \"2-digit\",\n\t\t\tday: \"2-digit\",\n\t\t\thour: \"2-digit\",\n\t\t\tminute: \"2-digit\",\n\t\t\thourCycle: \"h23\",\n\t\t\ttimeZoneName: \"short\",\n\t\t}).formatToParts(date);\n\t\tconst read = (type: string): string | undefined => parts.find((part) => part.type === type)?.value;\n\t\tconst year = read(\"year\");\n\t\tconst month = read(\"month\");\n\t\tconst day = read(\"day\");\n\t\tconst hour = read(\"hour\");\n\t\tconst minute = read(\"minute\");\n\t\tif (\n\t\t\tyear === undefined ||\n\t\t\tmonth === undefined ||\n\t\t\tday === undefined ||\n\t\t\thour === undefined ||\n\t\t\tminute === undefined\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst zone = read(\"timeZoneName\");\n\t\treturn `${year}-${month}-${day} ${hour}:${minute}${zone === undefined ? \"\" : ` ${zone}`}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function formatCacheTtl(seconds: number): string {\n\tif (seconds % 3600 === 0) return `${seconds / 3600}h`;\n\tif (seconds % 60 === 0) return `${seconds / 60}m`;\n\treturn `${seconds}s`;\n}\n\nexport function formatSavedUsd(value: number): string {\n\tif (value < 0.0005) return \"<$0.001\";\n\tif (value < 1) return `$${value.toFixed(3)}`;\n\treturn `$${value.toFixed(2)}`;\n}\n\nfunction trimTrailingZero(value: string): string {\n\treturn value.endsWith(\".0\") ? value.slice(0, -2) : value;\n}\n\nfunction clampTokens(value: number | undefined): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;\n}\n\nfunction toProviderEnv(env: NodeJS.ProcessEnv): ProviderEnv {\n\tconst resolved: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tif (value !== undefined) resolved[key] = value;\n\t}\n\treturn resolved;\n}\n"]}
1
+ {"version":3,"file":"cache-warm.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/cache-warm.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Api, Model, ProviderEnv } from \"@earendil-works/pi-ai\";\nimport { resolvePromptCacheTtlSeconds } from \"@earendil-works/pi-ai\";\nimport type { TokenUsageSnapshot } from \"./types.ts\";\n\n/** Custom session-entry type carrying the cache-warm continuation story. */\nexport const GOAL_CACHE_WARMUP_ENTRY_TYPE = \"goal-cache-warmup\";\n\n/**\n * Accounting fallback for a monitor continuation whose scheduled delay is no\n * longer known (a held timer restored across a reload). It is deliberately NOT\n * the armed delay: while a wake source is live the monitor arms the configured\n * backstop below, never a timer derived from the cache-safe wait.\n */\nexport const GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS = 240_000;\n/**\n * Default `promptCache.goalBackstopMaxSeconds` (settings-manager.ts) expressed in ms:\n * the 5-minute Anthropic prompt-cache TTL minus the 30s safety buffer, so the\n * periodic re-check lands inside the cache.\n */\nexport const GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS = 270_000;\nconst GOAL_MONITOR_CONTINUATION_MIN_DELAY_MS = 1_000;\nconst GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS = 3_600_000;\n\n/**\n * Delay of the goal-monitor backstop, in milliseconds.\n *\n * A live wake source normally resumes the goal when it delivers (the drain\n * fire), but a wake source can be misconfigured - a filter that never matches,\n * a stream that never ends - so the backstop is the floor underneath it: the\n * goal re-checks at least this often instead of trusting the source for an\n * hour. It is configured, never derived from the prompt-cache safe wait, through\n * `promptCache.goalBackstopMaxSeconds`; a missing, non-finite, or non-positive\n * setting falls back to the 270s default, and the result is clamped into\n * [1s, 1h].\n */\nexport function resolveGoalMonitorContinuationDelayMs(goalBackstopMaxSeconds: number | undefined): number {\n\tconst configuredMs =\n\t\ttypeof goalBackstopMaxSeconds === \"number\" &&\n\t\tNumber.isFinite(goalBackstopMaxSeconds) &&\n\t\tgoalBackstopMaxSeconds > 0\n\t\t\t? goalBackstopMaxSeconds * 1000\n\t\t\t: GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS;\n\treturn Math.max(\n\t\tGOAL_MONITOR_CONTINUATION_MIN_DELAY_MS,\n\t\tMath.min(configuredMs, GOAL_MONITOR_CONTINUATION_HARD_CEILING_MS),\n\t);\n}\n\n/** Cache context captured when a monitor-wait continuation is scheduled. */\nexport interface GoalCacheWarmMetrics {\n\t/** Prompt-cache TTL of the active model in seconds, when known. */\n\treadonly ttlSeconds?: number;\n\t/** Tokens sitting warm in the provider prompt cache after the last turn. */\n\treadonly cachedTokens: number;\n\t/** Estimated USD saved by re-reading those tokens from cache instead of paying a cold input read. */\n\treadonly estimatedSavedUsd?: number;\n}\n\nexport type GoalCacheWarmupPhase = \"scheduled\" | \"resumed\";\n\n/**\n * Durable payload appended as a `goal-cache-warmup` custom entry and carried by\n * the `goal_continuation_scheduled` / `goal_continuation_resumed` pi-events, so\n * external consumers (for example omo-desktop-app) can render the story later.\n */\nexport interface GoalCacheWarmupEntryData {\n\treadonly phase: GoalCacheWarmupPhase;\n\treadonly goalId: string;\n\t/** Display ordinal within the current in-memory Goal/wake epoch; absent on legacy persisted entries. */\n\treadonly iteration?: number;\n\t/** Planned continuation delay in milliseconds. */\n\treadonly delayMs: number;\n\t/** Epoch milliseconds when the scheduled continuation is expected to resume. */\n\treadonly dueAtMs?: number;\n\t/** Actual wait in milliseconds; present on the `resumed` phase only. */\n\treadonly waitedMs?: number;\n\t/** Backward-compatible field containing the total active wake-source count. */\n\treadonly activeMonitorCount: number;\n\t/** Full source-keyed snapshot; absent on entries written before wake sources were generalized. */\n\treadonly wakeSources?: Readonly<Record<string, number>>;\n\treadonly cache?: GoalCacheWarmMetrics;\n}\n\n/** Live entries always carry an iteration; the ordinal is intentionally not persisted into Goal state. */\nexport type LiveGoalCacheWarmupEntryData = GoalCacheWarmupEntryData & { readonly iteration: number };\n\nexport type GoalCacheWarmScheduleData = Omit<LiveGoalCacheWarmupEntryData, \"phase\" | \"waitedMs\">;\n\nexport function createGoalCacheWarmScheduleData(options: {\n\treadonly goalId: string;\n\treadonly delayMs: number;\n\treadonly scheduledAtMs: number;\n\treadonly iteration: number;\n\treadonly activeMonitorCount: number;\n\treadonly wakeSources: Readonly<Record<string, number>>;\n\treadonly cache?: GoalCacheWarmMetrics;\n}): GoalCacheWarmScheduleData {\n\treturn {\n\t\tgoalId: options.goalId,\n\t\tdelayMs: options.delayMs,\n\t\tdueAtMs: options.scheduledAtMs + options.delayMs,\n\t\titeration: options.iteration,\n\t\tactiveMonitorCount: options.activeMonitorCount,\n\t\twakeSources: options.wakeSources,\n\t\t...(options.cache !== undefined ? { cache: options.cache } : {}),\n\t};\n}\n\nconst TOKENS_PER_PRICE_UNIT = 1_000_000;\n\nexport function estimateCacheWarmMetrics(\n\tmodel: Model<Api> | undefined,\n\tenv: NodeJS.ProcessEnv,\n\tlastTurnUsage: Pick<TokenUsageSnapshot, \"cacheRead\" | \"cacheWrite\"> | undefined,\n): GoalCacheWarmMetrics | undefined {\n\tconst cachedTokens = clampTokens(lastTurnUsage?.cacheRead) + clampTokens(lastTurnUsage?.cacheWrite);\n\tconst ttlSeconds = model === undefined ? undefined : resolvePromptCacheTtlSeconds(model, toProviderEnv(env));\n\tif (ttlSeconds === undefined && cachedTokens === 0) return undefined;\n\tconst estimatedSavedUsd =\n\t\tmodel !== undefined && cachedTokens > 0\n\t\t\t? (Math.max(0, model.cost.input - model.cost.cacheRead) * cachedTokens) / TOKENS_PER_PRICE_UNIT\n\t\t\t: undefined;\n\treturn {\n\t\tcachedTokens,\n\t\t...(ttlSeconds !== undefined ? { ttlSeconds } : {}),\n\t\t...(estimatedSavedUsd !== undefined ? { estimatedSavedUsd } : {}),\n\t};\n}\n\nexport function formatWarmTokenCount(tokens: number): string {\n\tif (tokens >= 1_000_000) return `${trimTrailingZero((tokens / 1_000_000).toFixed(1))}M`;\n\tif (tokens >= 1000) return `${trimTrailingZero((tokens / 1000).toFixed(1))}K`;\n\treturn String(Math.max(0, Math.trunc(tokens)));\n}\n\nexport function formatWakeDuration(ms: number): string {\n\tconst seconds = Math.round(ms / 1000);\n\tif (seconds < 60) return `${seconds}s`;\n\tconst minutes = Math.floor(seconds / 60);\n\tconst restSeconds = seconds % 60;\n\tif (minutes < 60) return restSeconds === 0 ? `${minutes}m` : `${minutes}m ${restSeconds}s`;\n\tconst hours = Math.floor(minutes / 60);\n\tconst restMinutes = minutes % 60;\n\treturn restMinutes === 0 ? `${hours}h` : `${hours}h ${restMinutes}m`;\n}\n\n/**\n * Wake timestamp shown in cache-warm notices. Rendered in the user's local\n * system timezone with its short zone label (for example `GMT+9`); falls back\n * to the legacy UTC shape when local timezone formatting is unavailable.\n */\nexport function formatWakeTimestamp(dueAtMs: number): string {\n\tconst date = new Date(dueAtMs);\n\tconst local = formatLocalWakeTimestamp(date);\n\tif (local !== undefined) return local;\n\treturn `${date.toISOString().slice(0, 16).replace(\"T\", \" \")} UTC`;\n}\n\nfunction formatLocalWakeTimestamp(date: Date): string | undefined {\n\ttry {\n\t\tconst parts = new Intl.DateTimeFormat(\"en-CA\", {\n\t\t\tyear: \"numeric\",\n\t\t\tmonth: \"2-digit\",\n\t\t\tday: \"2-digit\",\n\t\t\thour: \"2-digit\",\n\t\t\tminute: \"2-digit\",\n\t\t\thourCycle: \"h23\",\n\t\t\ttimeZoneName: \"short\",\n\t\t}).formatToParts(date);\n\t\tconst read = (type: string): string | undefined => parts.find((part) => part.type === type)?.value;\n\t\tconst year = read(\"year\");\n\t\tconst month = read(\"month\");\n\t\tconst day = read(\"day\");\n\t\tconst hour = read(\"hour\");\n\t\tconst minute = read(\"minute\");\n\t\tif (\n\t\t\tyear === undefined ||\n\t\t\tmonth === undefined ||\n\t\t\tday === undefined ||\n\t\t\thour === undefined ||\n\t\t\tminute === undefined\n\t\t) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst zone = read(\"timeZoneName\");\n\t\treturn `${year}-${month}-${day} ${hour}:${minute}${zone === undefined ? \"\" : ` ${zone}`}`;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function formatCacheTtl(seconds: number): string {\n\tif (seconds % 3600 === 0) return `${seconds / 3600}h`;\n\tif (seconds % 60 === 0) return `${seconds / 60}m`;\n\treturn `${seconds}s`;\n}\n\nexport function formatSavedUsd(value: number): string {\n\tif (value < 0.0005) return \"<$0.001\";\n\tif (value < 1) return `$${value.toFixed(3)}`;\n\treturn `$${value.toFixed(2)}`;\n}\n\nfunction trimTrailingZero(value: string): string {\n\treturn value.endsWith(\".0\") ? value.slice(0, -2) : value;\n}\n\nfunction clampTokens(value: number | undefined): number {\n\treturn typeof value === \"number\" && Number.isFinite(value) && value > 0 ? Math.trunc(value) : 0;\n}\n\nfunction toProviderEnv(env: NodeJS.ProcessEnv): ProviderEnv {\n\tconst resolved: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(env)) {\n\t\tif (value !== undefined) resolved[key] = value;\n\t}\n\treturn resolved;\n}\n"]}
@@ -1,4 +1,4 @@
1
- import type { AgentMessage } from "@earendil-works/pi-agent-core";
1
+ import { type AgentMessage } from "@earendil-works/pi-agent-core";
2
2
  import type { Goal } from "./types.ts";
3
3
  type AssistantAgentMessage = Extract<AgentMessage, {
4
4
  role: "assistant";
@@ -22,6 +22,7 @@ export type GoalContinuationInput = {
22
22
  readonly hasPendingMessages: boolean;
23
23
  readonly path: GoalContinuationPath;
24
24
  readonly lastStopReason: AssistantAgentMessage["stopReason"] | undefined;
25
+ readonly lastTurnWasMalformedToolUse: boolean;
25
26
  readonly consecutiveContinuations: number;
26
27
  readonly lastContinuationSignature: string | undefined;
27
28
  readonly currentSignature: string | undefined;
@@ -42,6 +43,7 @@ export type GoalContinuationVerdict = {
42
43
  };
43
44
  export declare function shouldQueueGoalContinuationWhenIdle(goal: Goal | null, isIdle: boolean, hasPendingMessages: boolean): goal is Goal;
44
45
  export declare function shouldQueueGoalContinuationAfterAgentEnd(goal: Goal | null, hasPendingMessages: boolean, messages: readonly AgentMessage[]): goal is Goal;
46
+ export declare function isMalformedToolUseTurn(message: AssistantAgentMessage): boolean;
45
47
  export declare function evaluateGoalContinuation(input: GoalContinuationInput): GoalContinuationVerdict;
46
48
  /** Produces a stable, whitespace-insensitive representation for repetition detection. */
47
49
  export declare function normalizeAssistantText(text: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC } from "@earendil-works/pi-agent-core";
1
2
  export const GOAL_CONTINUATION_CAP = 8;
2
3
  export const GOAL_STALL_TOOLLESS_THRESHOLD = 3;
3
4
  export const GOAL_REPETITION_HASH_STREAK = 3;
@@ -21,7 +22,9 @@ function didAgentEndCleanly(messages) {
21
22
  if (lastAssistantIndex === undefined)
22
23
  return false;
23
24
  const lastAssistant = messages[lastAssistantIndex];
24
- if (lastAssistant?.role !== "assistant" || !isContinuableStopReason(lastAssistant.stopReason))
25
+ if (lastAssistant?.role !== "assistant")
26
+ return false;
27
+ if (!isContinuableStopReason(lastAssistant.stopReason) && !isMalformedToolUseTurn(lastAssistant))
25
28
  return false;
26
29
  for (let index = lastAssistantIndex + 1; index < messages.length; index++) {
27
30
  const message = messages[index];
@@ -42,6 +45,17 @@ function findLastAssistantMessageIndex(messages) {
42
45
  function isContinuableStopReason(stopReason) {
43
46
  return stopReason === "stop" || stopReason === "length";
44
47
  }
48
+ // The agent loop demotes a tool-call-less `toolUse` stop to `stop` before `agent_end`, so the
49
+ // original stop reason is gone by the time a goal sees the turn. Accept either shape: the raw
50
+ // message (extensions observing it pre-demotion) or the demotion diagnostic the loop leaves behind.
51
+ export function isMalformedToolUseTurn(message) {
52
+ if (message.content.some((content) => content.type === "toolCall"))
53
+ return false;
54
+ if (message.stopReason === "toolUse")
55
+ return true;
56
+ return (message.stopReason === "stop" &&
57
+ (message.diagnostics ?? []).some((diagnostic) => diagnostic.type === EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC));
58
+ }
45
59
  function isAbortedToolResult(message) {
46
60
  if (!message.isError)
47
61
  return false;
@@ -115,7 +129,8 @@ function isEligibleForGoalContinuation(input) {
115
129
  if (input.path === "systemRecovery" || input.path === "providerRecovery")
116
130
  return true;
117
131
  if (input.path === "immediate") {
118
- return input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason);
132
+ return (input.lastTurnWasMalformedToolUse ||
133
+ (input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason)));
119
134
  }
120
135
  return input.isIdle;
121
136
  }
@@ -1 +1 @@
1
- {"version":3,"file":"continuation.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { Goal } from \"./types.ts\";\n\ntype AssistantAgentMessage = Extract<AgentMessage, { role: \"assistant\" }>;\ntype ToolResultAgentMessage = Extract<AgentMessage, { role: \"toolResult\" }>;\n\nexport const GOAL_CONTINUATION_CAP = 8;\nexport const GOAL_STALL_TOOLLESS_THRESHOLD = 3;\nexport const GOAL_REPETITION_HASH_STREAK = 3;\nexport const GOAL_LENGTH_RECOVERY_LIMIT = 1;\n/**\n * Hard budget of automatic continuations without accepted direct user input (#1139).\n * Progress and tool use never refill it: only direct input does. Stays above the\n * #447 distinct-progress pin (50) and an 8-hour monitor-backstop cadence\n * (~120 deliveries at 240s), below the observed 289-continuation incident run.\n */\nexport const GOAL_UNATTENDED_CONTINUATION_LIMIT = 150;\nexport const GOAL_USER_GRACE_DELAY_MS = 10_000;\n\nexport type GoalContinuationPath =\n\t| \"immediate\"\n\t| \"monitorDelayed\"\n\t| \"userGrace\"\n\t| \"sessionStart\"\n\t| \"systemRecovery\"\n\t| \"providerRecovery\";\n\nexport type GoalContinuationInput = {\n\treadonly goal: Goal | null;\n\treadonly isIdle: boolean;\n\treadonly hasPendingMessages: boolean;\n\treadonly path: GoalContinuationPath;\n\treadonly lastStopReason: AssistantAgentMessage[\"stopReason\"] | undefined;\n\treadonly consecutiveContinuations: number;\n\treadonly lastContinuationSignature: string | undefined;\n\treadonly currentSignature: string | undefined;\n\treadonly consecutiveLengthRecoveries: number;\n\treadonly recentNormalizedOutputHashes: readonly string[];\n\treadonly toollessContinuationStreak: number;\n\treadonly continuationPending: boolean;\n\t/** The last turn was rejected (or silently starved) by context size and recovery did not shrink it. */\n\treadonly lastTurnStuckOnContextOverflow: boolean;\n};\n\nexport type GoalContinuationVerdict =\n\t| { kind: \"continue\"; prompt: \"full\" | \"minimal\"; stallNotice: boolean }\n\t| {\n\t\t\tkind: \"deny\";\n\t\t\treason:\n\t\t\t\t| \"not-eligible\"\n\t\t\t\t| \"single-flight\"\n\t\t\t\t| \"cap\"\n\t\t\t\t| \"stale\"\n\t\t\t\t| \"repetition\"\n\t\t\t\t| \"length-exhausted\"\n\t\t\t\t| \"unattended\"\n\t\t\t\t| \"context-overflow\";\n\t };\n\nexport function shouldQueueGoalContinuationWhenIdle(\n\tgoal: Goal | null,\n\tisIdle: boolean,\n\thasPendingMessages: boolean,\n): goal is Goal {\n\treturn goal?.status === \"active\" && isIdle && !hasPendingMessages;\n}\n\nexport function shouldQueueGoalContinuationAfterAgentEnd(\n\tgoal: Goal | null,\n\thasPendingMessages: boolean,\n\tmessages: readonly AgentMessage[],\n): goal is Goal {\n\treturn goal?.status === \"active\" && !hasPendingMessages && didAgentEndCleanly(messages);\n}\n\nfunction didAgentEndCleanly(messages: readonly AgentMessage[]): boolean {\n\tconst lastAssistantIndex = findLastAssistantMessageIndex(messages);\n\tif (lastAssistantIndex === undefined) return false;\n\n\tconst lastAssistant = messages[lastAssistantIndex];\n\tif (lastAssistant?.role !== \"assistant\" || !isContinuableStopReason(lastAssistant.stopReason)) return false;\n\n\tfor (let index = lastAssistantIndex + 1; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"toolResult\" && isAbortedToolResult(message)) return false;\n\t}\n\treturn true;\n}\n\nfunction findLastAssistantMessageIndex(messages: readonly AgentMessage[]): number | undefined {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") {\n\t\t\treturn index;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isContinuableStopReason(stopReason: AssistantAgentMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\";\n}\n\nfunction isAbortedToolResult(message: ToolResultAgentMessage): boolean {\n\tif (!message.isError) return false;\n\treturn message.content.some((content) => content.type === \"text\" && /\\babort(?:ed)?\\b/i.test(content.text));\n}\n\nexport function evaluateGoalContinuation(input: GoalContinuationInput): GoalContinuationVerdict {\n\t// Re-sending an overflowed context fails identically on every path (#1422).\n\tif (input.goal?.status === \"active\" && input.lastTurnStuckOnContextOverflow) {\n\t\treturn { kind: \"deny\", reason: \"context-overflow\" };\n\t}\n\tif (!isEligibleForGoalContinuation(input)) return { kind: \"deny\", reason: \"not-eligible\" };\n\tif (input.continuationPending) return { kind: \"deny\", reason: \"single-flight\" };\n\tif (hasRepeatedNormalizedOutputHash(input.recentNormalizedOutputHashes))\n\t\treturn { kind: \"deny\", reason: \"repetition\" };\n\tif (isUnattendedBudgetExhausted(input)) return { kind: \"deny\", reason: \"unattended\" };\n\tif (input.consecutiveContinuations >= GOAL_CONTINUATION_CAP) {\n\t\treturn { kind: \"deny\", reason: \"cap\" };\n\t}\n\tif (\n\t\t(input.path === \"immediate\" || input.path === \"userGrace\") &&\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.lastContinuationSignature === input.currentSignature\n\t) {\n\t\treturn { kind: \"deny\", reason: \"stale\" };\n\t}\n\tif (input.lastStopReason === \"length\") {\n\t\tif (input.consecutiveLengthRecoveries >= GOAL_LENGTH_RECOVERY_LIMIT) {\n\t\t\treturn { kind: \"deny\", reason: \"length-exhausted\" };\n\t\t}\n\t\treturn { kind: \"continue\", prompt: \"minimal\", stallNotice: shouldShowStallNotice(input) };\n\t}\n\treturn { kind: \"continue\", prompt: \"full\", stallNotice: shouldShowStallNotice(input) };\n}\n\n/** Produces a stable, whitespace-insensitive representation for repetition detection. */\nexport function normalizeAssistantText(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/** Hashes normalized assistant text with the deterministic 32-bit FNV-1a algorithm. */\nexport function hashAssistantText(text: string): string {\n\tconst normalized = normalizeAssistantText(text);\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < normalized.length; index++) {\n\t\thash ^= normalized.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Identifies the observable goal progress for stale-continuation admission.\n * Deliberately excludes goal.updatedAt because usage accounting updates it after every agent turn.\n */\nexport function buildGoalContinuationSignature(\n\tgoal: Pick<Goal, \"id\">,\n\topenTodos: number,\n\ttotalTodos: number,\n\tlastAssistantTextHash: string,\n): string {\n\treturn `${goal.id}:${openTodos}/${totalTodos}:${lastAssistantTextHash}`;\n}\n\nexport function hasGoalContinuationProgress(\n\tinput: Pick<GoalContinuationInput, \"lastContinuationSignature\" | \"currentSignature\">,\n): boolean {\n\treturn (\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.currentSignature !== undefined &&\n\t\tinput.lastContinuationSignature !== input.currentSignature\n\t);\n}\n\nexport function continuationTurnUsedTools(messages: readonly AgentMessage[]): boolean {\n\treturn messages.some((message) => {\n\t\tif (message?.role === \"toolResult\") return true;\n\t\treturn message?.role === \"assistant\" && message.content.some((content) => content.type === \"toolCall\");\n\t});\n}\n\nfunction isEligibleForGoalContinuation(input: GoalContinuationInput): boolean {\n\tif (input.goal?.status !== \"active\" || input.hasPendingMessages) return false;\n\tif (input.path === \"systemRecovery\" || input.path === \"providerRecovery\") return true;\n\tif (input.path === \"immediate\") {\n\t\treturn input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason);\n\t}\n\treturn input.isIdle;\n}\n\n/** Monitor-delayed deliveries are exempt: armed-wake waiting is by-design and rate-limited by the cache-aware timer. */\nfunction isUnattendedBudgetExhausted(input: GoalContinuationInput): boolean {\n\tif (input.path === \"monitorDelayed\") return false;\n\treturn (input.goal?.unattendedContinuations ?? 0) >= GOAL_UNATTENDED_CONTINUATION_LIMIT;\n}\n\nfunction hasRepeatedNormalizedOutputHash(hashes: readonly string[]): boolean {\n\tconst latestHash = hashes.at(-1);\n\tif (latestHash === undefined) return false;\n\n\tlet streak = 0;\n\tfor (let index = hashes.length - 1; index >= 0 && hashes[index] === latestHash; index--) {\n\t\tstreak += 1;\n\t}\n\treturn streak >= GOAL_REPETITION_HASH_STREAK;\n}\n\nfunction shouldShowStallNotice(input: GoalContinuationInput): boolean {\n\treturn input.toollessContinuationStreak >= GOAL_STALL_TOOLLESS_THRESHOLD;\n}\n"]}
1
+ {"version":3,"file":"continuation.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/continuation.ts"],"names":[],"mappings":"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","sourcesContent":["import { type AgentMessage, EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC } from \"@earendil-works/pi-agent-core\";\nimport type { Goal } from \"./types.ts\";\n\ntype AssistantAgentMessage = Extract<AgentMessage, { role: \"assistant\" }>;\ntype ToolResultAgentMessage = Extract<AgentMessage, { role: \"toolResult\" }>;\n\nexport const GOAL_CONTINUATION_CAP = 8;\nexport const GOAL_STALL_TOOLLESS_THRESHOLD = 3;\nexport const GOAL_REPETITION_HASH_STREAK = 3;\nexport const GOAL_LENGTH_RECOVERY_LIMIT = 1;\n/**\n * Hard budget of automatic continuations without accepted direct user input (#1139).\n * Progress and tool use never refill it: only direct input does. Stays above the\n * #447 distinct-progress pin (50) and an 8-hour monitor-backstop cadence\n * (~120 deliveries at 240s), below the observed 289-continuation incident run.\n */\nexport const GOAL_UNATTENDED_CONTINUATION_LIMIT = 150;\nexport const GOAL_USER_GRACE_DELAY_MS = 10_000;\n\nexport type GoalContinuationPath =\n\t| \"immediate\"\n\t| \"monitorDelayed\"\n\t| \"userGrace\"\n\t| \"sessionStart\"\n\t| \"systemRecovery\"\n\t| \"providerRecovery\";\n\nexport type GoalContinuationInput = {\n\treadonly goal: Goal | null;\n\treadonly isIdle: boolean;\n\treadonly hasPendingMessages: boolean;\n\treadonly path: GoalContinuationPath;\n\treadonly lastStopReason: AssistantAgentMessage[\"stopReason\"] | undefined;\n\treadonly lastTurnWasMalformedToolUse: boolean;\n\treadonly consecutiveContinuations: number;\n\treadonly lastContinuationSignature: string | undefined;\n\treadonly currentSignature: string | undefined;\n\treadonly consecutiveLengthRecoveries: number;\n\treadonly recentNormalizedOutputHashes: readonly string[];\n\treadonly toollessContinuationStreak: number;\n\treadonly continuationPending: boolean;\n\t/** The last turn was rejected (or silently starved) by context size and recovery did not shrink it. */\n\treadonly lastTurnStuckOnContextOverflow: boolean;\n};\n\nexport type GoalContinuationVerdict =\n\t| { kind: \"continue\"; prompt: \"full\" | \"minimal\"; stallNotice: boolean }\n\t| {\n\t\t\tkind: \"deny\";\n\t\t\treason:\n\t\t\t\t| \"not-eligible\"\n\t\t\t\t| \"single-flight\"\n\t\t\t\t| \"cap\"\n\t\t\t\t| \"stale\"\n\t\t\t\t| \"repetition\"\n\t\t\t\t| \"length-exhausted\"\n\t\t\t\t| \"unattended\"\n\t\t\t\t| \"context-overflow\";\n\t };\n\nexport function shouldQueueGoalContinuationWhenIdle(\n\tgoal: Goal | null,\n\tisIdle: boolean,\n\thasPendingMessages: boolean,\n): goal is Goal {\n\treturn goal?.status === \"active\" && isIdle && !hasPendingMessages;\n}\n\nexport function shouldQueueGoalContinuationAfterAgentEnd(\n\tgoal: Goal | null,\n\thasPendingMessages: boolean,\n\tmessages: readonly AgentMessage[],\n): goal is Goal {\n\treturn goal?.status === \"active\" && !hasPendingMessages && didAgentEndCleanly(messages);\n}\n\nfunction didAgentEndCleanly(messages: readonly AgentMessage[]): boolean {\n\tconst lastAssistantIndex = findLastAssistantMessageIndex(messages);\n\tif (lastAssistantIndex === undefined) return false;\n\n\tconst lastAssistant = messages[lastAssistantIndex];\n\tif (lastAssistant?.role !== \"assistant\") return false;\n\tif (!isContinuableStopReason(lastAssistant.stopReason) && !isMalformedToolUseTurn(lastAssistant)) return false;\n\n\tfor (let index = lastAssistantIndex + 1; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"toolResult\" && isAbortedToolResult(message)) return false;\n\t}\n\treturn true;\n}\n\nfunction findLastAssistantMessageIndex(messages: readonly AgentMessage[]): number | undefined {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") {\n\t\t\treturn index;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nfunction isContinuableStopReason(stopReason: AssistantAgentMessage[\"stopReason\"]): boolean {\n\treturn stopReason === \"stop\" || stopReason === \"length\";\n}\n\n// The agent loop demotes a tool-call-less `toolUse` stop to `stop` before `agent_end`, so the\n// original stop reason is gone by the time a goal sees the turn. Accept either shape: the raw\n// message (extensions observing it pre-demotion) or the demotion diagnostic the loop leaves behind.\nexport function isMalformedToolUseTurn(message: AssistantAgentMessage): boolean {\n\tif (message.content.some((content) => content.type === \"toolCall\")) return false;\n\tif (message.stopReason === \"toolUse\") return true;\n\treturn (\n\t\tmessage.stopReason === \"stop\" &&\n\t\t(message.diagnostics ?? []).some((diagnostic) => diagnostic.type === EMPTY_TOOL_USE_DEMOTION_DIAGNOSTIC)\n\t);\n}\n\nfunction isAbortedToolResult(message: ToolResultAgentMessage): boolean {\n\tif (!message.isError) return false;\n\treturn message.content.some((content) => content.type === \"text\" && /\\babort(?:ed)?\\b/i.test(content.text));\n}\n\nexport function evaluateGoalContinuation(input: GoalContinuationInput): GoalContinuationVerdict {\n\t// Re-sending an overflowed context fails identically on every path (#1422).\n\tif (input.goal?.status === \"active\" && input.lastTurnStuckOnContextOverflow) {\n\t\treturn { kind: \"deny\", reason: \"context-overflow\" };\n\t}\n\tif (!isEligibleForGoalContinuation(input)) return { kind: \"deny\", reason: \"not-eligible\" };\n\tif (input.continuationPending) return { kind: \"deny\", reason: \"single-flight\" };\n\tif (hasRepeatedNormalizedOutputHash(input.recentNormalizedOutputHashes))\n\t\treturn { kind: \"deny\", reason: \"repetition\" };\n\tif (isUnattendedBudgetExhausted(input)) return { kind: \"deny\", reason: \"unattended\" };\n\tif (input.consecutiveContinuations >= GOAL_CONTINUATION_CAP) {\n\t\treturn { kind: \"deny\", reason: \"cap\" };\n\t}\n\tif (\n\t\t(input.path === \"immediate\" || input.path === \"userGrace\") &&\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.lastContinuationSignature === input.currentSignature\n\t) {\n\t\treturn { kind: \"deny\", reason: \"stale\" };\n\t}\n\tif (input.lastStopReason === \"length\") {\n\t\tif (input.consecutiveLengthRecoveries >= GOAL_LENGTH_RECOVERY_LIMIT) {\n\t\t\treturn { kind: \"deny\", reason: \"length-exhausted\" };\n\t\t}\n\t\treturn { kind: \"continue\", prompt: \"minimal\", stallNotice: shouldShowStallNotice(input) };\n\t}\n\treturn { kind: \"continue\", prompt: \"full\", stallNotice: shouldShowStallNotice(input) };\n}\n\n/** Produces a stable, whitespace-insensitive representation for repetition detection. */\nexport function normalizeAssistantText(text: string): string {\n\treturn text.toLowerCase().replace(/\\s+/g, \" \").trim();\n}\n\n/** Hashes normalized assistant text with the deterministic 32-bit FNV-1a algorithm. */\nexport function hashAssistantText(text: string): string {\n\tconst normalized = normalizeAssistantText(text);\n\tlet hash = 0x811c9dc5;\n\tfor (let index = 0; index < normalized.length; index++) {\n\t\thash ^= normalized.charCodeAt(index);\n\t\thash = Math.imul(hash, 0x01000193);\n\t}\n\treturn (hash >>> 0).toString(16).padStart(8, \"0\");\n}\n\n/**\n * Identifies the observable goal progress for stale-continuation admission.\n * Deliberately excludes goal.updatedAt because usage accounting updates it after every agent turn.\n */\nexport function buildGoalContinuationSignature(\n\tgoal: Pick<Goal, \"id\">,\n\topenTodos: number,\n\ttotalTodos: number,\n\tlastAssistantTextHash: string,\n): string {\n\treturn `${goal.id}:${openTodos}/${totalTodos}:${lastAssistantTextHash}`;\n}\n\nexport function hasGoalContinuationProgress(\n\tinput: Pick<GoalContinuationInput, \"lastContinuationSignature\" | \"currentSignature\">,\n): boolean {\n\treturn (\n\t\tinput.lastContinuationSignature !== undefined &&\n\t\tinput.currentSignature !== undefined &&\n\t\tinput.lastContinuationSignature !== input.currentSignature\n\t);\n}\n\nexport function continuationTurnUsedTools(messages: readonly AgentMessage[]): boolean {\n\treturn messages.some((message) => {\n\t\tif (message?.role === \"toolResult\") return true;\n\t\treturn message?.role === \"assistant\" && message.content.some((content) => content.type === \"toolCall\");\n\t});\n}\n\nfunction isEligibleForGoalContinuation(input: GoalContinuationInput): boolean {\n\tif (input.goal?.status !== \"active\" || input.hasPendingMessages) return false;\n\tif (input.path === \"systemRecovery\" || input.path === \"providerRecovery\") return true;\n\tif (input.path === \"immediate\") {\n\t\treturn (\n\t\t\tinput.lastTurnWasMalformedToolUse ||\n\t\t\t(input.lastStopReason !== undefined && isContinuableStopReason(input.lastStopReason))\n\t\t);\n\t}\n\treturn input.isIdle;\n}\n\n/** Monitor-delayed deliveries are exempt: armed-wake waiting is by-design and rate-limited by the cache-aware timer. */\nfunction isUnattendedBudgetExhausted(input: GoalContinuationInput): boolean {\n\tif (input.path === \"monitorDelayed\") return false;\n\treturn (input.goal?.unattendedContinuations ?? 0) >= GOAL_UNATTENDED_CONTINUATION_LIMIT;\n}\n\nfunction hasRepeatedNormalizedOutputHash(hashes: readonly string[]): boolean {\n\tconst latestHash = hashes.at(-1);\n\tif (latestHash === undefined) return false;\n\n\tlet streak = 0;\n\tfor (let index = hashes.length - 1; index >= 0 && hashes[index] === latestHash; index--) {\n\t\tstreak += 1;\n\t}\n\treturn streak >= GOAL_REPETITION_HASH_STREAK;\n}\n\nfunction shouldShowStallNotice(input: GoalContinuationInput): boolean {\n\treturn input.toollessContinuationStreak >= GOAL_STALL_TOOLLESS_THRESHOLD;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-helpers.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAGN,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,mBAAmB,CAAC;AAa3B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEhG,KAAK,+BAA+B,GAAG;IACtC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iCAAiC,KAAK,MAAM,CAAC;IACzE,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,GAAG,EAAE,gBAAgB,EACrB,kBAAkB,EAAE,MAAM,EAC1B,IAAI,EAAE,IAAI,GAAG,IAAI,GACf,IAAI,IAAI,IAAI,CASd;AAED,sFAAsF;AACtF,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB;AAED,mHAAmH;AACnH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA2BtB;AAED,wBAAgB,qCAAqC,CACpD,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,iBAAiB,EAAE,MAAM,GACvB,MAAM,CAKR;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAM3E;AAiBD,wBAAgB,gCAAgC,CAC/C,GAAG,EAAE,gBAAgB,EACrB,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,GAAG,SAAS,GACrE,OAAO,CAET;AAuDD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK7E"}
1
+ {"version":3,"file":"lifecycle-helpers.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAIlE,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAErE,OAAO,EAGN,KAAK,qBAAqB,EAC1B,KAAK,uBAAuB,EAE5B,MAAM,mBAAmB,CAAC;AAa3B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,KAAK,iCAAiC,GAAG,OAAO,CAAC,uBAAuB,EAAE;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,CAAC,CAAC;AAEhG,KAAK,+BAA+B,GAAG;IACtC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC,qBAAqB,EAAE,MAAM,CAAC,CAAC;IACpD,QAAQ,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,iCAAiC,KAAK,MAAM,CAAC;IACzE,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC7C,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;IACtC,QAAQ,CAAC,uBAAuB,EAAE,MAAM,IAAI,CAAC;CAC7C,CAAC;AAQF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACpC,GAAG,EAAE,gBAAgB,EACrB,kBAAkB,EAAE,MAAM,EAC1B,IAAI,EAAE,IAAI,GAAG,IAAI,GACf,IAAI,IAAI,IAAI,CASd;AAED,sFAAsF;AACtF,wBAAsB,6BAA6B,CAClD,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAiBtB;AAED,mHAAmH;AACnH,wBAAsB,qBAAqB,CAC1C,EAAE,EAAE,YAAY,EAChB,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,OAAO,EAAE,+BAA+B,GACtC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA4BtB;AAED,wBAAgB,qCAAqC,CACpD,GAAG,EAAE,gBAAgB,EACrB,IAAI,EAAE,IAAI,EACV,iBAAiB,EAAE,MAAM,GACvB,MAAM,CAKR;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAM3E;AAiBD,wBAAgB,gCAAgC,CAC/C,GAAG,EAAE,gBAAgB,EACrB,aAAa,EAAE,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,GAAG,SAAS,GACrE,OAAO,CAET;AAuDD,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAK7E"}
@@ -50,6 +50,7 @@ export async function queueGoalContinuation(pi, ctx, goal, options) {
50
50
  hasPendingMessages: ctx.hasPendingMessages(),
51
51
  path: "sessionStart",
52
52
  lastStopReason: undefined,
53
+ lastTurnWasMalformedToolUse: false,
53
54
  consecutiveContinuations: goal.consecutiveContinuations ?? 0,
54
55
  lastContinuationSignature: goal.lastContinuationSignature,
55
56
  currentSignature: signature,
@@ -1 +1 @@
1
- {"version":3,"file":"lifecycle-helpers.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { isTurnStuckOnContextOverflow } from \"../../../compaction/stuck-overflow.ts\";\nimport { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { getLatestPhasesFromBranchEntries } from \"../todotools/state.ts\";\nimport {\n\tbuildGoalContinuationSignature,\n\tevaluateGoalContinuation,\n\ttype GoalContinuationInput,\n\ttype GoalContinuationVerdict,\n\thashAssistantText,\n} from \"./continuation.ts\";\nimport {\n\tCONTEXT_OVERFLOW_BLOCKED_REASON,\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tcontinuationCapRecoveryHint,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n} from \"./continuation-recovery.ts\";\nimport { buildContinuationPrompt } from \"./prompt.ts\";\nimport { recordContinuationDelivered, updateGoal } from \"./store.ts\";\nimport { goalStoreRef } from \"./store-ref.ts\";\nimport { openTodoTaskContents } from \"./todo-gate.ts\";\nimport type { Goal } from \"./types.ts\";\n\ntype ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\n\ntype GoalContinuationDeliveryOptions = {\n\treadonly input: Omit<GoalContinuationInput, \"goal\">;\n\treadonly content: (verdict: ContinuingGoalContinuationVerdict) => string;\n\treadonly markContinuationPending: () => void;\n};\n\nexport type SessionStartContinuationOptions = {\n\treadonly continuationPending: boolean;\n\treadonly markContinuationPending: () => void;\n};\n\n/**\n * Statuses that leave a goal stopped without finishing it. A restart is the only\n * moment the user can act on them, so both earn the resume prompt.\n */\nconst STOPPED_UNFINISHED_GOAL_STATUSES: readonly Goal[\"status\"][] = [\"paused\", \"blocked\"];\n\n/**\n * Mirrors codex `maybe_prompt_resume_paused_goal_after_resume`\n * (codex-rs/tui/src/app/thread_goal_actions.rs), which prompts on resume for every\n * stopped-but-unfinished status. senpi is budget-free, so codex's `UsageLimited`\n * has no counterpart here and the stopped set is `paused | blocked`.\n */\nexport function isResumeOfStoppedGoal(\n\tctx: ExtensionContext,\n\tsessionStartReason: string,\n\tgoal: Goal | null,\n): goal is Goal {\n\treturn (\n\t\tsessionStartReason === \"resume\" &&\n\t\tgoal !== null &&\n\t\tSTOPPED_UNFINISHED_GOAL_STATUSES.includes(goal.status) &&\n\t\tctx.hasUI &&\n\t\tctx.isIdle() &&\n\t\t!ctx.hasPendingMessages()\n\t);\n}\n\n/** Evaluates and delivers a continuation only after all guardrail checks admit it. */\nexport async function admitAndQueueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: GoalContinuationDeliveryOptions,\n): Promise<Goal | null> {\n\tconst verdict = evaluateGoalContinuation({ goal, ...options.input });\n\tif (verdict.kind === \"deny\") return handleDeniedContinuation(pi, ctx, goal, options.input, verdict.reason);\n\n\tif (options.input.currentSignature === undefined) {\n\t\tthrow new Error(\"Cannot queue a goal continuation without a progress signature\");\n\t}\n\tconst recordedGoal = await recordContinuationDelivered(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\toptions.input.currentSignature,\n\t\tgoal.id,\n\t\t{ countUnattended: options.input.path !== \"monitorDelayed\" },\n\t);\n\tif (recordedGoal === null) return null;\n\toptions.markContinuationPending();\n\tqueueHiddenGoalPrompt(pi, options.content(verdict));\n\treturn recordedGoal;\n}\n\n/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */\nexport async function queueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: SessionStartContinuationOptions,\n): Promise<Goal | null> {\n\tconst signature = buildCurrentGoalContinuationSignature(\n\t\tctx,\n\t\tgoal,\n\t\tlastAssistantTextFromEntries(ctx.sessionManager.getBranch()),\n\t);\n\treturn admitAndQueueGoalContinuation(pi, ctx, goal, {\n\t\tinput: {\n\t\t\tisIdle: ctx.isIdle(),\n\t\t\thasPendingMessages: ctx.hasPendingMessages(),\n\t\t\tpath: \"sessionStart\",\n\t\t\tlastStopReason: undefined,\n\t\t\tconsecutiveContinuations: goal.consecutiveContinuations ?? 0,\n\t\t\tlastContinuationSignature: goal.lastContinuationSignature,\n\t\t\tcurrentSignature: signature,\n\t\t\tconsecutiveLengthRecoveries: 0,\n\t\t\trecentNormalizedOutputHashes: [],\n\t\t\ttoollessContinuationStreak: 0,\n\t\t\tcontinuationPending: options.continuationPending,\n\t\t\tlastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(\n\t\t\t\tctx,\n\t\t\t\tlastAssistantFromEntries(ctx.sessionManager.getBranch()),\n\t\t\t),\n\t\t},\n\t\tcontent: () => buildContinuationPrompt(goal),\n\t\tmarkContinuationPending: options.markContinuationPending,\n\t});\n}\n\nexport function buildCurrentGoalContinuationSignature(\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tlastAssistantText: string,\n): string {\n\tconst entries = ctx.sessionManager.getBranch();\n\tconst openTodos = openTodoTaskContents(entries).length;\n\tconst totalTodos = getLatestPhasesFromBranchEntries(entries).reduce((count, phase) => count + phase.tasks.length, 0);\n\treturn buildGoalContinuationSignature(goal, openTodos, totalTodos, hashAssistantText(lastAssistantText));\n}\n\nexport function lastAssistantText(messages: readonly AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") return textContent(message);\n\t}\n\treturn \"\";\n}\n\nfunction lastAssistantTextFromEntries(entries: readonly SessionEntry[]): string {\n\tconst assistant = lastAssistantFromEntries(entries);\n\treturn assistant === undefined ? \"\" : textContent(assistant);\n}\n\nfunction lastAssistantFromEntries(\n\tentries: readonly SessionEntry[],\n): Extract<AgentMessage, { role: \"assistant\" }> | undefined {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"assistant\") return entry.message;\n\t}\n\treturn undefined;\n}\n\nexport function isLastTurnStuckOnContextOverflow(\n\tctx: ExtensionContext,\n\tlastAssistant: Extract<AgentMessage, { role: \"assistant\" }> | undefined,\n): boolean {\n\treturn lastAssistant !== undefined && isTurnStuckOnContextOverflow(lastAssistant, ctx.model?.contextWindow ?? 0);\n}\n\nfunction textContent(message: Extract<AgentMessage, { role: \"assistant\" }>): string {\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\\n\");\n}\n\nasync function handleDeniedContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tinput: Omit<GoalContinuationInput, \"goal\">,\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): Promise<Goal> {\n\tconst blockedReason = blockedReasonForContinuationGuard(reason);\n\tif (blockedReason === undefined) return goal;\n\n\tconst blocked = await updateGoal(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\t{ status: \"blocked\", reason: blockedReason },\n\t\t\"model\",\n\t);\n\tif (ctx.hasUI) ctx.ui.notify(continuationCapRecoveryHint(blockedReason), \"warning\");\n\tpi.events?.emit(\"goal_continuation_guard_tripped\", {\n\t\tgoalId: goal.id,\n\t\treason,\n\t\tcount: input.consecutiveContinuations,\n\t\tunattendedContinuations: goal.unattendedContinuations ?? 0,\n\t});\n\treturn blocked;\n}\n\nfunction blockedReasonForContinuationGuard(\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): string | undefined {\n\tswitch (reason) {\n\t\tcase \"cap\":\n\t\t\treturn CONTINUATION_CAP_BLOCKED_REASON;\n\t\tcase \"unattended\":\n\t\t\treturn UNATTENDED_CONTINUATION_BLOCKED_REASON;\n\t\tcase \"repetition\":\n\t\t\treturn REPETITION_BLOCKED_REASON;\n\t\tcase \"length-exhausted\":\n\t\t\treturn LENGTH_EXHAUSTED_BLOCKED_REASON;\n\t\tcase \"context-overflow\":\n\t\t\treturn CONTEXT_OVERFLOW_BLOCKED_REASON;\n\t\tcase \"not-eligible\":\n\t\tcase \"single-flight\":\n\t\tcase \"stale\":\n\t\t\treturn undefined;\n\t}\n}\n\nexport function queueHiddenGoalPrompt(pi: ExtensionAPI, content: string): void {\n\tpi.sendMessage(\n\t\t{ customType: GOAL_CONTINUATION_MESSAGE_TYPE, content, display: false },\n\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t);\n}\n"]}
1
+ {"version":3,"file":"lifecycle-helpers.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/lifecycle-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { isTurnStuckOnContextOverflow } from \"../../../compaction/stuck-overflow.ts\";\nimport { GOAL_CONTINUATION_MESSAGE_TYPE } from \"../../../messages.ts\";\nimport type { SessionEntry } from \"../../../session-manager.ts\";\nimport type { ExtensionAPI, ExtensionContext } from \"../../types.ts\";\nimport { getLatestPhasesFromBranchEntries } from \"../todotools/state.ts\";\nimport {\n\tbuildGoalContinuationSignature,\n\tevaluateGoalContinuation,\n\ttype GoalContinuationInput,\n\ttype GoalContinuationVerdict,\n\thashAssistantText,\n} from \"./continuation.ts\";\nimport {\n\tCONTEXT_OVERFLOW_BLOCKED_REASON,\n\tCONTINUATION_CAP_BLOCKED_REASON,\n\tcontinuationCapRecoveryHint,\n\tLENGTH_EXHAUSTED_BLOCKED_REASON,\n\tREPETITION_BLOCKED_REASON,\n\tUNATTENDED_CONTINUATION_BLOCKED_REASON,\n} from \"./continuation-recovery.ts\";\nimport { buildContinuationPrompt } from \"./prompt.ts\";\nimport { recordContinuationDelivered, updateGoal } from \"./store.ts\";\nimport { goalStoreRef } from \"./store-ref.ts\";\nimport { openTodoTaskContents } from \"./todo-gate.ts\";\nimport type { Goal } from \"./types.ts\";\n\ntype ContinuingGoalContinuationVerdict = Extract<GoalContinuationVerdict, { kind: \"continue\" }>;\n\ntype GoalContinuationDeliveryOptions = {\n\treadonly input: Omit<GoalContinuationInput, \"goal\">;\n\treadonly content: (verdict: ContinuingGoalContinuationVerdict) => string;\n\treadonly markContinuationPending: () => void;\n};\n\nexport type SessionStartContinuationOptions = {\n\treadonly continuationPending: boolean;\n\treadonly markContinuationPending: () => void;\n};\n\n/**\n * Statuses that leave a goal stopped without finishing it. A restart is the only\n * moment the user can act on them, so both earn the resume prompt.\n */\nconst STOPPED_UNFINISHED_GOAL_STATUSES: readonly Goal[\"status\"][] = [\"paused\", \"blocked\"];\n\n/**\n * Mirrors codex `maybe_prompt_resume_paused_goal_after_resume`\n * (codex-rs/tui/src/app/thread_goal_actions.rs), which prompts on resume for every\n * stopped-but-unfinished status. senpi is budget-free, so codex's `UsageLimited`\n * has no counterpart here and the stopped set is `paused | blocked`.\n */\nexport function isResumeOfStoppedGoal(\n\tctx: ExtensionContext,\n\tsessionStartReason: string,\n\tgoal: Goal | null,\n): goal is Goal {\n\treturn (\n\t\tsessionStartReason === \"resume\" &&\n\t\tgoal !== null &&\n\t\tSTOPPED_UNFINISHED_GOAL_STATUSES.includes(goal.status) &&\n\t\tctx.hasUI &&\n\t\tctx.isIdle() &&\n\t\t!ctx.hasPendingMessages()\n\t);\n}\n\n/** Evaluates and delivers a continuation only after all guardrail checks admit it. */\nexport async function admitAndQueueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: GoalContinuationDeliveryOptions,\n): Promise<Goal | null> {\n\tconst verdict = evaluateGoalContinuation({ goal, ...options.input });\n\tif (verdict.kind === \"deny\") return handleDeniedContinuation(pi, ctx, goal, options.input, verdict.reason);\n\n\tif (options.input.currentSignature === undefined) {\n\t\tthrow new Error(\"Cannot queue a goal continuation without a progress signature\");\n\t}\n\tconst recordedGoal = await recordContinuationDelivered(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\toptions.input.currentSignature,\n\t\tgoal.id,\n\t\t{ countUnattended: options.input.path !== \"monitorDelayed\" },\n\t);\n\tif (recordedGoal === null) return null;\n\toptions.markContinuationPending();\n\tqueueHiddenGoalPrompt(pi, options.content(verdict));\n\treturn recordedGoal;\n}\n\n/** Routes startup and resume continuations through the same verdict and delivery accounting as agent-end paths. */\nexport async function queueGoalContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\toptions: SessionStartContinuationOptions,\n): Promise<Goal | null> {\n\tconst signature = buildCurrentGoalContinuationSignature(\n\t\tctx,\n\t\tgoal,\n\t\tlastAssistantTextFromEntries(ctx.sessionManager.getBranch()),\n\t);\n\treturn admitAndQueueGoalContinuation(pi, ctx, goal, {\n\t\tinput: {\n\t\t\tisIdle: ctx.isIdle(),\n\t\t\thasPendingMessages: ctx.hasPendingMessages(),\n\t\t\tpath: \"sessionStart\",\n\t\t\tlastStopReason: undefined,\n\t\t\tlastTurnWasMalformedToolUse: false,\n\t\t\tconsecutiveContinuations: goal.consecutiveContinuations ?? 0,\n\t\t\tlastContinuationSignature: goal.lastContinuationSignature,\n\t\t\tcurrentSignature: signature,\n\t\t\tconsecutiveLengthRecoveries: 0,\n\t\t\trecentNormalizedOutputHashes: [],\n\t\t\ttoollessContinuationStreak: 0,\n\t\t\tcontinuationPending: options.continuationPending,\n\t\t\tlastTurnStuckOnContextOverflow: isLastTurnStuckOnContextOverflow(\n\t\t\t\tctx,\n\t\t\t\tlastAssistantFromEntries(ctx.sessionManager.getBranch()),\n\t\t\t),\n\t\t},\n\t\tcontent: () => buildContinuationPrompt(goal),\n\t\tmarkContinuationPending: options.markContinuationPending,\n\t});\n}\n\nexport function buildCurrentGoalContinuationSignature(\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tlastAssistantText: string,\n): string {\n\tconst entries = ctx.sessionManager.getBranch();\n\tconst openTodos = openTodoTaskContents(entries).length;\n\tconst totalTodos = getLatestPhasesFromBranchEntries(entries).reduce((count, phase) => count + phase.tasks.length, 0);\n\treturn buildGoalContinuationSignature(goal, openTodos, totalTodos, hashAssistantText(lastAssistantText));\n}\n\nexport function lastAssistantText(messages: readonly AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (message?.role === \"assistant\") return textContent(message);\n\t}\n\treturn \"\";\n}\n\nfunction lastAssistantTextFromEntries(entries: readonly SessionEntry[]): string {\n\tconst assistant = lastAssistantFromEntries(entries);\n\treturn assistant === undefined ? \"\" : textContent(assistant);\n}\n\nfunction lastAssistantFromEntries(\n\tentries: readonly SessionEntry[],\n): Extract<AgentMessage, { role: \"assistant\" }> | undefined {\n\tfor (let index = entries.length - 1; index >= 0; index--) {\n\t\tconst entry = entries[index];\n\t\tif (entry?.type === \"message\" && entry.message.role === \"assistant\") return entry.message;\n\t}\n\treturn undefined;\n}\n\nexport function isLastTurnStuckOnContextOverflow(\n\tctx: ExtensionContext,\n\tlastAssistant: Extract<AgentMessage, { role: \"assistant\" }> | undefined,\n): boolean {\n\treturn lastAssistant !== undefined && isTurnStuckOnContextOverflow(lastAssistant, ctx.model?.contextWindow ?? 0);\n}\n\nfunction textContent(message: Extract<AgentMessage, { role: \"assistant\" }>): string {\n\treturn message.content\n\t\t.filter((content) => content.type === \"text\")\n\t\t.map((content) => content.text)\n\t\t.join(\"\\n\");\n}\n\nasync function handleDeniedContinuation(\n\tpi: ExtensionAPI,\n\tctx: ExtensionContext,\n\tgoal: Goal,\n\tinput: Omit<GoalContinuationInput, \"goal\">,\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): Promise<Goal> {\n\tconst blockedReason = blockedReasonForContinuationGuard(reason);\n\tif (blockedReason === undefined) return goal;\n\n\tconst blocked = await updateGoal(\n\t\tgoalStoreRef(ctx.sessionManager, ctx.cwd),\n\t\t{ status: \"blocked\", reason: blockedReason },\n\t\t\"model\",\n\t);\n\tif (ctx.hasUI) ctx.ui.notify(continuationCapRecoveryHint(blockedReason), \"warning\");\n\tpi.events?.emit(\"goal_continuation_guard_tripped\", {\n\t\tgoalId: goal.id,\n\t\treason,\n\t\tcount: input.consecutiveContinuations,\n\t\tunattendedContinuations: goal.unattendedContinuations ?? 0,\n\t});\n\treturn blocked;\n}\n\nfunction blockedReasonForContinuationGuard(\n\treason: Extract<GoalContinuationVerdict, { kind: \"deny\" }>[\"reason\"],\n): string | undefined {\n\tswitch (reason) {\n\t\tcase \"cap\":\n\t\t\treturn CONTINUATION_CAP_BLOCKED_REASON;\n\t\tcase \"unattended\":\n\t\t\treturn UNATTENDED_CONTINUATION_BLOCKED_REASON;\n\t\tcase \"repetition\":\n\t\t\treturn REPETITION_BLOCKED_REASON;\n\t\tcase \"length-exhausted\":\n\t\t\treturn LENGTH_EXHAUSTED_BLOCKED_REASON;\n\t\tcase \"context-overflow\":\n\t\t\treturn CONTEXT_OVERFLOW_BLOCKED_REASON;\n\t\tcase \"not-eligible\":\n\t\tcase \"single-flight\":\n\t\tcase \"stale\":\n\t\t\treturn undefined;\n\t}\n}\n\nexport function queueHiddenGoalPrompt(pi: ExtensionAPI, content: string): void {\n\tpi.sendMessage(\n\t\t{ customType: GOAL_CONTINUATION_MESSAGE_TYPE, content, display: false },\n\t\t{ triggerTurn: true, deliverAs: \"followUp\" },\n\t);\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { ExtensionAPI, ExtensionContext } from "../../types.ts";
2
- export { GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS } from "./cache-warm.ts";
2
+ export { GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS, GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS, } from "./cache-warm.ts";
3
3
  import type { AgentEndOptions, ProviderRecoveryOptions, SystemAbortOptions } from "./monitor-continuation-types.ts";
4
4
  import type { Goal } from "./types.ts";
5
5
  import type { GoalWaitTicker } from "./wait-ticker.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAarE,OAAO,EAAE,2CAA2C,EAAE,MAAM,iBAAiB,CAAC;AAmB9E,OAAO,KAAK,EACX,eAAe,EAIf,uBAAuB,EAEvB,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,mCAAmC,kCAAkC,CAAC;AACnF,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;gBAiCvC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc;IAS5B,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAW5B,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA0D7D,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAoBnE,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAgB5E,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IA2B3D,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IASjC,sGAAsG;IACtG,oBAAoB,IAAI,OAAO;IAI/B;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMtC,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAwBtC,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAc5D,sEAAsE;IACtE,cAAc,IAAI,IAAI;IAOtB,mGAAmG;IACnG,uBAAuB,IAAI,IAAI;IAI/B,OAAO,IAAI,IAAI;CAmWf"}
1
+ {"version":3,"file":"monitor-continuation.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/goal/monitor-continuation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAarE,OAAO,EACN,sCAAsC,EACtC,2CAA2C,GAC3C,MAAM,iBAAiB,CAAC;AAoBzB,OAAO,KAAK,EACX,eAAe,EAIf,uBAAuB,EAEvB,kBAAkB,EAClB,MAAM,iCAAiC,CAAC;AAMzC,OAAO,KAAK,EAAE,IAAI,EAAsB,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEvD,eAAO,MAAM,iCAAiC,gCAAgC,CAAC;AAC/E,eAAO,MAAM,+BAA+B,8BAA8B,CAAC;AAC3E,eAAO,MAAM,mCAAmC,kCAAkC,CAAC;AACnF,eAAO,MAAM,wBAAwB,oCAAoC,CAAC;AAE1E,qBAAa,4BAA4B;;gBAiCvC,EAAE,EAAE,YAAY,EAChB,qBAAqB,GAAE,MAAM,OAAqB,EAClD,uBAAuB,GAAE,MAAM,IAAe,EAC9C,UAAU,CAAC,EAAE,cAAc;IAS5B,KAAK,CAAC,GAAG,EAAE,gBAAgB,GAAG,IAAI;IAW5B,aAAa,CAAC,OAAO,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IA0D7D,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAoBnE,oBAAoB,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;IAgB5E,iBAAiB,IAAI,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC;IA2B3D,QAAQ,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI;IASjC,sGAAsG;IACtG,oBAAoB,IAAI,OAAO;IAI/B;;;;;OAKG;IACH,oBAAoB,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAMtC,mGAAmG;IACnG,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAwBtC,6FAA6F;IAC7F,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAc5D,sEAAsE;IACtE,cAAc,IAAI,IAAI;IAOtB,mGAAmG;IACnG,uBAAuB,IAAI,IAAI;IAI/B,OAAO,IAAI,IAAI;CAqWf"}
@@ -1,7 +1,7 @@
1
1
  import { createGoalCacheWarmScheduleData, estimateCacheWarmMetrics, GOAL_CACHE_WARMUP_ENTRY_TYPE, GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS, resolveGoalMonitorContinuationDelayMs, } from "./cache-warm.js";
2
2
  import { subscribeGoalChannelState } from "./channel-state-subscriptions.js";
3
- export { GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS } from "./cache-warm.js";
4
- import { continuationTurnUsedTools, evaluateGoalContinuation, GOAL_USER_GRACE_DELAY_MS, hasGoalContinuationProgress, hashAssistantText, normalizeAssistantText, } from "./continuation.js";
3
+ export { GOAL_MONITOR_BACKSTOP_DEFAULT_DELAY_MS, GOAL_MONITOR_CONTINUATION_FALLBACK_DELAY_MS, } from "./cache-warm.js";
4
+ import { continuationTurnUsedTools, evaluateGoalContinuation, GOAL_USER_GRACE_DELAY_MS, hasGoalContinuationProgress, hashAssistantText, isMalformedToolUseTurn, normalizeAssistantText, } from "./continuation.js";
5
5
  import { lastAssistantMessage } from "./last-assistant-message.js";
6
6
  import { admitAndQueueGoalContinuation, buildCurrentGoalContinuationSignature, isLastTurnStuckOnContextOverflow, lastAssistantText, } from "./lifecycle-helpers.js";
7
7
  import { buildContinuationPrompt, buildGoalStallNotice, buildTruncationRecoveryPrompt } from "./prompt.js";
@@ -262,8 +262,12 @@ export class MonitorAwareGoalContinuation {
262
262
  #schedule(goal, kind) {
263
263
  if (this.#scheduledContinuationKind !== undefined)
264
264
  return;
265
+ // A live wake source arms the periodic backstop only: the normal resumption
266
+ // is the drain fire in #setWakeSourceCount, and the backstop re-checks the
267
+ // goal every `promptCache.goalBackstopMaxSeconds` in case the source never
268
+ // delivers.
265
269
  const delayMs = kind === "monitor"
266
- ? resolveGoalMonitorContinuationDelayMs(this.#ctx?.getPromptCacheSafeWaitSeconds?.(), this.#ctx?.getPromptCacheGoalBackstopMaxSeconds?.())
270
+ ? resolveGoalMonitorContinuationDelayMs(this.#ctx?.getPromptCacheGoalBackstopMaxSeconds?.())
267
271
  : GOAL_USER_GRACE_DELAY_MS;
268
272
  this.#scheduledDelayMs = delayMs;
269
273
  if (kind === "monitor") {
@@ -425,6 +429,7 @@ export class MonitorAwareGoalContinuation {
425
429
  hasPendingMessages: ctx.hasPendingMessages(),
426
430
  path,
427
431
  lastStopReason: lastAssistant?.stopReason,
432
+ lastTurnWasMalformedToolUse: lastAssistant?.role === "assistant" && isMalformedToolUseTurn(lastAssistant),
428
433
  consecutiveContinuations: goal.consecutiveContinuations ?? 0,
429
434
  lastContinuationSignature: goal.lastContinuationSignature,
430
435
  currentSignature: buildCurrentGoalContinuationSignature(ctx, goal, lastAssistantText(messages)),