@selesai/code 0.2.1 → 0.3.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 (284) hide show
  1. package/dist/config.d.ts +4 -15
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +5 -45
  4. package/dist/config.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +1 -0
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +8 -0
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/diagnostics.d.ts +1 -0
  10. package/dist/core/diagnostics.d.ts.map +1 -1
  11. package/dist/core/diagnostics.js.map +1 -1
  12. package/dist/core/model-registry.d.ts +4 -1
  13. package/dist/core/model-registry.d.ts.map +1 -1
  14. package/dist/core/model-registry.js +101 -51
  15. package/dist/core/model-registry.js.map +1 -1
  16. package/dist/core/package-manager.d.ts +10 -0
  17. package/dist/core/package-manager.d.ts.map +1 -1
  18. package/dist/core/package-manager.js +93 -8
  19. package/dist/core/package-manager.js.map +1 -1
  20. package/dist/core/package-manager.test.d.ts +2 -0
  21. package/dist/core/package-manager.test.d.ts.map +1 -0
  22. package/dist/core/package-manager.test.js +20 -0
  23. package/dist/core/package-manager.test.js.map +1 -0
  24. package/dist/core/resource-loader.d.ts +3 -0
  25. package/dist/core/resource-loader.d.ts.map +1 -1
  26. package/dist/core/resource-loader.js +10 -0
  27. package/dist/core/resource-loader.js.map +1 -1
  28. package/dist/core/settings-manager.d.ts +1 -0
  29. package/dist/core/settings-manager.d.ts.map +1 -1
  30. package/dist/core/settings-manager.js.map +1 -1
  31. package/dist/defaults/models.json +64 -113
  32. package/dist/extensions/caveman/caveman-instructions.cjs +39 -0
  33. package/dist/extensions/caveman/index.js +116 -0
  34. package/dist/extensions/caveman/package.json +8 -0
  35. package/dist/extensions/caveman/test/extension.test.js +185 -0
  36. package/dist/extensions/caveman/test/helpers.test.js +67 -0
  37. package/dist/extensions/copy-turn.ts +2 -2
  38. package/dist/extensions/pi-intercom/broker/broker.ts +2 -3
  39. package/dist/extensions/pi-intercom/broker/paths.ts +10 -5
  40. package/dist/extensions/pi-intercom/broker/spawn.ts +2 -3
  41. package/dist/extensions/pi-intercom/config.ts +2 -2
  42. package/dist/extensions/pi-intercom/index.ts +2 -2
  43. package/dist/extensions/pi-intercom/package.json +2 -2
  44. package/dist/extensions/pi-intercom/ui/compose.ts +3 -3
  45. package/dist/extensions/pi-intercom/ui/inline-message.ts +3 -3
  46. package/dist/extensions/pi-intercom/ui/session-list.ts +3 -3
  47. package/dist/extensions/pi-powerline-footer/bash-mode/editor.ts +2 -2
  48. package/dist/extensions/pi-powerline-footer/bash-mode/history.ts +2 -1
  49. package/dist/extensions/pi-powerline-footer/index.ts +40 -47
  50. package/dist/extensions/pi-powerline-footer/package.json +2 -2
  51. package/dist/extensions/pi-powerline-footer/tests/bash-mode.test.ts +10 -10
  52. package/dist/extensions/pi-powerline-footer/tests/jump-shortcuts.test.ts +1 -1
  53. package/dist/extensions/pi-powerline-footer/tests/working-vibes.test.ts +2 -2
  54. package/dist/extensions/pi-powerline-footer/theme.ts +6 -6
  55. package/dist/extensions/pi-powerline-footer/tps.ts +1 -1
  56. package/dist/extensions/pi-powerline-footer/types.ts +7 -7
  57. package/dist/extensions/pi-powerline-footer/welcome.ts +23 -16
  58. package/dist/extensions/pi-powerline-footer/working-vibes.ts +52 -59
  59. package/dist/extensions/pi-rewind-hook/index.ts +1 -1
  60. package/dist/extensions/pi-rewind-hook/package.json +1 -1
  61. package/dist/extensions/pi-subagents/README.md +224 -52
  62. package/dist/extensions/pi-subagents/agents/architect.md +188 -0
  63. package/dist/extensions/pi-subagents/agents/builder.md +120 -0
  64. package/dist/extensions/pi-subagents/agents/commentator.md +131 -0
  65. package/dist/extensions/pi-subagents/agents/explorer.md +51 -0
  66. package/dist/extensions/pi-subagents/agents/recapper.md +21 -0
  67. package/dist/extensions/pi-subagents/install.mjs +93 -0
  68. package/dist/extensions/pi-subagents/package.json +4 -5
  69. package/dist/extensions/pi-subagents/prompts/parallel-context-build.md +1 -1
  70. package/dist/extensions/pi-subagents/prompts/parallel-handoff-plan.md +1 -1
  71. package/dist/extensions/pi-subagents/skills/pi-subagents/SKILL.md +76 -13
  72. package/dist/extensions/pi-subagents/src/agents/agent-management.ts +179 -2
  73. package/dist/extensions/pi-subagents/src/agents/agent-memory.ts +254 -0
  74. package/dist/extensions/pi-subagents/src/agents/agent-serializer.ts +11 -0
  75. package/dist/extensions/pi-subagents/src/agents/agents.ts +193 -19
  76. package/dist/extensions/pi-subagents/src/agents/chain-serializer.ts +27 -2
  77. package/dist/extensions/pi-subagents/src/extension/config.ts +27 -4
  78. package/dist/extensions/pi-subagents/src/extension/doctor.ts +1 -7
  79. package/dist/extensions/pi-subagents/src/extension/fanout-child.ts +3 -2
  80. package/dist/extensions/pi-subagents/src/extension/index.ts +73 -71
  81. package/dist/extensions/pi-subagents/src/extension/rpc.ts +369 -0
  82. package/dist/extensions/pi-subagents/src/extension/schemas.ts +54 -10
  83. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +200 -0
  84. package/dist/extensions/pi-subagents/src/intercom/intercom-bridge.ts +21 -253
  85. package/dist/extensions/pi-subagents/src/intercom/native-supervisor-channel.ts +519 -0
  86. package/dist/extensions/pi-subagents/src/profiles/profiles.ts +1 -1
  87. package/dist/extensions/pi-subagents/src/runs/background/async-execution.ts +195 -105
  88. package/dist/extensions/pi-subagents/src/runs/background/async-job-tracker.ts +88 -2
  89. package/dist/extensions/pi-subagents/src/runs/background/async-status.ts +67 -10
  90. package/dist/extensions/pi-subagents/src/runs/background/chain-root-attachment.ts +34 -4
  91. package/dist/extensions/pi-subagents/src/runs/background/completion-batcher.ts +166 -0
  92. package/dist/extensions/pi-subagents/src/runs/background/control-channel.ts +156 -1
  93. package/dist/extensions/pi-subagents/src/runs/background/fleet-view.ts +515 -0
  94. package/dist/extensions/pi-subagents/src/runs/background/notify.ts +161 -44
  95. package/dist/extensions/pi-subagents/src/runs/background/result-watcher.ts +1 -2
  96. package/dist/extensions/pi-subagents/src/runs/background/run-id-resolver.ts +3 -2
  97. package/dist/extensions/pi-subagents/src/runs/background/run-status.ts +167 -6
  98. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +514 -0
  99. package/dist/extensions/pi-subagents/src/runs/background/stale-run-reconciler.ts +28 -1
  100. package/dist/extensions/pi-subagents/src/runs/background/subagent-runner.ts +873 -204
  101. package/dist/extensions/pi-subagents/src/runs/background/wait.ts +353 -0
  102. package/dist/extensions/pi-subagents/src/runs/foreground/chain-execution.ts +123 -27
  103. package/dist/extensions/pi-subagents/src/runs/foreground/execution.ts +174 -70
  104. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +569 -81
  105. package/dist/extensions/pi-subagents/src/runs/shared/acceptance.ts +45 -22
  106. package/dist/extensions/pi-subagents/src/runs/shared/completion-guard.ts +3 -1
  107. package/dist/extensions/pi-subagents/src/runs/shared/dynamic-fanout.ts +2 -2
  108. package/dist/extensions/pi-subagents/src/runs/shared/model-fallback.ts +172 -21
  109. package/dist/extensions/pi-subagents/src/runs/shared/model-scope.ts +128 -0
  110. package/dist/extensions/pi-subagents/src/runs/shared/nested-events.ts +89 -0
  111. package/dist/extensions/pi-subagents/src/runs/shared/parallel-utils.ts +50 -1
  112. package/dist/extensions/pi-subagents/src/runs/shared/pi-args.ts +35 -4
  113. package/dist/extensions/pi-subagents/src/runs/shared/pi-spawn.ts +63 -45
  114. package/dist/extensions/pi-subagents/src/runs/shared/single-output.ts +2 -0
  115. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +125 -5
  116. package/dist/extensions/pi-subagents/src/runs/shared/tool-budget.ts +74 -0
  117. package/dist/extensions/pi-subagents/src/runs/shared/turn-budget.ts +52 -0
  118. package/dist/extensions/pi-subagents/src/runs/shared/worktree.ts +28 -5
  119. package/dist/extensions/pi-subagents/src/shared/artifacts.ts +16 -1
  120. package/dist/extensions/pi-subagents/src/shared/atomic-json.ts +15 -2
  121. package/dist/extensions/pi-subagents/src/shared/child-transcript.ts +212 -0
  122. package/dist/extensions/pi-subagents/src/shared/fork-context.ts +133 -22
  123. package/dist/extensions/pi-subagents/src/shared/settings.ts +3 -1
  124. package/dist/extensions/pi-subagents/src/shared/types.ts +197 -4
  125. package/dist/extensions/pi-subagents/src/shared/utils.ts +108 -17
  126. package/dist/extensions/pi-subagents/src/slash/prompt-workflows.ts +330 -0
  127. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +135 -4
  128. package/dist/extensions/pi-subagents/src/tui/render.ts +32 -12
  129. package/dist/extensions/pi-subagents/test/e2e/real-session-subagent.test.ts +94 -0
  130. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +2772 -0
  131. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +1107 -0
  132. package/dist/extensions/pi-subagents/test/integration/async-status.test.ts +463 -0
  133. package/dist/extensions/pi-subagents/test/integration/chain-clarify.test.ts +274 -0
  134. package/dist/extensions/pi-subagents/test/integration/chain-execution.test.ts +1535 -0
  135. package/dist/extensions/pi-subagents/test/integration/detect-error.test.ts +242 -0
  136. package/dist/extensions/pi-subagents/test/integration/doctor-executor.test.ts +128 -0
  137. package/dist/extensions/pi-subagents/test/integration/error-handling.test.ts +252 -0
  138. package/dist/extensions/pi-subagents/test/integration/foreground-result-size.test.ts +188 -0
  139. package/dist/extensions/pi-subagents/test/integration/fork-context-execution.test.ts +1306 -0
  140. package/dist/extensions/pi-subagents/test/integration/intercom-result-delivery.test.ts +1033 -0
  141. package/dist/extensions/pi-subagents/test/integration/parallel-execution.test.ts +397 -0
  142. package/dist/extensions/pi-subagents/test/integration/render-fork-badge.test.ts +583 -0
  143. package/dist/extensions/pi-subagents/test/integration/render-widget.test.ts +691 -0
  144. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +892 -0
  145. package/dist/extensions/pi-subagents/test/integration/session-tokens.test.ts +62 -0
  146. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +1824 -0
  147. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +1284 -0
  148. package/dist/extensions/pi-subagents/test/integration/slash-live-state.test.ts +127 -0
  149. package/dist/extensions/pi-subagents/test/integration/template-resolution.test.ts +299 -0
  150. package/dist/extensions/pi-subagents/test/integration/top-level-async.test.ts +37 -0
  151. package/dist/extensions/pi-subagents/test/support/helpers.ts +189 -0
  152. package/dist/extensions/pi-subagents/test/support/mock-pi-script.mjs +335 -0
  153. package/dist/extensions/pi-subagents/test/support/mock-pi.ts +129 -0
  154. package/dist/extensions/pi-subagents/test/support/real-session-child-cli.mjs +184 -0
  155. package/dist/extensions/pi-subagents/test/support/real-session-runner.ts +285 -0
  156. package/dist/extensions/pi-subagents/test/support/register-loader.mjs +15 -0
  157. package/dist/extensions/pi-subagents/test/support/ts-loader.mjs +108 -0
  158. package/dist/extensions/pi-subagents/test/unit/acceptance.test.ts +421 -0
  159. package/dist/extensions/pi-subagents/test/unit/agent-disabled.test.ts +213 -0
  160. package/dist/extensions/pi-subagents/test/unit/agent-eject-disable.test.ts +343 -0
  161. package/dist/extensions/pi-subagents/test/unit/agent-frontmatter.test.ts +1199 -0
  162. package/dist/extensions/pi-subagents/test/unit/agent-management.test.ts +556 -0
  163. package/dist/extensions/pi-subagents/test/unit/agent-memory.test.ts +420 -0
  164. package/dist/extensions/pi-subagents/test/unit/agent-overrides.test.ts +579 -0
  165. package/dist/extensions/pi-subagents/test/unit/agent-scope.test.ts +20 -0
  166. package/dist/extensions/pi-subagents/test/unit/agent-selection.test.ts +88 -0
  167. package/dist/extensions/pi-subagents/test/unit/artifacts.test.ts +24 -0
  168. package/dist/extensions/pi-subagents/test/unit/async-execution.test.ts +86 -0
  169. package/dist/extensions/pi-subagents/test/unit/async-interrupt-action.test.ts +221 -0
  170. package/dist/extensions/pi-subagents/test/unit/async-permission-session.test.ts +98 -0
  171. package/dist/extensions/pi-subagents/test/unit/async-resume.test.ts +285 -0
  172. package/dist/extensions/pi-subagents/test/unit/atomic-json.test.ts +112 -0
  173. package/dist/extensions/pi-subagents/test/unit/chain-append.test.ts +206 -0
  174. package/dist/extensions/pi-subagents/test/unit/chain-root-attachment.test.ts +159 -0
  175. package/dist/extensions/pi-subagents/test/unit/chain-serializer.test.ts +229 -0
  176. package/dist/extensions/pi-subagents/test/unit/child-transcript.test.ts +225 -0
  177. package/dist/extensions/pi-subagents/test/unit/close-grace-timer.test.ts +101 -0
  178. package/dist/extensions/pi-subagents/test/unit/completion-batcher.test.ts +228 -0
  179. package/dist/extensions/pi-subagents/test/unit/completion-dedupe.test.ts +36 -0
  180. package/dist/extensions/pi-subagents/test/unit/completion-guard.test.ts +152 -0
  181. package/dist/extensions/pi-subagents/test/unit/config-dir-runtime.test.ts +72 -0
  182. package/dist/extensions/pi-subagents/test/unit/control-channel.test.ts +327 -0
  183. package/dist/extensions/pi-subagents/test/unit/control-notices.test.ts +161 -0
  184. package/dist/extensions/pi-subagents/test/unit/doctor.test.ts +159 -0
  185. package/dist/extensions/pi-subagents/test/unit/dynamic-fanout.test.ts +217 -0
  186. package/dist/extensions/pi-subagents/test/unit/extra-agent-dirs.test.ts +88 -0
  187. package/dist/extensions/pi-subagents/test/unit/file-coalescer.test.ts +68 -0
  188. package/dist/extensions/pi-subagents/test/unit/foreground-tool-call-compaction.test.ts +78 -0
  189. package/dist/extensions/pi-subagents/test/unit/fork-context.test.ts +415 -0
  190. package/dist/extensions/pi-subagents/test/unit/get-final-output.test.ts +126 -0
  191. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +271 -0
  192. package/dist/extensions/pi-subagents/test/unit/intercom-bridge.test.ts +173 -0
  193. package/dist/extensions/pi-subagents/test/unit/jsonl-writer.test.ts +119 -0
  194. package/dist/extensions/pi-subagents/test/unit/live-async-resume.test.ts +170 -0
  195. package/dist/extensions/pi-subagents/test/unit/model-fallback.test.ts +346 -0
  196. package/dist/extensions/pi-subagents/test/unit/model-info.test.ts +62 -0
  197. package/dist/extensions/pi-subagents/test/unit/model-scope-settings.test.ts +87 -0
  198. package/dist/extensions/pi-subagents/test/unit/model-scope.test.ts +145 -0
  199. package/dist/extensions/pi-subagents/test/unit/native-supervisor-channel.test.ts +162 -0
  200. package/dist/extensions/pi-subagents/test/unit/nested-control.test.ts +470 -0
  201. package/dist/extensions/pi-subagents/test/unit/nested-events.test.ts +329 -0
  202. package/dist/extensions/pi-subagents/test/unit/notify.test.ts +319 -0
  203. package/dist/extensions/pi-subagents/test/unit/package-manifest.test.ts +70 -0
  204. package/dist/extensions/pi-subagents/test/unit/parallel-utils.test.ts +247 -0
  205. package/dist/extensions/pi-subagents/test/unit/path-handling.test.ts +113 -0
  206. package/dist/extensions/pi-subagents/test/unit/path-resolution.test.ts +94 -0
  207. package/dist/extensions/pi-subagents/test/unit/pi-args.test.ts +803 -0
  208. package/dist/extensions/pi-subagents/test/unit/pi-coding-agent-dir.test.ts +209 -0
  209. package/dist/extensions/pi-subagents/test/unit/pi-spawn.test.ts +238 -0
  210. package/dist/extensions/pi-subagents/test/unit/proactive-skills.test.ts +118 -0
  211. package/dist/extensions/pi-subagents/test/unit/profiles.test.ts +299 -0
  212. package/dist/extensions/pi-subagents/test/unit/prompt-template-bridge.test.ts +408 -0
  213. package/dist/extensions/pi-subagents/test/unit/prompt-workflows.test.ts +134 -0
  214. package/dist/extensions/pi-subagents/test/unit/recursion-guard.test.ts +257 -0
  215. package/dist/extensions/pi-subagents/test/unit/render-helpers.test.ts +209 -0
  216. package/dist/extensions/pi-subagents/test/unit/result-intercom.test.ts +226 -0
  217. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +294 -0
  218. package/dist/extensions/pi-subagents/test/unit/run-id-resolver.test.ts +147 -0
  219. package/dist/extensions/pi-subagents/test/unit/run-status.test.ts +997 -0
  220. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +478 -0
  221. package/dist/extensions/pi-subagents/test/unit/schemas.test.ts +562 -0
  222. package/dist/extensions/pi-subagents/test/unit/single-output.test.ts +213 -0
  223. package/dist/extensions/pi-subagents/test/unit/skills-fallback.test.ts +438 -0
  224. package/dist/extensions/pi-subagents/test/unit/slash-bridge.test.ts +54 -0
  225. package/dist/extensions/pi-subagents/test/unit/slash-chain-groups.test.ts +464 -0
  226. package/dist/extensions/pi-subagents/test/unit/stale-run-reconciler.test.ts +255 -0
  227. package/dist/extensions/pi-subagents/test/unit/status-format.test.ts +19 -0
  228. package/dist/extensions/pi-subagents/test/unit/subagent-control.test.ts +245 -0
  229. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +517 -0
  230. package/dist/extensions/pi-subagents/test/unit/temp-paths.test.ts +72 -0
  231. package/dist/extensions/pi-subagents/test/unit/tool-budget.test.ts +52 -0
  232. package/dist/extensions/pi-subagents/test/unit/tool-description.test.ts +237 -0
  233. package/dist/extensions/pi-subagents/test/unit/total-cost.test.ts +60 -0
  234. package/dist/extensions/pi-subagents/test/unit/ts-loader.test.ts +33 -0
  235. package/dist/extensions/pi-subagents/test/unit/turn-budget.test.ts +202 -0
  236. package/dist/extensions/pi-subagents/test/unit/types-fork-preamble.test.ts +23 -0
  237. package/dist/extensions/pi-subagents/test/unit/wait.test.ts +398 -0
  238. package/dist/extensions/pi-subagents/test/unit/widget-nested-render.test.ts +88 -0
  239. package/dist/extensions/pi-subagents/test/unit/windows-hide-spawn.test.ts +26 -0
  240. package/dist/extensions/pi-subagents/test/unit/workflow-graph.test.ts +134 -0
  241. package/dist/extensions/pi-subagents/test/unit/worktree.test.ts +479 -0
  242. package/dist/extensions/pi-web-agent/package.json +2 -2
  243. package/dist/extensions/pi-web-agent/src/backends/settings-reader.ts +2 -2
  244. package/dist/extensions/pi-web-agent/src/changelog-notice.ts +5 -2
  245. package/dist/extensions/pi-web-agent/src/commands/web-agent-config.ts +1 -1
  246. package/dist/extensions/pi-web-agent/src/extension.ts +1 -1
  247. package/dist/extensions/pi-web-agent/src/presentation/config-store.ts +6 -4
  248. package/dist/extensions/question/helpers.ts +3 -3
  249. package/dist/extensions/question/index.ts +2 -2
  250. package/dist/extensions/question/navigation.ts +2 -2
  251. package/dist/extensions/question/package.json +2 -2
  252. package/dist/extensions/question/question-list.ts +2 -2
  253. package/dist/extensions/question/tests/question-list.test.ts +2 -2
  254. package/dist/extensions/question/tui-adapter.ts +2 -2
  255. package/dist/extensions/question/types.ts +2 -2
  256. package/dist/extensions/question/ui-protocol.ts +2 -2
  257. package/dist/extensions/rtk.ts +2 -2
  258. package/dist/extensions/tokenin-onboarding.ts +2 -2
  259. package/dist/extensions/undo.ts +1 -1
  260. package/dist/extensions/web-agent-onboarding.ts +3 -3
  261. package/dist/extensions/workflow/adapter.ts +16 -8
  262. package/dist/extensions/workflow/extension.ts +1 -1
  263. package/dist/extensions/workflow/package.json +1 -1
  264. package/dist/main.d.ts.map +1 -1
  265. package/dist/main.js +4 -0
  266. package/dist/main.js.map +1 -1
  267. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  268. package/dist/modes/interactive/components/assistant-message.js +2 -0
  269. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  270. package/dist/modes/interactive/components/first-time-setup.d.ts +1 -1
  271. package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -1
  272. package/dist/modes/interactive/components/first-time-setup.js +19 -5
  273. package/dist/modes/interactive/components/first-time-setup.js.map +1 -1
  274. package/dist/skills/caveman/SKILL.md +49 -0
  275. package/dist/skills/handoff-text/SKILL.md +13 -0
  276. package/dist/utils/thinking-tags.d.ts +7 -0
  277. package/dist/utils/thinking-tags.d.ts.map +1 -0
  278. package/dist/utils/thinking-tags.js +63 -0
  279. package/dist/utils/thinking-tags.js.map +1 -0
  280. package/docs/docs.json +4 -0
  281. package/docs/extensions.md +2 -0
  282. package/docs/settings.md +15 -0
  283. package/docs/shared-host-extensions.md +109 -0
  284. package/package.json +2 -2
