@entelligentsia/forgecli 1.1.0 → 1.1.3

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/CHANGELOG.md +26 -0
  2. package/README.md +4 -0
  3. package/dist/CHANGELOG-forge-plugin.md +114 -0
  4. package/dist/CHANGELOG-pi.md +56 -0
  5. package/dist/extensions/forgecli/forge-subagent.js +58 -0
  6. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  7. package/dist/extensions/forgecli/mcp-bridge/grove.d.ts +38 -0
  8. package/dist/extensions/forgecli/mcp-bridge/grove.js +88 -2
  9. package/dist/extensions/forgecli/mcp-bridge/grove.js.map +1 -1
  10. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.d.ts +26 -2
  11. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.js +68 -13
  12. package/dist/extensions/forgecli/mcp-bridge/json-rpc-stdio.js.map +1 -1
  13. package/dist/extensions/forgecli/mcp-bridge/mcp-bridge.js +13 -2
  14. package/dist/extensions/forgecli/mcp-bridge/mcp-bridge.js.map +1 -1
  15. package/dist/extensions/forgecli/mcp-bridge/mcp-session.d.ts +3 -3
  16. package/dist/extensions/forgecli/mcp-bridge/mcp-session.js +3 -6
  17. package/dist/extensions/forgecli/mcp-bridge/mcp-session.js.map +1 -1
  18. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.d.ts +49 -40
  19. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.js +59 -188
  20. package/dist/extensions/forgecli/orchestrators/init/init-phase-dispatch.js.map +1 -1
  21. package/dist/extensions/forgecli/orchestrators/init/init-phases.d.ts +0 -43
  22. package/dist/extensions/forgecli/orchestrators/init/init-phases.js +11 -59
  23. package/dist/extensions/forgecli/orchestrators/init/init-phases.js.map +1 -1
  24. package/dist/extensions/forgecli/orchestrators/init/init-steps.d.ts +115 -0
  25. package/dist/extensions/forgecli/orchestrators/init/init-steps.js +125 -0
  26. package/dist/extensions/forgecli/orchestrators/init/init-steps.js.map +1 -0
  27. package/dist/extensions/forgecli/orchestrators/init/run-init-pipeline.js +400 -211
  28. package/dist/extensions/forgecli/orchestrators/init/run-init-pipeline.js.map +1 -1
  29. package/dist/extensions/forgecli/orchestrators/init/run-init-types.d.ts +8 -2
  30. package/dist/extensions/forgecli/orchestrators/init/run-init-types.js +15 -5
  31. package/dist/extensions/forgecli/orchestrators/init/run-init-types.js.map +1 -1
  32. package/dist/forge-payload/.base-pack/workflows-js/wfl-init.js +99 -38
  33. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  34. package/dist/forge-payload/.init/generation/generate-kb-doc.md +24 -0
  35. package/dist/forge-payload/.schemas/migrations.json +77 -0
  36. package/dist/forge-payload/init/generation/generate-kb-doc.md +24 -0
  37. package/dist/forge-payload/init/phases/phase-2/context.md +23 -0
  38. package/dist/forge-payload/init/phases/phase-2/database.md +26 -0
  39. package/dist/forge-payload/init/phases/phase-2/deployment.md +26 -0
  40. package/dist/forge-payload/init/phases/phase-2/domain-concepts.md +26 -0
  41. package/dist/forge-payload/init/phases/phase-2/domain-model.md +26 -0
  42. package/dist/forge-payload/init/phases/phase-2/entity-model.md +26 -0
  43. package/dist/forge-payload/init/phases/phase-2/index.md +25 -0
  44. package/dist/forge-payload/init/phases/phase-2/processes.md +26 -0
  45. package/dist/forge-payload/init/phases/phase-2/routing.md +25 -0
  46. package/dist/forge-payload/init/phases/phase-2/stack-checklist.md +25 -0
  47. package/dist/forge-payload/init/phases/phase-2/stack.md +27 -0
  48. package/dist/forge-payload/init/phases/phase-2/testing.md +26 -0
  49. package/dist/forge-payload/init/phases/phase-2-discover.md +15 -9
  50. package/dist/forge-payload/payload-manifest.json +2 -2
  51. package/dist/forge-payload/tools/seed-store.cjs +14 -0
  52. package/dist/forge-payload/tools/verify-phase.cjs +61 -4
  53. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts +3 -1
  54. package/node_modules/@earendil-works/pi-agent-core/dist/agent.d.ts.map +1 -1
  55. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js +10 -1
  56. package/node_modules/@earendil-works/pi-agent-core/dist/agent.js.map +1 -1
  57. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.d.ts.map +1 -1
  58. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js +4 -6
  59. package/node_modules/@earendil-works/pi-agent-core/dist/harness/compaction/compaction.js.map +1 -1
  60. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.d.ts.map +1 -1
  61. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js +20 -2
  62. package/node_modules/@earendil-works/pi-agent-core/dist/harness/env/nodejs.js.map +1 -1
  63. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  64. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.d.ts.map +1 -1
  65. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js +2 -14
  66. package/node_modules/@earendil-works/pi-ai/dist/api/azure-openai-responses.js.map +1 -1
  67. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.d.ts.map +1 -1
  68. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js +16 -5
  69. package/node_modules/@earendil-works/pi-ai/dist/api/bedrock-converse-stream.js.map +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.d.ts.map +1 -1
  71. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js +2 -1
  72. package/node_modules/@earendil-works/pi-ai/dist/api/google-generative-ai.js.map +1 -1
  73. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.d.ts.map +1 -1
  74. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js +2 -1
  75. package/node_modules/@earendil-works/pi-ai/dist/api/google-vertex.js.map +1 -1
  76. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.d.ts.map +1 -1
  77. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js +61 -41
  78. package/node_modules/@earendil-works/pi-ai/dist/api/openai-codex-responses.js.map +1 -1
  79. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.d.ts.map +1 -1
  80. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js +8 -3
  81. package/node_modules/@earendil-works/pi-ai/dist/api/openai-completions.js.map +1 -1
  82. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.d.ts.map +1 -1
  83. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js +2 -14
  84. package/node_modules/@earendil-works/pi-ai/dist/api/openai-responses.js.map +1 -1
  85. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.d.ts.map +1 -1
  86. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js +2 -1
  87. package/node_modules/@earendil-works/pi-ai/dist/api/openrouter-images.js.map +1 -1
  88. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts +15 -45
  89. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.d.ts.map +1 -1
  90. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js +15 -45
  91. package/node_modules/@earendil-works/pi-ai/dist/image-models.generated.js.map +1 -1
  92. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +461 -22
  93. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  94. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts +140 -0
  95. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.d.ts.map +1 -1
  96. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js +141 -4
  97. package/node_modules/@earendil-works/pi-ai/dist/providers/amazon-bedrock.models.js.map +1 -1
  98. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts +20 -0
  99. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.d.ts.map +1 -1
  100. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js +18 -0
  101. package/node_modules/@earendil-works/pi-ai/dist/providers/anthropic.models.js.map +1 -1
  102. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts +21 -0
  103. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.d.ts.map +1 -1
  104. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js +18 -0
  105. package/node_modules/@earendil-works/pi-ai/dist/providers/cerebras.models.js.map +1 -1
  106. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts +21 -0
  107. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.d.ts.map +1 -1
  108. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js +18 -0
  109. package/node_modules/@earendil-works/pi-ai/dist/providers/cloudflare-ai-gateway.models.js.map +1 -1
  110. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts +10 -5
  111. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.d.ts.map +1 -1
  112. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js +4 -3
  113. package/node_modules/@earendil-works/pi-ai/dist/providers/fireworks.models.js.map +1 -1
  114. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts +82 -0
  115. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.d.ts.map +1 -1
  116. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js +57 -0
  117. package/node_modules/@earendil-works/pi-ai/dist/providers/github-copilot.models.js.map +1 -1
  118. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.d.ts.map +1 -1
  119. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js +1 -1
  120. package/node_modules/@earendil-works/pi-ai/dist/providers/groq.models.js.map +1 -1
  121. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts +1 -1
  122. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.d.ts.map +1 -1
  123. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js +4 -4
  124. package/node_modules/@earendil-works/pi-ai/dist/providers/mistral.models.js.map +1 -1
  125. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts +1 -1
  126. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.d.ts.map +1 -1
  127. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js +4 -4
  128. package/node_modules/@earendil-works/pi-ai/dist/providers/nvidia.models.js.map +1 -1
  129. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.d.ts.map +1 -1
  130. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js +5 -5
  131. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode-go.models.js.map +1 -1
  132. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts +88 -0
  133. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.d.ts.map +1 -1
  134. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js +75 -2
  135. package/node_modules/@earendil-works/pi-ai/dist/providers/opencode.models.js.map +1 -1
  136. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts +33 -15
  137. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.d.ts.map +1 -1
  138. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js +101 -84
  139. package/node_modules/@earendil-works/pi-ai/dist/providers/openrouter.models.js.map +1 -1
  140. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.d.ts.map +1 -1
  141. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js +2 -2
  142. package/node_modules/@earendil-works/pi-ai/dist/providers/together.models.js.map +1 -1
  143. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts +44 -0
  144. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.d.ts.map +1 -1
  145. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js +41 -4
  146. package/node_modules/@earendil-works/pi-ai/dist/providers/vercel-ai-gateway.models.js.map +1 -1
  147. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.d.ts.map +1 -1
  148. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js +12 -12
  149. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-ams.models.js.map +1 -1
  150. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.d.ts.map +1 -1
  151. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js +12 -12
  152. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-cn.models.js.map +1 -1
  153. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.d.ts.map +1 -1
  154. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js +12 -12
  155. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi-token-plan-sgp.models.js.map +1 -1
  156. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.d.ts.map +1 -1
  157. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js +15 -15
  158. package/node_modules/@earendil-works/pi-ai/dist/providers/xiaomi.models.js.map +1 -1
  159. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts +25 -0
  160. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.d.ts.map +1 -0
  161. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js +109 -0
  162. package/node_modules/@earendil-works/pi-ai/dist/utils/error-body.js.map +1 -0
  163. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts +2 -0
  164. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.d.ts.map +1 -1
  165. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js +15 -2
  166. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/device-code.js.map +1 -1
  167. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.d.ts.map +1 -1
  168. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js +3 -2
  169. package/node_modules/@earendil-works/pi-ai/dist/utils/oauth/github-copilot.js.map +1 -1
  170. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -0
  171. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  172. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +3 -0
  173. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  174. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.d.ts.map +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js +1 -0
  176. package/node_modules/@earendil-works/pi-ai/dist/utils/retry.js.map +1 -1
  177. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  178. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +56 -0
  179. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts +6 -1
  180. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
  181. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js +39 -15
  182. package/node_modules/@earendil-works/pi-coding-agent/dist/core/agent-session.js.map +1 -1
  183. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.d.ts.map +1 -1
  184. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js +13 -5
  185. package/node_modules/@earendil-works/pi-coding-agent/dist/core/auth-storage.js.map +1 -1
  186. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.d.ts.map +1 -1
  187. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js +5 -8
  188. package/node_modules/@earendil-works/pi-coding-agent/dist/core/compaction/compaction.js.map +1 -1
  189. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts +1 -1
  190. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.d.ts.map +1 -1
  191. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/index.js.map +1 -1
  192. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
  193. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js +6 -0
  194. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
  195. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -1
  196. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
  197. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js +33 -1
  198. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
  199. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts +16 -2
  200. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
  201. package/node_modules/@earendil-works/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
  202. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.d.ts.map +1 -1
  203. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js +28 -1
  204. package/node_modules/@earendil-works/pi-coding-agent/dist/core/http-dispatcher.js.map +1 -1
  205. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts +10 -0
  206. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.d.ts.map +1 -1
  207. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js +14 -17
  208. package/node_modules/@earendil-works/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
  209. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.d.ts.map +1 -1
  210. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js +0 -10
  211. package/node_modules/@earendil-works/pi-coding-agent/dist/core/provider-attribution.js.map +1 -1
  212. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts +20 -1
  213. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
  214. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js +87 -68
  215. package/node_modules/@earendil-works/pi-coding-agent/dist/core/session-manager.js.map +1 -1
  216. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
  217. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
  218. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js +8 -0
  219. package/node_modules/@earendil-works/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
  220. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  221. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +20 -5
  222. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  223. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  224. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +2 -2
  225. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  226. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts +3 -2
  227. package/node_modules/@earendil-works/pi-coding-agent/dist/index.d.ts.map +1 -1
  228. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js +2 -1
  229. package/node_modules/@earendil-works/pi-coding-agent/dist/index.js.map +1 -1
  230. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts +3 -1
  231. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  232. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js +15 -7
  233. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
  234. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.d.ts +19 -0
  235. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.d.ts.map +1 -0
  236. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.js +52 -0
  237. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/custom-entry.js.map +1 -0
  238. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  239. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  240. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +13 -1
  241. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
  242. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts +28 -0
  243. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
  244. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js +60 -0
  245. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/status-indicator.js.map +1 -0
  246. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts +6 -2
  247. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.d.ts.map +1 -1
  248. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js +18 -5
  249. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/components/user-message.js.map +1 -1
  250. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +10 -10
  251. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  252. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js +143 -130
  253. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
  254. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +15 -0
  255. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  256. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +14 -0
  257. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  258. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  259. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +16 -0
  260. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  261. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts +26 -0
  262. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  263. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-types.js.map +1 -1
  264. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.d.ts.map +1 -1
  265. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js +7 -0
  266. package/node_modules/@earendil-works/pi-coding-agent/dist/package-manager-cli.js.map +1 -1
  267. package/node_modules/@earendil-works/pi-coding-agent/docs/extensions.md +55 -8
  268. package/node_modules/@earendil-works/pi-coding-agent/docs/rpc.md +58 -0
  269. package/node_modules/@earendil-works/pi-coding-agent/docs/sdk.md +28 -0
  270. package/node_modules/@earendil-works/pi-coding-agent/docs/session-format.md +18 -8
  271. package/node_modules/@earendil-works/pi-coding-agent/docs/settings.md +1 -0
  272. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/README.md +1 -0
  273. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  274. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  275. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/entry-renderer.ts +41 -0
  276. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package-lock.json +2 -2
  277. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/gondolin/package.json +1 -1
  278. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/question.ts +1 -0
  279. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  280. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  281. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -12
  282. package/node_modules/@earendil-works/pi-coding-agent/package.json +4 -4
  283. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  284. package/package.json +7 -7
