@chorus-aidlc/chorus 0.19.1 → 0.20.0

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 (322) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +349 -349
  3. package/.next/standalone/.next/app-path-routes-manifest.json +38 -38
  4. package/.next/standalone/.next/build-manifest.json +5 -5
  5. package/.next/standalone/.next/prerender-manifest.json +18 -18
  6. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js +1 -1
  7. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js.nft.json +1 -1
  8. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js +1 -1
  10. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page_client-reference-manifest.js +1 -1
  11. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js +1 -1
  12. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js +2 -2
  14. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js +2 -2
  17. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page_client-reference-manifest.js +1 -1
  18. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js +2 -2
  19. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page_client-reference-manifest.js +1 -1
  20. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page.js +2 -2
  21. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/graph/page_client-reference-manifest.js +1 -1
  23. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js +2 -2
  24. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page_client-reference-manifest.js +1 -1
  26. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js +2 -2
  27. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js +2 -2
  29. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page_client-reference-manifest.js +1 -1
  30. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js +1 -1
  31. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js +1 -1
  34. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js.nft.json +1 -1
  35. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page_client-reference-manifest.js +1 -1
  36. package/.next/standalone/.next/server/app/(dashboard)/projects/page.js +1 -1
  37. package/.next/standalone/.next/server/app/(dashboard)/projects/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js +2 -2
  40. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/_not-found.html +2 -2
  44. package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
  45. package/.next/standalone/.next/server/app/admin/companies/[uuid]/page.js +1 -1
  46. package/.next/standalone/.next/server/app/admin/companies/[uuid]/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/admin/companies/new/page.js +1 -1
  48. package/.next/standalone/.next/server/app/admin/companies/new/page_client-reference-manifest.js +1 -1
  49. package/.next/standalone/.next/server/app/admin/companies/new.html +2 -2
  50. package/.next/standalone/.next/server/app/admin/companies/new.rsc +3 -3
  51. package/.next/standalone/.next/server/app/admin/companies/page_client-reference-manifest.js +1 -1
  52. package/.next/standalone/.next/server/app/admin/companies.html +2 -2
  53. package/.next/standalone/.next/server/app/admin/companies.rsc +2 -2
  54. package/.next/standalone/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  55. package/.next/standalone/.next/server/app/admin.html +2 -2
  56. package/.next/standalone/.next/server/app/admin.rsc +2 -2
  57. package/.next/standalone/.next/server/app/api/admin/companies/[uuid]/route_client-reference-manifest.js +1 -1
  58. package/.next/standalone/.next/server/app/api/admin/companies/route_client-reference-manifest.js +1 -1
  59. package/.next/standalone/.next/server/app/api/admin/login/route_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/api/admin/session/route_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/api/agent-connections/route_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/api/agents/[uuid]/route_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/api/agents/route_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/api/api-keys/[uuid]/route_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/api/api-keys/route_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/api/auth/callback/route_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/api/auth/check-default/route_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/api/auth/company-oidc/route_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/app/api/auth/default-login/route_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/api/auth/identify/route_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -1
  74. package/.next/standalone/.next/server/app/api/auth/sync-token/route_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/api/comments/route_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/api/daemon/connection-heartbeat/route_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/api/daemon/control/route_client-reference-manifest.js +1 -1
  78. package/.next/standalone/.next/server/app/api/daemon/directory-request/report/route_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/api/daemon/execution-state/route_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/api/daemon/executions/route_client-reference-manifest.js +1 -1
  81. package/.next/standalone/.next/server/app/api/daemon/pending-turns/route_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/app/api/daemon/report-interrupt/route_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/api/daemon/resume/route_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/api/daemon/transcript/route_client-reference-manifest.js +1 -1
  85. package/.next/standalone/.next/server/app/api/daemon/turn-advance/route_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/api/daemon-directory-requests/[uuid]/route_client-reference-manifest.js +1 -1
  87. package/.next/standalone/.next/server/app/api/daemon-directory-requests/route_client-reference-manifest.js +1 -1
  88. package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/instruction/route_client-reference-manifest.js +1 -1
  89. package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/repoint/route_client-reference-manifest.js +1 -1
  90. package/.next/standalone/.next/server/app/api/daemon-sessions/[sessionUuid]/route_client-reference-manifest.js +1 -1
  91. package/.next/standalone/.next/server/app/api/daemon-sessions/ad-hoc/route_client-reference-manifest.js +1 -1
  92. package/.next/standalone/.next/server/app/api/daemon-sessions/route_client-reference-manifest.js +1 -1
  93. package/.next/standalone/.next/server/app/api/documents/[uuid]/route_client-reference-manifest.js +1 -1
  94. package/.next/standalone/.next/server/app/api/entities/[type]/[uuid]/root-idea/route_client-reference-manifest.js +1 -1
  95. package/.next/standalone/.next/server/app/api/events/notifications/route_client-reference-manifest.js +1 -1
  96. package/.next/standalone/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
  97. package/.next/standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  98. package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/preview/route_client-reference-manifest.js +1 -1
  100. package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/api/ideas/[uuid]/parent/route_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route_client-reference-manifest.js +1 -1
  103. package/.next/standalone/.next/server/app/api/ideas/[uuid]/route_client-reference-manifest.js +1 -1
  104. package/.next/standalone/.next/server/app/api/ideas/[uuid]/wake-preview/route_client-reference-manifest.js +1 -1
  105. package/.next/standalone/.next/server/app/api/ideas/conversational/route_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
  107. package/.next/standalone/.next/server/app/api/me/assignments/route_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/api/mentionables/route_client-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route_client-reference-manifest.js +1 -1
  110. package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route_client-reference-manifest.js +1 -1
  111. package/.next/standalone/.next/server/app/api/notifications/preferences/route_client-reference-manifest.js +1 -1
  112. package/.next/standalone/.next/server/app/api/notifications/read-all/route_client-reference-manifest.js +1 -1
  113. package/.next/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  114. package/.next/standalone/.next/server/app/api/notifications/unread-count/route_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/dashboard/route_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/route_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/api/project-groups/route_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/app/api/project-visits/pin/route_client-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/app/api/project-visits/route_client-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/app/api/project-visits/visit/route_client-reference-manifest.js +1 -1
  121. package/.next/standalone/.next/server/app/api/projects/[uuid]/activity/route_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/api/projects/[uuid]/agent-cwds/route_client-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route_client-reference-manifest.js +1 -1
  124. package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route_client-reference-manifest.js +1 -1
  125. package/.next/standalone/.next/server/app/api/projects/[uuid]/group/route_client-reference-manifest.js +1 -1
  126. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route_client-reference-manifest.js +1 -1
  127. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route_client-reference-manifest.js +1 -1
  128. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route_client-reference-manifest.js +1 -1
  129. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route_client-reference-manifest.js +1 -1
  130. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route_client-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/app/api/projects/[uuid]/resource-graph/route_client-reference-manifest.js +1 -1
  132. package/.next/standalone/.next/server/app/api/projects/[uuid]/route_client-reference-manifest.js +1 -1
  133. package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route_client-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route_client-reference-manifest.js +1 -1
  135. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/api/projects/agent-cwd-options/route_client-reference-manifest.js +1 -1
  137. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  138. package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route_client-reference-manifest.js +1 -1
  139. package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route_client-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route_client-reference-manifest.js +1 -1
  141. package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route_client-reference-manifest.js +1 -1
  142. package/.next/standalone/.next/server/app/api/proposals/[uuid]/route_client-reference-manifest.js +1 -1
  143. package/.next/standalone/.next/server/app/api/references/[uuid]/route_client-reference-manifest.js +1 -1
  144. package/.next/standalone/.next/server/app/api/references/route_client-reference-manifest.js +1 -1
  145. package/.next/standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  146. package/.next/standalone/.next/server/app/api/session/route_client-reference-manifest.js +1 -1
  147. package/.next/standalone/.next/server/app/api/sessions/[uuid]/route_client-reference-manifest.js +1 -1
  148. package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route_client-reference-manifest.js +1 -1
  149. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route_client-reference-manifest.js +1 -1
  150. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route_client-reference-manifest.js +1 -1
  151. package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route_client-reference-manifest.js +1 -1
  152. package/.next/standalone/.next/server/app/api/tasks/[uuid]/route_client-reference-manifest.js +1 -1
  153. package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  154. package/.next/standalone/.next/server/app/index.html +2 -2
  155. package/.next/standalone/.next/server/app/index.rsc +2 -2
  156. package/.next/standalone/.next/server/app/login/admin/page.js +1 -1
  157. package/.next/standalone/.next/server/app/login/admin/page_client-reference-manifest.js +1 -1
  158. package/.next/standalone/.next/server/app/login/admin.html +2 -2
  159. package/.next/standalone/.next/server/app/login/admin.rsc +3 -3
  160. package/.next/standalone/.next/server/app/login/callback/page_client-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/app/login/callback.html +2 -2
  162. package/.next/standalone/.next/server/app/login/callback.rsc +2 -2
  163. package/.next/standalone/.next/server/app/login/page.js +1 -1
  164. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/app/login/pick-workspace/page_client-reference-manifest.js +1 -1
  166. package/.next/standalone/.next/server/app/login/pick-workspace.html +2 -2
  167. package/.next/standalone/.next/server/app/login/pick-workspace.rsc +2 -2
  168. package/.next/standalone/.next/server/app/login.html +2 -2
  169. package/.next/standalone/.next/server/app/login.rsc +3 -3
  170. package/.next/standalone/.next/server/app/onboarding/page.js +2 -2
  171. package/.next/standalone/.next/server/app/onboarding/page.js.nft.json +1 -1
  172. package/.next/standalone/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  173. package/.next/standalone/.next/server/app/onboarding.html +2 -2
  174. package/.next/standalone/.next/server/app/onboarding.rsc +3 -3
  175. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  176. package/.next/standalone/.next/server/app/projects.html +2 -2
  177. package/.next/standalone/.next/server/app/projects.rsc +4 -4
  178. package/.next/standalone/.next/server/app/settings.html +2 -2
  179. package/.next/standalone/.next/server/app/settings.rsc +4 -4
  180. package/.next/standalone/.next/server/app-paths-manifest.json +38 -38
  181. package/.next/standalone/.next/server/chunks/1559.js +2 -2
  182. package/.next/standalone/.next/server/chunks/{657.js → 186.js} +1 -1
  183. package/.next/standalone/.next/server/chunks/2572.js +1 -1
  184. package/.next/standalone/.next/server/chunks/2961.js +1 -1
  185. package/.next/standalone/.next/server/chunks/3371.js +1 -0
  186. package/.next/standalone/.next/server/chunks/4131.js +39 -0
  187. package/.next/standalone/.next/server/chunks/4672.js +2 -2
  188. package/.next/standalone/.next/server/chunks/5245.js +1 -1
  189. package/.next/standalone/.next/server/chunks/5577.js +1 -0
  190. package/.next/standalone/.next/server/chunks/5728.js +1 -0
  191. package/.next/standalone/.next/server/chunks/6113.js +1 -1
  192. package/.next/standalone/.next/server/chunks/6515.js +2 -2
  193. package/.next/standalone/.next/server/chunks/6753.js +4 -4
  194. package/.next/standalone/.next/server/chunks/679.js +1 -1
  195. package/.next/standalone/.next/server/chunks/7368.js +1 -1
  196. package/.next/standalone/.next/server/chunks/7530.js +1 -1
  197. package/.next/standalone/.next/server/chunks/8017.js +1 -1
  198. package/.next/standalone/.next/server/chunks/8177.js +1 -0
  199. package/.next/standalone/.next/server/chunks/920.js +1 -1
  200. package/.next/standalone/.next/server/chunks/9213.js +1 -1
  201. package/.next/standalone/.next/server/chunks/9378.js +1 -1
  202. package/.next/standalone/.next/server/chunks/9931.js +1 -1
  203. package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  204. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  205. package/.next/standalone/.next/server/pages/404.html +2 -2
  206. package/.next/standalone/.next/server/pages/500.html +1 -1
  207. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  208. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  209. package/.next/standalone/.next/static/chunks/{11595.b2b74d1d1d0ae9d9.js → 11595.4cebcf7499ef6a78.js} +1 -1
  210. package/.next/standalone/.next/static/chunks/27370-0d04028ec8a339f0.js +1 -0
  211. package/.next/standalone/.next/static/chunks/27787-3566e3dd0506e241.js +1 -0
  212. package/.next/standalone/.next/static/chunks/{33456.978e5f47d1c8eb93.js → 33456.a5a9acba61ca3665.js} +1 -1
  213. package/.next/standalone/.next/static/chunks/{4860.79d6eefc2861716f.js → 4860.61dfcdf8c4c5c703.js} +1 -1
  214. package/.next/standalone/.next/static/chunks/53504-3a0f6dee12a39d5d.js +1 -0
  215. package/.next/standalone/.next/static/chunks/63819-c52b5bc9ee32ca9b.js +1 -0
  216. package/.next/standalone/.next/static/chunks/76103-5d656aebd9cd5e7f.js +1 -0
  217. package/.next/standalone/.next/static/chunks/78847-9f65efbac984cf70.js +1 -0
  218. package/.next/standalone/.next/static/chunks/79919-32af2c1abfc92a30.js +1 -0
  219. package/.next/standalone/.next/static/chunks/91997-3592827b5a069beb.js +1 -0
  220. package/.next/standalone/.next/static/chunks/{99373.e659954ff5148796.js → 99373.747e181e4c952509.js} +1 -1
  221. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-6135300353d6c6ad.js +1 -0
  222. package/.next/standalone/.next/static/chunks/app/(dashboard)/project-groups/[uuid]/page-bb9f26c107a6b113.js +1 -0
  223. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-31dd9042ee36b8e5.js +1 -0
  224. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-a6bfa3b6b6893bc4.js +1 -0
  225. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-01e81333c9beee2d.js +1 -0
  226. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/graph/{page-487d071317ae5e6d.js → page-795b161eb0694092.js} +1 -1
  227. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-e20e65b5df817433.js +1 -0
  228. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/page-fc2d02d552bbdbf0.js +1 -0
  229. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/page-0cfa38d2de9fa9d9.js +1 -0
  230. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/{page-7cb8a91f6ca34ba8.js → page-529dec4c3bcd5ba9.js} +1 -1
  231. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/{page-6466141ca355d9a4.js → page-b211c29669eaa3e4.js} +1 -1
  232. package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-0606d1497512e155.js +1 -0
  233. package/.next/standalone/.next/static/chunks/app/admin/companies/[uuid]/page-47e49c331996193b.js +1 -0
  234. package/.next/standalone/.next/static/chunks/app/admin/companies/new/page-c1e28205503002de.js +1 -0
  235. package/.next/standalone/.next/static/chunks/app/login/admin/page-b0d0ea2fa4102ff7.js +1 -0
  236. package/.next/standalone/.next/static/chunks/app/login/page-ee63e900cb54148c.js +1 -0
  237. package/.next/standalone/.next/static/chunks/app/onboarding/{page-323e1d93218758c5.js → page-1f3ab167cab58610.js} +1 -1
  238. package/.next/standalone/.next/static/chunks/{webpack-ec441a05567c82b0.js → webpack-6151d196b2fab7e8.js} +1 -1
  239. package/.next/standalone/.next/static/css/09ac5931fe977492.css +1 -0
  240. package/.next/standalone/package.json +1 -1
  241. package/.next/standalone/public/chorus-plugin/.claude-plugin/plugin.json +1 -1
  242. package/.next/standalone/public/chorus-plugin/bin/chorus-api.sh +1 -1
  243. package/.next/standalone/public/chorus-plugin/skills/brainstorm/SKILL.md +1 -1
  244. package/.next/standalone/public/chorus-plugin/skills/chorus/SKILL.md +1 -1
  245. package/.next/standalone/public/chorus-plugin/skills/chorus-cli/SKILL.md +1 -1
  246. package/.next/standalone/public/chorus-plugin/skills/develop/SKILL.md +1 -1
  247. package/.next/standalone/public/chorus-plugin/skills/docs/SKILL.md +1 -1
  248. package/.next/standalone/public/chorus-plugin/skills/idea/SKILL.md +1 -1
  249. package/.next/standalone/public/chorus-plugin/skills/openspec-aware/SKILL.md +1 -1
  250. package/.next/standalone/public/chorus-plugin/skills/orchestrate/SKILL.md +1 -1
  251. package/.next/standalone/public/chorus-plugin/skills/proposal/SKILL.md +1 -1
  252. package/.next/standalone/public/chorus-plugin/skills/quick-dev/SKILL.md +1 -1
  253. package/.next/standalone/public/chorus-plugin/skills/research/SKILL.md +1 -1
  254. package/.next/standalone/public/chorus-plugin/skills/review/SKILL.md +1 -1
  255. package/.next/standalone/public/chorus-plugin/skills/spec-lite/SKILL.md +1 -1
  256. package/.next/standalone/public/chorus-plugin/skills/yolo/SKILL.md +1 -1
  257. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-brainstorm/SKILL.md +1 -1
  258. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-cli/SKILL.md +1 -1
  259. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-develop/SKILL.md +1 -1
  260. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-docs/SKILL.md +1 -1
  261. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-idea/SKILL.md +1 -1
  262. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-openspec-aware/SKILL.md +1 -1
  263. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-orchestrate/SKILL.md +1 -1
  264. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-proposal/SKILL.md +1 -1
  265. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-quick-dev/SKILL.md +1 -1
  266. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-research/SKILL.md +1 -1
  267. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-review/SKILL.md +1 -1
  268. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-spec-lite/SKILL.md +1 -1
  269. package/.next/standalone/public/kiro-plugin/.kiro/skills/chorus-yolo/SKILL.md +1 -1
  270. package/.next/standalone/public/kiro-plugin/bin/chorus-api.sh +1 -1
  271. package/README.ja.md +3 -1
  272. package/README.ko.md +3 -1
  273. package/README.md +18 -1
  274. package/README.zh.md +18 -1
  275. package/chorus.mjs +7 -1
  276. package/cli/agent-cli-config.mjs +28 -2
  277. package/cli/claude-spawner.mjs +330 -18
  278. package/cli/codex-app-server-client.mjs +402 -0
  279. package/cli/codex-app-server-events.mjs +176 -0
  280. package/cli/codex-session-map.mjs +1 -1
  281. package/cli/codex-spawner.mjs +247 -241
  282. package/cli/init/adapters.mjs +2 -2
  283. package/cli/init/file-template.mjs +11 -4
  284. package/cli/init/install-methods.mjs +3 -1
  285. package/cli/pi-spawner.mjs +472 -78
  286. package/cli/process-killer.mjs +138 -13
  287. package/cli/process-stop-hooks.mjs +13 -0
  288. package/cli/upgrade-plugins.mjs +202 -0
  289. package/cli/upgrade-process.mjs +159 -0
  290. package/cli/upgrade.mjs +125 -0
  291. package/cli/upload-hooks.mjs +2 -2
  292. package/cli/windows-process-tree.mjs +154 -0
  293. package/package.json +1 -1
  294. package/.next/standalone/.next/server/chunks/3420.js +0 -1
  295. package/.next/standalone/.next/server/chunks/38.js +0 -39
  296. package/.next/standalone/.next/server/chunks/5576.js +0 -1
  297. package/.next/standalone/.next/server/chunks/6125.js +0 -1
  298. package/.next/standalone/.next/server/chunks/6509.js +0 -1
  299. package/.next/standalone/.next/static/chunks/17081-63e2cad8c15da2fb.js +0 -1
  300. package/.next/standalone/.next/static/chunks/27370-f998ed24dd4fa975.js +0 -1
  301. package/.next/standalone/.next/static/chunks/46437-88abf056c31c7e4e.js +0 -1
  302. package/.next/standalone/.next/static/chunks/48365-13b8e43016ba16a4.js +0 -1
  303. package/.next/standalone/.next/static/chunks/71522-de2b2a9d8e5059e1.js +0 -1
  304. package/.next/standalone/.next/static/chunks/79919-d423af700ae83862.js +0 -1
  305. package/.next/standalone/.next/static/chunks/91796-d235607f577fbaeb.js +0 -1
  306. package/.next/standalone/.next/static/chunks/99540-99af558190f96511.js +0 -1
  307. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-adf9dbdf339e9038.js +0 -1
  308. package/.next/standalone/.next/static/chunks/app/(dashboard)/project-groups/[uuid]/page-cf8ff072a8bc7409.js +0 -1
  309. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-00d18e26961cee2f.js +0 -1
  310. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-9ed4d7d4f5897de0.js +0 -1
  311. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/page-9d6d86299f2fadd9.js +0 -1
  312. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-b18a2aba2c4f4f1c.js +0 -1
  313. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/page-32149a823e7abf42.js +0 -1
  314. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/page-0ac33f9146fc53c5.js +0 -1
  315. package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/page-ba19aec7fde5086c.js +0 -1
  316. package/.next/standalone/.next/static/chunks/app/admin/companies/[uuid]/page-56118c51122ad639.js +0 -1
  317. package/.next/standalone/.next/static/chunks/app/admin/companies/new/page-4e20deb41357171c.js +0 -1
  318. package/.next/standalone/.next/static/chunks/app/login/admin/page-c8e40259e5d3d9e0.js +0 -1
  319. package/.next/standalone/.next/static/chunks/app/login/page-0a16a64855b771ef.js +0 -1
  320. package/.next/standalone/.next/static/css/652fcc1f1ab231c8.css +0 -1
  321. /package/.next/standalone/.next/static/{AWk31w7ryFKaxOnUlIeo2 → kDppeym4sfPwXjGFspZG8}/_buildManifest.js +0 -0
  322. /package/.next/standalone/.next/static/{AWk31w7ryFKaxOnUlIeo2 → kDppeym4sfPwXjGFspZG8}/_ssgManifest.js +0 -0
