@prestyj/cli 5.28.1 → 5.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (339) hide show
  1. package/assets/motion/bin/contact-sheet.mjs +9 -3
  2. package/assets/motion/bin/cues.mjs +337 -0
  3. package/assets/motion/bin/library.mjs +53 -10
  4. package/assets/motion/bin/motion-blur.mjs +943 -0
  5. package/assets/motion/bin/motion-check.mjs +354 -3
  6. package/assets/motion/bin/music-fit.mjs +436 -0
  7. package/assets/motion/bin/pdf-extract.mjs +22 -10
  8. package/assets/motion/bin/reference-study.mjs +346 -0
  9. package/assets/motion/bin/score-synth.mjs +1052 -93
  10. package/assets/motion/library/README.md +50 -14
  11. package/assets/motion/library/kit/moves.js +1981 -0
  12. package/assets/motion/library/library.json +232 -0
  13. package/assets/motion/library/pieces/camera-rig/meta.json +13 -0
  14. package/assets/motion/library/pieces/camera-rig/piece.html +153 -0
  15. package/assets/motion/library/pieces/camera-rig/preview.jpg +0 -0
  16. package/assets/motion/library/pieces/chain-knock/meta.json +13 -0
  17. package/assets/motion/library/pieces/chain-knock/piece.html +195 -0
  18. package/assets/motion/library/pieces/chain-knock/preview.jpg +0 -0
  19. package/assets/motion/library/pieces/gather-to-logo/meta.json +13 -0
  20. package/assets/motion/library/pieces/gather-to-logo/piece.html +159 -0
  21. package/assets/motion/library/pieces/gather-to-logo/preview.jpg +0 -0
  22. package/assets/motion/library/pieces/morph-carry/meta.json +13 -0
  23. package/assets/motion/library/pieces/morph-carry/piece.html +173 -0
  24. package/assets/motion/library/pieces/morph-carry/preview.jpg +0 -0
  25. package/assets/motion/library/pieces/one-shape-journey/meta.json +13 -0
  26. package/assets/motion/library/pieces/one-shape-journey/piece.html +195 -0
  27. package/assets/motion/library/pieces/one-shape-journey/preview.jpg +0 -0
  28. package/assets/motion/library/pieces/open-from-subject/meta.json +13 -0
  29. package/assets/motion/library/pieces/open-from-subject/piece.html +168 -0
  30. package/assets/motion/library/pieces/open-from-subject/preview.jpg +0 -0
  31. package/assets/motion/library/pieces/request-to-result/meta.json +13 -0
  32. package/assets/motion/library/pieces/request-to-result/piece.html +212 -0
  33. package/assets/motion/library/pieces/request-to-result/preview.jpg +0 -0
  34. package/assets/motion/library/pieces/scale-dive/meta.json +13 -0
  35. package/assets/motion/library/pieces/scale-dive/piece.html +321 -0
  36. package/assets/motion/library/pieces/scale-dive/preview.jpg +0 -0
  37. package/assets/motion/library/pieces/screen-replica-steps/meta.json +13 -0
  38. package/assets/motion/library/pieces/screen-replica-steps/piece.html +366 -0
  39. package/assets/motion/library/pieces/screen-replica-steps/preview.jpg +0 -0
  40. package/assets/motion/library/pieces/zoom-into-card/meta.json +13 -0
  41. package/assets/motion/library/pieces/zoom-into-card/piece.html +179 -0
  42. package/assets/motion/library/pieces/zoom-into-card/preview.jpg +0 -0
  43. package/assets/motion/library/sheets/diagram.jpg +0 -0
  44. package/assets/motion/library/sheets/frame.jpg +0 -0
  45. package/assets/motion/library/sheets/transition.jpg +0 -0
  46. package/assets/motion/library/sheets/ui.jpg +0 -0
  47. package/assets/motion/references/build-sheet.md +206 -0
  48. package/assets/motion/references/runtime/determinism-rules.md +1 -1
  49. package/assets/motion/references/runtime/gsap-easing-and-stagger.md +29 -29
  50. package/assets/motion/references/runtime/inputs-and-assets.md +7 -12
  51. package/assets/motion/references/runtime/lint-validate-inspect.md +3 -3
  52. package/assets/motion/references/runtime/minimal-composition.md +1 -1
  53. package/assets/motion/references/runtime/preview-render.md +3 -3
  54. package/assets/motion/skills/app-walkthrough/SKILL.md +70 -0
  55. package/assets/motion/skills/before-after/SKILL.md +57 -0
  56. package/assets/motion/skills/brand-kit/SKILL.md +3 -3
  57. package/assets/motion/skills/dev-tool-video/SKILL.md +61 -0
  58. package/assets/motion/skills/launch-video/SKILL.md +66 -0
  59. package/assets/motion/skills/match-reference/SKILL.md +62 -0
  60. package/assets/motion/skills/motion/SKILL.md +148 -98
  61. package/assets/motion/skills/source-ingest/SKILL.md +17 -6
  62. package/assets/motion/skills/website-video/SKILL.md +76 -0
  63. package/assets/skills/bulletproof/SKILL.md +36 -11
  64. package/assets/skills/bulletproof/references/agent-surface.md +19 -9
  65. package/assets/skills/bulletproof/references/audit-protocol.md +20 -5
  66. package/assets/skills/bulletproof/references/platform-playbooks.md +5 -4
  67. package/assets/skills/bulletproof/references/provenance.md +26 -1
  68. package/assets/skills/bulletproof/references/secure-defaults.md +6 -5
  69. package/assets/skills/bulletproof/references/supply-chain.md +21 -17
  70. package/assets/skills/bulletproof/references/threat-landscape.md +28 -26
  71. package/assets/skills/bulletproof/references/verification.md +2 -0
  72. package/assets/skills/clarify/SKILL.md +25 -16
  73. package/assets/skills/code-review/SKILL.md +71 -13
  74. package/assets/skills/code-review/references/agent-diffs.md +27 -0
  75. package/assets/skills/code-review/references/tests.md +19 -0
  76. package/assets/skills/compliance-guard/SKILL.md +20 -5
  77. package/assets/skills/compliance-guard/references/artifacts.md +1 -1
  78. package/assets/skills/compliance-guard/references/eu-uk.md +16 -16
  79. package/assets/skills/compliance-guard/references/lawsuit-vectors.md +5 -5
  80. package/assets/skills/compliance-guard/references/provenance.md +41 -2
  81. package/assets/skills/compliance-guard/references/sector-gates.md +3 -3
  82. package/assets/skills/compliance-guard/references/security-baseline.md +2 -2
  83. package/assets/skills/compliance-guard/references/trigger-map.md +5 -5
  84. package/assets/skills/compliance-guard/references/us.md +27 -21
  85. package/assets/skills/durable/SKILL.md +87 -79
  86. package/assets/skills/durable/references/agent-db-safety.md +69 -0
  87. package/assets/skills/durable/references/backups-and-runtime.md +19 -12
  88. package/assets/skills/durable/references/migrations-and-schema.md +13 -6
  89. package/assets/skills/evidence-led-ui/SKILL.md +69 -127
  90. package/assets/skills/evidence-led-ui/references/anti-defaults.md +107 -208
  91. package/assets/skills/evidence-led-ui/references/direction.md +124 -0
  92. package/assets/skills/evidence-led-ui/references/production-contract.md +8 -0
  93. package/assets/skills/evidence-led-ui/references/provenance.md +24 -1
  94. package/assets/skills/lean/SKILL.md +90 -71
  95. package/assets/skills/lean/references/memory-and-processes.md +3 -2
  96. package/assets/skills/lean/references/playbooks.md +37 -12
  97. package/assets/skills/refactoring/SKILL.md +24 -3
  98. package/assets/skills/refactoring/references/agent-pitfalls.md +4 -1
  99. package/assets/skills/refactoring/references/legacy.md +21 -0
  100. package/assets/skills/root-cause/SKILL.md +20 -10
  101. package/assets/skills/shared-language/SKILL.md +16 -14
  102. package/assets/skills/tdd/SKILL.md +27 -15
  103. package/dist/app-sidecar.js +206 -47
  104. package/dist/app-sidecar.js.map +1 -1
  105. package/dist/cli/thinking-arg.d.ts +6 -0
  106. package/dist/cli/thinking-arg.js +13 -0
  107. package/dist/cli/thinking-arg.js.map +1 -0
  108. package/dist/cli.d.ts +1 -2
  109. package/dist/cli.js +18 -34
  110. package/dist/cli.js.map +1 -1
  111. package/dist/core/acceptance-checks.d.ts +48 -0
  112. package/dist/core/acceptance-checks.js +144 -0
  113. package/dist/core/acceptance-checks.js.map +1 -0
  114. package/dist/core/agent-session.d.ts +107 -72
  115. package/dist/core/agent-session.js +562 -407
  116. package/dist/core/agent-session.js.map +1 -1
  117. package/dist/core/agents.d.ts +6 -5
  118. package/dist/core/agents.js.map +1 -1
  119. package/dist/core/ask-user.d.ts +90 -8
  120. package/dist/core/ask-user.js +124 -13
  121. package/dist/core/ask-user.js.map +1 -1
  122. package/dist/core/bundled-agents.js +1 -3
  123. package/dist/core/bundled-agents.js.map +1 -1
  124. package/dist/core/cache-diagnostics.d.ts +68 -0
  125. package/dist/core/cache-diagnostics.js +196 -0
  126. package/dist/core/cache-diagnostics.js.map +1 -0
  127. package/dist/core/cache-expiry.d.ts +87 -0
  128. package/dist/core/cache-expiry.js +111 -0
  129. package/dist/core/cache-expiry.js.map +1 -0
  130. package/dist/core/codex-request-shape.d.ts +12 -0
  131. package/dist/core/codex-request-shape.js +31 -0
  132. package/dist/core/codex-request-shape.js.map +1 -0
  133. package/dist/core/compaction/compactor.js +84 -48
  134. package/dist/core/compaction/compactor.js.map +1 -1
  135. package/dist/core/compaction/plan-step-policy.d.ts +46 -0
  136. package/dist/core/compaction/plan-step-policy.js +57 -0
  137. package/dist/core/compaction/plan-step-policy.js.map +1 -0
  138. package/dist/core/destructive-git-guard.d.ts +90 -0
  139. package/dist/core/destructive-git-guard.js +871 -0
  140. package/dist/core/destructive-git-guard.js.map +1 -0
  141. package/dist/core/event-bus.d.ts +3 -0
  142. package/dist/core/event-bus.js +5 -0
  143. package/dist/core/event-bus.js.map +1 -1
  144. package/dist/core/injection-detect.d.ts +38 -0
  145. package/dist/core/injection-detect.js +232 -0
  146. package/dist/core/injection-detect.js.map +1 -0
  147. package/dist/core/keep-awake.d.ts +88 -0
  148. package/dist/core/keep-awake.js +251 -0
  149. package/dist/core/keep-awake.js.map +1 -0
  150. package/dist/core/mcp/client.d.ts +72 -0
  151. package/dist/core/mcp/client.js +264 -41
  152. package/dist/core/mcp/client.js.map +1 -1
  153. package/dist/core/mcp/content.js +6 -2
  154. package/dist/core/mcp/content.js.map +1 -1
  155. package/dist/core/mcp/store.d.ts +6 -1
  156. package/dist/core/mcp/store.js +12 -1
  157. package/dist/core/mcp/store.js.map +1 -1
  158. package/dist/core/mcp/types.d.ts +18 -0
  159. package/dist/core/model-unavailable.d.ts +14 -0
  160. package/dist/core/model-unavailable.js +23 -0
  161. package/dist/core/model-unavailable.js.map +1 -0
  162. package/dist/core/node-debugger.d.ts +148 -0
  163. package/dist/core/node-debugger.js +642 -0
  164. package/dist/core/node-debugger.js.map +1 -0
  165. package/dist/core/nolan-context.js +7 -3
  166. package/dist/core/nolan-context.js.map +1 -1
  167. package/dist/core/package-threats.d.ts +18 -0
  168. package/dist/core/package-threats.js +168 -0
  169. package/dist/core/package-threats.js.map +1 -0
  170. package/dist/core/persistent-shell.d.ts +58 -6
  171. package/dist/core/persistent-shell.js +331 -49
  172. package/dist/core/persistent-shell.js.map +1 -1
  173. package/dist/core/process-manager.d.ts +14 -0
  174. package/dist/core/process-manager.js +61 -0
  175. package/dist/core/process-manager.js.map +1 -1
  176. package/dist/core/progress/git-xp.js +8 -14
  177. package/dist/core/progress/git-xp.js.map +1 -1
  178. package/dist/core/session-history.d.ts +12 -0
  179. package/dist/core/session-history.js +27 -0
  180. package/dist/core/session-history.js.map +1 -1
  181. package/dist/core/session-manager.d.ts +13 -1
  182. package/dist/core/session-manager.js +38 -18
  183. package/dist/core/session-manager.js.map +1 -1
  184. package/dist/core/session-summary-index.d.ts +37 -0
  185. package/dist/core/session-summary-index.js +172 -0
  186. package/dist/core/session-summary-index.js.map +1 -0
  187. package/dist/core/settings-manager.d.ts +12 -0
  188. package/dist/core/settings-manager.js +20 -0
  189. package/dist/core/settings-manager.js.map +1 -1
  190. package/dist/core/shell-threats-popular-packages.d.ts +11 -0
  191. package/dist/core/shell-threats-popular-packages.js +675 -0
  192. package/dist/core/shell-threats-popular-packages.js.map +1 -0
  193. package/dist/core/shell-threats.d.ts +8 -0
  194. package/dist/core/shell-threats.js +186 -0
  195. package/dist/core/shell-threats.js.map +1 -0
  196. package/dist/core/skills.js +3 -1
  197. package/dist/core/skills.js.map +1 -1
  198. package/dist/core/stream-rules.d.ts +30 -0
  199. package/dist/core/stream-rules.js +151 -0
  200. package/dist/core/stream-rules.js.map +1 -0
  201. package/dist/core/subagent-manager.d.ts +20 -5
  202. package/dist/core/subagent-manager.js +22 -8
  203. package/dist/core/subagent-manager.js.map +1 -1
  204. package/dist/core/subagent-receipt.d.ts +54 -0
  205. package/dist/core/subagent-receipt.js +278 -0
  206. package/dist/core/subagent-receipt.js.map +1 -0
  207. package/dist/core/subagent-turn-record.d.ts +2 -0
  208. package/dist/core/subagent-turn-record.js.map +1 -1
  209. package/dist/core/test-impact.d.ts +73 -0
  210. package/dist/core/test-impact.js +467 -0
  211. package/dist/core/test-impact.js.map +1 -0
  212. package/dist/core/thinking-level.d.ts +1 -1
  213. package/dist/core/thinking-level.js +1 -1
  214. package/dist/core/thinking-level.js.map +1 -1
  215. package/dist/core/verification-gate.d.ts +2 -0
  216. package/dist/core/verification-gate.js +4 -0
  217. package/dist/core/verification-gate.js.map +1 -1
  218. package/dist/core/verification-snapshot.js +3 -5
  219. package/dist/core/verification-snapshot.js.map +1 -1
  220. package/dist/core/workspace-guard.d.ts +18 -7
  221. package/dist/core/workspace-guard.js +227 -60
  222. package/dist/core/workspace-guard.js.map +1 -1
  223. package/dist/interactive.js +2 -1
  224. package/dist/interactive.js.map +1 -1
  225. package/dist/modes/subagent-worker-mode.js +34 -6
  226. package/dist/modes/subagent-worker-mode.js.map +1 -1
  227. package/dist/motion-agent/motion-agent.d.ts +6 -2
  228. package/dist/motion-agent/motion-agent.js +5 -7
  229. package/dist/motion-agent/motion-agent.js.map +1 -1
  230. package/dist/motion-agent/motion-prompt.d.ts +1 -1
  231. package/dist/motion-agent/motion-prompt.js +16 -20
  232. package/dist/motion-agent/motion-prompt.js.map +1 -1
  233. package/dist/motion-agent/motion-review-session.js +7 -3
  234. package/dist/motion-agent/motion-review-session.js.map +1 -1
  235. package/dist/motion-agent/motion-review.d.ts +10 -3
  236. package/dist/motion-agent/motion-review.js +15 -7
  237. package/dist/motion-agent/motion-review.js.map +1 -1
  238. package/dist/motion-agent/motion-studio-context.js +1 -1
  239. package/dist/motion-agent/motion-studio-context.js.map +1 -1
  240. package/dist/system-prompt.js +15 -11
  241. package/dist/system-prompt.js.map +1 -1
  242. package/dist/test-support/keep-alive.d.ts +14 -0
  243. package/dist/test-support/keep-alive.js +17 -0
  244. package/dist/test-support/keep-alive.js.map +1 -0
  245. package/dist/tools/ask-user.js +3 -3
  246. package/dist/tools/ask-user.js.map +1 -1
  247. package/dist/tools/bash-read-evidence.d.ts +10 -0
  248. package/dist/tools/bash-read-evidence.js +133 -0
  249. package/dist/tools/bash-read-evidence.js.map +1 -0
  250. package/dist/tools/bash.d.ts +10 -1
  251. package/dist/tools/bash.js +115 -7
  252. package/dist/tools/bash.js.map +1 -1
  253. package/dist/tools/debug.d.ts +54 -0
  254. package/dist/tools/debug.js +233 -0
  255. package/dist/tools/debug.js.map +1 -0
  256. package/dist/tools/edit-targets.d.ts +10 -0
  257. package/dist/tools/edit-targets.js +31 -0
  258. package/dist/tools/edit-targets.js.map +1 -0
  259. package/dist/tools/edit.d.ts +11 -3
  260. package/dist/tools/edit.js +387 -305
  261. package/dist/tools/edit.js.map +1 -1
  262. package/dist/tools/goals.d.ts +1 -1
  263. package/dist/tools/index.d.ts +19 -2
  264. package/dist/tools/index.js +47 -7
  265. package/dist/tools/index.js.map +1 -1
  266. package/dist/tools/prompt-hints.js +6 -1
  267. package/dist/tools/prompt-hints.js.map +1 -1
  268. package/dist/tools/read-tracker.d.ts +5 -0
  269. package/dist/tools/read-tracker.js +19 -9
  270. package/dist/tools/read-tracker.js.map +1 -1
  271. package/dist/tools/read.js +3 -2
  272. package/dist/tools/read.js.map +1 -1
  273. package/dist/tools/skill.js +11 -3
  274. package/dist/tools/skill.js.map +1 -1
  275. package/dist/tools/subagent-control.js +44 -8
  276. package/dist/tools/subagent-control.js.map +1 -1
  277. package/dist/tools/subagent-shared.d.ts +29 -8
  278. package/dist/tools/subagent-shared.js +45 -14
  279. package/dist/tools/subagent-shared.js.map +1 -1
  280. package/dist/tools/subagent.d.ts +14 -2
  281. package/dist/tools/subagent.js +43 -13
  282. package/dist/tools/subagent.js.map +1 -1
  283. package/dist/tools/task-output.js +3 -2
  284. package/dist/tools/task-output.js.map +1 -1
  285. package/dist/tools/task-send.d.ts +1 -1
  286. package/dist/tools/task-send.js +15 -1
  287. package/dist/tools/task-send.js.map +1 -1
  288. package/dist/tools/tool-tiers.d.ts +8 -2
  289. package/dist/tools/tool-tiers.js +10 -3
  290. package/dist/tools/tool-tiers.js.map +1 -1
  291. package/dist/tools/truncate.d.ts +21 -0
  292. package/dist/tools/truncate.js +187 -0
  293. package/dist/tools/truncate.js.map +1 -1
  294. package/dist/tools/ui-adopt.js +2 -0
  295. package/dist/tools/ui-adopt.js.map +1 -1
  296. package/dist/ui/App.d.ts +0 -4
  297. package/dist/ui/App.js +12 -31
  298. package/dist/ui/App.js.map +1 -1
  299. package/dist/ui/components/ActivityIndicator.js +1 -0
  300. package/dist/ui/components/ActivityIndicator.js.map +1 -1
  301. package/dist/ui/components/ToolExecution.js +3 -2
  302. package/dist/ui/components/ToolExecution.js.map +1 -1
  303. package/dist/ui/hooks/useAgentLoop.d.ts +1 -8
  304. package/dist/ui/hooks/useAgentLoop.js +1 -119
  305. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  306. package/dist/ui/render.d.ts +0 -4
  307. package/dist/ui/render.js +0 -2
  308. package/dist/ui/render.js.map +1 -1
  309. package/dist/ui/terminal-history.js +3 -1
  310. package/dist/ui/terminal-history.js.map +1 -1
  311. package/dist/ui/tool-line-summary.js +3 -1
  312. package/dist/ui/tool-line-summary.js.map +1 -1
  313. package/dist/utils/format.js +5 -1
  314. package/dist/utils/format.js.map +1 -1
  315. package/dist/utils/git.d.ts +77 -0
  316. package/dist/utils/git.js +285 -21
  317. package/dist/utils/git.js.map +1 -1
  318. package/dist/utils/github-ci.js +2 -1
  319. package/dist/utils/github-ci.js.map +1 -1
  320. package/dist/utils/github.js +11 -9
  321. package/dist/utils/github.js.map +1 -1
  322. package/dist/utils/image.d.ts +14 -0
  323. package/dist/utils/image.js +16 -0
  324. package/dist/utils/image.js.map +1 -1
  325. package/dist/utils/process.d.ts +20 -0
  326. package/dist/utils/process.js +98 -0
  327. package/dist/utils/process.js.map +1 -1
  328. package/package.json +4 -4
  329. package/assets/motion/references/motion-language.md +0 -128
  330. package/assets/motion/skills/video-qa/SKILL.md +0 -89
  331. package/dist/core/ideal-review-subagent.d.ts +0 -56
  332. package/dist/core/ideal-review-subagent.js +0 -112
  333. package/dist/core/ideal-review-subagent.js.map +0 -1
  334. package/dist/core/ideal-review.d.ts +0 -82
  335. package/dist/core/ideal-review.js +0 -242
  336. package/dist/core/ideal-review.js.map +0 -1
  337. package/dist/motion-agent/motion-check-tool.d.ts +0 -35
  338. package/dist/motion-agent/motion-check-tool.js +0 -514
  339. package/dist/motion-agent/motion-check-tool.js.map +0 -1