@@ -0,0 +1,26 @@
1
+ <!-- kb-doc-fragment: domain-concepts -->
2
+ # Substance — `business-domain/domain-concepts`
3
+
4
+ **Output path:** `{kbFolder}/business-domain/domain-concepts.md`
5
+
6
+ **Topic focus:** the ubiquitous language — the core domain concepts, terms, and
7
+ vocabulary the project uses, with precise definitions. Describe *the words the
8
+ team uses and what they mean*, so a newcomer can read the codebase's language.
9
+
10
+ **Discovery input to read:** all Phase-1 discovery findings, mined for recurring
11
+ domain vocabulary (names of entities, states, actions, roles). Lean on the
12
+ `database` and `routing` findings for the richest terminology.
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - A glossary: each core concept/term with a one-to-two-sentence definition.
17
+ - Note synonyms and any terms with a project-specific (non-obvious) meaning.
18
+ - Concepts only — no field types or route tables (those are other docs).
19
+
20
+ **Not applicable:** if no distinct domain vocabulary emerges, the topic is *absent*
21
+ — a certainty, not a low-confidence guess. Write a **confident** not-applicable
22
+ stub: line 1 is
23
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
24
+ then the `# Domain Concepts` heading and the one-sentence absence statement `No
25
+ specialized domain vocabulary — this project uses only general programming terms.`
26
+ Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,26 @@
1
+ <!-- kb-doc-fragment: domain-model -->
2
+ # Substance — `business-domain/domain-model`
3
+
4
+ **Output path:** `{kbFolder}/business-domain/domain-model.md`
5
+
6
+ **Topic focus:** the *conceptual* business model — the real-world entities the
7
+ system represents, their relationships, and the business invariants/rules that
8
+ govern them. This is business meaning, distinct from `entity-model`'s technical
9
+ field inventory.
10
+
11
+ **Discovery input to read:** the `database` domain findings from Phase 1, read
12
+ through a business lens (what does each stored thing *mean* to the domain).
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - Business entities with their conceptual purpose (not field types).
17
+ - Relationships expressed in domain terms (e.g. "a Sprint owns many Tasks").
18
+ - Business invariants and rules (e.g. "a Task cannot be committed before approval").
19
+ - Entity names exactly as in the brief's `## Domain Entities`.
20
+
21
+ **Not applicable:** if the project has no discernible business domain (e.g. a
22
+ pure tool/utility), the topic is *absent* — a certainty, not a low-confidence
23
+ guess. Write a **confident** not-applicable stub: line 1 is
24
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
25
+ then the `# Domain Model` heading and the one-sentence absence statement `No
26
+ business domain — this is a technical utility with no domain entities.` Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,26 @@
1
+ <!-- kb-doc-fragment: entity-model -->
2
+ # Substance — `architecture/entity-model`
3
+
4
+ **Output path:** `{kbFolder}/architecture/entity-model.md`
5
+
6
+ **Topic focus:** the *technical* inventory of what is stored — every entity with
7
+ its full field list, types, and structural relationships as they exist in code.
8
+ This is the exhaustive structural catalogue; `domain-model` is its conceptual,
9
+ business-facing counterpart.
10
+
11
+ **Discovery input to read:** the `database` domain findings from Phase 1 (models,
12
+ schema definitions, type declarations).
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - One section per entity with the complete field inventory (name, type, nullability).
17
+ - Structural relationships (FK, embedding, references).
18
+ - Entity names used exactly as they appear in the brief's `## Domain Entities`.
19
+ - Technical inventory only — no business invariants (those live in `domain-model`).
20
+
21
+ **Not applicable:** if nothing is stored, the topic is *absent* — a certainty,
22
+ not a low-confidence guess. Write a **confident** not-applicable stub: line 1 is
23
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
24
+ then the `# Entity Model` heading and the one-sentence absence statement `No
25
+ entities — this project persists no structured data.` Do NOT lower confidence
26
+ — an absent topic is known with certainty.
@@ -0,0 +1,25 @@
1
+ <!-- kb-doc-fragment: index -->
2
+ # Substance — `index` step
3
+
4
+ **Output paths:** `{kbFolder}/architecture/INDEX.md`,
5
+ `{kbFolder}/business-domain/INDEX.md`, and `{kbFolder}/MASTER_INDEX.md`.
6
+
7
+ **Topic focus:** build the navigation indexes that link the KB docs already
8
+ written to disk. You run *after* all 10 leaf docs exist. Describe *how to
9
+ navigate the knowledge base*, linking only what is present.
10
+
11
+ **Discovery input to read:** the filesystem — list the docs actually written
12
+ under `architecture/` and `business-domain/`. Do NOT anticipate files that were
13
+ not produced.
14
+
15
+ **Required output:**
16
+ 1. `architecture/INDEX.md` — list and link every architecture doc on disk.
17
+ 2. `business-domain/INDEX.md` — list and link `domain-model.md` and
18
+ `domain-concepts.md` if present.
19
+ 3. `MASTER_INDEX.md` — link both INDEX files and include a `## Domain Entities`
20
+ section listing discovered entities, one per line.
21
+ - Link **only** docs that exist on disk at write time (AC3).
22
+ - Confidence header on the first line of each written index.
23
+
24
+ **Not applicable:** never fully skipped — if a section has no docs, write the
25
+ index with an explicit `(none generated)` note rather than a broken link.
@@ -0,0 +1,26 @@
1
+ <!-- kb-doc-fragment: processes -->
2
+ # Substance — `architecture/processes`
3
+
4
+ **Output path:** `{kbFolder}/architecture/processes.md`
5
+
6
+ **Topic focus:** the runnable processes and build/deploy topology — services,
7
+ daemons, workers, entry points, and the commands that build, start, and run
8
+ them. Describe *what runs* and *how it is built*, not the deployment targets.
9
+
10
+ **Discovery input to read:** the `processes` domain findings from Phase 1
11
+ (entry points, scripts, service definitions, build commands). Cross-reference
12
+ `stack` for the build tool.
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - Each service/process with its entry point and start command.
17
+ - Build pipeline steps in order.
18
+ - Inter-process relationships (who calls/spawns whom), if any.
19
+ - No CI/CD or environment content — that is `deployment`.
20
+
21
+ **Not applicable:** if the project defines no runnable processes (e.g. a library
22
+ with no service), the topic is *absent* — a certainty, not a low-confidence guess.
23
+ Write a **confident** not-applicable stub: line 1 is
24
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
25
+ then the `# Processes` heading and the one-sentence absence statement `No standalone
26
+ processes — this is a library/package consumed by other code.` Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,25 @@
1
+ <!-- kb-doc-fragment: routing -->
2
+ # Substance — `architecture/routing`
3
+
4
+ **Output path:** `{kbFolder}/architecture/routing.md`
5
+
6
+ **Topic focus:** the API/route surface — endpoints or command surface, route
7
+ groups, HTTP methods (or CLI verbs), and the auth/authorization strategy that
8
+ gates them. Describe *how the system is entered*, not what it stores.
9
+
10
+ **Discovery input to read:** the `routing` domain findings from Phase 1 (route
11
+ definitions, controllers, handlers, middleware, auth guards).
12
+
13
+ **Required output:**
14
+ - Confidence header on line 1.
15
+ - Route/endpoint groups with methods and paths (or CLI command surface).
16
+ - Auth strategy and where it is enforced.
17
+ - Middleware / interceptors in the request path.
18
+ - No persistence or deployment content.
19
+
20
+ **Not applicable:** if the project exposes no routes or command surface (e.g. a
21
+ pure library), the topic is *absent* — a certainty, not a low-confidence guess.
22
+ Write a **confident** not-applicable stub: line 1 is
23
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
24
+ then the `# Routing` heading and the one-sentence absence statement `No routing
25
+ surface — this project exposes no HTTP routes or command entry points.` Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,25 @@
1
+ <!-- kb-doc-fragment: stack-checklist -->
2
+ # Substance — `architecture/stack-checklist`
3
+
4
+ **Output path:** `{kbFolder}/architecture/stack-checklist.md`
5
+
6
+ **Topic focus:** an actionable review checklist derived from the stack and
7
+ testing facts — the concrete gates a change in this codebase must pass (syntax
8
+ check, lint, test, build, type-check commands). Describe *what to verify before
9
+ a change is considered done*.
10
+
11
+ **Discovery input to read:** the `stack` and `testing` domain findings from
12
+ Phase 1 (build tool, linter, type checker, test runner, their commands).
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - A checklist of verification steps, each with its exact command.
17
+ - Ordered from cheapest (syntax) to most expensive (full suite/build).
18
+ - Derived strictly from stack + testing facts — no invented tooling.
19
+
20
+ **Not applicable:** if no verification tooling exists, the topic is *absent* — a
21
+ certainty, not a low-confidence guess. Write a **confident** not-applicable stub:
22
+ line 1 is
23
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
24
+ then the `# Stack Checklist` heading and the one-sentence absence statement `No
25
+ verification tooling detected — no automated checklist can be derived.` Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,27 @@
1
+ <!-- kb-doc-fragment: stack -->
2
+ # Substance — `architecture/stack`
3
+
4
+ **Output path:** `{kbFolder}/architecture/stack.md`
5
+
6
+ **Topic focus:** the technology stack — languages and their versions, frameworks
7
+ and major libraries, the runtime/interpreter, package manager, and the build
8
+ toolchain. Describe *what the project is built with*, not how it is deployed.
9
+
10
+ **Discovery input to read:** the `stack` domain findings from Phase 1
11
+ (languages, dependency manifests, lockfiles, runtime versions). Cross-reference
12
+ the `testing` findings only for the test-runner dependency.
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - A languages/versions table or list (each fact `[?]`-marked if not directly observed).
17
+ - Frameworks & major libraries with their roles.
18
+ - Runtime + package manager + build tool.
19
+ - No deployment, routing, or schema content — those are other docs.
20
+
21
+ **Not applicable:** if the project has no identifiable stack (e.g. an empty or
22
+ docs-only repo), the topic is *absent* — a certainty, not a low-confidence guess.
23
+ Write a **confident** not-applicable stub: line 1 is
24
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
25
+ then the `# Stack` heading and the one-sentence absence statement `No application
26
+ stack detected — this repository contains no build- or runtime-bearing sources.`
27
+ Do NOT lower confidence — an absent topic is known with certainty.
@@ -0,0 +1,26 @@
1
+ <!-- kb-doc-fragment: testing -->
2
+ # Substance — `architecture/testing`
3
+
4
+ **Output path:** `{kbFolder}/architecture/testing.md`
5
+
6
+ **Topic focus:** the test strategy — frameworks and runners, the exact command
7
+ to run the suite, test layout (unit/integration/e2e), and coverage tooling.
8
+ Describe *how the project is verified*.
9
+
10
+ **Discovery input to read:** the `testing` domain findings from Phase 1 (test
11
+ directories, test frameworks, runner config, coverage config). Cross-reference
12
+ `stack` for the runner dependency version.
13
+
14
+ **Required output:**
15
+ - Confidence header on line 1.
16
+ - Test framework(s) and the resolved run command (from `commands.test`).
17
+ - Test layout and naming conventions.
18
+ - Coverage tool and threshold, if any.
19
+ - No CI wiring — that is `deployment`.
20
+
21
+ **Not applicable:** if the project has no tests, the topic is *absent* — a
22
+ certainty, not a low-confidence guess. Write a **confident** not-applicable stub:
23
+ line 1 is
24
+ `<!-- AUTO-GENERATED by /forge:init — confidence: 100% — status: not-applicable -->`,
25
+ then the `# Testing` heading and the one-sentence absence statement `No test suite
26
+ detected — this project currently has no automated tests.` Do NOT lower confidence — an absent topic is known with certainty.
@@ -1,6 +1,6 @@
1
1
  # Phase 2 — Discover