@@ -14,11 +14,12 @@ import { applyThinkingSuffix } from "../shared/pi-args.ts";
14
14
  import { injectOutputPathSystemPrompt, injectSingleOutputInstruction, normalizeSingleOutputOverride, resolveSingleOutputPath, validateFileOnlyOutputMode } from "../shared/single-output.ts";
15
15
  import { buildChainInstructions, isDynamicParallelStep, isParallelStep, resolveStepBehavior, suppressProgressForReadOnlyTask, writeInitialProgressFile, type ChainStep, type ResolvedStepBehavior, type SequentialStep, type StepOverrides } from "../../shared/settings.ts";
16
16
  import type { RunnerStep } from "../shared/parallel-utils.ts";
17
- import { getSelesaiSpawnCommand, resolveSelesaiPackageRoot } from "../shared/pi-spawn.ts";
17
+ import { resolveSelesaiPackageRoot } from "../shared/pi-spawn.ts";
18
18
  import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
19
- import { createSpawnTimingMarkers, emitSpawnTiming } from "../shared/spawn-timing.ts";
20
- import { resolveChildCwd } from "../../shared/utils.ts";
19
+ import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
20
+ import { SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV, resolveChildCwd } from "../../shared/utils.ts";
21
21
  import { buildModelCandidates, resolveModelCandidate, resolveSubagentModelOverride, type AvailableModelInfo, type ParentModel } from "../shared/model-fallback.ts";