@@ -2,67 +2,107 @@
2
2
  // Cross-platform headless pi spawner — the `pi` counterpart to ClaudeSpawner /
3
3
  // CodexSpawner, satisfying the SAME backend-agnostic Spawner.wake(...) contract so
4
4
  // the daemon's wake pipeline (queue, waker, directed delivery, headless guard,
5
- // reporters) stays backend-neutral and drives whichever spawner is injected purely
6
- // through the shared wake(...) contract.
5
+ // reporters) stays backend-neutral.
7
6
  //
8
- // Verified against the pi source (earendil-works/pi, packages/coding-agent) before
9
- // coding — NOT guessed:
10
- // • `pi --mode json -p` runs headless and emits a JSONL event stream on stdout
11
- // (one JSON object per line): the session header first, then AgentSessionEvents
12
- // (`message_start` / `message_update` / `message_end` / `tool_execution_*` /
13
- // `turn_end` / `agent_end`). Verified against src/modes/print-mode.ts +
14
- // src/modes/json-event.ts + docs/json.md.
15
- // • The prompt is read from PIPED STDIN (never argv): main.ts `readPipedStdin()`
16
- // folds stdin into the initial message when stdin is not a TTY. We pipe the
17
- // prompt and close stdin, exactly like the Claude/Codex spawners.
18
- // • SESSION MODEL — client-owned id (like Claude, unlike Codex). pi accepts a
19
- // caller-provided `--session-id <id>` which is IDEMPOTENT create-or-resume:
20
- // main.ts createSessionManager() resumes the existing session when one with
21
- // that exact id already exists in the cwd-scoped session dir, otherwise creates
22
- // a fresh session WITH that id. (`--session <id>` is the resume-ONLY flag — it
23
- // hard-errors when the session is absent — so it is the wrong choice for a wake
24
- // that must create-if-missing.) So we pass `--session-id <anchor>` for BOTH the
25
- // first wake and every resume; pi owns the new-vs-resume decision from its own
26
- // on-disk state. No persisted anchor→id map is needed (that is only for Codex,
27
- // which generates its own id). We NEVER pass `--no-session` (that flag is for
28
- // ephemeral subagent children only).
29
- // • pi has NO permission system (no sandbox / no tool-approval prompt in headless
30
- // mode), so `permissionMode` is a NO-OP: we emit no sandbox / skip-permissions
31
- // flag. `chorus` and `yolo` modes run identically.
7
+ // Transport: pi's native RPC mode (`pi --mode rpc`), one process per wake
8
+ // (OpenSpec change switch-pi-daemon-to-rpc). Verified against the pi 0.85.1 package
9
+ // (docs/rpc.md, dist/modes/rpc/rpc-mode.js, dist/main.js, dist/core/agent-session.js)
10
+ // and live probes — NOT guessed:
11
+ // • stdin carries JSONL commands (`get_state`, `prompt`, `abort`,
12
+ // `extension_ui_response`); stdout carries `response` frames, `extension_ui_request`
13
+ // frames and the same AgentSessionEvents JSON mode printed (no `session` header).
14
+ // • stdin EOF makes RPC mode shut down immediately (even mid-run), so stdin stays
15
+ // open until the run is settled. `agent_settled` is the only "nothing more will
16
+ // run" signal (`agent_end` may be followed by retry/compaction). A `prompt`
17
+ // response only means "accepted".
18
+ // • Some accepted prompts start no run (an extension slash command, or an
19
+ // extension `input` handler returning "handled"): no `agent_start` /
20
+ // `agent_settled` follows. A normal run sets `isStreaming` synchronously before
21
+ // pi can read our next line, so a follow-up `get_state` reporting
22
+ // `isStreaming:false` with no `agent_start` seen means "handled, nothing to wait for".
23
+ // • SESSION MODEL — client-owned id. `--session-id <anchor>` is create-or-resume in
24
+ // the cwd-scoped session dir; an unparseable session file counts as absent (pi
25
+ // then creates a new session with the same id). `get_state.messageCount` tells us
26
+ // which happened. We NEVER pass `--no-session`.
27
+ // • pi has NO permission system, so `permissionMode` is a NO-OP. Blocking extension
28
+ // dialogs are cancelled immediately (headless: nobody can answer them).
32
29
  // • pi has no native MCP: a woken pi reaches Chorus tools only via the chorus-pi