@@ -39,11 +39,12 @@ import { isWorkflowCommandText, countAssistantMessages, shouldStartAutopilotCycl
39
39
  import { driveAutopilotCycle, frameAutopilotInjection } from "./core/autopilot-cycle.js";
40
40
  import { describeRunVerification, describeTurnVerification } from "./core/run-status.js";
41
41
  import { validateNolanModelPref, effectiveNolanModel, } from "./core/nolan-model.js";
42
- import { normalizeAutopilotMarkersForHistory, normalizeAppMarkersForHistory, normalizeNolanTurnsForHistory, getHistoryMessageVisibility, replayMessagesInOrder, restoreUserRow, restoreAssistantTexts, resolveRestoredCommand, autopilotMarkerCopySeed, } from "./core/session-history.js";
42
+ import { normalizeAutopilotMarkersForHistory, normalizeAppMarkersForHistory, normalizeNolanTurnsForHistory, getHistoryMessageVisibility, replayMessagesInOrder, restoreUserRow, restoreAssistantTexts, resolveRestoredCommand, autopilotMarkerCopySeed, extractToolImagePaths, } from "./core/session-history.js";
43
43
  import { sessionToMarkdown, defaultExportFilename, } from "./core/session-export.js";
44
44
  import { AuthStorage } from "./core/auth-storage.js";
45
45
  import { advancesPhase, explainPullFailure, isGgufShard, isValidHfRepoId, parseOllamaPullLine, pickGgufQuant, SHARDED_MESSAGE, toHfSearchRow, } from "./hf-pull.js";
46
46
  import { cleanupToolOutputs } from "./tools/overflow.js";
47
+ import { spawnedTasks } from "./tools/subagent-shared.js";
47
48
  import { readCappedBody } from "./utils/http-body.js";
48
49
  import { fetchSubscriptionUsage, SubscriptionUsageError, XIAOMI_CREDITS_KEY, dualAuthProvider, oauthStorageKey, discoverLocalModels, findProbedModel, formatLocalModelId, parseLocalModelId, probeEndpoint, toModelInfo as localModelInfo, } from "@prestyj/core";
49
50
  import { LocalEndpointError, addCustomEndpoint, listAllEndpoints, removeCustomEndpoint, syncEndpointCredentials, } from "./core/local-endpoint-store.js";
@@ -63,6 +64,7 @@ import { getGitHubOpenCounts, getGitHubRepoSlug } from "./utils/github.js";
63
64
  import { startGitHubCIPoll } from "./utils/github-ci.js";
64
65
  import { extractPlanSteps } from "./utils/plan-steps.js";
65
66
  import { getNextThinkingLevel, getSupportedThinkingLevels, isThinkingLevelSupported, } from "./core/thinking-level.js";
67
+ import { parseThinkingLevel } from "./cli/thinking-arg.js";
66
68
  import { PROMPT_COMMANDS } from "./core/prompt-commands.js";
67
69
  import { loadCustomCommands } from "./core/custom-commands.js";
68
70
  import { discoverProjects } from "./core/project-discovery.js";
@@ -70,20 +72,25 @@ import { listSidecarSessions } from "./app-sidecar-sessions.js";
70
72
  import { loadTasksSync, saveTasksSync, pruneDoneTasksSync, getNextPendingTask, markTaskInProgress, } from "./core/task-store.js";
71
73
  import { initLogger, log } from "./core/logger.js";
72
74
  import { installTerminationHandlers } from "./core/shutdown.js";
75
+ import { KeepAwake } from "./core/keep-awake.js";
73
76
  import { RADIO_STATIONS, getCurrentStation, getRadioVolume, playRadio, setRadioVolume, stopRadio, } from "./core/radio.js";
74
77
  import { enrichProcessPath } from "./core/shell-path.js";
75
- import { downscaleForPreview, shrinkToFit, validateVisionImage } from "./utils/image.js";
78
+ import { downscaleForPreview, IMAGE_MEDIA_TYPES, shrinkToFit, validateVisionImage, } from "./utils/image.js";
79
+ import { readFileBounded } from "./tools/operations.js";
76
80
  import { startServeMode } from "./modes/serve-mode.js";
77
81
  import { installSteroids, probeSteroids } from "./core/steroids.js";
78
82
  import { loadTelegramConfig, saveTelegramConfig, verifyBotToken } from "./core/telegram-config.js";
79
83
  import { loadServers, addServer, removeServer, getServer, parseMcpAddCommand, MCPClientManager, McpOAuthStore, createElicitationBridge, } from "./core/mcp/index.js";
80
- import { createAskUserBridge } from "./core/ask-user.js";
84
+ import { askSoftDeadlineMs, createAskUserBridge, deliverLateAnswer, } from "./core/ask-user.js";
81
85
  import { createAskUserTool } from "./tools/ask-user.js";
82
86
  import { buildSnapshot, levelForXp, rankForLevel } from "./core/progress/ranks.js";
83
87
  import { loadProgress, peekProgress, updateProgress } from "./core/progress/store.js";
84
88
  import { awardPrompt, awardCommits } from "./core/progress/engine.js";
85
89
  import { detectNewCommits, repoKey } from "./core/progress/git-xp.js";
86
90
  import { rebuildFromSessions } from "./core/progress/rebuild.js";
91
+ /** App-wide idle-sleep guard: every window's runs share one OS assertion. The
92
+ * `keepAwake` setting is applied at daemon start and live via /keep-awake. */
93
+ const keepAwake = new KeepAwake();
87
94
  const AUTOMATION_PROVENANCE = {
88
95
  source: "runtime",
89
96
  kind: "automation",
@@ -488,6 +495,7 @@ async function runJsonModeIfRequested() {
488
495
  tools: { type: "string" },
489
496
  "mcp-servers": { type: "string" },
490
497
  "prompt-cache-key": { type: "string" },
498
+ thinking: { type: "string" },
491
499
  },
492
500
  allowPositionals: true,
493
501
  strict: true,
@@ -524,6 +532,7 @@ async function runJsonModeIfRequested() {
524
532
  allowedTools,
525
533
  allowedMcpServers,
526
534
  promptCacheKey: values["prompt-cache-key"],
535
+ thinkingLevel: parseThinkingLevel(values.thinking),
527
536
  }).catch(async (err) => {
528
537
  process.stderr.write((err instanceof Error ? err.message : String(err)) + "\n");
529
538
  process.exit(1);
@@ -570,6 +579,8 @@ async function main() {
570
579
  // ~/.ezcoder/debug.log (initLogger truncates on each start).
571
580
  const sidecarLog = path.join(paths.agentDir, "ezcoder-app-sidecar.log");
572
581
  initLogger(sidecarLog);
582
+ // Apply the saved keepAwake setting before any run can acquire the guard.
583
+ keepAwake.setEnabled((await new SettingsManager(paths.settingsFile).load()).keepAwake);
573
584
  // The desktop sidecar previously omitted the stream diagnostic hook used by
574
585
  // the CLI, leaving device-specific provider stalls impossible to distinguish from
575
586
  // event-loop starvation or a broken streaming network path. Keep routine
@@ -908,6 +919,40 @@ async function main() {
908
919
  daemonJson(res, 200, { ok: true });
909
920
  return;
910
921
  }
922
+ // Keep-awake is app-wide (one OS assertion for every window), so its
923
+ // setting lives at the daemon level and applies live to in-flight runs.
924
+ if (method === "GET" && url === "/keep-awake") {
925
+ daemonJson(res, 200, { enabled: keepAwake.isEnabled });
926
+ return;
927
+ }
928
+ if (method === "POST" && url === "/keep-awake") {
929
+ void daemonReadBody(req, res).then(async (raw) => {
930
+ if (raw === null)
931
+ return;
932
+ let enabled;
933
+ try {
934
+ enabled = JSON.parse(raw).enabled;
935
+ }
936
+ catch {
937
+ enabled = undefined;
938
+ }
939
+ if (typeof enabled !== "boolean") {
940
+ daemonJson(res, 400, { error: "enabled must be a boolean" });
941
+ return;
942
+ }
943
+ keepAwake.setEnabled(enabled);
944
+ try {
945
+ const sm = new SettingsManager(paths.settingsFile);
946
+ await sm.load();
947
+ await sm.set("keepAwake", enabled);
948
+ }
949
+ catch (err) {
950
+ log("WARN", "app-sidecar", "failed to persist keepAwake", { err: String(err) });
951
+ }
952
+ daemonJson(res, 200, { enabled });
953
+ });
954
+ return;
955
+ }
911
956
  // Progress is daemon-level so the Home screen can paint before a project
912
957
  // session exists; per-session callers still work through the same endpoint.
913
958
  if (method === "GET" && url === "/progress") {
@@ -955,9 +1000,16 @@ async function main() {
955
1000
  const shutdown = installTerminationHandlers({
956
1001
  scope: "app-sidecar",
957
1002
  teardown: async () => {
1003
+ // Before any await: background commands live outside the daemon's process
1004
+ // group, and the app force-kills that group after 3 s. If a slow session
1005
+ // teardown is still pending then, these would never be stopped.
1006
+ for (const c of sessions.values())
1007
+ c.stopBackgroundProcesses();
958
1008
  clearInterval(parentWatch);
959
1009
  // Radio playback is app-wide (one stream across all windows).
960
1010
  stopRadio();
1011
+ // Drop the idle-sleep assertion first: session teardown may hang.
1012
+ keepAwake.dispose();
961
1013
  // Close the ~/.ezcoder progress fs.watch handle.
962
1014
  progress.dispose();
963
1015
  await Promise.all([...sessions.values()].map((c) => c.dispose().catch(() => { })));
@@ -1312,9 +1364,33 @@ async function createSession(deps, opts) {
1312
1364
  // MCP bridge above: broadcast over SSE, resolved when the webview POSTs
1313
1365
  // /ask/:id. Registered ONLY here — a TUI/headless/subagent run has nobody to
1314
1366
  // answer, so the tool is absent there rather than hanging on a dead channel.
1367
+ //
1368
+ // Soft deadline ("async ask"): past it the tool returns "no answer yet —
1369
+ // proceed on your best guess" and the question STAYS OPEN. A later answer
1370
+ // rides the ordinary user queue: steering into a live run, or the next turn
1371
+ // when idle. The deadline is short when nobody is watching (autopilot, task
1372
+ // run-all, a scheduled prompt).
1373
+ let scheduledRunActive = false;
1315
1374
  const asks = createAskUserBridge({
1316
1375
  broadcast: (prompt) => broadcast("ask_user", prompt),
1317
- onTimeout: (prompt) => log("WARN", "app-sidecar", "ask_user timed out", { id: prompt.id }),
1376
+ timeoutMs: () => askSoftDeadlineMs(autopilot || autopilotActive || taskRunAll || scheduledRunActive),
1377
+ onTimeout: (prompt) => {
1378
+ log("WARN", "app-sidecar", "ask_user deadline passed; agent proceeding", { id: prompt.id });
1379
+ broadcast("ask_user_deferred", { id: prompt.id });
1380
+ },
1381
+ onClosed: (ids) => broadcast("ask_user_closed", { ids }),
1382
+ onLateAnswer: (late) => {
1383
+ log("INFO", "app-sidecar", "ask_user late answer queued", { id: late.prompt.id });
1384
+ deliverLateAnswer(late, {
1385
+ queueMessage: (text) => session.queueMessage(text),
1386
+ isBusy: () => running || runClaim.active || autopilotActive,
1387
+ onQueued: () => broadcast("queued", {
1388
+ count: session.getQueuedCount(),
1389
+ messages: session.listQueuedMessages(),
1390
+ }),
1391
+ startIdleRun: () => void runStrandedQueue(),
1392
+ });
1393
+ },
1318
1394
  });
1319
1395
  const askUserTool = createAskUserTool(asks.park);
1320
1396
  // The session file path to resume (passed by the daemon's POST /session);
@@ -1867,11 +1943,14 @@ async function createSession(deps, opts) {
1867
1943
  if (!d.silent)
1868
1944
  broadcast("retry", { reason: d.reason, attempt: d.attempt, delayMs: d.delayMs });
1869
1945
  });
1946
+ session.eventBus.on("stream_rule_triggered", (d) => broadcast("stream_rule_triggered", { rules: d.rules, source: d.source, toolName: d.toolName }));
1870
1947
  session.eventBus.on("max_turns", (d) => broadcast("max_turns", d));
1871
1948
  // The agent consumed queued steering at a turn boundary. Re-broadcast as the
1872
1949
  // usual `queued` depth update so the webview drops the pending affordance the
1873
1950
  // moment the message lands in the loop, not at run_end.
1874
1951
  session.eventBus.on("queue_drained", (d) => broadcast("queued", { count: d.count, messages: session.listQueuedMessages() }));
1952
+ // A fresh/loaded session must not receive late answers to the old one's questions.
1953
+ session.eventBus.on("session_start", () => asks.closeDeferred());
1875
1954
  session.eventBus.on("tool_call_start", (d) => {
1876
1955
  toolCallNames.set(d.toolCallId, d.name);
1877
1956
  broadcast("tool_call_start", d);
@@ -1931,12 +2010,38 @@ async function createSession(deps, opts) {
1931
2010
  session.eventBus.on("hook", (d) => broadcast("hook", d));
1932
2011
  session.eventBus.on("diagnostics", (d) => broadcast("diagnostics", d));
1933
2012
  // Fires BEFORE the candidate final answer streams. The webview holds assistant
1934
- // text back while armed, so an Ideal review supersedes a draft that was never
2013
+ // text back while armed, so an injected follow-up supersedes a draft that was never
1935
2014
  // painted instead of deleting one the user already started reading.
1936
2015
  session.eventBus.on("hook_armed", (d) => broadcast("hook_armed", d));
1937
2016
  session.eventBus.on("subagent_state", (d) => broadcast("subagent_state", d));
1938
2017
  session.eventBus.on("compaction_start", (d) => broadcast("compaction_start", d));
1939
2018
  session.eventBus.on("compaction_end", (d) => broadcast("compaction_end", d));
2019
+ // Cold-prompt-cache notice: push the fresh TTL anchor + context size whenever
2020
+ // a run or compaction settles. Expiry itself is time-based, so the webview
2021
+ // also re-reads `cacheExpiry` from /state when the user returns or types.
2022
+ for (const ev of ["agent_done", "compaction_end", "model_change"]) {
2023
+ session.eventBus.on(ev, () => broadcast("cache_expiry", session.getCacheExpiryStatus()));
2024
+ }
2025
+ // Keep the computer awake while this window's agent works: owned runs and
2026
+ // autopilot cycles (RunLifecycle state), Nolan replies, and background
2027
+ // sub-agents that outlive their parent run. Released on settle (completed,
2028
+ // failed or cancelled) and on session dispose.
2029
+ let releaseRunAwake = null;
2030
+ let releaseSubagentAwake = null;
2031
+ const activeSubagents = new Set();
2032
+ session.eventBus.on("subagent_state", (d) => {
2033
+ if (d.state === "starting" || d.state === "running")
2034
+ activeSubagents.add(d.agent_id);
2035
+ else
2036
+ activeSubagents.delete(d.agent_id);
2037
+ if (activeSubagents.size > 0) {
2038
+ releaseSubagentAwake ??= keepAwake.acquire("subagent");
2039
+ }
2040
+ else {
2041
+ releaseSubagentAwake?.();
2042
+ releaseSubagentAwake = null;
2043
+ }
2044
+ });
1940
2045
  let running = false;
1941
2046
  // Closes the window between `/prompt` deciding to start a run and `runAgent`
1942
2047
  // flipping `running` — that stretch awaits, so Node yields inside it. See
@@ -1944,6 +2049,13 @@ async function createSession(deps, opts) {
1944
2049
  const runClaim = new RunClaim();
1945
2050
  const runLifecycle = new RunLifecycle((runState) => {
1946
2051
  running = runState !== "idle";
2052
+ if (running) {
2053
+ releaseRunAwake ??= keepAwake.acquire("run");
2054
+ }
2055
+ else {
2056
+ releaseRunAwake?.();
2057
+ releaseRunAwake = null;
2058
+ }
1947
2059
  if (runState === "cancelling")
1948
2060
  broadcast("run_cancelling", { runState });
1949
2061
  },
@@ -1970,10 +2082,8 @@ async function createSession(deps, opts) {
1970
2082
  // Autopilot (auto-review) toggle for THIS window's project. Loaded from
1971
2083
  // ezcoder-app.json on boot; flipped via POST /autopilot. When on, POST /prompt runs
1972
2084
  // runAutopilotCycle after the user's turn settles — Nolan auto-reviews the work
1973
- // and drives the review→prompt→review loop. Nolan is the sole verification
1974
- // owner in this mode, so suppress the build session's redundant Ideal hook.
2085
+ // and drives the review→prompt→review loop.
1975
2086
  let autopilot = mode === "code" && (await loadAutopilot(cwd));
1976
- session.setIdealReviewSuppressed(autopilot);
1977
2087
  // True while an autopilot review is in flight (used to defer nolanAuto model
1978
2088
  // switches, like nolanRunning does for chat Nolan, and to drive the spinner).
1979
2089
  let autopilotReviewing = false;
@@ -2173,9 +2283,6 @@ async function createSession(deps, opts) {
2173
2283
  // (often >5 min) regardless of the user's global speedProfile pick.
2174
2284
  forceLongCacheRetention: true,
2175
2285
  });
2176
- // Nolan is already the independent autopilot reviewer; recursively running
2177
- // his own Ideal self-review adds latency and can corrupt the verdict shape.
2178
- nolanAgent.setIdealReviewSuppressed(true);
2179
2286
  await nolanAgent.initialize();
2180
2287
  // Keep review text/tools silent; report usage only for whole-task accounting.
2181
2288
  nolanAgent.eventBus.on("turn_end", (d) => {
@@ -2444,7 +2551,6 @@ async function createSession(deps, opts) {
2444
2551
  const generation = runLifecycle.begin(abortOwnedWork).generation;
2445
2552
  pendingCancelDrain = null;
2446
2553
  autopilotActive = true;
2447
- session.setIdealReviewSuppressed(true);
2448
2554
  // Generation captured by the last plan review; acceptPlan re-checks it so
2449
2555
  // a user Accept/Reject landing mid-review always wins.
2450
2556
  let planGenAtReview = -1;
@@ -2562,7 +2668,6 @@ async function createSession(deps, opts) {
2562
2668
  }
2563
2669
  finally {
2564
2670
  autopilotActive = false;
2565
- session.setIdealReviewSuppressed(autopilot);
2566
2671
  finishOwnedGeneration(generation, true, session.getVerificationProblem() ? "unverified" : "completed");
2567
2672
  queueMicrotask(() => void runStrandedQueue());
2568
2673
  }
@@ -2861,6 +2966,7 @@ async function createSession(deps, opts) {
2861
2966
  supportedThinkingLevels: getSupportedThinkingLevels(st.provider, st.model),
2862
2967
  supportsVideo: getModel(st.model)?.supportsVideo ?? false,
2863
2968
  autopilot,
2969
+ cacheExpiry: session.getCacheExpiryStatus(),
2864
2970
  ...nolanStatePayload(),
2865
2971
  ...footerExtras(),
2866
2972
  });
@@ -3340,7 +3446,16 @@ async function createSession(deps, opts) {
3340
3446
  // Pre-index tool results by toolCallId so we can pair tool calls with
3341
3447
  // their results (for sub-agent status + image extraction).
3342
3448
  const toolResultMap = new Map();
3449
+ // Tool name per call, so restore only trusts generate_image's own text
3450
+ // when it reads extra images from disk (an MCP server could fake it).
3451
+ const toolNameById = new Map();
3343
3452
  for (const msg of messages) {
3453
+ if (msg.role === "assistant" && typeof msg.content !== "string") {
3454
+ for (const c of msg.content) {
3455
+ if (c.type === "tool_call")
3456
+ toolNameById.set(c.id, c.name);
3457
+ }
3458
+ }
3344
3459
  if (msg.role !== "tool")
3345
3460
  continue;
3346
3461
  for (const tr of msg.content) {
@@ -3500,31 +3615,43 @@ async function createSession(deps, opts) {
3500
3615
  const imageBlocks = tr.content.filter((c) => c.type === "image");
3501
3616
  if (imageBlocks.length === 0)
3502
3617
  continue;
3503
- // Extract the path from the text block (e.g. "Generated image → /path").
3618
+ // Extract the path from the text block so the restored image
3619
+ // stays clickable (read / screenshot / generate_image formats).
3504
3620
  const textBlock = tr.content.find((c) => c.type === "text" && "text" in c && typeof c.text === "string");
3505
3621
  const textContent = textBlock && textBlock.type === "text" ? textBlock.text : "";
3506
- const pathMatch = textContent.match(/→\s*(\S+)/);
3507
- const imgPath = pathMatch?.[1];
3508
- // Downscale each image for the webview preview.
3622
+ const imgPaths = extractToolImagePaths(textContent);
3623
+ // Downscale each image for the webview preview. Paths pair with
3624
+ // image blocks in order (a tool saves one file per image).
3509
3625
  const toolImages = [];
3626
+ let blockIndex = 0;
3510
3627
  for (const block of imageBlocks) {
3511
3628
  if (block.type !== "image")
3512
3629
  continue;
3513
- try {
3514
- const rawBuf = Buffer.from(block.data, "base64");
3515
- const previewBuf = await downscaleForPreview(rawBuf);
3516
- toolImages.push({
3517
- src: `data:${block.mediaType};base64,${previewBuf.toString("base64")}`,
3518
- path: imgPath,
3519
- });
3520
- }
3521
- catch {
3522
- // Downscale failed — use the raw data.
3523
- toolImages.push({
3524
- src: `data:${block.mediaType};base64,${block.data}`,
3525
- path: imgPath,
3526
- });
3527
- }
3630
+ const imgPath = imgPaths[blockIndex++];
3631
+ const previewBuf = await downscaleForPreview(Buffer.from(block.data, "base64"));
3632
+ toolImages.push({
3633
+ src: `data:${block.mediaType};base64,${previewBuf.toString("base64")}`,
3634
+ path: imgPath,
3635
+ });
3636
+ }
3637
+ // generate_image persists only its first image's pixels; the
3638
+ // rest exist only on disk, so preview them from their files.
3639
+ // Skip silently if a file was moved or deleted since.
3640
+ const extraPaths = toolNameById.get(tr.toolCallId) === "generate_image"
3641
+ ? imgPaths.slice(blockIndex)
3642
+ : [];
3643
+ for (const extraPath of extraPaths) {
3644
+ const mediaType = IMAGE_MEDIA_TYPES[path.extname(extraPath).toLowerCase()];
3645
+ if (!mediaType)
3646
+ continue;
3647
+ const raw = await readFileBounded(extraPath).catch(() => null);
3648
+ if (!raw)
3649
+ continue;
3650
+ const previewBuf = await downscaleForPreview(raw);
3651
+ toolImages.push({
3652
+ src: `data:${mediaType};base64,${previewBuf.toString("base64")}`,
3653
+ path: extraPath,
3654
+ });
3528
3655
  }
3529
3656
  if (toolImages.length > 0) {
3530
3657
  history.push({
@@ -3609,18 +3736,25 @@ async function createSession(deps, opts) {
3609
3736
  if (msg.role === "assistant" && typeof msg.content !== "string") {
3610
3737
  const subagentCalls = msg.content.filter((c) => c.type === "tool_call" && (c.name === "subagent" || c.name === "spawn_agent"));
3611
3738
  if (subagentCalls.length > 0) {
3612
- const agents = subagentCalls.map((c) => {
3739
+ const agents = subagentCalls.flatMap((c) => {
3613
3740
  const result = toolResultMap.get(c.id);
3614
- return {
3615
- agentName: c.name === "spawn_agent" && typeof c.args?.task_name === "string"
3616
- ? c.args.task_name
3617
- : typeof c.args?.agent === "string"
3618
- ? c.args.agent
3619
- : undefined,
3620
- // Async workers are intentionally non-resumable; restored rows are historical.
3621
- status: result?.isError ? "error" : "done",
3622
- toolUseCount: 0,
3623
- };
3741
+ // Async workers are intentionally non-resumable; restored rows are historical.
3742
+ const status = result?.isError ? "error" : "done";
3743
+ if (c.name === "spawn_agent") {
3744
+ // One row per child: a batch call starts several.
3745
+ return spawnedTasks(c.args).map((spawn) => ({
3746
+ agentName: spawn.task_name ?? spawn.agent,
3747
+ status,
3748
+ toolUseCount: 0,
3749
+ }));
3750
+ }
3751
+ return [
3752
+ {
3753
+ agentName: typeof c.args?.agent === "string" ? c.args.agent : undefined,
3754
+ status,
3755
+ toolUseCount: 0,
3756
+ },
3757
+ ];
3624
3758
  });
3625
3759
  history.push({
3626
3760
  role: "assistant",
@@ -3754,6 +3888,8 @@ async function createSession(deps, opts) {
3754
3888
  // Claim the run NOW, synchronously. Everything below this line may
3755
3889
  // yield, and `running` does not flip until runAgent begins.
3756
3890
  claimedStart = runClaim.claim();
3891
+ // A scheduled prompt has nobody watching: short ask deadline.
3892
+ scheduledRunActive = meta?.scheduled === true;
3757
3893
  json(res, 202, { accepted: true });
3758
3894
  // Gate inputs captured around the run: whether this turn is a workflow
3759
3895
  // slash command (attachment prompts skip slash expansion entirely), and
@@ -3852,8 +3988,10 @@ async function createSession(deps, opts) {
3852
3988
  await runStrandedQueue();
3853
3989
  })
3854
3990
  .finally(() => {
3855
- if (claimedStart)
3991
+ if (claimedStart) {
3992
+ scheduledRunActive = false;
3856
3993
  runClaim.release();
3994
+ }
3857
3995
  });
3858
3996
  return;
3859
3997
  }
@@ -3889,6 +4027,7 @@ async function createSession(deps, opts) {
3889
4027
  }
3890
4028
  json(res, 202, { accepted: true });
3891
4029
  nolanRunning = true;
4030
+ const releaseNolanAwake = keepAwake.acquire("nolan");
3892
4031
  broadcast("nolan_run_start", { text });
3893
4032
  try {
3894
4033
  const nolanAgent = await ensureNolanSession();
@@ -3905,6 +4044,7 @@ async function createSession(deps, opts) {
3905
4044
  broadcastError("nolan_error", "nolan run failed", err);
3906
4045
  }
3907
4046
  finally {
4047
+ releaseNolanAwake();
3908
4048
  nolanRunning = false;
3909
4049
  broadcast("nolan_run_end", {});
3910
4050
  const pending = pendingNolanModel;
@@ -3943,9 +4083,6 @@ async function createSession(deps, opts) {
3943
4083
  return;
3944
4084
  }
3945
4085
  autopilot = enabled;
3946
- // A toggle-off during an active cycle takes effect after Nolan finishes;
3947
- // until then, injected build runs must not re-enable Ideal self-review.
3948
- session.setIdealReviewSuppressed(enabled || autopilotActive);
3949
4086
  await saveAutopilot(cwd, enabled);
3950
4087
  log("INFO", "app-sidecar", "autopilot toggled", { enabled: String(enabled) });
3951
4088
  broadcast("autopilot", { autopilot: enabled });
@@ -4471,6 +4608,18 @@ async function createSession(deps, opts) {
4471
4608
  json(res, 200, payload);
4472
4609
  return;
4473
4610
  }
4611
+ if (method === "POST" && url === "/prewarm") {
4612
+ // Best-effort Anthropic cache prewarm on the composer's first keystroke.
4613
+ // Fire-and-forget: AgentSession.prewarm() applies its own gating (provider,
4614
+ // setting, history size, TTL, in-flight) and swallows request errors.
4615
+ if (!running) {
4616
+ void session.prewarm().catch((err) => {
4617
+ log("WARN", "app-sidecar", "prewarm failed", { err: String(err) });
4618
+ });
4619
+ }
4620
+ json(res, 202, { accepted: !running });
4621
+ return;
4622
+ }
4474
4623
  if (method === "POST" && url === "/cancel") {
4475
4624
  void (async () => {
4476
4625
  // Even between task runs, cancellation stops the sweep. Active provider
@@ -5327,6 +5476,10 @@ async function createSession(deps, opts) {
5327
5476
  json(res, 404, { error: "not found" });
5328
5477
  }
5329
5478
  async function dispose() {
5479
+ releaseRunAwake?.();
5480
+ releaseRunAwake = null;
5481
+ releaseSubagentAwake?.();
5482
+ releaseSubagentAwake = null;
5330
5483
  elicitations.cancelAll();
5331
5484
  asks.cancelAll();
5332
5485
  tasksPollStopped = true;
@@ -5350,6 +5503,11 @@ async function createSession(deps, opts) {
5350
5503
  await nolanAutoSession?.dispose().catch(() => { });
5351
5504
  await session.dispose().catch(() => { });
5352
5505
  }
5506
+ function stopBackgroundProcesses() {
5507
+ session.stopBackgroundProcesses();
5508
+ nolanSession?.stopBackgroundProcesses();
5509
+ nolanAutoSession?.stopBackgroundProcesses();
5510
+ }
5353
5511
  return {
5354
5512
  id: opts.id,
5355
5513
  mode,
@@ -5363,6 +5521,7 @@ async function createSession(deps, opts) {
5363
5521
  broadcast,
5364
5522
  handle,
5365
5523
  dispose,
5524
+ stopBackgroundProcesses,
5366
5525
  };
5367
5526
  }
5368
5527
  main().catch(async (err) => {