2
2
 
3
- **Deliverable:** 7 KB architecture docs + 3 index files + `project-context.json` + calibration baseline.
3
+ **Deliverable:** 10 KB docs (8 architecture + 2 business-domain) + 3 index files + `project-context.json` + calibration baseline.
4
4
 
5
5
  Set `$FORGE_ROOT` and resolve `$KB_PATH` from `.forge/config.json`:
6
6
 
@@ -49,31 +49,37 @@ touch "{KB_PATH}/sprints/.gitkeep" "{KB_PATH}/bugs/.gitkeep" \
49
49
 
50
50
  Read `$FORGE_ROOT/init/generation/generate-kb-doc.md` once (the per-subagent rulebook).
51
51
 
52
- Generate all 7 knowledge-base documents. For each document, analyse the
52
+ Generate all 10 knowledge-base documents. For each document, analyse the
53
53
  project codebase for that topic and write to its output path. After writing
54
54
  each document, read it back and verify the confidence header is present
55
55
  (`<!-- AUTO-GENERATED — confidence: NN% -->`).
56
56
 
57
+ Output paths below are the canonical `{KB_PATH}/{docId}.md` targets for the
58
+ shared 10-doc contract (verify-phase.cjs `--phase 2` checks exactly these).
59
+
57
60
  | Document | Output path | Focus |
58
61
  |----------|-------------|-------|
59
62
  | stack.md | `{KB_PATH}/architecture/stack.md` | Languages, frameworks, runtime, versions |
60
63
  | processes.md | `{KB_PATH}/architecture/processes.md` | Services, build/deploy topology |
61
- | database.md | `{KB_PATH}/architecture/database.md` | Entities, relationships, field types |
62
64
  | routing.md | `{KB_PATH}/architecture/routing.md` | API surface, route groups, auth strategy |
65
+ | database.md | `{KB_PATH}/architecture/database.md` | Entities, relationships, field types |
66
+ | testing.md | `{KB_PATH}/architecture/testing.md` | Test frameworks, run commands, coverage |
63
67
  | deployment.md | `{KB_PATH}/architecture/deployment.md` | Environments, CI/CD, infra targets |