33
- // extension / pi-mcp-adapter installed in the woken environment. This spawner
34
- // does not inject MCP; it exports the daemon's creds into the child env, which
35
- // the extension consumes (CHORUS_URL / CHORUS_API_KEY / CHORUS_AGENT_PROFILE).
30
+ // extension, which consumes CHORUS_URL / CHORUS_API_KEY / CHORUS_AGENT_PROFILE.
36
31
  //
37
- // Reuses claude-spawner's platform-neutral NDJSON parser (parseNdjsonChunk) and the
38
- // PATH-walk / .cmd-shim shape of the other spawners' path resolution.
32
+ // Reuses claude-spawner's LF-only NDJSON parser (parseNdjsonChunk — pi requires
33
+ // LF-only framing, i.e. NOT readline) and the shared stop-hook / settlement seams.
39
34
 
40
- import { spawn } from "node:child_process";
35
+ import { spawn, execFile } from "node:child_process";
41
36
  import { safeSpawnError } from "./launch-diagnostics.mjs";
42
37
  import { validateAgentCliConfig, overlayAgentEnv, getAgentEnv, assertConfiguredShimArgs } from "./agent-cli-config.mjs";
43
- import { statSync } from "node:fs";
38
+ import { statSync, readdirSync } from "node:fs";
44
39
  import { win32 as pathWin32, posix as pathPosix } from "node:path";