22
+ import type { ModelScopeConfig } from "../shared/model-scope.ts";
22
23
  import { resolveEffectiveThinking } from "../../shared/model-info.ts";
23
24
  import { resolveExpectedWorktreeAgentCwd } from "../shared/worktree.ts";
24
25
  import { buildWorkflowGraphSnapshot } from "../shared/workflow-graph.ts";
@@ -32,19 +33,24 @@ import {
32
33
  type MaxOutputConfig,
33
34
  type NestedRouteInfo,
34
35
  type ResolvedControlConfig,
36
+ type ResolvedTurnBudget,
37
+ type ResolvedToolBudget,
35
38
  type SubagentRunMode,
36
39
  ASYNC_DIR,
37
40
  RESULTS_DIR,
38
41
  SUBAGENT_ASYNC_STARTED_EVENT,
42
+ SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
39
43
  TEMP_ROOT_DIR,
40
44
  getAsyncConfigPath,
41
45
  resolveChildMaxSubagentDepth,
42
46
  } from "../../shared/types.ts";
43
47
  import { nestedResultsPath, resolveInheritedNestedRouteFromEnv, resolveNestedParentAddressFromEnv, writeNestedEvent } from "../shared/nested-events.ts";
48
+ import { initialTurnBudgetState } from "../shared/turn-budget.ts";
49
+ import { validateToolBudgetConfig } from "../shared/tool-budget.ts";
44
50
  import type { ImportedAsyncRoot } from "./chain-root-attachment.ts";