64
- | entity-model.md | `{KB_PATH}/business-domain/entity-model.md` | Full entity inventory with fields |
65
- | stack-checklist.md | `{KB_PATH}/stack-checklist.md` | Review checklist items from stack + testing |
68
+ | entity-model.md | `{KB_PATH}/architecture/entity-model.md` | Full entity inventory with fields |
69
+ | stack-checklist.md | `{KB_PATH}/architecture/stack-checklist.md` | Review checklist items from stack + testing |
70
+ | domain-model.md | `{KB_PATH}/business-domain/domain-model.md` | Business entities, relationships, invariants |
71
+ | domain-concepts.md | `{KB_PATH}/business-domain/domain-concepts.md` | Ubiquitous language, core domain concepts |
66
72
 
67
- You may spawn all 7 as parallel subagents in a single Agent tool message for speed.
68
- Wait for all 7 to return. Retry any that returned FAILED: once.
73
+ You may spawn all 10 as parallel subagents in a single Agent tool message for speed.
74
+ Wait for all 10 to return. Retry any that returned FAILED: once.
69
75
  Any still failing after one retry: halt and surface the id list.
70
76
 
71
77
  ### Step 4 — Create index files (sequential)
72
78
 
73
79
  After all leaf docs are written:
74
80
 
75
- 1. **`{KB_PATH}/architecture/INDEX.md`** — list and link to the 5 architecture docs
76
- 2. **`{KB_PATH}/business-domain/INDEX.md`** — list and link to entity-model.md
81
+ 1. **`{KB_PATH}/architecture/INDEX.md`** — list and link to the 8 architecture docs
82
+ 2. **`{KB_PATH}/business-domain/INDEX.md`** — list and link to domain-model.md and domain-concepts.md
77
83
  3. **`{KB_PATH}/MASTER_INDEX.md`** — scaffold linking both INDEX files; include
78
84
  `## Domain Entities` section listing discovered entities (one per line)
79
85
 
@@ -210,10 +210,10 @@
210
210
  "install": ".forge/init/phases/",
211
211
  "owner": "forge-scaffold",
212
212
  "select": {
213
- "recursive": false,
213
+ "recursive": true,
214
214
  "ext": [".md"]
215
215
  },
216
- "note": "Init rulebook phase prompts read by run-phases.ts / wfl-init.js (build-payload Pass-2 2e-pre)."
216
+ "note": "Init rulebook phase prompts read by run-phases.ts / wfl-init.js (build-payload Pass-2 2e-pre). Recursive so the phase-2/ per-step substance fragments (index.md, context.md, and the 10 KB-doc fragments) vendor into .forge/init/phases/phase-2/ — wfl-init.js references them literally (FORGE-S35-T06 / Slice 5)."
217
217
  },
218
218
  {
219
219
  "source": "init/discovery",
@@ -215,6 +215,20 @@ try {
215
215
  }
216
216
  }
217
217
 
218
+ // Always lay down the store skeleton — a COLLATION_STATE.json baseline is
219
+ // written even when zero entities are seeded, so a fresh `/forge:init`
220
+ // leaves a consistent `.forge/store/` (dir + watermark) instead of nothing.
221
+ // The next real `/forge:collate` overwrites this with live counts.
222
+ if (!DRY_RUN) {
223
+ Store.writeCollationState({
224
+ collatedAt: new Date().toISOString(),
225
+ featureCount: 0,
226
+ sprintCount,
227
+ taskCount,
228
+ bugCount,
229
+ });
230
+ }
231
+
218
232
  const prefix_ = DRY_RUN ? '[dry-run] ' : '';
219
233
  console.log(`${prefix_}Seeded: ${sprintCount} sprint(s), ${taskCount} task(s), ${bugCount} bug(s)`);
220
234
  } catch (e) {
@@ -166,19 +166,76 @@ function verifyPhase1Foundation(cwd) {
166
166
  };
167
167
  }
168
168
 
169
- // ── Phase 2: KB architecture docs verification ────────────────────────────────
169
+ // ── Phase 2: KB documents verification ────────────────────────────────────────
170
170
 
171
- const ARCH_DOCS = ['stack', 'processes', 'database', 'routing', 'deployment', 'entity-model', 'stack-checklist'];
171
+ // The canonical 10-doc KB contract (FORGE-S35-T01). docIds carry their KB folder
172
+ // prefix (architecture/ or business-domain/); the gate resolves each as
173
+ // {kbPath}/{docId}.md — no hardcoded architecture/ prefix.
174
+ //
175
+ // DRIFT-GUARD: this list MUST stay byte-identical (same items, same order) to
176
+ // KB_DOC_IDS in init/base-pack/workflows-js/wfl-init.js and in forge-cli's
177
+ // run-init-types.ts. Asserted by tools/__tests__/kb-doc-contract.test.cjs.
178
+ const ARCH_DOCS = [
179
+ 'architecture/stack',
180
+ 'architecture/processes',
181
+ 'architecture/routing',
182
+ 'architecture/database',
183
+ 'architecture/testing',
184
+ 'architecture/deployment',
185
+ 'architecture/entity-model',
186
+ 'architecture/stack-checklist',
187
+ 'business-domain/domain-model',
188
+ 'business-domain/domain-concepts',
189
+ ];
190
+
191
+ // A doc satisfies the gate when it exists AND carries a confidence-header token.
192
+ // Keyed on the `confidence: N%` token only (real digit required, case-
193
+ // insensitive) so BOTH in-tree header forms pass:
194
+ // <!-- AUTO-GENERATED by /forge:init — confidence: 85% --> (generate-kb-doc.md Rule 2)
195
+ // <!-- AUTO-GENERATED — confidence: 85% --> (phase-2-discover.md Step 3)
196
+ const CONFIDENCE_HEADER = /confidence:\s*\d+%/i;
197
+
198
+ // A not-applicable topic is a certainty, not a guess: an absent-topic stub is a
199
+ // confident (100%) doc carrying this marker (FORGE-S35-T04 / Slice 3). The marker
200
+ // may live inside the AUTO-GENERATED header comment or in the body. A doc bearing
201
+ // it satisfies the substantive-OR-not-applicable gate even with no other body.
202
+ const STATUS_NOT_APPLICABLE = /status:\s*not-applicable/i;
203
+
204
+ // Strip HTML comments (the AUTO-GENERATED confidence/status header lives in one)
205
+ // so a doc that is nothing but its header is recognised as empty.
206
+ const HTML_COMMENT = /<!--[\s\S]*?-->/g;
172
207
 