45
40
  import { parseNdjsonChunk } from "./claude-spawner.mjs";
46
41
  import { awaitChildSettled } from "./child-exit.mjs";
42
+ import { registerProcessStopHook } from "./process-stop-hooks.mjs";
47
43
 
48
44
  const NOOP_LOGGER = { info() {}, warn() {}, error() {} };
49
45
 
46
+ /** Oldest pi release the RPC wake path is verified against (owner decision q8). */
47
+ export const MIN_PI_VERSION = "0.85.0";
48
+
49
+ /** Upgrade command shown when the installed pi is too old. */
50
+ export const PI_UPGRADE_COMMAND = "npm install -g @earendil-works/pi-coding-agent@latest";
51
+
52
+ /** Transcript notice when pi could not restore an anchor's earlier session. */
53
+ export const PI_CONTINUITY_NOTICE =
54
+ "Previous Pi history could not be restored; continuing in a new Pi session with Chorus context.";
55
+
56
+ /** Extension UI methods that block until the client answers. */
57
+ const DIALOG_METHODS = new Set(["select", "confirm", "input", "editor"]);
58
+
59
+ const UUID_RE = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
60
+
61
+ // Request ids the spawner correlates responses by. One of each per wake.
62
+ const ID_STATE = "chorus-state-1";
63
+ const ID_PROMPT = "chorus-prompt-1";
64
+ const ID_IDLE_CHECK = "chorus-state-2";
65
+ const ID_ABORT = "chorus-abort-1";
66
+
50
67
  /**
51
- * Build the argv for a headless pi run. The prompt is NEVER here — it goes over
52
- * stdin (`pi -p` folds piped stdin into the initial message).
53
- *
54
- * `--session-id <anchor>` is pi's idempotent create-or-resume flag (verified against
55
- * pi main.ts createSessionManager): it resumes the session with that exact id if one
56
- * exists in the cwd-scoped dir, else creates a new session with that id — so the SAME
57
- * args serve both the first wake and every resume, and pi owns the decision. `-p` is
58
- * placed LAST so pi's arg parser can never mistake a following token for a message
59
- * positional (its `-p` handler slurps the next bare, non-flag arg as a message).
68
+ * Build the argv for a headless pi RPC run. The prompt is NEVER here — it goes over
69
+ * stdin as a `prompt` command. `--session-id <anchor>` is pi's create-or-resume flag,
70
+ * so the SAME args serve the first wake and every resume.
60
71
  *
61
72
  * @param {{ sessionId: string }} o
62
73
  * @returns {string[]}
63
74
  */