45
51
 
46
52
  const require = createRequire(import.meta.url);
47
- const piPackageRoot = resolveSelesaiPackageRoot();
53
+ const selesaiPackageRoot = resolveSelesaiPackageRoot();
48
54
 
49
55
  function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefined {
50
56
  if (!fs.existsSync(packageJsonPath)) return undefined;
@@ -67,15 +73,15 @@ function resolveJitiCliFromPackageJson(packageJsonPath: string): string | undefi
67
73
  function resolveJitiCliPath(): string | undefined {
68
74
  const candidates: Array<() => string | undefined> = [
69
75
  () => require.resolve("jiti/package.json"),
70
- () => piPackageRoot
71
- ? createRequire(path.join(piPackageRoot, "package.json")).resolve("jiti/package.json")
76
+ () => selesaiPackageRoot
77
+ ? createRequire(path.join(selesaiPackageRoot, "package.json")).resolve("jiti/package.json")
72
78
  : undefined,
73
79
  () => {
74
80
  if (!process.argv[1]) return undefined;
75
81
  const piEntry = fs.realpathSync(process.argv[1]);
76
82
  return createRequire(piEntry).resolve("jiti/package.json");
77
83
  },
78
- () => piPackageRoot ? path.join(piPackageRoot, "node_modules", "jiti", "package.json") : undefined,
84
+ () => selesaiPackageRoot ? path.join(selesaiPackageRoot, "node_modules", "jiti", "package.json") : undefined,
79
85
  ];
80
86
  for (const candidate of candidates) {
81
87
  try {
@@ -100,6 +106,8 @@ interface AsyncExecutionContext {
100
106
  parentSessionId?: string;
101
107
  currentModelProvider?: string;
102
108
  currentModel?: ParentModel;
109
+ /** Optional model-scope enforcement resolved from subagent settings. */
110
+ modelScope?: ModelScopeConfig;
103
111
  }
104
112
 
105
113
  interface AsyncChainParams {
@@ -118,16 +126,24 @@ interface AsyncChainParams {
118
126
  sessionRoot?: string;
119
127
  chainSkills?: string[];
120
128
  sessionFilesByFlatIndex?: (string | undefined)[];
129
+ thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
121
130
  progressDir?: string;
122
131
  dynamicFanoutMaxItems?: number;
123
132
  maxSubagentDepth: number;
124
133
  worktreeSetupHook?: string;
125
134
  worktreeSetupHookTimeoutMs?: number;
135
+ worktreeBaseDir?: string;
126
136
  controlConfig?: ResolvedControlConfig;
127
137
  controlIntercomTarget?: string;
128
138
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
129
139
  nestedRoute?: NestedRouteInfo;
130
140
  acceptance?: AcceptanceInput;
141
+ timeoutMs?: number;
142
+ turnBudget?: ResolvedTurnBudget;
143
+ toolBudget?: ResolvedToolBudget;
144
+ configToolBudget?: ResolvedToolBudget;
145
+ /** Global cap on simultaneously-running subagent tasks within the async run. */
146
+ globalConcurrencyLimit?: number;
131
147
  }
132
148
 
133
149
  interface AsyncSingleParams {
@@ -145,16 +161,23 @@ interface AsyncSingleParams {
145
161
  skills?: string[];
146
162
  output?: string | boolean;
147
163
  outputMode?: "inline" | "file-only";
164
+ outputBaseDir?: string;
148
165
  modelOverride?: string;
166
+ thinkingOverride?: AgentConfig["thinking"];
149
167
  availableModels?: AvailableModelInfo[];
150
168
  maxSubagentDepth: number;
151
169
  worktreeSetupHook?: string;
152
170
  worktreeSetupHookTimeoutMs?: number;
171
+ worktreeBaseDir?: string;
153
172
  controlConfig?: ResolvedControlConfig;
154
173
  controlIntercomTarget?: string;
155
174
  childIntercomTarget?: (agent: string, index: number) => string | undefined;
156
175
  nestedRoute?: NestedRouteInfo;
157
176
  acceptance?: AcceptanceInput;
177
+ timeoutMs?: number;
178
+ turnBudget?: ResolvedTurnBudget;
179
+ toolBudget?: ResolvedToolBudget;
180
+ configToolBudget?: ResolvedToolBudget;
158
181
  }
159
182
 
160
183
  interface AsyncExecutionResult {
@@ -174,11 +197,16 @@ export interface AsyncRunnerStepBuildParams {
174
197
  cwd?: string;
175
198
  chainSkills?: string[];
176
199
  sessionFilesByFlatIndex?: (string | undefined)[];
200
+ thinkingOverridesByFlatIndex?: (AgentConfig["thinking"] | undefined)[];
177
201
  progressDir?: string;
178
202
  dynamicFanoutMaxItems?: number;
179
203
  maxSubagentDepth: number;
204
+ worktreeBaseDir?: string;
180
205
  asyncDir: string;
206
+ outputBaseDir?: string;
181
207
  validateOutputBindings?: boolean;
208
+ toolBudget?: ResolvedToolBudget;
209
+ configToolBudget?: ResolvedToolBudget;
182
210
  }
183
211
 
184
212
  export type AsyncRunnerStepBuildResult =
@@ -196,8 +224,8 @@ export function formatAsyncStartedMessage(headline: string): string {
196
224
  headline,
197
225
  "",
198
226
  "The async run is detached. Do not run sleep timers or polling loops just to wait for it.",
199
- "If you have independent work, continue that work. If you have nothing else to do until the async result arrives, end your turn now; Pi will deliver the completion when the run finishes.",
200
- "Use subagent({ action: \"status\", id: \"...\" }) when you need the current status/result, or to inspect a blocked/stale run. Do not poll just to wait.",
227
+ "If you have independent work, continue that work. When you have nothing left to do until the async result arrives, call wait() — it blocks until the run finishes and delivers the completion here. Only if you are certain you will get another turn (an interactive session where the user will prompt you again) can you instead stop and let Pi wake you; inside a skill that must run to completion, or in a non-interactive run, there is no next turn, so use wait().",
228
+ "Use subagent({ action: \"status\", id: \"...\" }) when you need a one-shot status/result or to inspect a blocked/stale run. To block until completion, prefer wait(). Do not poll in a loop just to wait.",
201
229
  ].join("\n");
202
230
  }
203
231
 
@@ -205,74 +233,58 @@ export function formatAsyncStartedMessage(headline: string): string {
205
233
  * Check if jiti is available for async execution
206
234
  */
207
235
  export function isAsyncAvailable(): boolean {
208
- return resolveRunnerScript() !== undefined || getSelesaiSpawnCommand(["--offline", "--mode", "json", "-p"], { ...(piPackageRoot ? { piPackageRoot } : {}) }).command !== "" || jitiCliPath !== undefined;
236
+ return jitiCliPath !== undefined;
209
237
  }
210
238
 
211
- function resolveAsyncRunnerNodeCommand(): string {
212
- const basename = path.basename(process.execPath).toLowerCase();
213
- if (basename === "node" || basename === "node.exe" || basename === "nodejs" || basename === "nodejs.exe") {
214
- return process.execPath;
215
- }
216
- return process.platform === "win32" ? "node.exe" : "node";
239
+ function isNodeExecutableName(execPath: string): boolean {
240
+ const basename = path.basename(execPath).toLowerCase();
241
+ return basename === "node" || basename === "node.exe" || basename === "nodejs" || basename === "nodejs.exe";
217
242
  }
218
243
 
219
- function resolveRunnerScript(): string | undefined {
220
- const localDir = path.dirname(fileURLToPath(import.meta.url));
221
- const candidates = [
222
- piPackageRoot ? path.join(piPackageRoot, "dist/runner/runs/background/subagent-runner.js") : undefined,
223
- path.join(localDir, "../../../dist/runner/runs/background/subagent-runner.js"),
224
- path.join(localDir, "subagent-runner.ts"),
225
- ].filter((candidate): candidate is string => Boolean(candidate));
226
- for (const candidate of candidates) {
227
- if (fs.existsSync(candidate)) return candidate;
244
+ function canUseCurrentNodeExecutable(execPath: string): boolean {
245
+ try {
246
+ fs.accessSync(execPath, process.platform === "win32" ? fs.constants.F_OK : fs.constants.X_OK);
247
+ return true;
248
+ } catch {
249
+ return false;
228
250
  }
229
- return undefined;
230
251
  }
231
252
 
232
- type AsyncRunnerSpawnMode = "compiled" | "cli" | "jiti";
233
-
234
- function resolveAsyncRunnerSpawnMode(): AsyncRunnerSpawnMode {
235
- if (process.env.SELESAI_SUBAGENT_RUNNER_USE_JITI === "1") return "jiti";
236
- const runnerScript = resolveRunnerScript();
237
- if (runnerScript?.endsWith(".js")) return "compiled";
238
- return "cli";
253
+ function resolveAsyncRunnerNodeCommand(): string {
254
+ if (isNodeExecutableName(process.execPath) && canUseCurrentNodeExecutable(process.execPath)) {
255
+ return process.execPath;
256
+ }
257
+ return process.platform === "win32" ? "node.exe" : "node";
239
258
  }
240
259
 
241
- interface SpawnCommandSpec {
242
- command: string;
243
- args: string[];
244
- env?: Record<string, string | undefined>;
260
+ export function resolveAsyncRunnerLogPaths(cfg: object): { stdoutPath: string; stderrPath: string } | undefined {
261
+ const asyncDir = typeof (cfg as { asyncDir?: unknown }).asyncDir === "string"
262
+ ? (cfg as { asyncDir: string }).asyncDir
263
+ : undefined;
264
+ if (!asyncDir) return undefined;
265
+ return {
266
+ stdoutPath: path.join(asyncDir, "runner.stdout.log"),
267
+ stderrPath: path.join(asyncDir, "runner.stderr.log"),
268
+ };
245
269
  }
246
270
 
247
- function buildAsyncRunnerSpawnSpec(cfgPath: string): SpawnCommandSpec | { error: string } {
248
- const mode = resolveAsyncRunnerSpawnMode();
249
- if (mode === "compiled") {
250
- const runnerScript = resolveRunnerScript();
251
- if (!runnerScript) return { error: "compiled async runner script could not be resolved" };
252
- return { command: resolveAsyncRunnerNodeCommand(), args: [runnerScript, cfgPath] };
253
- }
254
- if (mode === "cli") {
255
- const spawnSpec = getSelesaiSpawnCommand(["--offline", "--mode", "json", "-p"], {
256
- ...(piPackageRoot ? { piPackageRoot } : {}),
257
- });
258
- return {
259
- command: spawnSpec.command,
260
- args: spawnSpec.args,
261
- env: { ...process.env, SELESAI_SUBAGENT_RUNNER_CONFIG: cfgPath },
262
- };
263
- }
264
- if (!jitiCliPath) {
265
- return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
271
+ function closeFd(fd: number | undefined): void {
272
+ if (fd === undefined) return;
273
+ try {
274
+ fs.closeSync(fd);
275
+ } catch {
276
+ // Best-effort cleanup; child process already owns its duplicated stdio fd.
266
277
  }
267
- const runnerScript = resolveRunnerScript();
268
- if (!runnerScript) return { error: "async runner script could not be resolved" };
269
- return { command: resolveAsyncRunnerNodeCommand(), args: [jitiCliPath, runnerScript, cfgPath] };
270
278
  }
271
279
 
272
280
  /**
273
281
  * Spawn the async runner process
274
282
  */
275
283
  function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number; error?: string } {
284
+ if (!jitiCliPath) {
285
+ return { error: "upstream jiti for TypeScript execution could not be found; ensure package dependencies are installed" };
286
+ }
287
+
276
288
  try {
277
289
  const cwdStats = fs.statSync(cwd);
278
290
  if (!cwdStats.isDirectory()) {
@@ -285,32 +297,43 @@ function spawnRunner(cfg: object, suffix: string, cwd: string): { pid?: number;
285
297
  fs.mkdirSync(TEMP_ROOT_DIR, { recursive: true });
286
298
  const cfgPath = getAsyncConfigPath(suffix);
287
299
  fs.writeFileSync(cfgPath, JSON.stringify(cfg));
288
- const asyncDir = (cfg as { asyncDir?: string }).asyncDir;
289
- const eventsPath = asyncDir ? path.join(asyncDir, "events.jsonl") : undefined;
290
- const markers = createSpawnTimingMarkers();
291
- emitSpawnTiming({ eventsPath, marker: "spawn_start", markers, runId: suffix });
292
-
293
- const spec = buildAsyncRunnerSpawnSpec(cfgPath);
294
- if ("error" in spec) {
295
- return { error: spec.error };
296
- }
297
- emitSpawnTiming({ eventsPath, marker: "spawn_command_resolved", markers, runId: suffix });
300
+ const runner = path.join(path.dirname(fileURLToPath(import.meta.url)), "subagent-runner.ts");
301
+ const nodeCommand = resolveAsyncRunnerNodeCommand();
298
302
 
299
- const proc = spawn(spec.command, spec.args, {
300
- cwd,
301
- detached: true,
302
- stdio: "ignore",
303
- windowsHide: true,
304
- ...(spec.env ? { env: spec.env } : {}),
305
- });
306
- proc.on("error", (error) => {
307
- console.error(`[pi-subagents] async spawn failed: ${error.message}`);
308
- });
309
- if (typeof proc.pid !== "number") {
310
- return { error: `async runner did not produce a pid for cwd: ${cwd}` };
303
+ const logPaths = resolveAsyncRunnerLogPaths(cfg);
304
+ let stdoutFd: number | undefined;
305
+ let stderrFd: number | undefined;
306
+ try {
307
+ if (logPaths) {
308
+ fs.mkdirSync(path.dirname(logPaths.stdoutPath), { recursive: true });
309
+ stdoutFd = fs.openSync(logPaths.stdoutPath, "a");
310
+ stderrFd = fs.openSync(logPaths.stderrPath, "a");
311
+ }
312
+ const proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {
313
+ cwd,
314
+ detached: true,
315
+ stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
316
+ windowsHide: true,
317
+ env: {
318
+ ...process.env,
319
+ ...(selesaiPackageRoot ? { [SELESAI_CODING_AGENT_PACKAGE_ROOT_ENV]: selesaiPackageRoot } : {}),
320
+ },
321
+ });
322
+ closeFd(stdoutFd);
323
+ closeFd(stderrFd);
324
+ proc.on("error", (error) => {
325
+ console.error(`[pi-subagents] async spawn failed: ${error.message}`);
326
+ });
327
+ if (typeof proc.pid !== "number") {
328
+ return { error: `async runner did not produce a pid for cwd: ${cwd}` };
329
+ }
330
+ proc.unref();
331
+ return { pid: proc.pid };
332
+ } catch (error) {
333
+ closeFd(stdoutFd);
334
+ closeFd(stderrFd);
335
+ return { error: error instanceof Error ? error.message : String(error) };
311
336
  }
312
- proc.unref();
313
- return { pid: proc.pid };
314
337
  }
315
338
 
316
339
  function formatAsyncStartError(mode: SubagentRunMode, message: string): AsyncExecutionResult {
@@ -333,9 +356,12 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
333
356
  ctx,
334
357
  cwd,
335
358
  sessionFilesByFlatIndex,
359
+ thinkingOverridesByFlatIndex,
336
360
  maxSubagentDepth,
361
+ worktreeBaseDir,
337
362
  asyncDir,
338
363
  } = params;
364
+ const outputBaseDir = params.outputBaseDir;
339
365
  const resultMode = params.resultMode ?? "chain";
340
366
  const chainSkills = params.chainSkills ?? [];
341
367
  const availableModels = params.availableModels;
@@ -392,8 +418,11 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
392
418
  ...(s.model ? { model: s.model } : {}),
393
419
  };
394
420
  };
395
- const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior) => {
421
+ const buildSeqStep = (s: SequentialStep, sessionFile?: string, behaviorCwd?: string, progressPrecreated = false, resolvedBehavior?: ResolvedStepBehavior, flatIndex?: number) => {
396
422
  const a = agents.find((x) => x.name === s.agent)!;
423
+ const toolBudgetInput = s.toolBudget ?? params.toolBudget ?? a.toolBudget ?? params.configToolBudget;
424
+ const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, s.toolBudget ? "toolBudget" : a.toolBudget ? "agent.toolBudget" : "config.toolBudget");
425
+ if (resolvedToolBudget.error) throw new AsyncStartValidationError(resolvedToolBudget.error);
397
426
  const stepCwd = resolveChildCwd(runnerCwd, s.cwd);
398
427
  const instructionCwd = behaviorCwd ?? stepCwd;
399
428
  const behavior = suppressProgressForReadOnlyTask(resolvedBehavior ?? resolveStepBehavior(a, buildStepOverrides(s), chainSkills), s.task, originalTask);
@@ -406,12 +435,16 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
406
435
  const injection = buildSkillInjection(resolvedSkills);
407
436
  systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
408
437
  }
438
+ const memoryInjection = buildAgentMemoryInjection(a, stepCwd);
439
+ if (memoryInjection) {
440
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
441
+ }
409
442
 
410
443
  const readInstructions = buildChainInstructions({ ...behavior, output: false, progress: false }, instructionCwd, false);
411
444
  const isFirstProgressAgent = behavior.progress && !progressPrecreated && !progressInstructionCreated;
412
445
  if (behavior.progress) progressInstructionCreated = true;
413
446
  const progressInstructions = buildChainInstructions({ ...behavior, output: false, reads: false }, progressDir, isFirstProgressAgent);
414
- const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd);
447
+ const outputPath = resolveSingleOutputPath(behavior.output, ctx.cwd, instructionCwd, outputBaseDir);
415
448
  systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
416
449
  const validationError = validateFileOnlyOutputMode(behavior.outputMode, outputPath, `Async step (${s.agent})`);
417
450
  if (validationError) throw new AsyncStartValidationError(validationError);
@@ -421,8 +454,10 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
421
454
  const task = injectSingleOutputInstruction(`${readInstructions.prefix}${taskTemplate}${progressInstructions.suffix}`, outputPath);
422
455
 
423
456
  const requestedModel = behavior.model ?? a.model;
424
- const primaryModel = resolveSubagentModelOverride(requestedModel, ctx.currentModel, availableModels, ctx.currentModelProvider);
425
- const model = applyThinkingSuffix(primaryModel, a.thinking);
457
+ const primaryModel = resolveSubagentModelOverride(requestedModel, ctx.currentModel, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope, source: behavior.model ? "explicit" : "inherited" });
458
+ const thinkingOverride = flatIndex === undefined ? undefined : thinkingOverridesByFlatIndex?.[flatIndex];
459
+ const effectiveThinking = thinkingOverride ?? a.thinking;
460
+ const model = applyThinkingSuffix(primaryModel, effectiveThinking, thinkingOverride !== undefined);
426
461
  return {
427
462
  parentSessionId: ctx.parentSessionId ?? ctx.currentSessionId,
428
463
  agent: s.agent,
@@ -433,9 +468,9 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
433
468
  structured: Boolean(s.outputSchema),
434
469
  cwd: stepCwd,
435
470
  model,
436
- thinking: resolveEffectiveThinking(model, a.thinking),
437
- modelCandidates: buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
438
- applyThinkingSuffix(candidate, a.thinking),
471
+ thinking: resolveEffectiveThinking(model, effectiveThinking),
472
+ modelCandidates: buildModelCandidates(primaryModel, a.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) =>
473
+ applyThinkingSuffix(candidate, effectiveThinking, thinkingOverride !== undefined),
439
474
  ),
440
475
  tools: a.tools,
441
476
  extensions: a.extensions,
@@ -461,14 +496,21 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
461
496
  }),
462
497
  ...(s.outputSchema ? { structuredOutputSchema: s.outputSchema } : {}),
463
498
  ...(s.outputSchema ? { structuredOutput: createStructuredOutputRuntime(s.outputSchema, path.join(asyncDir, "structured-output")) } : {}),
499
+ ...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
464
500
  };
465
501
  };
466
502
 
467
503
  let flatStepIndex = 0;
468
- const nextSessionFile = (): string | undefined => {
504
+ const nextFlatStep = (): { index: number; sessionFile?: string; thinkingOverride?: AgentConfig["thinking"] } => {
505
+ const index = flatStepIndex;
469
506
  const sessionFile = sessionFilesByFlatIndex?.[flatStepIndex];
507
+ const thinkingOverride = thinkingOverridesByFlatIndex?.[flatStepIndex];
470
508
  flatStepIndex++;
471
- return sessionFile;
509
+ return {
510
+ index,
511
+ ...(sessionFile ? { sessionFile } : {}),
512
+ ...(thinkingOverride ? { thinkingOverride } : {}),
513
+ };
472
514
  };
473
515
 
474
516
  try {
@@ -488,12 +530,13 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
488
530
  let behaviorCwd: string | undefined;
489
531
  if (s.worktree) {
490
532
  try {
491
- behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, taskIndex);
533
+ behaviorCwd = resolveExpectedWorktreeAgentCwd(runnerCwd, `${id}-s${stepIndex}`, taskIndex, worktreeBaseDir);
492
534
  } catch {
493
535
  behaviorCwd = undefined;
494
536
  }
495
537
  }
496
- return buildSeqStep(t, nextSessionFile(), behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex]);
538
+ const staticStep = nextFlatStep();
539
+ return buildSeqStep(t, staticStep.sessionFile, behaviorCwd, progressPrecreated, parallelBehaviors[taskIndex], staticStep.index);
497
540
  }),
498
541
  concurrency: s.concurrency,
499
542
  failFast: s.failFast,
@@ -508,6 +551,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
508
551
  writeInitialProgressFile(progressDir);
509
552
  progressInstructionCreated = true;
510
553
  }
554
+ const maxItems = s.expand.maxItems ?? params.dynamicFanoutMaxItems ?? 0;
555
+ const dynamicFlatSteps = Array.from({ length: maxItems }, () => nextFlatStep());
511
556
  return {
512
557
  expand: s.expand,
513
558
  parallel: buildSeqStep(s.parallel as SequentialStep, undefined, undefined, progressPrecreated, behavior),
@@ -516,6 +561,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
516
561
  failFast: s.failFast,
517
562
  phase: s.phase,
518
563
  label: s.label,
564
+ sessionFiles: dynamicFlatSteps.map((step) => step.sessionFile),
565
+ thinkingOverrides: dynamicFlatSteps.map((step) => step.thinkingOverride),
519
566
  effectiveAcceptance: resolveEffectiveAcceptance({
520
567
  explicit: s.acceptance,
521
568
  agentName: s.parallel.agent,
@@ -526,7 +573,8 @@ export function buildAsyncRunnerSteps(id: string, params: AsyncRunnerStepBuildPa
526
573
  }),
527
574
  };
528
575
  }
529
- return buildSeqStep(s as SequentialStep, nextSessionFile());
576
+ const staticStep = nextFlatStep();
577
+ return buildSeqStep(s as SequentialStep, staticStep.sessionFile, undefined, false, undefined, staticStep.index);
530
578
  });
531
579
  const steps = params.attachRoot
532
580
  ? [{
@@ -569,9 +617,11 @@ export function executeAsyncChain(
569
617
  shareEnabled,
570
618
  sessionRoot,
571
619
  sessionFilesByFlatIndex,
620
+ thinkingOverridesByFlatIndex,
572
621
  maxSubagentDepth,
573
622
  worktreeSetupHook,
574
623
  worktreeSetupHookTimeoutMs,
624
+ worktreeBaseDir,
575
625
  controlConfig,
576
626
  controlIntercomTarget,
577
627
  childIntercomTarget,
@@ -605,10 +655,15 @@ export function executeAsyncChain(
605
655
  cwd,
606
656
  chainSkills: params.chainSkills,
607
657
  sessionFilesByFlatIndex,
608
- progressDir: params.progressDir ?? (resultMode === "parallel" ? path.join(asyncDir, "progress") : undefined),
658
+ thinkingOverridesByFlatIndex,
659
+ progressDir: params.progressDir ?? (artifactsDir ? path.join(artifactsDir, "progress", id) : resultMode === "parallel" ? path.join(asyncDir, "progress") : undefined),
660
+ outputBaseDir: artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined,
609
661
  dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
610
662
  maxSubagentDepth,
663
+ worktreeBaseDir,
611
664
  asyncDir,
665
+ toolBudget: params.toolBudget,
666
+ configToolBudget: params.configToolBudget,
612
667
  });
613
668
  if ("error" in built) {
614
669
  try {
@@ -619,6 +674,8 @@ export function executeAsyncChain(
619
674
  return formatAsyncStartError(resultMode, built.error);
620
675
  }
621
676
  const { steps, runnerCwd, workflowGraph, eventChain } = built;
677
+ const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
678
+ const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
622
679
  let childTargetIndex = 0;
623
680
  const childIntercomTargets = childIntercomTarget ? steps.flatMap((step) => {
624
681
  if (!("parallel" in step) && step.importAsyncRoot) {
@@ -651,15 +708,21 @@ export function executeAsyncChain(
651
708
  sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
652
709
  asyncDir,
653
710
  sessionId: ctx.currentSessionId,
654
- piPackageRoot,
711
+ selesaiPackageRoot,
655
712
  piArgv1: process.argv[1],
656
713
  worktreeSetupHook,
657
714
  worktreeSetupHookTimeoutMs,
715
+ worktreeBaseDir,
658
716
  controlConfig,
717
+ turnBudget: params.turnBudget,
718
+ toolBudget: params.toolBudget,
659
719
  controlIntercomTarget,
660
720
  childIntercomTargets,
661
721
  resultMode,
662
722
  dynamicFanoutMaxItems: params.dynamicFanoutMaxItems,
723
+ timeoutMs: params.timeoutMs,
724
+ deadlineAt,
725
+ globalConcurrencyLimit: params.globalConcurrencyLimit,
663
726
  workflowGraph,
664
727
  nestedRoute: nestedRoute ?? inheritedNestedRoute,
665
728
  nestedSelf: inheritedNestedRoute && nestedAddress ? {
@@ -732,6 +795,8 @@ export function executeAsyncChain(
732
795
  agents: flatAgents,
733
796
  chainStepCount: eventChain.length,
734
797
  parallelGroups,
798
+ ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
799
+ ...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
735
800
  startedAt: now,
736
801
  lastUpdate: now,
737
802
  },
@@ -741,6 +806,7 @@ export function executeAsyncChain(
741
806
  }
742
807
  }
743
808
  ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
809
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
744
810
  id,
745
811
  pid: spawnResult.pid,
746
812
  sessionId: ctx.currentSessionId,
@@ -760,6 +826,8 @@ export function executeAsyncChain(
760
826
  workflowGraph,
761
827
  cwd: runnerCwd,
762
828
  asyncDir,
829
+ ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
830
+ ...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
763
831
  nestedRoute,
764
832
  });
765
833
  }
@@ -772,7 +840,7 @@ export function executeAsyncChain(
772
840
 
773
841
  return {
774
842
  content: [{ type: "text", text: formatAsyncStartedMessage(`Async ${resultMode}: ${chainDesc} [${id}]`) }],
775
- details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph },
843
+ details: { mode: resultMode, runId: id, results: [], asyncId: id, asyncDir, workflowGraph, ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
776
844
  };
777
845
  }
778
846
 
@@ -797,6 +865,7 @@ export function executeAsyncSingle(
797
865
  maxSubagentDepth,
798
866
  worktreeSetupHook,
799
867
  worktreeSetupHookTimeoutMs,
868
+ worktreeBaseDir,
800
869
  controlConfig,
801
870
  controlIntercomTarget,
802
871
  childIntercomTarget,
@@ -813,6 +882,10 @@ export function executeAsyncSingle(
813
882
  const injection = buildSkillInjection(resolvedSkills);
814
883
  systemPrompt = systemPrompt ? `${systemPrompt}\n\n${injection}` : injection;
815
884
  }
885
+ const memoryInjection = buildAgentMemoryInjection(agentConfig, runnerCwd);
886
+ if (memoryInjection) {
887
+ systemPrompt = systemPrompt ? `${systemPrompt}\n\n${memoryInjection}` : memoryInjection;
888
+ }
816
889
 
817
890
  const inheritedNestedRoute = resolveInheritedNestedRouteFromEnv();
818
891
  const nestedAddress = inheritedNestedRoute ? resolveNestedParentAddressFromEnv() : undefined;
@@ -831,7 +904,7 @@ export function executeAsyncSingle(
831
904
  }
832
905
 
833
906
  const effectiveOutput = normalizeSingleOutputOverride(params.output, agentConfig.output);
834
- const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd);
907
+ const outputPath = resolveSingleOutputPath(effectiveOutput, ctx.cwd, runnerCwd, params.outputBaseDir ?? (artifactsDir ? path.join(artifactsDir, "outputs", id) : undefined));
835
908
  systemPrompt = injectOutputPathSystemPrompt(systemPrompt, outputPath);
836
909
  const outputMode = params.outputMode ?? "inline";
837
910
  const validationError = validateFileOnlyOutputMode(outputMode, outputPath, `Async single run (${agent})`);
@@ -843,7 +916,13 @@ export function executeAsyncSingle(
843
916
  availableModels,
844
917
  ctx.currentModelProvider,
845
918
  );
846
- const model = applyThinkingSuffix(primaryModel, agentConfig.thinking);
919
+ const effectiveThinking = params.thinkingOverride ?? agentConfig.thinking;
920
+ const model = applyThinkingSuffix(primaryModel, effectiveThinking, params.thinkingOverride !== undefined);
921
+ const toolBudgetInput = params.toolBudget ?? agentConfig.toolBudget ?? params.configToolBudget;
922
+ const resolvedToolBudget = validateToolBudgetConfig(toolBudgetInput, params.toolBudget ? "toolBudget" : agentConfig.toolBudget ? "agent.toolBudget" : "config.toolBudget");
923
+ if (resolvedToolBudget.error) return formatAsyncStartError("single", resolvedToolBudget.error);
924
+ const deadlineAt = params.timeoutMs !== undefined ? Date.now() + params.timeoutMs : undefined;
925
+ const initialTurnBudget = params.turnBudget ? initialTurnBudgetState(params.turnBudget) : undefined;
847
926
  let spawnResult: { pid?: number; error?: string } = {};
848
927
  try {
849
928
  spawnResult = spawnRunner(
@@ -856,9 +935,9 @@ export function executeAsyncSingle(
856
935
  task: taskWithOutputInstruction,
857
936
  cwd: runnerCwd,
858
937
  model,
859
- thinking: resolveEffectiveThinking(model, agentConfig.thinking),
860
- modelCandidates: buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider).map((candidate) =>
861
- applyThinkingSuffix(candidate, agentConfig.thinking),
938
+ thinking: resolveEffectiveThinking(model, effectiveThinking),
939
+ modelCandidates: buildModelCandidates(primaryModel, agentConfig.fallbackModels, availableModels, ctx.currentModelProvider, { scope: ctx.modelScope }).map((candidate) =>
940
+ applyThinkingSuffix(candidate, effectiveThinking, params.thinkingOverride !== undefined),
862
941
  ),
863
942
  tools: agentConfig.tools,
864
943
  extensions: agentConfig.extensions,
@@ -881,6 +960,7 @@ export function executeAsyncSingle(
881
960
  mode: "single",
882
961
  async: true,
883
962
  }),
963
+ ...(resolvedToolBudget.budget ? { toolBudget: resolvedToolBudget.budget } : {}),
884
964
  },
885
965
  ],
886
966
  resultPath: inheritedNestedRoute ? nestedResultsPath(inheritedNestedRoute.rootRunId, id) : path.join(RESULTS_DIR, `${id}.json`),
@@ -893,11 +973,16 @@ export function executeAsyncSingle(
893
973
  sessionDir: sessionRoot ? path.join(sessionRoot, `async-${id}`) : undefined,
894
974
  asyncDir,
895
975
  sessionId: ctx.currentSessionId,
896
- piPackageRoot,
976
+ selesaiPackageRoot,
897
977
  piArgv1: process.argv[1],
898
978
  worktreeSetupHook,
899
979
  worktreeSetupHookTimeoutMs,
980
+ worktreeBaseDir,
900
981
  controlConfig,
982
+ timeoutMs: params.timeoutMs,
983
+ deadlineAt,
984
+ turnBudget: params.turnBudget,
985
+ toolBudget: params.toolBudget,
901
986
  controlIntercomTarget,
902
987
  childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
903
988
  resultMode: "single",
@@ -947,6 +1032,8 @@ export function executeAsyncSingle(
947
1032
  agent,
948
1033
  agents: [agent],
949
1034
  chainStepCount: 1,
1035
+ ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
1036
+ ...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
950
1037
  startedAt: now,
951
1038
  lastUpdate: now,
952
1039
  },
@@ -956,6 +1043,7 @@ export function executeAsyncSingle(
956
1043
  }
957
1044
  }
958
1045
  ctx.pi.events.emit(SUBAGENT_ASYNC_STARTED_EVENT, {
1046
+ lifecycleArtifactVersion: SUBAGENT_LIFECYCLE_ARTIFACT_VERSION,
959
1047
  id,
960
1048
  pid: spawnResult.pid,
961
1049
  sessionId: ctx.currentSessionId,
@@ -964,12 +1052,14 @@ export function executeAsyncSingle(
964
1052
  task: task?.slice(0, 50),
965
1053
  cwd: runnerCwd,
966
1054
  asyncDir,
1055
+ ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}),
1056
+ ...(initialTurnBudget ? { turnBudget: initialTurnBudget } : {}),
967
1057
  nestedRoute,
968
1058
  });
969
1059
  }
970
1060
 
971
1061
  return {
972
1062
  content: [{ type: "text", text: formatAsyncStartedMessage(`Async: ${agent} [${id}]`) }],
973
- details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir },
1063
+ details: { mode: "single", runId: id, results: [], asyncId: id, asyncDir, ...(params.timeoutMs !== undefined ? { timeoutMs: params.timeoutMs, deadlineAt } : {}), ...(params.turnBudget ? { turnBudget: params.turnBudget } : {}), ...(params.toolBudget ? { toolBudget: params.toolBudget } : {}) },
974
1064
  };
975
1065
  }