173
208
  function verifyPhase2(cwd, kbPath) {
174
209
  const missing = [];
175
210
  const checked = [];
176
211
 
177
212
  for (const doc of ARCH_DOCS) {
178
- const rel = path.join(kbPath, 'architecture', `${doc}.md`);
213
+ const rel = path.join(kbPath, `${doc}.md`);
179
214
  checked.push(rel);
180
- if (!fs.existsSync(path.join(cwd, rel))) {
215
+ const abs = path.join(cwd, rel);
216
+ if (!fs.existsSync(abs)) {
181
217
  missing.push(rel);
218
+ continue;
219
+ }
220
+ let content = '';
221
+ try {
222
+ content = fs.readFileSync(abs, 'utf8');
223
+ } catch {
224
+ content = '';
225
+ }
226
+ if (!CONFIDENCE_HEADER.test(content)) {
227
+ missing.push(`${rel} (no confidence header)`);
228
+ continue;
229
+ }
230
+ // Substantive-OR-not-applicable gate (FORGE-S35-T04): a doc passes iff it
231
+ // carries a `status: not-applicable` marker OR has non-empty body content
232
+ // after the header. Header-only/empty docs are rejected. Completeness stays
233
+ // ADVISORY — no confidence-percentage threshold, no entity-count bar — so an
234
+ // entity-less/library project passes with confident not-applicable stubs.
235
+ const hasNotApplicable = STATUS_NOT_APPLICABLE.test(content);
236
+ const body = content.replace(HTML_COMMENT, '').trim();
237
+ if (!hasNotApplicable && body.length === 0) {
238
+ missing.push(`${rel} (empty — no substance and no status: not-applicable marker)`);
182
239
  }
183
240
  }
184
241
 
@@ -1,5 +1,5 @@
1
1
  import { type ImageContent, type Message, type SimpleStreamOptions, type ThinkingBudgets, type Transport } from "@earendil-works/pi-ai/compat";
2
- import type { AfterToolCallContext, AfterToolCallResult, AgentEvent, AgentLoopTurnUpdate, AgentMessage, AgentState, BeforeToolCallContext, BeforeToolCallResult, QueueMode, StreamFn, ToolExecutionMode } from "./types.ts";
2
+ import type { AfterToolCallContext, AfterToolCallResult, AgentEvent, AgentLoopTurnUpdate, AgentMessage, AgentState, BeforeToolCallContext, BeforeToolCallResult, PrepareNextTurnContext, QueueMode, StreamFn, ToolExecutionMode } from "./types.ts";
3
3
  export type { QueueMode } from "./types.ts";
4
4
  /** Options for constructing an {@link Agent}. */
5
5
  export interface AgentOptions {
@@ -13,6 +13,7 @@ export interface AgentOptions {
13
13
  beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
14
14
  afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
15
15
  prepareNextTurn?: (signal?: AbortSignal) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
16
+ prepareNextTurnWithContext?: (context: PrepareNextTurnContext, signal?: AbortSignal) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
16
17
  steeringMode?: QueueMode;
17
18
  followUpMode?: QueueMode;
18
19
  sessionId?: string;
@@ -41,6 +42,7 @@ export declare class Agent {
41
42
  beforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;
42
43
  afterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;
43
44
  prepareNextTurn?: (signal?: AbortSignal) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
45
+ prepareNextTurnWithContext?: (context: PrepareNextTurnContext, signal?: AbortSignal) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;
44
46
  private activeRun?;
45
47
  /** Session identifier forwarded to providers for cache-aware backends. */
46
48
  sessionId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@earendil-works/pi-ai/compat\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.ts\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentLoopTurnUpdate,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tQueueMode,\n\tStreamFn,\n\tToolExecutionMode,\n} from \"./types.ts\";\n\nexport type { QueueMode } from \"./types.ts\";\n\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter(\n\t\t(message) => message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\",\n\t);\n}\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n};\n\nconst DEFAULT_MODEL = {\n\tid: \"unknown\",\n\tname: \"unknown\",\n\tapi: \"unknown\",\n\tprovider: \"unknown\",\n\tbaseUrl: \"\",\n\treasoning: false,\n\tinput: [],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 0,\n\tmaxTokens: 0,\n} satisfies Model<any>;\n\ntype MutableAgentState = Omit<AgentState, \"isStreaming\" | \"streamingMessage\" | \"pendingToolCalls\" | \"errorMessage\"> & {\n\tisStreaming: boolean;\n\tstreamingMessage?: AgentMessage;\n\tpendingToolCalls: Set<string>;\n\terrorMessage?: string;\n};\n\nfunction createMutableAgentState(\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>,\n): MutableAgentState {\n\tlet tools = initialState?.tools?.slice() ?? [];\n\tlet messages = initialState?.messages?.slice() ?? [];\n\n\treturn {\n\t\tsystemPrompt: initialState?.systemPrompt ?? \"\",\n\t\tmodel: initialState?.model ?? DEFAULT_MODEL,\n\t\tthinkingLevel: initialState?.thinkingLevel ?? \"off\",\n\t\tget tools() {\n\t\t\treturn tools;\n\t\t},\n\t\tset tools(nextTools: AgentTool<any>[]) {\n\t\t\ttools = nextTools.slice();\n\t\t},\n\t\tget messages() {\n\t\t\treturn messages;\n\t\t},\n\t\tset messages(nextMessages: AgentMessage[]) {\n\t\t\tmessages = nextMessages.slice();\n\t\t},\n\t\tisStreaming: false,\n\t\tstreamingMessage: undefined,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terrorMessage: undefined,\n\t};\n}\n\n/** Options for constructing an {@link Agent}. */\nexport interface AgentOptions {\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>;\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tstreamFn?: StreamFn;\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\tonResponse?: SimpleStreamOptions[\"onResponse\"];\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\tprepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tsteeringMode?: QueueMode;\n\tfollowUpMode?: QueueMode;\n\tsessionId?: string;\n\tthinkingBudgets?: ThinkingBudgets;\n\ttransport?: Transport;\n\tmaxRetryDelayMs?: number;\n\ttoolExecution?: ToolExecutionMode;\n}\n\nclass PendingMessageQueue {\n\tprivate messages: AgentMessage[] = [];\n\tpublic mode: QueueMode;\n\n\tconstructor(mode: QueueMode) {\n\t\tthis.mode = mode;\n\t}\n\n\tenqueue(message: AgentMessage): void {\n\t\tthis.messages.push(message);\n\t}\n\n\thasItems(): boolean {\n\t\treturn this.messages.length > 0;\n\t}\n\n\tdrain(): AgentMessage[] {\n\t\tif (this.mode === \"all\") {\n\t\t\tconst drained = this.messages.slice();\n\t\t\tthis.messages = [];\n\t\t\treturn drained;\n\t\t}\n\n\t\tconst first = this.messages[0];\n\t\tif (!first) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.messages = this.messages.slice(1);\n\t\treturn [first];\n\t}\n\n\tclear(): void {\n\t\tthis.messages = [];\n\t}\n}\n\ntype ActiveRun = {\n\tpromise: Promise<void>;\n\tresolve: () => void;\n\tabortController: AbortController;\n};\n\n/**\n * Stateful wrapper around the low-level agent loop.\n *\n * `Agent` owns the current transcript, emits lifecycle events, executes tools,\n * and exposes queueing APIs for steering and follow-up messages.\n */\nexport class Agent {\n\tprivate _state: MutableAgentState;\n\tprivate readonly listeners = new Set<(event: AgentEvent, signal: AbortSignal) => Promise<void> | void>();\n\tprivate readonly steeringQueue: PendingMessageQueue;\n\tprivate readonly followUpQueue: PendingMessageQueue;\n\n\tpublic convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tpublic transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tpublic streamFn: StreamFn;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tpublic onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tpublic onResponse?: SimpleStreamOptions[\"onResponse\"];\n\tpublic beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tpublic afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tpublic prepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tprivate activeRun?: ActiveRun;\n\t/** Session identifier forwarded to providers for cache-aware backends. */\n\tpublic sessionId?: string;\n\t/** Optional per-level thinking token budgets forwarded to the stream function. */\n\tpublic thinkingBudgets?: ThinkingBudgets;\n\t/** Preferred transport forwarded to the stream function. */\n\tpublic transport: Transport;\n\t/** Optional cap for provider-requested retry delays. */\n\tpublic maxRetryDelayMs?: number;\n\t/** Tool execution strategy for assistant messages that contain multiple tool calls. */\n\tpublic toolExecution: ToolExecutionMode;\n\n\tconstructor(options: AgentOptions = {}) {\n\t\tthis._state = createMutableAgentState(options.initialState);\n\t\tthis.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;\n\t\tthis.transformContext = options.transformContext;\n\t\tthis.streamFn = options.streamFn ?? streamSimple;\n\t\tthis.getApiKey = options.getApiKey;\n\t\tthis.onPayload = options.onPayload;\n\t\tthis.onResponse = options.onResponse;\n\t\tthis.beforeToolCall = options.beforeToolCall;\n\t\tthis.afterToolCall = options.afterToolCall;\n\t\tthis.prepareNextTurn = options.prepareNextTurn;\n\t\tthis.steeringQueue = new PendingMessageQueue(options.steeringMode ?? \"one-at-a-time\");\n\t\tthis.followUpQueue = new PendingMessageQueue(options.followUpMode ?? \"one-at-a-time\");\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.thinkingBudgets = options.thinkingBudgets;\n\t\tthis.transport = options.transport ?? \"auto\";\n\t\tthis.maxRetryDelayMs = options.maxRetryDelayMs;\n\t\tthis.toolExecution = options.toolExecution ?? \"parallel\";\n\t}\n\n\t/**\n\t * Subscribe to agent lifecycle events.\n\t *\n\t * Listener promises are awaited in subscription order and are included in\n\t * the current run's settlement. Listeners also receive the active abort\n\t * signal for the current run.\n\t *\n\t * `agent_end` is the final emitted event for a run, but the agent does not\n\t * become idle until all awaited listeners for that event have settled.\n\t */\n\tsubscribe(listener: (event: AgentEvent, signal: AbortSignal) => Promise<void> | void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/**\n\t * Current agent state.\n\t *\n\t * Assigning `state.tools` or `state.messages` copies the provided top-level array.\n\t */\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\t/** Controls how queued steering messages are drained. */\n\tset steeringMode(mode: QueueMode) {\n\t\tthis.steeringQueue.mode = mode;\n\t}\n\n\tget steeringMode(): QueueMode {\n\t\treturn this.steeringQueue.mode;\n\t}\n\n\t/** Controls how queued follow-up messages are drained. */\n\tset followUpMode(mode: QueueMode) {\n\t\tthis.followUpQueue.mode = mode;\n\t}\n\n\tget followUpMode(): QueueMode {\n\t\treturn this.followUpQueue.mode;\n\t}\n\n\t/** Queue a message to be injected after the current assistant turn finishes. */\n\tsteer(message: AgentMessage): void {\n\t\tthis.steeringQueue.enqueue(message);\n\t}\n\n\t/** Queue a message to run only after the agent would otherwise stop. */\n\tfollowUp(message: AgentMessage): void {\n\t\tthis.followUpQueue.enqueue(message);\n\t}\n\n\t/** Remove all queued steering messages. */\n\tclearSteeringQueue(): void {\n\t\tthis.steeringQueue.clear();\n\t}\n\n\t/** Remove all queued follow-up messages. */\n\tclearFollowUpQueue(): void {\n\t\tthis.followUpQueue.clear();\n\t}\n\n\t/** Remove all queued steering and follow-up messages. */\n\tclearAllQueues(): void {\n\t\tthis.clearSteeringQueue();\n\t\tthis.clearFollowUpQueue();\n\t}\n\n\t/** Returns true when either queue still contains pending messages. */\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.hasItems() || this.followUpQueue.hasItems();\n\t}\n\n\t/** Active abort signal for the current run, if any. */\n\tget signal(): AbortSignal | undefined {\n\t\treturn this.activeRun?.abortController.signal;\n\t}\n\n\t/** Abort the current run, if one is active. */\n\tabort(): void {\n\t\tthis.activeRun?.abortController.abort();\n\t}\n\n\t/**\n\t * Resolve when the current run and all awaited event listeners have finished.\n\t *\n\t * This resolves after `agent_end` listeners settle.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.activeRun?.promise ?? Promise.resolve();\n\t}\n\n\t/** Clear transcript state, runtime state, and queued messages. */\n\treset(): void {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.errorMessage = undefined;\n\t\tthis.clearFollowUpQueue();\n\t\tthis.clearSteeringQueue();\n\t}\n\n\t/** Start a new prompt from text, a single message, or a batch of messages. */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\t\tconst messages = this.normalizePromptInput(input, images);\n\t\tawait this.runPromptMessages(messages);\n\t}\n\n\t/** Continue from the current transcript. The last message must be a user or tool-result message. */\n\tasync continue(): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst lastMessage = this._state.messages[this._state.messages.length - 1];\n\t\tif (!lastMessage) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tif (lastMessage.role === \"assistant\") {\n\t\t\tconst queuedSteering = this.steeringQueue.drain();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUps = this.followUpQueue.drain();\n\t\t\tif (queuedFollowUps.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedFollowUps);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this.runContinuation();\n\t}\n\n\tprivate normalizePromptInput(\n\t\tinput: string | AgentMessage | AgentMessage[],\n\t\timages?: ImageContent[],\n\t): AgentMessage[] {\n\t\tif (Array.isArray(input)) {\n\t\t\treturn input;\n\t\t}\n\n\t\tif (typeof input !== \"string\") {\n\t\t\treturn [input];\n\t\t}\n\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (images && images.length > 0) {\n\t\t\tcontent.push(...images);\n\t\t}\n\t\treturn [{ role: \"user\", content, timestamp: Date.now() }];\n\t}\n\n\tprivate async runPromptMessages(\n\t\tmessages: AgentMessage[],\n\t\toptions: { skipInitialSteeringPoll?: boolean } = {},\n\t): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoop(\n\t\t\t\tmessages,\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(options),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate async runContinuation(): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoopContinue(\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate createContextSnapshot(): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools.slice(),\n\t\t};\n\t}\n\n\tprivate createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {\n\t\tlet skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;\n\t\treturn {\n\t\t\tmodel: this._state.model,\n\t\t\treasoning: this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel,\n\t\t\tsessionId: this.sessionId,\n\t\t\tonPayload: this.onPayload,\n\t\t\tonResponse: this.onResponse,\n\t\t\ttransport: this.transport,\n\t\t\tthinkingBudgets: this.thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this.maxRetryDelayMs,\n\t\t\ttoolExecution: this.toolExecution,\n\t\t\tbeforeToolCall: this.beforeToolCall,\n\t\t\tafterToolCall: this.afterToolCall,\n\t\t\tprepareNextTurn: this.prepareNextTurn ? async () => await this.prepareNextTurn?.(this.signal) : undefined,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.steeringQueue.drain();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.followUpQueue.drain(),\n\t\t};\n\t}\n\n\tprivate async runWithLifecycle(executor: (signal: AbortSignal) => Promise<void>): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing.\");\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tlet resolvePromise = () => {};\n\t\tconst promise = new Promise<void>((resolve) => {\n\t\t\tresolvePromise = resolve;\n\t\t});\n\t\tthis.activeRun = { promise, resolve: resolvePromise, abortController };\n\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.errorMessage = undefined;\n\n\t\ttry {\n\t\t\tawait executor(abortController.signal);\n\t\t} catch (error) {\n\t\t\tawait this.handleRunFailure(error, abortController.signal.aborted);\n\t\t} finally {\n\t\t\tthis.finishRun();\n\t\t}\n\t}\n\n\tprivate async handleRunFailure(error: unknown, aborted: boolean): Promise<void> {\n\t\tconst failureMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\tapi: this._state.model.api,\n\t\t\tprovider: this._state.model.provider,\n\t\t\tmodel: this._state.model.id,\n\t\t\tusage: EMPTY_USAGE,\n\t\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\t\ttimestamp: Date.now(),\n\t\t} satisfies AgentMessage;\n\t\tawait this.processEvents({ type: \"message_start\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"message_end\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"turn_end\", message: failureMessage, toolResults: [] });\n\t\tawait this.processEvents({ type: \"agent_end\", messages: [failureMessage] });\n\t}\n\n\tprivate finishRun(): void {\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis.activeRun?.resolve();\n\t\tthis.activeRun = undefined;\n\t}\n\n\t/**\n\t * Reduce internal state for a loop event, then await listeners.\n\t *\n\t * `agent_end` only means no further loop events will be emitted. The run is\n\t * considered idle later, after all awaited listeners for `agent_end` finish\n\t * and `finishRun()` clears runtime-owned state.\n\t */\n\tprivate async processEvents(event: AgentEvent): Promise<void> {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tthis._state.messages.push(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && event.message.errorMessage) {\n\t\t\t\t\tthis._state.errorMessage = event.message.errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tconst signal = this.activeRun?.abortController.signal;\n\t\tif (!signal) {\n\t\t\tthrow new Error(\"Agent listener invoked outside active run\");\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n}\n"]}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype ImageContent,\n\ttype Message,\n\ttype Model,\n\ttype SimpleStreamOptions,\n\tstreamSimple,\n\ttype TextContent,\n\ttype ThinkingBudgets,\n\ttype Transport,\n} from \"@earendil-works/pi-ai/compat\";\nimport { runAgentLoop, runAgentLoopContinue } from \"./agent-loop.ts\";\nimport type {\n\tAfterToolCallContext,\n\tAfterToolCallResult,\n\tAgentContext,\n\tAgentEvent,\n\tAgentLoopConfig,\n\tAgentLoopTurnUpdate,\n\tAgentMessage,\n\tAgentState,\n\tAgentTool,\n\tBeforeToolCallContext,\n\tBeforeToolCallResult,\n\tPrepareNextTurnContext,\n\tQueueMode,\n\tStreamFn,\n\tToolExecutionMode,\n} from \"./types.ts\";\n\nexport type { QueueMode } from \"./types.ts\";\n\nfunction defaultConvertToLlm(messages: AgentMessage[]): Message[] {\n\treturn messages.filter(\n\t\t(message) => message.role === \"user\" || message.role === \"assistant\" || message.role === \"toolResult\",\n\t);\n}\n\nconst EMPTY_USAGE = {\n\tinput: 0,\n\toutput: 0,\n\tcacheRead: 0,\n\tcacheWrite: 0,\n\ttotalTokens: 0,\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },\n};\n\nconst DEFAULT_MODEL = {\n\tid: \"unknown\",\n\tname: \"unknown\",\n\tapi: \"unknown\",\n\tprovider: \"unknown\",\n\tbaseUrl: \"\",\n\treasoning: false,\n\tinput: [],\n\tcost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },\n\tcontextWindow: 0,\n\tmaxTokens: 0,\n} satisfies Model<any>;\n\ntype MutableAgentState = Omit<AgentState, \"isStreaming\" | \"streamingMessage\" | \"pendingToolCalls\" | \"errorMessage\"> & {\n\tisStreaming: boolean;\n\tstreamingMessage?: AgentMessage;\n\tpendingToolCalls: Set<string>;\n\terrorMessage?: string;\n};\n\nfunction createMutableAgentState(\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>,\n): MutableAgentState {\n\tlet tools = initialState?.tools?.slice() ?? [];\n\tlet messages = initialState?.messages?.slice() ?? [];\n\n\treturn {\n\t\tsystemPrompt: initialState?.systemPrompt ?? \"\",\n\t\tmodel: initialState?.model ?? DEFAULT_MODEL,\n\t\tthinkingLevel: initialState?.thinkingLevel ?? \"off\",\n\t\tget tools() {\n\t\t\treturn tools;\n\t\t},\n\t\tset tools(nextTools: AgentTool<any>[]) {\n\t\t\ttools = nextTools.slice();\n\t\t},\n\t\tget messages() {\n\t\t\treturn messages;\n\t\t},\n\t\tset messages(nextMessages: AgentMessage[]) {\n\t\t\tmessages = nextMessages.slice();\n\t\t},\n\t\tisStreaming: false,\n\t\tstreamingMessage: undefined,\n\t\tpendingToolCalls: new Set<string>(),\n\t\terrorMessage: undefined,\n\t};\n}\n\n/** Options for constructing an {@link Agent}. */\nexport interface AgentOptions {\n\tinitialState?: Partial<Omit<AgentState, \"pendingToolCalls\" | \"isStreaming\" | \"streamingMessage\" | \"errorMessage\">>;\n\tconvertToLlm?: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\ttransformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tstreamFn?: StreamFn;\n\tgetApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tonPayload?: SimpleStreamOptions[\"onPayload\"];\n\tonResponse?: SimpleStreamOptions[\"onResponse\"];\n\tbeforeToolCall?: (context: BeforeToolCallContext, signal?: AbortSignal) => Promise<BeforeToolCallResult | undefined>;\n\tafterToolCall?: (context: AfterToolCallContext, signal?: AbortSignal) => Promise<AfterToolCallResult | undefined>;\n\tprepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tprepareNextTurnWithContext?: (\n\t\tcontext: PrepareNextTurnContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tsteeringMode?: QueueMode;\n\tfollowUpMode?: QueueMode;\n\tsessionId?: string;\n\tthinkingBudgets?: ThinkingBudgets;\n\ttransport?: Transport;\n\tmaxRetryDelayMs?: number;\n\ttoolExecution?: ToolExecutionMode;\n}\n\nclass PendingMessageQueue {\n\tprivate messages: AgentMessage[] = [];\n\tpublic mode: QueueMode;\n\n\tconstructor(mode: QueueMode) {\n\t\tthis.mode = mode;\n\t}\n\n\tenqueue(message: AgentMessage): void {\n\t\tthis.messages.push(message);\n\t}\n\n\thasItems(): boolean {\n\t\treturn this.messages.length > 0;\n\t}\n\n\tdrain(): AgentMessage[] {\n\t\tif (this.mode === \"all\") {\n\t\t\tconst drained = this.messages.slice();\n\t\t\tthis.messages = [];\n\t\t\treturn drained;\n\t\t}\n\n\t\tconst first = this.messages[0];\n\t\tif (!first) {\n\t\t\treturn [];\n\t\t}\n\t\tthis.messages = this.messages.slice(1);\n\t\treturn [first];\n\t}\n\n\tclear(): void {\n\t\tthis.messages = [];\n\t}\n}\n\ntype ActiveRun = {\n\tpromise: Promise<void>;\n\tresolve: () => void;\n\tabortController: AbortController;\n};\n\n/**\n * Stateful wrapper around the low-level agent loop.\n *\n * `Agent` owns the current transcript, emits lifecycle events, executes tools,\n * and exposes queueing APIs for steering and follow-up messages.\n */\nexport class Agent {\n\tprivate _state: MutableAgentState;\n\tprivate readonly listeners = new Set<(event: AgentEvent, signal: AbortSignal) => Promise<void> | void>();\n\tprivate readonly steeringQueue: PendingMessageQueue;\n\tprivate readonly followUpQueue: PendingMessageQueue;\n\n\tpublic convertToLlm: (messages: AgentMessage[]) => Message[] | Promise<Message[]>;\n\tpublic transformContext?: (messages: AgentMessage[], signal?: AbortSignal) => Promise<AgentMessage[]>;\n\tpublic streamFn: StreamFn;\n\tpublic getApiKey?: (provider: string) => Promise<string | undefined> | string | undefined;\n\tpublic onPayload?: SimpleStreamOptions[\"onPayload\"];\n\tpublic onResponse?: SimpleStreamOptions[\"onResponse\"];\n\tpublic beforeToolCall?: (\n\t\tcontext: BeforeToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<BeforeToolCallResult | undefined>;\n\tpublic afterToolCall?: (\n\t\tcontext: AfterToolCallContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AfterToolCallResult | undefined>;\n\tpublic prepareNextTurn?: (\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tpublic prepareNextTurnWithContext?: (\n\t\tcontext: PrepareNextTurnContext,\n\t\tsignal?: AbortSignal,\n\t) => Promise<AgentLoopTurnUpdate | undefined> | AgentLoopTurnUpdate | undefined;\n\tprivate activeRun?: ActiveRun;\n\t/** Session identifier forwarded to providers for cache-aware backends. */\n\tpublic sessionId?: string;\n\t/** Optional per-level thinking token budgets forwarded to the stream function. */\n\tpublic thinkingBudgets?: ThinkingBudgets;\n\t/** Preferred transport forwarded to the stream function. */\n\tpublic transport: Transport;\n\t/** Optional cap for provider-requested retry delays. */\n\tpublic maxRetryDelayMs?: number;\n\t/** Tool execution strategy for assistant messages that contain multiple tool calls. */\n\tpublic toolExecution: ToolExecutionMode;\n\n\tconstructor(options: AgentOptions = {}) {\n\t\tthis._state = createMutableAgentState(options.initialState);\n\t\tthis.convertToLlm = options.convertToLlm ?? defaultConvertToLlm;\n\t\tthis.transformContext = options.transformContext;\n\t\tthis.streamFn = options.streamFn ?? streamSimple;\n\t\tthis.getApiKey = options.getApiKey;\n\t\tthis.onPayload = options.onPayload;\n\t\tthis.onResponse = options.onResponse;\n\t\tthis.beforeToolCall = options.beforeToolCall;\n\t\tthis.afterToolCall = options.afterToolCall;\n\t\tthis.prepareNextTurn = options.prepareNextTurn;\n\t\tthis.prepareNextTurnWithContext = options.prepareNextTurnWithContext;\n\t\tthis.steeringQueue = new PendingMessageQueue(options.steeringMode ?? \"one-at-a-time\");\n\t\tthis.followUpQueue = new PendingMessageQueue(options.followUpMode ?? \"one-at-a-time\");\n\t\tthis.sessionId = options.sessionId;\n\t\tthis.thinkingBudgets = options.thinkingBudgets;\n\t\tthis.transport = options.transport ?? \"auto\";\n\t\tthis.maxRetryDelayMs = options.maxRetryDelayMs;\n\t\tthis.toolExecution = options.toolExecution ?? \"parallel\";\n\t}\n\n\t/**\n\t * Subscribe to agent lifecycle events.\n\t *\n\t * Listener promises are awaited in subscription order and are included in\n\t * the current run's settlement. Listeners also receive the active abort\n\t * signal for the current run.\n\t *\n\t * `agent_end` is the final emitted event for a run, but the agent does not\n\t * become idle until all awaited listeners for that event have settled.\n\t */\n\tsubscribe(listener: (event: AgentEvent, signal: AbortSignal) => Promise<void> | void): () => void {\n\t\tthis.listeners.add(listener);\n\t\treturn () => this.listeners.delete(listener);\n\t}\n\n\t/**\n\t * Current agent state.\n\t *\n\t * Assigning `state.tools` or `state.messages` copies the provided top-level array.\n\t */\n\tget state(): AgentState {\n\t\treturn this._state;\n\t}\n\n\t/** Controls how queued steering messages are drained. */\n\tset steeringMode(mode: QueueMode) {\n\t\tthis.steeringQueue.mode = mode;\n\t}\n\n\tget steeringMode(): QueueMode {\n\t\treturn this.steeringQueue.mode;\n\t}\n\n\t/** Controls how queued follow-up messages are drained. */\n\tset followUpMode(mode: QueueMode) {\n\t\tthis.followUpQueue.mode = mode;\n\t}\n\n\tget followUpMode(): QueueMode {\n\t\treturn this.followUpQueue.mode;\n\t}\n\n\t/** Queue a message to be injected after the current assistant turn finishes. */\n\tsteer(message: AgentMessage): void {\n\t\tthis.steeringQueue.enqueue(message);\n\t}\n\n\t/** Queue a message to run only after the agent would otherwise stop. */\n\tfollowUp(message: AgentMessage): void {\n\t\tthis.followUpQueue.enqueue(message);\n\t}\n\n\t/** Remove all queued steering messages. */\n\tclearSteeringQueue(): void {\n\t\tthis.steeringQueue.clear();\n\t}\n\n\t/** Remove all queued follow-up messages. */\n\tclearFollowUpQueue(): void {\n\t\tthis.followUpQueue.clear();\n\t}\n\n\t/** Remove all queued steering and follow-up messages. */\n\tclearAllQueues(): void {\n\t\tthis.clearSteeringQueue();\n\t\tthis.clearFollowUpQueue();\n\t}\n\n\t/** Returns true when either queue still contains pending messages. */\n\thasQueuedMessages(): boolean {\n\t\treturn this.steeringQueue.hasItems() || this.followUpQueue.hasItems();\n\t}\n\n\t/** Active abort signal for the current run, if any. */\n\tget signal(): AbortSignal | undefined {\n\t\treturn this.activeRun?.abortController.signal;\n\t}\n\n\t/** Abort the current run, if one is active. */\n\tabort(): void {\n\t\tthis.activeRun?.abortController.abort();\n\t}\n\n\t/**\n\t * Resolve when the current run and all awaited event listeners have finished.\n\t *\n\t * This resolves after `agent_end` listeners settle.\n\t */\n\twaitForIdle(): Promise<void> {\n\t\treturn this.activeRun?.promise ?? Promise.resolve();\n\t}\n\n\t/** Clear transcript state, runtime state, and queued messages. */\n\treset(): void {\n\t\tthis._state.messages = [];\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis._state.errorMessage = undefined;\n\t\tthis.clearFollowUpQueue();\n\t\tthis.clearSteeringQueue();\n\t}\n\n\t/** Start a new prompt from text, a single message, or a batch of messages. */\n\tasync prompt(message: AgentMessage | AgentMessage[]): Promise<void>;\n\tasync prompt(input: string, images?: ImageContent[]): Promise<void>;\n\tasync prompt(input: string | AgentMessage | AgentMessage[], images?: ImageContent[]): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Agent is already processing a prompt. Use steer() or followUp() to queue messages, or wait for completion.\",\n\t\t\t);\n\t\t}\n\t\tconst messages = this.normalizePromptInput(input, images);\n\t\tawait this.runPromptMessages(messages);\n\t}\n\n\t/** Continue from the current transcript. The last message must be a user or tool-result message. */\n\tasync continue(): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing. Wait for completion before continuing.\");\n\t\t}\n\n\t\tconst lastMessage = this._state.messages[this._state.messages.length - 1];\n\t\tif (!lastMessage) {\n\t\t\tthrow new Error(\"No messages to continue from\");\n\t\t}\n\n\t\tif (lastMessage.role === \"assistant\") {\n\t\t\tconst queuedSteering = this.steeringQueue.drain();\n\t\t\tif (queuedSteering.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedSteering, { skipInitialSteeringPoll: true });\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst queuedFollowUps = this.followUpQueue.drain();\n\t\t\tif (queuedFollowUps.length > 0) {\n\t\t\t\tawait this.runPromptMessages(queuedFollowUps);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthrow new Error(\"Cannot continue from message role: assistant\");\n\t\t}\n\n\t\tawait this.runContinuation();\n\t}\n\n\tprivate normalizePromptInput(\n\t\tinput: string | AgentMessage | AgentMessage[],\n\t\timages?: ImageContent[],\n\t): AgentMessage[] {\n\t\tif (Array.isArray(input)) {\n\t\t\treturn input;\n\t\t}\n\n\t\tif (typeof input !== \"string\") {\n\t\t\treturn [input];\n\t\t}\n\n\t\tconst content: Array<TextContent | ImageContent> = [{ type: \"text\", text: input }];\n\t\tif (images && images.length > 0) {\n\t\t\tcontent.push(...images);\n\t\t}\n\t\treturn [{ role: \"user\", content, timestamp: Date.now() }];\n\t}\n\n\tprivate async runPromptMessages(\n\t\tmessages: AgentMessage[],\n\t\toptions: { skipInitialSteeringPoll?: boolean } = {},\n\t): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoop(\n\t\t\t\tmessages,\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(options),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate async runContinuation(): Promise<void> {\n\t\tawait this.runWithLifecycle(async (signal) => {\n\t\t\tawait runAgentLoopContinue(\n\t\t\t\tthis.createContextSnapshot(),\n\t\t\t\tthis.createLoopConfig(),\n\t\t\t\t(event) => this.processEvents(event),\n\t\t\t\tsignal,\n\t\t\t\tthis.streamFn,\n\t\t\t);\n\t\t});\n\t}\n\n\tprivate createContextSnapshot(): AgentContext {\n\t\treturn {\n\t\t\tsystemPrompt: this._state.systemPrompt,\n\t\t\tmessages: this._state.messages.slice(),\n\t\t\ttools: this._state.tools.slice(),\n\t\t};\n\t}\n\n\tprivate createLoopConfig(options: { skipInitialSteeringPoll?: boolean } = {}): AgentLoopConfig {\n\t\tlet skipInitialSteeringPoll = options.skipInitialSteeringPoll === true;\n\t\treturn {\n\t\t\tmodel: this._state.model,\n\t\t\treasoning: this._state.thinkingLevel === \"off\" ? undefined : this._state.thinkingLevel,\n\t\t\tsessionId: this.sessionId,\n\t\t\tonPayload: this.onPayload,\n\t\t\tonResponse: this.onResponse,\n\t\t\ttransport: this.transport,\n\t\t\tthinkingBudgets: this.thinkingBudgets,\n\t\t\tmaxRetryDelayMs: this.maxRetryDelayMs,\n\t\t\ttoolExecution: this.toolExecution,\n\t\t\tbeforeToolCall: this.beforeToolCall,\n\t\t\tafterToolCall: this.afterToolCall,\n\t\t\tprepareNextTurn:\n\t\t\t\tthis.prepareNextTurnWithContext || this.prepareNextTurn\n\t\t\t\t\t? async (context) => {\n\t\t\t\t\t\t\tif (this.prepareNextTurnWithContext) {\n\t\t\t\t\t\t\t\treturn await this.prepareNextTurnWithContext(context, this.signal);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn await this.prepareNextTurn?.(this.signal);\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\tconvertToLlm: this.convertToLlm,\n\t\t\ttransformContext: this.transformContext,\n\t\t\tgetApiKey: this.getApiKey,\n\t\t\tgetSteeringMessages: async () => {\n\t\t\t\tif (skipInitialSteeringPoll) {\n\t\t\t\t\tskipInitialSteeringPoll = false;\n\t\t\t\t\treturn [];\n\t\t\t\t}\n\t\t\t\treturn this.steeringQueue.drain();\n\t\t\t},\n\t\t\tgetFollowUpMessages: async () => this.followUpQueue.drain(),\n\t\t};\n\t}\n\n\tprivate async runWithLifecycle(executor: (signal: AbortSignal) => Promise<void>): Promise<void> {\n\t\tif (this.activeRun) {\n\t\t\tthrow new Error(\"Agent is already processing.\");\n\t\t}\n\n\t\tconst abortController = new AbortController();\n\t\tlet resolvePromise = () => {};\n\t\tconst promise = new Promise<void>((resolve) => {\n\t\t\tresolvePromise = resolve;\n\t\t});\n\t\tthis.activeRun = { promise, resolve: resolvePromise, abortController };\n\n\t\tthis._state.isStreaming = true;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.errorMessage = undefined;\n\n\t\ttry {\n\t\t\tawait executor(abortController.signal);\n\t\t} catch (error) {\n\t\t\tawait this.handleRunFailure(error, abortController.signal.aborted);\n\t\t} finally {\n\t\t\tthis.finishRun();\n\t\t}\n\t}\n\n\tprivate async handleRunFailure(error: unknown, aborted: boolean): Promise<void> {\n\t\tconst failureMessage = {\n\t\t\trole: \"assistant\",\n\t\t\tcontent: [{ type: \"text\", text: \"\" }],\n\t\t\tapi: this._state.model.api,\n\t\t\tprovider: this._state.model.provider,\n\t\t\tmodel: this._state.model.id,\n\t\t\tusage: EMPTY_USAGE,\n\t\t\tstopReason: aborted ? \"aborted\" : \"error\",\n\t\t\terrorMessage: error instanceof Error ? error.message : String(error),\n\t\t\ttimestamp: Date.now(),\n\t\t} satisfies AgentMessage;\n\t\tawait this.processEvents({ type: \"message_start\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"message_end\", message: failureMessage });\n\t\tawait this.processEvents({ type: \"turn_end\", message: failureMessage, toolResults: [] });\n\t\tawait this.processEvents({ type: \"agent_end\", messages: [failureMessage] });\n\t}\n\n\tprivate finishRun(): void {\n\t\tthis._state.isStreaming = false;\n\t\tthis._state.streamingMessage = undefined;\n\t\tthis._state.pendingToolCalls = new Set<string>();\n\t\tthis.activeRun?.resolve();\n\t\tthis.activeRun = undefined;\n\t}\n\n\t/**\n\t * Reduce internal state for a loop event, then await listeners.\n\t *\n\t * `agent_end` only means no further loop events will be emitted. The run is\n\t * considered idle later, after all awaited listeners for `agent_end` finish\n\t * and `finishRun()` clears runtime-owned state.\n\t */\n\tprivate async processEvents(event: AgentEvent): Promise<void> {\n\t\tswitch (event.type) {\n\t\t\tcase \"message_start\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_update\":\n\t\t\t\tthis._state.streamingMessage = event.message;\n\t\t\t\tbreak;\n\n\t\t\tcase \"message_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tthis._state.messages.push(event.message);\n\t\t\t\tbreak;\n\n\t\t\tcase \"tool_execution_start\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.add(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"tool_execution_end\": {\n\t\t\t\tconst pendingToolCalls = new Set(this._state.pendingToolCalls);\n\t\t\t\tpendingToolCalls.delete(event.toolCallId);\n\t\t\t\tthis._state.pendingToolCalls = pendingToolCalls;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase \"turn_end\":\n\t\t\t\tif (event.message.role === \"assistant\" && event.message.errorMessage) {\n\t\t\t\t\tthis._state.errorMessage = event.message.errorMessage;\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase \"agent_end\":\n\t\t\t\tthis._state.streamingMessage = undefined;\n\t\t\t\tbreak;\n\t\t}\n\n\t\tconst signal = this.activeRun?.abortController.signal;\n\t\tif (!signal) {\n\t\t\tthrow new Error(\"Agent listener invoked outside active run\");\n\t\t}\n\t\tfor (const listener of this.listeners) {\n\t\t\tawait listener(event, signal);\n\t\t}\n\t}\n}\n"]}