64
75
  export function buildPiArgs({ sessionId }) {
65
- return ["--mode", "json", "--session-id", sessionId, "-p"];
76
+ return ["--mode", "rpc", "--session-id", sessionId];
77
+ }
78
+
79
+ /**
80
+ * Parse the first `x.y.z` out of `pi --version` output.
81
+ * @param {unknown} text @returns {[number, number, number] | null}
82
+ */
83
+ export function parsePiVersion(text) {
84
+ const m = /(\d+)\.(\d+)\.(\d+)/.exec(String(text ?? ""));
85
+ return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
86
+ }
87
+
88
+ /** @param {[number,number,number]} a @param {[number,number,number]} b */
89
+ function compareVersions(a, b) {
90
+ for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] - b[i];
91
+ return 0;
92
+ }
93
+
94
+ /**
95
+ * Default `pi --version` probe. Resolves the raw stdout, or rejects on failure.
96
+ * @param {{ command: string, argv: string[], env: NodeJS.ProcessEnv }} o
97
+ * @returns {Promise<string>}
98
+ */
99
+ function defaultVersionProbe({ command, argv, env }) {
100
+ return new Promise((resolve, reject) => {
101
+ execFile(command, argv, { env, shell: false, windowsHide: true }, (err, stdout) => {
102
+ if (err) reject(err);
103
+ else resolve(String(stdout));
104
+ });
105
+ });
66
106
  }
67
107
 
68
108
  /**
@@ -123,6 +163,295 @@ export function resolveSpawnCommand(piPath, args, platform = process.platform, e
123
163
  return { command: piPath, argv: args };
124
164
  }
125
165
 
166
+
167
+ /**
168
+ * Per-wake stdin side of the pi RPC protocol: safe JSONL writes, the one stdin
169
+ * close, correlation of our own command responses, extension-dialog answers, the
170
+ * continuity check and the protocol stop. Kept as its own unit so the stop hook
171
+ * builds on the same write/close/settled state (mirrors ClaudeControlChannel).
172
+ */
173
+ export class PiRpcChannel {
174
+ /**
175
+ * @param {{ stdin: any, logger: {info(m:string):void,warn(m:string):void,error(m:string):void},
176
+ * prompt: string, anchor: string, isNew: boolean, platform?: NodeJS.Platform,
177
+ * forward?: (obj: any) => void, readdirImpl?: (dir: string) => string[] }} o
178
+ */
179
+ constructor({ stdin, logger, prompt, anchor, isNew, platform = process.platform, forward, readdirImpl }) {
180
+ this.stdin = stdin ?? null;
181
+ this.logger = logger;
182
+ this.prompt = prompt;
183
+ this.anchor = anchor;
184
+ this.platform = platform;
185
+ this.forward = forward ?? (() => {});
186
+ this.readdirImpl = readdirImpl ?? ((dir) => readdirSync(dir));
187
+ /** Result `isNew`: the caller's value until get_state says otherwise. */
188
+ this.isNew = Boolean(isNew);
189
+ /** True once we called end() (or the child is gone and stdin is unusable). */
190
+ this.stdinClosed = false;
191
+ /** True once the child exited or errored. */
192
+ this.exited = false;
193
+ /** True once the `prompt` command was written (a run may be in progress). */
194
+ this.promptSent = false;
195
+ /** True once `agent_start` / `agent_settled` were seen after the prompt. */
196
+ this.agentStarted = false;
197
+ this.settled = false;
198
+ /** True when the prompt was rejected or could not be delivered (D6). */
199
+ this.failed = false;
200
+ /** True when a protocol stop cut the wake short (before or during the run). */
201
+ this.cancelled = false;
202
+ }
203
+
204
+ /** In-flight protocol stop (shared by repeated stop-hook invocations). */
205
+ #stopWork = null;
206
+ /** Resolver for the pending abort wait, or null. */
207
+ #abortWait = null;
208
+
209
+ /** @returns {boolean} whether stdin can still take a write. */
210
+ get writable() {
211
+ const s = this.stdin;
212
+ if (this.stdinClosed || !s || typeof s.write !== "function") return false;
213
+ if (s.destroyed || s.writableEnded || s.writable === false) return false;
214
+ return true;
215
+ }
216
+
217
+ /**
218
+ * Write one JSONL command. Never throws: a closed/destroyed stdin or a
219
+ * synchronous write error is logged and reported as `false` (async EPIPE is
220
+ * caught by the stdin 'error' listener the spawner installs).
221
+ * @param {object} frame @param {string} what Log label.
222
+ * @returns {boolean}
223
+ */
224
+ write(frame, what) {
225
+ if (!this.writable) {
226
+ this.logger.warn(`[Chorus] pi stdin is closed; dropped ${what}`);
227
+ return false;
228
+ }
229
+ try {
230
+ this.stdin.write(`${JSON.stringify(frame)}\n`);
231
+ return true;
232
+ } catch (err) {
233
+ this.logger.warn(`[Chorus] failed writing ${what} to pi stdin: ${err}`);
234
+ return false;
235
+ }
236
+ }
237
+
238
+ /** Open the exchange: ask for the session state before sending the prompt. */
239
+ start() {
240
+ // A stop that already started (from inside onChild) owns the outcome.
241
+ if (!this.write({ id: ID_STATE, type: "get_state" }, "get_state") && !this.#stopWork) this.failed = true;
242
+ }
243
+
244
+ /**
245
+ * Write the wake's single prompt, unless a protocol stop already started (the
246
+ * waker's cancel-before-spawn branch stops the child from inside `onChild`, or
247
+ * an interrupt can land while get_state is in flight). A cancelled wake must
248
+ * never deliver its prompt.
249
+ */
250
+ #sendPrompt() {
251
+ if (this.#stopWork) {
252
+ this.logger.info("[Chorus] pi stop started before the prompt was written; prompt not delivered");
253
+ return;
254
+ }
255
+ this.promptSent = this.write({ id: ID_PROMPT, type: "prompt", message: this.prompt }, "prompt");
256
+ if (!this.promptSent) this.failed = true;
257
+ }
258
+
259
+ /** Close stdin once; idempotent and never throws. pi exits on EOF. */
260
+ closeStdin() {
261
+ if (this.stdinClosed) return;
262
+ this.stdinClosed = true;
263
+ const s = this.stdin;
264
+ if (!s || typeof s.end !== "function" || s.destroyed || s.writableEnded) return;
265
+ try {
266
+ s.end();
267
+ } catch (err) {
268
+ this.logger.warn(`[Chorus] failed closing pi stdin: ${err}`);
269
+ }
270
+ }
271
+
272
+ /** Normal completion closes stdin — unless a stop owns the close (beforeClose first). */
273
+ #finish() {
274
+ if (!this.#stopWork) this.closeStdin();
275
+ }
276
+
277
+ /** The child is gone or its stdin errored (EPIPE): no further writes. */
278
+ markStdinUnusable() {
279
+ this.stdinClosed = true;
280
+ }
281
+
282
+ /** The child exited or errored: stdin is unusable and a pending abort wait ends. */
283
+ markExited() {
284
+ this.exited = true;
285
+ this.markStdinUnusable();
286
+ this.#endAbortWait();
287
+ }
288
+
289
+ #endAbortWait() {
290
+ const wait = this.#abortWait;
291
+ this.#abortWait = null;
292
+ wait?.();
293
+ }
294
+
295
+ /**
296
+ * Protocol stop (design D4), invoked by the process killer through the stop hook.
297
+ * Repeated calls share one promise.
298
+ * 1. If the prompt was sent, the run has not settled and stdin is open: write ONE
299
+ * `abort` and wait for the first of agent_settled, the abort response, child
300
+ * exit, or the killer's `protocolDeadline` (the killer's own deadline — no
301
+ * other duration exists here, #569).
302
+ * 2. On EVERY path: await `beforeClose` (Windows tree-identity capture needs the
303
+ * root alive), logging a failure without skipping step 3.
304
+ * 3. Close stdin; pi exits on EOF.
305
+ * @param {{ protocolDeadline: number, beforeClose?: () => unknown }} o
306
+ * @returns {Promise<void>}
307
+ */
308
+ stop({ protocolDeadline, beforeClose }) {
309
+ if (this.#stopWork) return this.#stopWork;
310
+ // pi exits 0 on the EOF that ends a stop, so remember that this wake was cut
311
+ // short: a run that had not settled (or never started) is not a clean finish.
312
+ this.cancelled = !this.settled && !this.stdinClosed;
313
+ this.#stopWork = (async () => {
314
+ try {
315
+ if (this.promptSent && !this.settled && !this.stdinClosed) {
316
+ const outcome = new Promise((resolve) => { this.#abortWait = resolve; });
317
+ if (this.write({ id: ID_ABORT, type: "abort" }, "abort")) {
318
+ this.logger.info("[Chorus] interrupt: sent pi abort");
319
+ let timer;
320
+ try {
321
+ await Promise.race([
322
+ outcome,
323
+ // The killer-supplied protocol deadline — not a new timeout.
324
+ new Promise((resolve) => { timer = setTimeout(resolve, Math.max(0, protocolDeadline - Date.now())); }),
325
+ ]);
326
+ } finally {
327
+ clearTimeout(timer);
328
+ }
329
+ }
330
+ this.#abortWait = null;
331
+ }
332
+ } finally {
333
+ try {
334
+ await beforeClose?.();
335
+ } catch (err) {
336
+ this.logger.warn(`[Chorus] pi stop: beforeClose failed (continuing to close stdin): ${err}`);
337
+ }
338
+ this.closeStdin();
339
+ }
340
+ })();
341
+ return this.#stopWork;
342
+ }
343
+
344
+ /**
345
+ * Observe one stdout frame. Returns true when the frame is protocol plumbing the
346
+ * spawner consumed (so it must NOT be forwarded to `onMessage`).
347
+ * @param {any} frame
348
+ * @returns {boolean} consumed
349
+ */
350
+ handleFrame(frame) {
351
+ const type = frame && typeof frame === "object" ? frame.type : undefined;
352
+ if (type === "response") {
353
+ this.#onResponse(frame);
354
+ return true;
355
+ }
356
+ if (type === "extension_ui_request") {
357
+ this.#onExtensionUiRequest(frame);
358
+ return true;
359
+ }
360
+ if (type === "agent_start" && this.promptSent) this.agentStarted = true;
361
+ if (type === "agent_settled" && this.promptSent && !this.settled) {
362
+ this.settled = true;
363
+ this.#finish();
364
+ this.#endAbortWait();
365
+ }
366
+ if (type === "extension_error") {
367
+ this.logger.warn(`[Chorus] pi extension error (${String(frame.event)}): ${String(frame.error)}`);
368
+ }
369
+ return false;
370
+ }
371
+
372
+ #onResponse(frame) {
373
+ const ok = frame.success === true;
374
+ switch (frame.id) {
375
+ case ID_STATE:
376
+ if (ok && frame.data && typeof frame.data === "object") this.#onInitialState(frame.data);
377
+ else this.logger.warn(`[Chorus] pi get_state failed (continuity unknown): ${String(frame.error)}`);
378
+ this.#sendPrompt();
379
+ return;
380
+ case ID_PROMPT:
381
+ if (ok) {
382
+ // Accepted is not done: check whether a run actually started (design D2 step 5).
383
+ // A very short run can settle before this response is read; nothing to check then.
384
+ if (this.settled) return;
385
+ this.write({ id: ID_IDLE_CHECK, type: "get_state" }, "get_state (idle check)");
386
+ return;
387
+ }
388
+ this.logger.error(`[Chorus] pi rejected the prompt: ${String(frame.error)}`);
389
+ this.failed = true;
390
+ this.#finish();
391
+ return;
392
+ case ID_IDLE_CHECK:
393
+ if (!ok || !frame.data || typeof frame.data !== "object") {
394
+ this.logger.warn(`[Chorus] pi idle check failed (waiting for agent_settled): ${String(frame.error)}`);
395
+ return;
396
+ }
397
+ if (frame.data.isStreaming === false && !this.agentStarted && !this.settled) {
398
+ this.logger.info("[Chorus] pi handled the prompt without an agent run");
399
+ this.#finish();
400
+ }
401
+ return;
402
+ case ID_ABORT:
403
+ if (!ok) this.logger.warn(`[Chorus] pi rejected abort: ${String(frame.error)}`);
404
+ this.#endAbortWait();
405
+ return;
406
+ default:
407
+ if (!ok) this.logger.warn(`[Chorus] pi ${String(frame.command)} command failed: ${String(frame.error)}`);
408
+ }
409
+ }
410
+
411
+ /** Cancel blocking dialogs at once; fire-and-forget requests need no reply. */
412
+ #onExtensionUiRequest(frame) {
413
+ const method = String(frame.method);
414
+ if (!DIALOG_METHODS.has(method)) return;
415
+ if (typeof frame.id !== "string" || !frame.id) {
416
+ this.logger.warn(`[Chorus] pi extension ${method} dialog has no id; not answered`);
417
+ return;
418
+ }
419
+ // Method only — the title/message/options are extension-controlled text.
420
+ this.logger.warn(`[Chorus] cancelled pi extension ${method} dialog (headless daemon)`);
421
+ this.write({ type: "extension_ui_response", id: frame.id, cancelled: true }, `extension ${method} cancel`);
422
+ }
423
+
424
+ /** get_state before the prompt: truthful isNew plus the lost-history notice (D5). */
425
+ #onInitialState(data) {
426
+ if (typeof data.messageCount !== "number") return;
427
+ this.isNew = data.messageCount === 0;
428
+ if (this.isNew && this.#historyWasLost(data.sessionFile)) {
429
+ this.logger.warn(`[Chorus] ${PI_CONTINUITY_NOTICE} Session: ${this.anchor}.`);
430
+ this.forward({ type: "message_end", message: { role: "assistant", content: [{ type: "text", text: PI_CONTINUITY_NOTICE }] } });
431
+ }
432
+ }
433
+
434
+ /**
435
+ * A new session whose directory already holds another `<timestamp>_<anchor>.jsonl`
436
+ * means pi could not restore that file. Only a notice depends on this: a listing
437
+ * error is logged and treated as "no evidence".
438
+ * @param {unknown} sessionFile
439
+ */
440
+ #historyWasLost(sessionFile) {
441
+ if (!UUID_RE.test(this.anchor) || typeof sessionFile !== "string" || !sessionFile) return false;
442
+ const p = this.platform === "win32" ? pathWin32 : pathPosix;
443
+ const dir = p.dirname(sessionFile);
444
+ const own = p.basename(sessionFile);
445
+ const suffix = `_${this.anchor}.jsonl`;
446
+ try {
447
+ return this.readdirImpl(dir).some((name) => name !== own && String(name).toLowerCase().endsWith(suffix.toLowerCase()));
448
+ } catch (err) {
449
+ this.logger.warn(`[Chorus] could not inspect pi session dir for continuity: ${err?.code ?? err}`);
450
+ return false;
451
+ }
452
+ }
453
+ }
454
+
126
455
  /**
127
456
  * @typedef {Object} PiSpawnerOptions
128
457
  * @property {string} [piPath] Resolved pi path (resolved lazily if omitted).
@@ -135,16 +464,18 @@ export function resolveSpawnCommand(piPath, args, platform = process.platform, e
135
464
  * tooling and SessionStart bookkeeping.
136
465
  * @property {NodeJS.Platform} [platform] Injectable for tests; gates POSIX `detached`.
137
466
  * @property {(deps?: object) => (string|null)} [resolvePiPathFn] Injectable resolver.
467
+ * @property {(o: { command: string, argv: string[], env: NodeJS.ProcessEnv }) => Promise<string>} [versionProbeFn]
468
+ * Injectable `pi --version` probe (resolves stdout, rejects on failure).
469
+ * @property {(dir: string) => string[]} [readdirImpl] Injectable session-dir listing.
138
470
  */