@@ -97,6 +97,7 @@ export class Agent {
97
97
  beforeToolCall;
98
98
  afterToolCall;
99
99
  prepareNextTurn;
100
+ prepareNextTurnWithContext;
100
101
  activeRun;
101
102
  /** Session identifier forwarded to providers for cache-aware backends. */
102
103
  sessionId;
@@ -119,6 +120,7 @@ export class Agent {
119
120
  this.beforeToolCall = options.beforeToolCall;
120
121
  this.afterToolCall = options.afterToolCall;
121
122
  this.prepareNextTurn = options.prepareNextTurn;
123
+ this.prepareNextTurnWithContext = options.prepareNextTurnWithContext;
122
124
  this.steeringQueue = new PendingMessageQueue(options.steeringMode ?? "one-at-a-time");
123
125
  this.followUpQueue = new PendingMessageQueue(options.followUpMode ?? "one-at-a-time");
124
126
  this.sessionId = options.sessionId;
@@ -289,7 +291,14 @@ export class Agent {
289
291
  toolExecution: this.toolExecution,
290
292
  beforeToolCall: this.beforeToolCall,
291
293
  afterToolCall: this.afterToolCall,
292
- prepareNextTurn: this.prepareNextTurn ? async () => await this.prepareNextTurn?.(this.signal) : undefined,
294
+ prepareNextTurn: this.prepareNextTurnWithContext || this.prepareNextTurn
295
+ ? async (context) => {
296
+ if (this.prepareNextTurnWithContext) {
297
+ return await this.prepareNextTurnWithContext(context, this.signal);
298
+ }
299
+ return await this.prepareNextTurn?.(this.signal);
300
+ }
301
+ : undefined,
293
302
  convertToLlm: this.convertToLlm,
294
303
  transformContext: this.transformContext,
295
304
  getApiKey: this.getApiKey,