139
471
 
140
472
  export class PiSpawner {
141
473
  /** @param {PiSpawnerOptions} [opts] */
142
474
  constructor(opts = {}) {
143
- // pi manages new-vs-resume internally from its own on-disk session state (the
144
- // `--session-id` anchor is idempotent create-or-resume), so the daemon's shared
145
- // Claude transcript probe is NOT authoritative for pi. Setting this false makes
146
- // the waker skip the Claude-specific "take over with `claude --resume`" log line
147
- // (mirrors CodexSpawner) — pi is not resumable via `claude --resume`.
475
+ // pi manages new-vs-resume internally (the `--session-id` anchor is
476
+ // create-or-resume), so the daemon's shared Claude transcript probe is NOT
477
+ // authoritative for pi. The waker then logs the spawner's own isNew instead of
478
+ // the Claude-specific "take over with `claude --resume`" line.
148
479
  this.sessionDecision = { probeIsAuthoritative: false };
149
480
  this.piPath = opts.piPath ?? null;
150
481
  this.spawnImpl = opts.spawnImpl ?? spawn;
@@ -157,24 +488,60 @@ export class PiSpawner {
157
488
  this.cliConfig = validateAgentCliConfig(opts.cliConfig, "pi", opts.label);
158
489
  this.env = overlayAgentEnv(opts.env ?? process.env, this.cliConfig.env, this.platform);
159
490
  this.resolvePiPathFn = opts.resolvePiPathFn ?? resolvePiPath;
491
+ this.versionProbeFn = opts.versionProbeFn ?? defaultVersionProbe;
492
+ this.readdirImpl = opts.readdirImpl;
493
+ /** @type {Map<string, Promise<boolean>>} resolved path → "may wake" */
494
+ this.versionChecks = new Map();
495
+ }
496
+
497
+ /**
498
+ * Version gate (design D7): read `pi --version` once per resolved executable. Too
499
+ * old → visible error and no wake; an unreadable version never blocks a wake.
500
+ * @param {string} piPath @returns {Promise<boolean>} whether the wake may proceed
501
+ */
502
+ checkVersion(piPath) {
503
+ let check = this.versionChecks.get(piPath);
504
+ if (!check) {
505
+ const { command, argv } = resolveSpawnCommand(piPath, ["--version"], this.platform, this.env);
506
+ check = Promise.resolve()
507
+ .then(() => this.versionProbeFn({ command, argv, env: this.env }))
508
+ .then(
509
+ (out) => {
510
+ const found = parsePiVersion(out);
511
+ if (!found) {
512
+ this.logger.warn(`[Chorus] could not read the pi version from \`pi --version\`; continuing (RPC wakes need pi >= ${MIN_PI_VERSION})`);
513
+ return true;
514
+ }
515
+ if (compareVersions(found, parsePiVersion(MIN_PI_VERSION)) < 0) {
516
+ this.logger.error(
517
+ `[Chorus] pi ${found.join(".")} is too old for RPC daemon wakes (need >= ${MIN_PI_VERSION}). Upgrade: ${PI_UPGRADE_COMMAND}`
518
+ );
519
+ return false;
520
+ }
521
+ return true;
522
+ },
523
+ (err) => {
524
+ this.logger.warn(`[Chorus] \`pi --version\` failed (${safeSpawnError(err)}); continuing (RPC wakes need pi >= ${MIN_PI_VERSION})`);
525
+ return true;
526
+ }
527
+ );
528
+ this.versionChecks.set(piPath, check);
529
+ }
530
+ return check;
160
531
  }
161
532
 
162
533
  /**
163
- * Spawn a headless pi run. Resolves when the subprocess exits. The prompt is
164
- * written to stdin (never argv). `sessionId` is the Chorus anchor (direct idea
165
- * uuid, or the entity uuid for an ad-hoc session) and is passed straight through as
166
- * pi's client-owned `--session-id`, which pi resolves as create-or-resume from its
167
- * own on-disk session dir — so the passed `isNew` (derived by the waker's
168
- * Claude-specific transcript probe, which never matches a pi session) does NOT
169
- * drive the flag; it is only echoed back for the waker's log line.
534
+ * Run one headless pi RPC wake. Resolves when the subprocess exits. The prompt is
535
+ * sent as the `prompt` command (never argv). `sessionId` is the Chorus anchor,
536
+ * passed straight through as pi's client-owned `--session-id`; the result's
537
+ * `isNew` comes from pi's own `get_state` (the passed value is only a fallback).
170
538
  *
171
539
  * @param {{ prompt: string, sessionId: string|null, isNew?: boolean, cwd?: string,
172
540
  * onMessage?: (obj: any) => void,
173
541
  * onChild?: (child: import("node:child_process").ChildProcess) => void }} params
174
542
  * @returns {Promise<{ sessionId: string, backendSessionId: string|null, exitCode: number|null, isNew: boolean }>}
175
- * `backendSessionId` is the resumable anchor (the input `sessionId`) after a spawn,
176
- * so the conversation UI can offer a resumable id — mirrors the other spawners.
177
- * `null` on the pre-spawn failure paths (no run started, nothing to resume).
543
+ * `backendSessionId` is the resumable anchor after a spawn; `null` on the
544
+ * pre-spawn failure paths (no run started, nothing to resume).
178
545
  */
179
546
  async wake({ prompt, sessionId, isNew, cwd, onMessage, onChild }) {
180
547
  const anchor = typeof sessionId === "string" ? sessionId : "";
@@ -189,13 +556,15 @@ export class PiSpawner {
189
556
  }
190
557
 
191
558
  assertConfiguredShimArgs(piPath, this.cliConfig.args, this.platform);
192
- const fixed = buildPiArgs({ sessionId: anchor });
193
- const args = [...fixed.slice(0, -1), ...this.cliConfig.args, fixed.at(-1)];
559
+ if (!(await this.checkVersion(piPath))) {
560
+ return { sessionId: anchor, backendSessionId: null, exitCode: null, isNew: isNewFlag };
561
+ }
562
+ const args = [...buildPiArgs({ sessionId: anchor }), ...this.cliConfig.args];
194
563
  const { command, argv } = resolveSpawnCommand(piPath, args, this.platform, this.env);
195
564
 
196
565
  // POSIX: detached process group so the interrupt path can group-kill the tree
197
566
  // (pi may fork child shells / subagents). Windows uses taskkill /T. stdio stays
198
- // piped — prompt over stdin + NDJSON stdout parse are unaffected.
567
+ // piped — JSONL commands over stdin + JSONL stdout parse are unaffected.
199
568
  const detached = this.platform !== "win32";
200
569
 
201
570
  // Export the daemon's resolved connection pair for the chorus-pi extension's
@@ -211,6 +580,15 @@ export class PiSpawner {
211
580
  childEnv.CHORUS_AGENT_PROFILE = this.creds.agentUuid || this.creds.agentName;
212
581
  }
213
582
 
583
+ const forward = (obj) => {
584
+ if (!onMessage) return;
585
+ try {
586
+ onMessage(obj);
587
+ } catch (err) {
588
+ this.logger.warn(`[Chorus] onMessage handler threw: ${err}`);
589
+ }
590
+ };
591
+
214
592
  return new Promise((resolve) => {
215
593
  let child;
216
594
  try {
@@ -228,6 +606,17 @@ export class PiSpawner {
228
606
  return;
229
607
  }
230
608
 
609
+ const channel = new PiRpcChannel({
610
+ stdin: child.stdin, logger: this.logger, prompt, anchor, isNew: isNewFlag,
611
+ platform: this.platform, forward, readdirImpl: this.readdirImpl,
612
+ });
613
+ // Protocol interrupt (design D4): the shared killer calls this instead of
614
+ // SIGINT and then force-cleans within the same deadline if the child remains.
615
+ // Registered before onChild so an interrupt can never see an unhooked child.
616
+ const unregisterStopHook = registerProcessStopHook(child, ({ deadline, protocolDeadline = deadline, beforeClose }) =>
617
+ channel.stop({ protocolDeadline, beforeClose })
618
+ );
619
+
231
620
  // Hand the live child to the caller (interrupt registry) before resolving.
232
621
  // Never let a throwing callback escape into the spawn path.
233
622
  if (onChild) {
@@ -246,13 +635,8 @@ export class PiSpawner {
246
635
  stdoutBuf,
247
636
  String(chunk),
248
637
  (obj) => {
249
- if (onMessage) {
250
- try {
251
- onMessage(obj);
252
- } catch (err) {
253
- this.logger.warn(`[Chorus] onMessage handler threw: ${err}`);
254
- }
255
- }
638
+ if (channel.handleFrame(obj)) return; // RPC plumbing — never forwarded
639
+ forward(obj);
256
640
  },
257
641
  (msg) => this.logger.warn(`[Chorus] ${msg}`)
258
642
  );
@@ -266,31 +650,41 @@ export class PiSpawner {
266
650
 
267
651
  child.on("error", (error) => {
268
652
  this.logger.error(`[Chorus] pi process error: ${safeSpawnError(error)}`);
269
- resolve({ sessionId: anchor, backendSessionId: anchor || null, exitCode: null, isNew: isNewFlag });
653
+ channel.markExited();
654
+ unregisterStopHook();
655
+ resolve({ sessionId: anchor, backendSessionId: anchor || null, exitCode: null, isNew: channel.isNew });
270
656
  });
271
657
 
658
+ // A dead child's stdin must never be written again (late dialog answers).
659
+ child.on?.("exit", () => channel.markExited());
660
+
272
661
  // Settle on process exit, not only on stdio close: a detached descendant can
273
662
  // inherit the pipes and keep `close` from ever firing (see cli/child-exit.mjs).
274
- awaitChildSettled(child, { logger: this.logger, label: "pi" }).then((code) => {
663
+ awaitChildSettled(child, { logger: this.logger, label: "pi" }).then((raw) => {
664
+ channel.markExited();
665
+ unregisterStopHook();
666
+ // pi RPC exits 0 on stdin EOF, so neither a cancelled wake (130, like the Codex
667
+ // backend) nor a rejected/undelivered prompt (1) may read as a clean finish
668
+ // (design D6) — the waker records a clean exit as an `ended` turn. A non-zero
669
+ // raw code is always kept.
670
+ const code = raw !== 0 ? raw : channel.cancelled ? 130 : channel.failed ? 1 : 0;
275
671
  if (code !== 0) {
276
672
  this.logger.warn(`[Chorus] pi exited with code ${code}`);
277
673
  }
278
- resolve({ sessionId: anchor, backendSessionId: anchor || null, exitCode: code, isNew: isNewFlag });
674
+ resolve({ sessionId: anchor, backendSessionId: anchor || null, exitCode: code, isNew: channel.isNew });
279
675
  });
280
676
 
281
677
  // Guard against an ASYNC stdin error (EPIPE) so it never becomes an
282
678
  // uncaughtException that kills the daemon.
283
679
  child.stdin?.on?.("error", (err) => {
284
680
  this.logger.warn(`[Chorus] pi stdin error (ignored): ${err}`);
681
+ channel.markStdinUnusable();
285
682
  });
286
683
 
287
- // Feed the prompt over stdin, then close it so the model runs.
288
- try {
289
- child.stdin?.write(prompt);
290
- child.stdin?.end();
291
- } catch (err) {
292
- this.logger.warn(`[Chorus] failed writing prompt to pi stdin: ${err}`);
293
- }
684
+ // get_state first; its response sends the one prompt (skipped when a stop
685
+ // already started, e.g. from inside onChild above). stdin stays open until
686
+ // agent_settled, a handled-without-run idle check, or a rejected prompt.
687
+ channel.start();
294
688
  });
295
689
  }
296
690
  }