@chorus-aidlc/chorus 0.12.0 → 0.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +364 -363
  3. package/.next/standalone/.next/app-path-routes-manifest.json +43 -43
  4. package/.next/standalone/.next/build-manifest.json +5 -5
  5. package/.next/standalone/.next/prerender-manifest.json +43 -43
  6. package/.next/standalone/.next/react-loadable-manifest.json +3 -3
  7. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js +1 -1
  8. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page.js.nft.json +1 -1
  9. package/.next/standalone/.next/server/app/(dashboard)/project-groups/[uuid]/page_client-reference-manifest.js +1 -1
  10. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js +2 -2
  11. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/activity/page_client-reference-manifest.js +1 -1
  13. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js +2 -2
  14. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/[ideaUuid]/page_client-reference-manifest.js +1 -1
  16. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js +2 -2
  17. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/dashboard/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js +2 -2
  20. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page_client-reference-manifest.js +1 -1
  22. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js +2 -2
  23. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/documents/page_client-reference-manifest.js +1 -1
  25. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js +2 -2
  26. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page_client-reference-manifest.js +1 -1
  28. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js +2 -2
  29. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/new/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js +2 -2
  32. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page.js.nft.json +1 -1
  33. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/proposals/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js +1 -1
  35. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js +1 -1
  38. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page.js.nft.json +1 -1
  39. package/.next/standalone/.next/server/app/(dashboard)/projects/[uuid]/tasks/page_client-reference-manifest.js +1 -1
  40. package/.next/standalone/.next/server/app/(dashboard)/projects/page.js +1 -1
  41. package/.next/standalone/.next/server/app/(dashboard)/projects/page.js.nft.json +1 -1
  42. package/.next/standalone/.next/server/app/(dashboard)/projects/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js +2 -2
  44. package/.next/standalone/.next/server/app/(dashboard)/settings/page.js.nft.json +1 -1
  45. package/.next/standalone/.next/server/app/(dashboard)/settings/page_client-reference-manifest.js +1 -1
  46. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  48. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  49. package/.next/standalone/.next/server/app/admin/companies/[uuid]/page_client-reference-manifest.js +1 -1
  50. package/.next/standalone/.next/server/app/admin/companies/new/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/admin/companies/new.html +1 -1
  52. package/.next/standalone/.next/server/app/admin/companies/new.rsc +1 -1
  53. package/.next/standalone/.next/server/app/admin/companies/page_client-reference-manifest.js +1 -1
  54. package/.next/standalone/.next/server/app/admin/companies.html +1 -1
  55. package/.next/standalone/.next/server/app/admin/companies.rsc +1 -1
  56. package/.next/standalone/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  57. package/.next/standalone/.next/server/app/admin.html +1 -1
  58. package/.next/standalone/.next/server/app/admin.rsc +1 -1
  59. package/.next/standalone/.next/server/app/api/admin/companies/[uuid]/route_client-reference-manifest.js +1 -1
  60. package/.next/standalone/.next/server/app/api/admin/companies/route_client-reference-manifest.js +1 -1
  61. package/.next/standalone/.next/server/app/api/admin/login/route_client-reference-manifest.js +1 -1
  62. package/.next/standalone/.next/server/app/api/admin/session/route_client-reference-manifest.js +1 -1
  63. package/.next/standalone/.next/server/app/api/agent-connections/route_client-reference-manifest.js +1 -1
  64. package/.next/standalone/.next/server/app/api/agents/[uuid]/route_client-reference-manifest.js +1 -1
  65. package/.next/standalone/.next/server/app/api/agents/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  66. package/.next/standalone/.next/server/app/api/agents/route_client-reference-manifest.js +1 -1
  67. package/.next/standalone/.next/server/app/api/api-keys/[uuid]/route_client-reference-manifest.js +1 -1
  68. package/.next/standalone/.next/server/app/api/api-keys/route_client-reference-manifest.js +1 -1
  69. package/.next/standalone/.next/server/app/api/auth/callback/route_client-reference-manifest.js +1 -1
  70. package/.next/standalone/.next/server/app/api/auth/check-default/route_client-reference-manifest.js +1 -1
  71. package/.next/standalone/.next/server/app/api/auth/company-oidc/route_client-reference-manifest.js +1 -1
  72. package/.next/standalone/.next/server/app/api/auth/default-login/route_client-reference-manifest.js +1 -1
  73. package/.next/standalone/.next/server/app/api/auth/identify/route_client-reference-manifest.js +1 -1
  74. package/.next/standalone/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
  75. package/.next/standalone/.next/server/app/api/auth/me/route_client-reference-manifest.js +1 -1
  76. package/.next/standalone/.next/server/app/api/auth/refresh/route_client-reference-manifest.js +1 -1
  77. package/.next/standalone/.next/server/app/api/auth/session/route_client-reference-manifest.js +1 -1
  78. package/.next/standalone/.next/server/app/api/auth/sync-token/route_client-reference-manifest.js +1 -1
  79. package/.next/standalone/.next/server/app/api/comments/route_client-reference-manifest.js +1 -1
  80. package/.next/standalone/.next/server/app/api/daemon/control/route_client-reference-manifest.js +1 -1
  81. package/.next/standalone/.next/server/app/api/daemon/execution-state/route_client-reference-manifest.js +1 -1
  82. package/.next/standalone/.next/server/app/api/daemon/executions/route_client-reference-manifest.js +1 -1
  83. package/.next/standalone/.next/server/app/api/daemon/pending-turns/route_client-reference-manifest.js +1 -1
  84. package/.next/standalone/.next/server/app/api/daemon/report-interrupt/route_client-reference-manifest.js +1 -1
  85. package/.next/standalone/.next/server/app/api/daemon/resume/route_client-reference-manifest.js +1 -1
  86. package/.next/standalone/.next/server/app/api/daemon/transcript/route_client-reference-manifest.js +1 -1
  87. package/.next/standalone/.next/server/app/api/daemon/turn-advance/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.js +6 -4
  96. package/.next/standalone/.next/server/app/api/events/notifications/route_client-reference-manifest.js +1 -1
  97. package/.next/standalone/.next/server/app/api/events/route.js +7 -5
  98. package/.next/standalone/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
  99. package/.next/standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  100. package/.next/standalone/.next/server/app/api/ideas/[uuid]/claim/route_client-reference-manifest.js +1 -1
  101. package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/preview/route_client-reference-manifest.js +1 -1
  102. package/.next/standalone/.next/server/app/api/ideas/[uuid]/move/route_client-reference-manifest.js +1 -1
  103. package/.next/standalone/.next/server/app/api/ideas/[uuid]/parent/route_client-reference-manifest.js +1 -1
  104. package/.next/standalone/.next/server/app/api/ideas/[uuid]/release/route_client-reference-manifest.js +1 -1
  105. package/.next/standalone/.next/server/app/api/ideas/[uuid]/route_client-reference-manifest.js +1 -1
  106. package/.next/standalone/.next/server/app/api/keepalive/route_client-reference-manifest.js +1 -1
  107. package/.next/standalone/.next/server/app/api/mcp/route_client-reference-manifest.js +1 -1
  108. package/.next/standalone/.next/server/app/api/me/assignments/route_client-reference-manifest.js +1 -1
  109. package/.next/standalone/.next/server/app/api/mentionables/route_client-reference-manifest.js +1 -1
  110. package/.next/standalone/.next/server/app/api/notifications/[uuid]/archive/route_client-reference-manifest.js +1 -1
  111. package/.next/standalone/.next/server/app/api/notifications/[uuid]/read/route_client-reference-manifest.js +1 -1
  112. package/.next/standalone/.next/server/app/api/notifications/preferences/route_client-reference-manifest.js +1 -1
  113. package/.next/standalone/.next/server/app/api/notifications/read-all/route_client-reference-manifest.js +1 -1
  114. package/.next/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
  115. package/.next/standalone/.next/server/app/api/notifications/unread-count/route_client-reference-manifest.js +1 -1
  116. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/dashboard/route_client-reference-manifest.js +1 -1
  117. package/.next/standalone/.next/server/app/api/project-groups/[uuid]/route_client-reference-manifest.js +1 -1
  118. package/.next/standalone/.next/server/app/api/project-groups/route_client-reference-manifest.js +1 -1
  119. package/.next/standalone/.next/server/app/api/projects/[uuid]/activity/route_client-reference-manifest.js +1 -1
  120. package/.next/standalone/.next/server/app/api/projects/[uuid]/available/route_client-reference-manifest.js +1 -1
  121. package/.next/standalone/.next/server/app/api/projects/[uuid]/documents/route_client-reference-manifest.js +1 -1
  122. package/.next/standalone/.next/server/app/api/projects/[uuid]/group/route_client-reference-manifest.js +1 -1
  123. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/route_client-reference-manifest.js +1 -1
  124. package/.next/standalone/.next/server/app/api/projects/[uuid]/ideas/tracker/route_client-reference-manifest.js +1 -1
  125. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/[proposalUuid]/validate/route_client-reference-manifest.js +1 -1
  126. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/route_client-reference-manifest.js +1 -1
  127. package/.next/standalone/.next/server/app/api/projects/[uuid]/proposals/summary/route_client-reference-manifest.js +1 -1
  128. package/.next/standalone/.next/server/app/api/projects/[uuid]/route_client-reference-manifest.js +1 -1
  129. package/.next/standalone/.next/server/app/api/projects/[uuid]/stats/route_client-reference-manifest.js +1 -1
  130. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/dependencies/route_client-reference-manifest.js +1 -1
  131. package/.next/standalone/.next/server/app/api/projects/[uuid]/tasks/route_client-reference-manifest.js +1 -1
  132. package/.next/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
  133. package/.next/standalone/.next/server/app/api/proposals/[uuid]/approve/route_client-reference-manifest.js +1 -1
  134. package/.next/standalone/.next/server/app/api/proposals/[uuid]/close/route_client-reference-manifest.js +1 -1
  135. package/.next/standalone/.next/server/app/api/proposals/[uuid]/reject/route_client-reference-manifest.js +1 -1
  136. package/.next/standalone/.next/server/app/api/proposals/[uuid]/revoke/route_client-reference-manifest.js +1 -1
  137. package/.next/standalone/.next/server/app/api/proposals/[uuid]/route_client-reference-manifest.js +1 -1
  138. package/.next/standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  139. package/.next/standalone/.next/server/app/api/sessions/[uuid]/route_client-reference-manifest.js +1 -1
  140. package/.next/standalone/.next/server/app/api/tasks/[uuid]/claim/route_client-reference-manifest.js +1 -1
  141. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/[dependsOnUuid]/route_client-reference-manifest.js +1 -1
  142. package/.next/standalone/.next/server/app/api/tasks/[uuid]/dependencies/route_client-reference-manifest.js +1 -1
  143. package/.next/standalone/.next/server/app/api/tasks/[uuid]/release/route_client-reference-manifest.js +1 -1
  144. package/.next/standalone/.next/server/app/api/tasks/[uuid]/route_client-reference-manifest.js +1 -1
  145. package/.next/standalone/.next/server/app/api/tasks/[uuid]/sessions/route_client-reference-manifest.js +1 -1
  146. package/.next/standalone/.next/server/app/index.html +1 -1
  147. package/.next/standalone/.next/server/app/index.rsc +2 -2
  148. package/.next/standalone/.next/server/app/login/admin/page.js +2 -2
  149. package/.next/standalone/.next/server/app/login/admin/page.js.nft.json +1 -1
  150. package/.next/standalone/.next/server/app/login/admin/page_client-reference-manifest.js +1 -1
  151. package/.next/standalone/.next/server/app/login/admin.html +1 -1
  152. package/.next/standalone/.next/server/app/login/admin.rsc +1 -1
  153. package/.next/standalone/.next/server/app/login/callback/page.js +2 -2
  154. package/.next/standalone/.next/server/app/login/callback/page.js.nft.json +1 -1
  155. package/.next/standalone/.next/server/app/login/callback/page_client-reference-manifest.js +1 -1
  156. package/.next/standalone/.next/server/app/login/callback.html +1 -1
  157. package/.next/standalone/.next/server/app/login/callback.rsc +1 -1
  158. package/.next/standalone/.next/server/app/login/page.js +2 -2
  159. package/.next/standalone/.next/server/app/login/page.js.nft.json +1 -1
  160. package/.next/standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  161. package/.next/standalone/.next/server/app/login/pick-workspace/page_client-reference-manifest.js +1 -1
  162. package/.next/standalone/.next/server/app/login/pick-workspace.html +1 -1
  163. package/.next/standalone/.next/server/app/login/pick-workspace.rsc +1 -1
  164. package/.next/standalone/.next/server/app/login/silent-refresh/page_client-reference-manifest.js +1 -1
  165. package/.next/standalone/.next/server/app/login/silent-refresh.html +1 -1
  166. package/.next/standalone/.next/server/app/login/silent-refresh.rsc +1 -1
  167. package/.next/standalone/.next/server/app/login.html +1 -1
  168. package/.next/standalone/.next/server/app/login.rsc +2 -2
  169. package/.next/standalone/.next/server/app/onboarding/page.js +2 -2
  170. package/.next/standalone/.next/server/app/onboarding/page.js.nft.json +1 -1
  171. package/.next/standalone/.next/server/app/onboarding/page_client-reference-manifest.js +1 -1
  172. package/.next/standalone/.next/server/app/onboarding.html +1 -1
  173. package/.next/standalone/.next/server/app/onboarding.rsc +2 -2
  174. package/.next/standalone/.next/server/app/page.js +2 -2
  175. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  176. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  177. package/.next/standalone/.next/server/app/projects.html +1 -1
  178. package/.next/standalone/.next/server/app/projects.rsc +2 -2
  179. package/.next/standalone/.next/server/app/settings.html +1 -1
  180. package/.next/standalone/.next/server/app/settings.rsc +3 -3
  181. package/.next/standalone/.next/server/app-paths-manifest.json +43 -43
  182. package/.next/standalone/.next/server/chunks/1596.js +2 -2
  183. package/.next/standalone/.next/server/chunks/2779.js +31 -0
  184. package/.next/standalone/.next/server/chunks/2961.js +1 -1
  185. package/.next/standalone/.next/server/chunks/3219.js +1 -1
  186. package/.next/standalone/.next/server/chunks/4292.js +1 -0
  187. package/.next/standalone/.next/server/chunks/4872.js +1 -1
  188. package/.next/standalone/.next/server/chunks/5538.js +1 -0
  189. package/.next/standalone/.next/server/chunks/6113.js +1 -1
  190. package/.next/standalone/.next/server/chunks/6207.js +1 -1
  191. package/.next/standalone/.next/server/chunks/7063.js +2 -2
  192. package/.next/standalone/.next/server/chunks/7160.js +1 -1
  193. package/.next/standalone/.next/server/chunks/7368.js +1 -1
  194. package/.next/standalone/.next/server/chunks/7515.js +1 -1
  195. package/.next/standalone/.next/server/chunks/8732.js +1 -0
  196. package/.next/standalone/.next/server/chunks/920.js +1 -1
  197. package/.next/standalone/.next/server/chunks/9273.js +1 -0
  198. package/.next/standalone/.next/server/chunks/937.js +1 -1
  199. package/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
  200. package/.next/standalone/.next/server/middleware-manifest.json +5 -5
  201. package/.next/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  202. package/.next/standalone/.next/server/pages/404.html +1 -1
  203. package/.next/standalone/.next/server/pages/500.html +1 -1
  204. package/.next/standalone/.next/server/pages-manifest.json +1 -1
  205. package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
  206. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  207. package/.next/standalone/.next/static/chunks/10344-2e9a9d767e2e8ed1.js +1 -0
  208. package/.next/standalone/.next/static/chunks/{33187.13c12f5c0821ac57.js → 33187.1328c15b9dab78a8.js} +1 -1
  209. package/.next/standalone/.next/static/chunks/{40867-04cc0d00205eaf9b.js → 46983-8bf90f1ffeb55957.js} +1 -1
  210. package/.next/standalone/.next/static/chunks/{32497-886aa4d3d7bf4967.js → 46999-f53da8f53fe1b8a9.js} +1 -1
  211. package/.next/standalone/.next/static/chunks/{4860.f4242c493fcf182d.js → 4860.e3d0dd79a4d37626.js} +1 -1
  212. package/.next/standalone/.next/static/chunks/{78917-2b959ab0a7f610b4.js → 78917-aa5bbff9248799a4.js} +1 -1
  213. package/.next/standalone/.next/static/chunks/85573-fb4d2cbd67612d47.js +1 -0
  214. package/.next/standalone/.next/static/chunks/{97784-7b90a17294840e32.js → 97784-42081b75f8cc6da8.js} +1 -1
  215. package/.next/standalone/.next/static/chunks/97868-55f18a05fe483d85.js +1 -0
  216. package/.next/standalone/.next/static/chunks/{99373.880b43de1fa6411f.js → 99373.cc6319fa4cf36081.js} +1 -1
  217. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-b3a497ba5eb08036.js +1 -0
  218. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-d786f0c18a9c4f06.js +1 -0
  219. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-c68f1eda3779b448.js +1 -0
  220. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/{page-6620b9d7a2cc766f.js → page-e0935445535c28cb.js} +1 -1
  221. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-f824632ff61a9fa7.js +1 -0
  222. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/page-dbcb6e50fc4816c6.js +1 -0
  223. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/page-1295bc505e727383.js +1 -0
  224. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/[taskUuid]/{page-d5c3dde34d6291a8.js → page-ceeec49af0396432.js} +1 -1
  225. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/tasks/{page-0da08d8b425acb6a.js → page-1286afe43c086d6a.js} +1 -1
  226. package/.next/standalone/.next/static/chunks/app/(dashboard)/settings/{page-44014947a679f91c.js → page-0fb1d51016f6bd56.js} +1 -1
  227. package/.next/standalone/.next/static/chunks/app/login/page-d2513f477211dd6f.js +1 -0
  228. package/.next/standalone/.next/static/chunks/app/onboarding/{page-9a41067d4c5d29d7.js → page-2936274fb252329e.js} +1 -1
  229. package/.next/standalone/.next/static/chunks/app/page-075afd218994d377.js +1 -0
  230. package/.next/standalone/.next/static/chunks/{webpack-1556cfd11f7e90d1.js → webpack-ed3555ea61243391.js} +1 -1
  231. package/.next/standalone/package.json +1 -1
  232. package/README.md +2 -2
  233. package/README.zh.md +2 -2
  234. package/cli/client-args.mjs +1 -1
  235. package/cli/codex-session-map.mjs +117 -0
  236. package/cli/codex-spawner.mjs +312 -0
  237. package/cli/daemon-agent.mjs +32 -6
  238. package/cli/daemon-banner.mjs +43 -5
  239. package/cli/daemon.mjs +133 -14
  240. package/cli/login.mjs +77 -34
  241. package/cli/spawner-select.mjs +30 -0
  242. package/cli/sse-listener.mjs +29 -1
  243. package/cli/upload-hooks.mjs +73 -8
  244. package/package.json +1 -1
  245. package/.next/standalone/.next/server/chunks/3371.js +0 -1
  246. package/.next/standalone/.next/server/chunks/345.js +0 -1
  247. package/.next/standalone/.next/server/chunks/4958.js +0 -1
  248. package/.next/standalone/.next/server/chunks/5581.js +0 -1
  249. package/.next/standalone/.next/server/chunks/7897.js +0 -1
  250. package/.next/standalone/.next/server/chunks/8245.js +0 -31
  251. package/.next/standalone/.next/static/chunks/42111-1935df3b0dab2095.js +0 -1
  252. package/.next/standalone/.next/static/chunks/6239-d78e6f830ce0a7f8.js +0 -1
  253. package/.next/standalone/.next/static/chunks/97868-9406a4014b667c2d.js +0 -1
  254. package/.next/standalone/.next/static/chunks/app/(dashboard)/layout-f04d5fa070f15acd.js +0 -1
  255. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/dashboard/page-c4271886ef004611.js +0 -1
  256. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/documents/[documentUuid]/page-7a8b60de97bfbd29.js +0 -1
  257. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/[proposalUuid]/page-0a55289b2bc510c2.js +0 -1
  258. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/new/page-99639479cc98965f.js +0 -1
  259. package/.next/standalone/.next/static/chunks/app/(dashboard)/projects/[uuid]/proposals/page-5d09141beb5d6aee.js +0 -1
  260. package/.next/standalone/.next/static/chunks/app/login/page-e7493d55573a52f1.js +0 -1
  261. package/.next/standalone/.next/static/chunks/app/page-8b4baa950c78ffe7.js +0 -1
  262. /package/.next/standalone/.next/static/{v1yRpXkt1wNBkZMTE3tiz → GmBblfxcDziUENBiEap4B}/_buildManifest.js +0 -0
  263. /package/.next/standalone/.next/static/{v1yRpXkt1wNBkZMTE3tiz → GmBblfxcDziUENBiEap4B}/_ssgManifest.js +0 -0
package/cli/daemon.mjs CHANGED
@@ -18,8 +18,8 @@ import {
18
18
  resolvePermissionMode,
19
19
  yoloWarningLine,
20
20
  } from "./daemon-permission-mode.mjs";
21
- import { resolveAgentType } from "./daemon-agent.mjs";
22
- import { formatBanner } from "./daemon-banner.mjs";
21
+ import { resolveAgentType, backendClientType } from "./daemon-agent.mjs";
22
+ import { formatBanner, agentNotFoundWarningLine } from "./daemon-banner.mjs";
23
23
  import { ChorusClient, validateAndFetchIdentity } from "./chorus-client.mjs";
24
24
  import { SseListener } from "./sse-listener.mjs";
25
25
  import { createBackfill } from "./backfill.mjs";
@@ -27,7 +27,9 @@ import { EventRouter } from "./event-router.mjs";
27
27
  import { WakeQueue } from "./wake-queue.mjs";
28
28
  import { Waker } from "./waker.mjs";
29
29
  import { LineageResolver } from "./lineage.mjs";
30
- import { ClaudeSpawner, resolveClaudePath } from "./claude-spawner.mjs";
30
+ import { resolveClaudePath } from "./claude-spawner.mjs";
31
+ import { resolveCodexPath } from "./codex-spawner.mjs";
32
+ import { selectSpawner } from "./spawner-select.mjs";
31
33
  import {
32
34
  createExecutionUploadHooks,
33
35
  createTranscriptUploadHooks,
@@ -95,9 +97,12 @@ function defaultLogger() {
95
97
  export function buildDaemon(creds, deps = {}) {
96
98
  const logger = deps.logger ?? defaultLogger();
97
99
  const permissionMode = deps.permissionMode ?? "chorus";
100
+ // The resolved agent backend selects which spawner the daemon injects
101
+ // (add-daemon-codex-backend). Defaults to claude-code, matching the prior
102
+ // hard-wired ClaudeSpawner. The spawn path below is backend-agnostic — it only
103
+ // ever calls the shared wake(...) contract.
104
+ const agentType = deps.agentType ?? "claude-code";
98
105
  // Per-wake verbose logging (daemon-startup-output), threaded into the Waker.
99
- // agentType is currently display-only (banner/logs) — the spawn path is
100
- // claude-code regardless (daemon-agent-selection reserves the slot only).
101
106
  const verbose = deps.verbose ?? false;
102
107
  // Escalation window for the interrupt killer (子3). Pre-resolved by runDaemon via
103
108
  // the layered resolver; falls back to the resolver's default here when not given,
@@ -112,7 +117,11 @@ export function buildDaemon(creds, deps = {}) {
112
117
  const lineage =
113
118
  deps.lineage ??
114
119
  new LineageResolver({ url: creds.url, apiKey: creds.apiKey, logger, fetchImpl: deps.fetchImpl });
115
- const spawner = deps.spawner ?? new ClaudeSpawner({ logger, permissionMode });
120
+ // Inject the spawner for the resolved backend. claude-code → ClaudeSpawner
121
+ // (construction byte-identical to before); codex → CodexSpawner. `creds` are
122
+ // passed through so the Codex backend can export the daemon key into the woken
123
+ // process env (the Claude backend ignores creds — it gets its key via --mcp-config).
124
+ const spawner = deps.spawner ?? selectSpawner(agentType, { logger, permissionMode, creds });
116
125
  // The WakeQueue serializes per DIRECT idea (keyFor's key). It is shared across all
117
126
  // path-connections: serialization-per-idea still holds, and maxConcurrency caps the
118
127
  // whole process's in-flight wakes rather than per-cwd — the right global budget.
@@ -131,6 +140,40 @@ export function buildDaemon(creds, deps = {}) {
131
140
  ? deps.cwds
132
141
  : [deps.cwd];
133
142
 
143
+ // ===== All-conflict exit latch (add-daemon-connection-conflict-skip, Q3) =====
144
+ // Each declared connection resolves to exactly one terminal outcome: REGISTERED
145
+ // (saw connection_registered → onConnectionId) or CONFLICTED (saw
146
+ // connection_conflict → onConflict). When EVERY declared connection has resolved
147
+ // AND none registered (≥1 conflicted, 0 survived), the daemon has nothing to serve,
148
+ // so we settle `allConflict` — runDaemon races this against waitForever and returns
149
+ // a non-zero exit. The latch is evaluated only after all connections resolve (R5):
150
+ // a path still mid-handshake leaves `resolved < total`, so a partial conflict never
151
+ // triggers the exit. A connection that registers (even after some conflicted) flips
152
+ // `anyRegistered`, so the exit never fires while at least one path serves.
153
+ const totalConnections = cwdSet.length;
154
+ let resolvedConnections = 0;
155
+ let anyRegistered = false;
156
+ let settleAllConflict; // resolver, wired into the promise below
157
+ // A promise that settles ONLY in the all-paths-conflicted case. It never rejects and
158
+ // never settles otherwise (a serving daemon simply keeps waiting on waitForever).
159
+ const allConflict = new Promise((resolve) => {
160
+ settleAllConflict = resolve;
161
+ });
162
+ // Record one connection's terminal outcome and, once all have resolved, decide whether
163
+ // the whole daemon should exit. Idempotent per connection BY CONSTRUCTION: the guard
164
+ // lives here (flips the connection's own `outcome.resolved`) so a reconnect's repeated
165
+ // connection_registered — or any future caller — can never double-count, no matter the
166
+ // call site. The first terminal signal for a connection wins; later ones are no-ops.
167
+ function recordConnectionOutcome(outcome, registered) {
168
+ if (outcome.resolved) return;
169
+ outcome.resolved = true;
170
+ resolvedConnections += 1;
171
+ if (registered) anyRegistered = true;
172
+ if (resolvedConnections === totalConnections && !anyRegistered) {
173
+ settleAllConflict();
174
+ }
175
+ }
176
+
134
177
  /**
135
178
  * Build ONE independent path-connection bound to `cwd` (one SSE stream → one
136
179
  * DaemonConnection row, keyed by that cwd server-side). All per-connection state —
@@ -149,6 +192,13 @@ export function buildDaemon(creds, deps = {}) {
149
192
  /** @type {{ connectionUuid: string|null }} */
150
193
  const connectionState = { connectionUuid: null };
151
194
 
195
+ // Per-connection terminal-outcome bookkeeping (add-daemon-connection-conflict-skip).
196
+ // `resolved` guards recordConnectionOutcome against double-counting a reconnect's
197
+ // repeated connection_registered. `skipped` marks a cwd surrendered to a live
198
+ // different-process daemon — purely informational (the listener is torn down, so it
199
+ // simply never reconnects), kept on the returned object for inspection/tests.
200
+ const outcome = { resolved: false, skipped: false };
201
+
152
202
  // Interrupt reporter (子3): REST POST with the daemon's Bearer key. Injectable for
153
203
  // tests (shared across connections when injected — tests use a single connection).
154
204
  const reportInterrupt =
@@ -273,6 +323,9 @@ export function buildDaemon(creds, deps = {}) {
273
323
  makeSse({
274
324
  url: creds.url,
275
325
  apiKey: creds.apiKey,
326
+ // Self-report the SELECTED backend so the connection registry + presence UI
327
+ // label a codex daemon as `codex` (not the hardcoded `claude_code`).
328
+ clientType: backendClientType(agentType),
276
329
  // The working directory THIS connection serves (T3). `undefined` ⇒ the listener
277
330
  // reports its process cwd (single-path / HARD-1). It is just the served path.
278
331
  cwd,
@@ -283,13 +336,37 @@ export function buildDaemon(creds, deps = {}) {
283
336
  `[Chorus] registered as connection ${connectionUuid}` +
284
337
  (cwd ? ` (cwd=${cwd})` : "")
285
338
  );
339
+ // Terminal outcome: this path registered. recordConnectionOutcome is idempotent
340
+ // per connection, so a reconnect's repeated connection_registered counts once.
341
+ recordConnectionOutcome(outcome, true);
342
+ },
343
+ // Connection conflict (add-daemon-connection-conflict-skip, Q3/Q4): the server
344
+ // refused to register THIS cwd because a live different-process daemon already
345
+ // holds the same (agent, host, cwd). Warn prominently, tear the listener down
346
+ // (disconnect clears the reconnect timer + aborts), and mark the path skipped —
347
+ // it is never reconnected/re-probed for the process lifetime. Takeover happens
348
+ // only on the NEXT daemon start/restart. Independent closures mean skipping this
349
+ // path never disturbs another connection's dispatch/control.
350
+ onConflict: (event) => {
351
+ const conflictCwd = (event && event.cwd) ?? cwd ?? process.cwd();
352
+ const conflictHost = (event && event.host) || "this host";
353
+ logger.warn(
354
+ `[Chorus] ⚠ connection conflict: a live daemon already serves ` +
355
+ `(host=${conflictHost}, cwd=${conflictCwd}) — skipping this path. ` +
356
+ `Stop the other daemon (or wait for it to go offline) and restart to take it over.`
357
+ );
358
+ outcome.skipped = true;
359
+ // Tear down THIS path's listener: no reconnect, no re-probe (Q4).
360
+ sseListener.disconnect?.();
361
+ // Terminal outcome: this path conflicted (idempotent — counts once).
362
+ recordConnectionOutcome(outcome, false);
286
363
  },
287
364
  onControl,
288
365
  onReconnect: backfill,
289
366
  logger,
290
367
  });
291
368
 
292
- return { cwd, connectionState, waker, router, backfill, sseListener, hooks };
369
+ return { cwd, connectionState, waker, router, backfill, sseListener, hooks, outcome };
293
370
  }
294
371
 
295
372
  // Build one connection per declared cwd. The order is the declaration order; the
@@ -309,6 +386,10 @@ export function buildDaemon(creds, deps = {}) {
309
386
  router: primary.router,
310
387
  sseListener: primary.sseListener,
311
388
  connections,
389
+ // Settles ONLY when every declared connection resolved and none registered
390
+ // (all paths already served by a live daemon — Q3). runDaemon races this against
391
+ // waitForever to exit non-zero. Never settles for a serving daemon.
392
+ allConflict,
312
393
  async start() {
313
394
  // Connect every path-connection. Each fires its own onConnect hook + SSE connect;
314
395
  // the daemon process cwd is untouched (NFR-3).
@@ -356,7 +437,11 @@ export async function runDaemon(flags = {}, deps = {}) {
356
437
  const askPrompt = deps.prompt ?? prompt;
357
438
  const writeCreds = deps.writeLoginFile ?? writeLoginFile;
358
439
  const version = deps.version ?? readVersion();
440
+ // Backend executable resolvers — injectable for tests. The SELECTED backend's
441
+ // resolver runs below (claude-code → findClaude, codex → findCodex), so the
442
+ // banner shows the right binary instead of always probing for `claude`.
359
443
  const findClaude = deps.resolveClaudePath ?? resolveClaudePath;
444
+ const findCodex = deps.resolveCodexPath ?? resolveCodexPath;
360
445
  const verbose = flags.verbose === true || env.CHORUS_VERBOSE === "1";
361
446
 
362
447
  // Resolve the agent backend (default claude-code). An unknown --agent /
@@ -410,10 +495,12 @@ export async function runDaemon(flags = {}, deps = {}) {
410
495
  if (typeof pf === "number") return pf;
411
496
  const { creds, identity, permissionMode } = pf;
412
497
 
413
- // Detect the claude executable (non-fatal): the daemon still subscribes when
414
- // it's missing; a wake surfaces the error visibly when one arrives. The
415
- // resolved path (or absence) is shown in the banner below.
416
- const claudePath = findClaude();
498
+ // Detect the SELECTED backend's executable (non-fatal): the daemon still
499
+ // subscribes when it's missing; a wake surfaces the error visibly when one
500
+ // arrives. The resolved path (or absence) is shown in the banner below. codex
501
+ // resolveCodexPath, otherwise resolveClaudePath — so a `--agent codex` run
502
+ // probes (and the banner reports) `codex`, not `claude`.
503
+ const cliPath = agentType === "codex" ? findCodex() : findClaude();
417
504
 
418
505
  // The daemon.json the layered config readers (credentials, sigint timeout, cwds)
419
506
  // consult. Surfacing its absolute path + presence in the banner makes it obvious
@@ -432,7 +519,7 @@ export async function runDaemon(flags = {}, deps = {}) {
432
519
  permissionMode,
433
520
  credentialSource: creds.source,
434
521
  agentType,
435
- claudePath,
522
+ cliPath,
436
523
  connection: "connecting…",
437
524
  configPath,
438
525
  configExists,
@@ -445,6 +532,14 @@ export async function runDaemon(flags = {}, deps = {}) {
445
532
  if (permissionMode === "yolo") {
446
533
  errLog(`[Chorus] ${yoloWarningLine()}`);
447
534
  }
535
+ // A missing backend binary is non-fatal (the daemon still subscribes), but the
536
+ // banner row alone is easy to miss in a systemd journal — emit one loud ⚠ line
537
+ // on stderr so the operator sees it at startup, not only when a wake fails. The
538
+ // warning names the SELECTED backend (claude / CHORUS_CLAUDE_PATH or codex /
539
+ // CHORUS_CODEX_PATH).
540
+ if (cliPath === null) {
541
+ errLog(`[Chorus] ${agentNotFoundWarningLine(agentType)}`);
542
+ }
448
543
 
449
544
  // Surface the served paths so an operator sees a multi-path daemon at a glance.
450
545
  // A single `[undefined]` (the default) prints the process cwd it falls back to.
@@ -476,8 +571,32 @@ export async function runDaemon(flags = {}, deps = {}) {
476
571
  await daemon.start();
477
572
  log("[Chorus] daemon running. Waiting for task dispatches (Ctrl+C to stop).");
478
573
 
479
- // Keep the process alive for the long-lived SSE subscription.
480
- await (deps.waitForever ?? (() => new Promise(() => {})))();
574
+ // Keep the process alive for the long-lived SSE subscription, but exit non-zero if
575
+ // EVERY declared path turns out to be already served by a live daemon
576
+ // (add-daemon-connection-conflict-skip, Q3). `daemon.allConflict` settles only in
577
+ // that all-paths-conflicted case; otherwise we wait forever on the subscription. A
578
+ // sentinel distinguishes the two so a waitForever that never settles can't be mistaken
579
+ // for the conflict exit.
580
+ const ALL_CONFLICT = Symbol("all-conflict");
581
+ const waitForever = deps.waitForever ?? (() => new Promise(() => {}));
582
+ // `daemon.allConflict` is present on the real buildDaemon output; guard for injected
583
+ // test fakes that don't provide it (those never settle the conflict branch). A
584
+ // never-settling fallback keeps the race purely waitForever-driven in that case.
585
+ const allConflictSignal =
586
+ daemon.allConflict ?? new Promise(() => {});
587
+ const outcome = await Promise.race([
588
+ waitForever().then(() => 0),
589
+ allConflictSignal.then(() => ALL_CONFLICT),
590
+ ]);
591
+ if (outcome === ALL_CONFLICT) {
592
+ const n = servedPaths.length;
593
+ errLog(
594
+ `[Chorus] all ${n} declared ${n === 1 ? "path is" : "paths are"} already served by a live daemon — nothing to do. ` +
595
+ `Stop the other daemon(s) or remove the conflicting path(s), then restart.`
596
+ );
597
+ await daemon.stop();
598
+ return 1;
599
+ }
481
600
  return 0;
482
601
  }
483
602
 
package/cli/login.mjs CHANGED
@@ -3,7 +3,7 @@
3
3
  // ~/.chorus/daemon.json (0600). On validation failure, nothing is written.
4
4
  // Plain ESM; the only dependency is the in-repo MCP SDK (via chorus-client).
5
5
 
6
- import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
6
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
7
7
  import { dirname } from "node:path";
8
8
  import { createInterface } from "node:readline";
9
9
 
@@ -49,56 +49,99 @@ export function prompt(query, opts = {}) {
49
49
  }
50
50
 
51
51
  /**
52
- * Persist credentials + identity to the login file with owner-only perms.
52
+ * Field-level merge update of the login file (`~/.chorus/daemon.json`) the
53
+ * single read → merge → write(0600) helper every config writer routes through.
54
+ *
55
+ * Reads any existing file, shallow-merges `partial` over it (partial keys win,
56
+ * every other pre-existing field is preserved), and writes the result back with
57
+ * owner-only permissions. A shallow merge is correct because every field is a
58
+ * scalar or a flat array (`cwds`) — there are no nested objects to deep-merge.
53
59
  *
54
- * Writing the file with a fresh credential object intentionally OMITS any
55
- * `yoloAckAt` that a previous file carried a credential change (re-login)
56
- * clears the yolo acknowledgement, so the next yolo TTY start re-confirms once
57
- * (daemon-permission-mode spec). To preserve an existing ack across an
58
- * unrelated rewrite, the caller must read it first and pass it in `data`.
60
+ * The write is atomic: the JSON is written to a sibling `<path>.tmp` at 0600 and
61
+ * then `rename()`d over the target, so a crash mid-write never truncates the
62
+ * live file. A missing / unreadable / malformed existing file is treated as an
63
+ * empty object (defensive parse), so a re-login still produces a valid file
64
+ * rather than aborting on a corrupt one.
59
65
  *
60
- * @param {{ url: string, apiKey: string, agentUuid: string, agentName: string, yoloAckAt?: string }} data
61
- * @param {{ path?: string, write?: (p: string, c: string, o: object) => void, mkdir?: (p: string, o: object) => void }} [deps]
66
+ * Because writes now merge, NO field is ever silently dropped in particular a
67
+ * `chorus login` preserves any pre-existing `cwds` / `yoloAckAt` /
68
+ * `sigintTimeoutMs` (daemon-config-field-merge change; supersedes the prior
69
+ * "credential change clears the yolo acknowledgement" behavior).
70
+ *
71
+ * @param {Record<string, unknown>} partial The fields to set/overwrite.
72
+ * @param {{
73
+ * path?: string,
74
+ * read?: (p: string) => string,
75
+ * write?: (p: string, c: string, o: object) => void,
76
+ * mkdir?: (p: string, o: object) => void,
77
+ * rename?: (from: string, to: string) => void,
78
+ * }} [deps]
79
+ * @returns {string} the file path written
62
80
  */
63
- export function writeLoginFile(data, deps = {}) {
81
+ export function updateDaemonConfig(partial, deps = {}) {
64
82
  const path = deps.path ?? loginFilePath();
83
+ const read = deps.read ?? ((p) => readFileSync(p, "utf8"));
65
84
  const write = deps.write ?? writeFileSync;
66
85
  const mkdir = deps.mkdir ?? mkdirSync;
86
+ const rename = deps.rename ?? renameSync;
87
+
88
+ // Defensive read: missing / unreadable / malformed → start from empty so a
89
+ // re-login over a corrupt file still writes a valid one (no silent abort).
90
+ let current = {};
91
+ try {
92
+ const parsed = JSON.parse(read(path));
93
+ // Only a plain object is a valid config; arrays / scalars → start fresh.
94
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) current = parsed;
95
+ } catch {
96
+ // treat as empty
97
+ }
98
+
99
+ const merged = { ...current, ...partial };
67
100
  mkdir(dirname(path), { recursive: true });
68
- write(path, JSON.stringify(data, null, 2) + "\n", { mode: 0o600 });
101
+ // Atomic write: temp file (0600) in the same dir, then rename over target.
102
+ const tmp = `${path}.tmp`;
103
+ write(tmp, JSON.stringify(merged, null, 2) + "\n", { mode: 0o600 });
104
+ rename(tmp, path);
69
105
  return path;
70
106
  }
71
107
 
72
108
  /**
73
- * Record (or refresh) the yolo acknowledgement in the existing login file,
74
- * preserving the credentials already on disk. Reads the current file, merges
75
- * `yoloAckAt`, and rewrites with 0600. Used by the daemon after an interactive
76
- * TTY yolo confirmation it does NOT touch url/apiKey/identity.
109
+ * Persist credentials + identity to the login file with owner-only perms.
110
+ *
111
+ * Thin wrapper over {@link updateDaemonConfig}: it field-merges the given
112
+ * credential/identity fields over any existing file, so every OTHER pre-existing
113
+ * field (`cwds`, `yoloAckAt`, `sigintTimeoutMs`, …) is preserved across a
114
+ * `chorus login` or a daemon-start credential completion. It no longer omits
115
+ * `yoloAckAt` — a credential write never discards the recorded yolo ack.
116
+ *
117
+ * The `deps` seams (`path`, `write`, `mkdir`) are kept for callers/tests that
118
+ * inject IO; `write` is the low-level `(path, content, opts)` writer.
119
+ *
120
+ * @param {{ url: string, apiKey: string, agentUuid: string, agentName: string }} data
121
+ * @param {{ path?: string, write?: (p: string, c: string, o: object) => void, mkdir?: (p: string, o: object) => void, read?: (p: string) => string, rename?: (from: string, to: string) => void }} [deps]
122
+ */
123
+ export function writeLoginFile(data, deps = {}) {
124
+ return updateDaemonConfig(data, deps);
125
+ }
126
+
127
+ /**
128
+ * Record (or refresh) the yolo acknowledgement in the login file, preserving
129
+ * everything already on disk. Delegates to {@link updateDaemonConfig}, which
130
+ * reads the current file, merges `yoloAckAt`, and rewrites with 0600. Used by
131
+ * the daemon after an interactive TTY yolo confirmation — it does NOT touch
132
+ * url/apiKey/identity.
77
133
  *
78
- * No-silent-errors: a read/parse failure is surfaced to the caller (throws), so
79
- * the daemon can log it rather than silently losing the ack.
134
+ * A TTY user whose credentials come from env / flags has no login file yet, but
135
+ * the ack must still persist (else they'd re-confirm yolo on every start). A
136
+ * file carrying only `yoloAckAt` is harmless — resolveCredentials simply falls
137
+ * through it.
80
138
  *
81
139
  * @param {string} yoloAckAt ISO-8601 timestamp of the confirmation.
82
- * @param {{ path?: string, read?: (p: string) => string, write?: typeof writeLoginFile }} [deps]
140
+ * @param {{ path?: string, read?: (p: string) => string, write?: (p: string, c: string, o: object) => void, mkdir?: (p: string, o: object) => void, rename?: (from: string, to: string) => void }} [deps]
83
141
  * @returns {string} the file path written
84
142
  */
85
143
  export function recordYoloAck(yoloAckAt, deps = {}) {
86
- const path = deps.path ?? loginFilePath();
87
- const read = deps.read ?? ((p) => readFileSync(p, "utf8"));
88
- const write = deps.write ?? writeLoginFile;
89
- // Start from the existing file when present, else an empty object: a TTY user
90
- // whose credentials come from env / flags has no login file yet, but the ack
91
- // must still persist (else they'd re-confirm yolo on every start). A file
92
- // carrying only `yoloAckAt` is harmless — resolveCredentials simply falls
93
- // through it, and a later `chorus login` overwrites (clearing the ack).
94
- let current = {};
95
- try {
96
- const parsed = JSON.parse(read(path));
97
- if (parsed && typeof parsed === "object") current = parsed;
98
- } catch {
99
- // missing / unreadable / malformed → treat as empty (start fresh)
100
- }
101
- return write({ ...current, yoloAckAt }, { path });
144
+ return updateDaemonConfig({ yoloAckAt }, deps);
102
145
  }
103
146
 
104
147
  /**
@@ -0,0 +1,30 @@
1
+ // cli/spawner-select.mjs
2
+ // Backend selection seam: maps the resolved agent type (daemon-agent.mjs) to a
3
+ // concrete Spawner instance. This is the ONLY place the daemon branches on the
4
+ // backend — the wake pipeline above it (queue, waker, directed delivery, headless
5
+ // guard, reporters) stays backend-agnostic and drives whichever spawner is
6
+ // injected purely through the shared wake(...) contract.
7
+ //
8
+ // The function is TOTAL: resolveAgentType has already rejected unknown values
9
+ // before this runs, so an unrecognized type here is not a user error path — we
10
+ // fall back to the safe default (claude-code) rather than throw.
11
+
12
+ import { ClaudeSpawner } from "./claude-spawner.mjs";
13
+ import { CodexSpawner } from "./codex-spawner.mjs";
14
+
15
+ /**
16
+ * Construct the spawner backend for `agentType`.
17
+ * @param {string} agentType "claude-code" | "codex" (already validated upstream).
18
+ * @param {{ logger?: any, permissionMode?: "chorus"|"yolo", creds?: { url: string, apiKey: string } }} [opts]
19
+ * @returns {import("./codex-spawner.mjs").Spawner}
20
+ */
21
+ export function selectSpawner(agentType, opts = {}) {
22
+ const { logger, permissionMode, creds } = opts;
23
+ if (agentType === "codex") {
24
+ return new CodexSpawner({ logger, permissionMode, creds });
25
+ }
26
+ // Default / "claude-code": construction byte-identical to the prior daemon
27
+ // (ClaudeSpawner takes only { logger, permissionMode } — creds are not used by
28
+ // the Claude backend, which gets its key via the --mcp-config file).
29
+ return new ClaudeSpawner({ logger, permissionMode });
30
+ }
@@ -69,6 +69,13 @@ const PROCESS_STARTED_AT = new Date();
69
69
  * wake: the control event is forked here BEFORE `onEvent`, so the router / WakeQueue
70
70
  * never sees it and it can never spawn a new Claude. The handler verifies the target
71
71
  * connection + entity and interrupts the running subprocess (see control-handler.mjs).
72
+ * @property {(event: Record<string, unknown>) => void} [onConflict] Called for a
73
+ * `type:"connection_conflict"` data event (add-daemon-connection-conflict-skip): the
74
+ * server refused to register THIS cwd because a live different-process daemon already
75
+ * holds the same (agent, host, cwd). Like `control`, it is forked BEFORE `onEvent` so
76
+ * it can NEVER reach the router / WakeQueue — a conflict is not a wake. The daemon's
77
+ * handler warns and permanently tears down this cwd's listener (it does not reconnect).
78
+ * The event carries `{ host, cwd }` of the conflicting identity.
72
79
  * @property {() => Promise<void>} [onReconnect] Called after a reconnect so the
73
80
  * caller can back-fill notifications missed during the gap.
74
81
  * @property {string} [cwd] The working directory THIS connection serves (T3 — 单
@@ -91,6 +98,7 @@ export class SseListener {
91
98
  this.onEvent = opts.onEvent;
92
99
  this.onConnectionId = opts.onConnectionId ?? (() => {});
93
100
  this.onControl = opts.onControl ?? (() => {});
101
+ this.onConflict = opts.onConflict ?? (() => {});
94
102
  this.onReconnect = opts.onReconnect ?? (async () => {});
95
103
  /** @type {string|null} The connection this stream registered as (once reported). */
96
104
  this.connectionUuid = null;
@@ -108,8 +116,14 @@ export class SseListener {
108
116
  // cwd is supplied the daemon is single-path: report the process cwd (the directory
109
117
  // it was launched from), identical to today / an old daemon (HARD-1).
110
118
  this.cwd = opts.cwd ?? process.cwd();
119
+ // The daemon backend driving this connection (daemon-agent-selection): the
120
+ // server registers the row's `clientType` from this self-report, so a
121
+ // `--agent codex` daemon shows as `codex` instead of mislabelling itself
122
+ // `claude_code`. Defaults to claude_code (the default backend / an old
123
+ // single-backend daemon that never sets it).
124
+ this.clientType = opts.clientType ?? "claude_code";
111
125
  const params = new URLSearchParams({
112
- clientType: "claude_code",
126
+ clientType: this.clientType,
113
127
  clientVersion: CLI_VERSION,
114
128
  host: hostname(),
115
129
  cwd: this.cwd,
@@ -255,6 +269,20 @@ export class SseListener {
255
269
  }
256
270
  continue;
257
271
  }
272
+ // Connection conflict (add-daemon-connection-conflict-skip): the server refused
273
+ // to register THIS cwd because a live different-process daemon already holds the
274
+ // same (agent, host, cwd). Like `control`, fork it to onConflict and `continue`
275
+ // — it MUST NEVER fall through to onEvent (the router / WakeQueue), or a conflict
276
+ // would be mistaken for a wake and could spawn a subprocess. A conflict is not a
277
+ // wake; the daemon's handler warns and tears down this cwd's listener.
278
+ if (event && event.type === "connection_conflict") {
279
+ try {
280
+ this.onConflict(event);
281
+ } catch (err) {
282
+ this.logger.warn(`[Chorus] onConflict callback error: ${err}`);
283
+ }
284
+ continue;
285
+ }
258
286
  this.onEvent(event);
259
287
  }
260
288
  }
@@ -145,24 +145,84 @@ export function mergeUploadHooks(...args) {
145
145
  // top-level type — otherwise a tool-result-only user message would leak. The server
146
146
  // ingest stores ONLY `user`/`assistant` text (see /api/daemon/transcript), so this
147
147
  // filter mirrors exactly what the server will persist.
148
+ //
149
+ // Harness-injected synthetic content: when the model loads a skill, the skill's full
150
+ // markdown body is delivered as a SYNTHETIC user turn. On the live stream-json stdout
151
+ // (verified against Claude Code CLI 2.1.195 by capturing real `claude -p
152
+ // --output-format stream-json --verbose` output) that envelope is
153
+ // `{ type:"user", isSynthetic:true, message:{ content:[{type:"text", text:"Base
154
+ // directory for this skill: …"}] } }`. Because it's a plain `text` block, the
155
+ // block-level filter alone would KEEP it and leak the whole skill body to Chorus, so
156
+ // we drop `type:"user"` envelopes flagged `isSynthetic:true` outright. ⚠️ FIELD NAME:
157
+ // the live stream marks this `isSynthetic`; the on-disk transcript JSONL
158
+ // (~/.claude/projects/.../*.jsonl, which the daemon does NOT read) marks the SAME
159
+ // message `isMeta` — keying on `isMeta` here would be a silent no-op. Genuine human
160
+ // instructions and the agent's own replies never carry `isSynthetic`, so this is a
161
+ // purely structural match (no size/content heuristic) and cannot drop real content.
162
+ // MCP-server instructions, CLAUDE.md context, and the deferred-tool listing are folded
163
+ // into the dropped `type:"system"` init envelope and never reach this filter. Scope is
164
+ // the Claude Code dialect only — the codex (`item.completed`) path is unaffected.
148
165
 
149
166
  /** Top-level stream-json envelope types that carry a conversation message. */
150
167
  const CONVERSATION_TYPES = new Set(["user", "assistant"]);
151
168
 
152
169
  /**
153
- * Extract the plain user/assistant TEXT from one Claude Code stream-json object,
154
- * dropping everything that is not a conversation text block (system/result
155
- * envelopes, thinking, tool_use, tool_result). Returns null when the object is not a
156
- * keepable conversation message (so the caller can skip it). Never throws a shape
157
- * it doesn't recognize yields null rather than an error (defensive against CLI drift).
170
+ * Remove `<system-reminder>…</system-reminder>` spans from a retained text block
171
+ * (defense-in-depth for harness-injected reminders that ride inside a kept text
172
+ * block). Structural tag match not a size/content heuristic. Non-reminder text is
173
+ * left untouched; a message that is only a reminder collapses to empty and is then
174
+ * dropped by the caller's emptiness check.
175
+ * @param {string} s
176
+ * @returns {string}
177
+ */
178
+ function stripSystemReminders(s) {
179
+ return s.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, "");
180
+ }
181
+
182
+ /**
183
+ * Extract the plain user/assistant TEXT from one stream object emitted by EITHER
184
+ * backend's headless stream, dropping everything that is not a conversation text
185
+ * block. Returns null when the object is not a keepable conversation message (so
186
+ * the caller can skip it). Never throws — a shape it doesn't recognize yields null
187
+ * rather than an error (defensive against CLI drift).
188
+ *
189
+ * Two stream dialects are recognized (their top-level shapes are disjoint, so no
190
+ * backend flag is needed):
191
+ * - Claude Code stream-json: `{ type: "user"|"assistant", message: { content … } }`.
192
+ * - codex `codex exec --json`: conversation/tool output rides on `item.completed`
193
+ * events discriminated by `item.type`; assistant text is an `agent_message`
194
+ * item with a top-level `item.text` (verified against codex-cli 0.142.3). codex
195
+ * does NOT echo the user prompt (the chat UI renders that from the turn's
196
+ * promptText), and `reasoning` / `command_execution` / lifecycle envelopes are
197
+ * not conversation text — all dropped.
158
198
  *
159
- * @param {any} obj One parsed stream-json NDJSON object.
199
+ * @param {any} obj One parsed stream NDJSON object.
160
200
  * @returns {{ role: "user"|"assistant", text: string } | null}
161
201
  */
162
202
  export function extractTranscriptText(obj) {
163
203
  if (!obj || typeof obj !== "object") return null;
204
+
205
+ // ── codex `codex exec --json` dialect ──
206
+ // Only `item.completed` carries finished output; an `agent_message` item is the
207
+ // assistant's conversation text. Everything else (reasoning, command_execution,
208
+ // file_change, thread.started/turn.* lifecycle) is not user/assistant text.
209
+ if (obj.type === "item.completed") {
210
+ const item = obj.item;
211
+ if (!item || typeof item !== "object" || item.type !== "agent_message") return null;
212
+ const itemText = typeof item.text === "string" ? item.text : "";
213
+ if (!itemText.trim()) return null;
214
+ return { role: "assistant", text: itemText };
215
+ }
216
+
217
+ // ── Claude Code stream-json dialect ──
164
218
  if (!CONVERSATION_TYPES.has(obj.type)) return null;
165
219
 
220
+ // Drop harness-injected synthetic content (e.g. a loaded skill body). Claude Code
221
+ // marks these user turns `isSynthetic:true` on the live stream (NOT the on-disk
222
+ // `isMeta` field). Gated on `type:"user"` so a synthetic *assistant* envelope, if one
223
+ // ever appears, is still treated as real assistant text. Structural match only.
224
+ if (obj.type === "user" && obj.isSynthetic === true) return null;
225
+
166
226
  const message = obj.message;
167
227
  if (!message || typeof message !== "object") return null;
168
228
  // The persisted role is the message's role; fall back to the envelope type (they
@@ -190,8 +250,13 @@ export function extractTranscriptText(obj) {
190
250
  return null;
191
251
  }
192
252
 
193
- // A message with no text (e.g. a user message that was purely a tool_result, or an
194
- // assistant message that was purely tool_use/thinking) is dropped — nothing to store.
253
+ // Strip any wrapped <system-reminder> spans (defense-in-depth) before deciding
254
+ // emptiness, so a reminder-only message collapses to nothing and is dropped.
255
+ text = stripSystemReminders(text);
256
+
257
+ // A message with no text (e.g. a user message that was purely a tool_result, an
258
+ // assistant message that was purely tool_use/thinking, or text that was only a
259
+ // system-reminder) is dropped — nothing to store.
195
260
  if (!text.trim()) return null;
196
261
  return { role, text };
197
262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chorus-aidlc/chorus",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "The Agent Harness for AI-Human Collaboration — session lifecycle, task state, sub-agent orchestration, observability, and failure recovery",
5
5
  "homepage": "https://github.com/Chorus-AIDLC/Chorus",
6
6
  "repository": {
@@ -1 +0,0 @@
1
- exports.id=3371,exports.ids=[3371],exports.modules={2978:(a,b,c)=>{Promise.resolve().then(c.bind(c,69187))},18151:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,29723,23)),Promise.resolve().then(c.t.bind(c,31706,23)),Promise.resolve().then(c.t.bind(c,75324,23)),Promise.resolve().then(c.t.bind(c,20227,23)),Promise.resolve().then(c.t.bind(c,2931,23)),Promise.resolve().then(c.t.bind(c,17531,23)),Promise.resolve().then(c.t.bind(c,69357,23)),Promise.resolve().then(c.t.bind(c,69442,23)),Promise.resolve().then(c.t.bind(c,99875,23))},18421:(a,b,c)=>{var d={"./en.json":[50920,920],"./zh.json":[86113,6113]};function e(a){if(!c.o(d,a))return Promise.resolve().then(()=>{var b=Error("Cannot find module '"+a+"'");throw b.code="MODULE_NOT_FOUND",b});var b=d[a],e=b[0];return c.e(b[1]).then(()=>c.t(e,19))}e.keys=()=>Object.keys(d),e.id=18421,a.exports=e},21130:(a,b,c)=>{Promise.resolve().then(c.bind(c,58857))},21865:(a,b,c)=>{"use strict";c.d(b,{HB:()=>e,JK:()=>h,XQ:()=>g,bN:()=>i,vh:()=>f});var d=c(99582);function e(a){let b=function(a){let b=process.env.NEXTAUTH_URL||"http://localhost:8637";return{authority:a.issuer,client_id:a.clientId,redirect_uri:`${b}/login/callback`,post_logout_redirect_uri:`${b}/login`,response_type:"code",scope:"openid profile email",automaticSilentRenew:!1,silent_redirect_uri:`${b}/login/silent-refresh`,accessTokenExpiringNotificationTimeInSeconds:60,userStore:void 0,extraQueryParams:{company:a.companyUuid}}}(a);return new d.$5(b)}function f(a){}function g(){return null}function h(){}function i(a){return{sub:a.profile.sub,email:a.profile.email,name:a.profile.name,picture:a.profile.picture,accessToken:a.access_token,refreshToken:a.refresh_token,expiresAt:a.expires_at,idToken:a.id_token}}},32376:()=>{},33367:(a,b,c)=>{"use strict";c.r(b),c.d(b,{default:()=>e});var d=c(10948);let e=async a=>[{type:"image/x-icon",sizes:"256x256",url:(0,d.fillMetadataSegment)(".",await a.params,"favicon.ico")+""}]},35198:(a,b,c)=>{"use strict";c.d(b,{BT:()=>i,Wu:()=>j,ZB:()=>h,Zp:()=>f,aR:()=>g});var d=c(10837);c(21968);var e=c(96307);function f({className:a,...b}){return(0,d.jsx)("div",{"data-slot":"card",className:(0,e.cn)("bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",a),...b})}function g({className:a,...b}){return(0,d.jsx)("div",{"data-slot":"card-header",className:(0,e.cn)("@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-2 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",a),...b})}function h({className:a,...b}){return(0,d.jsx)("div",{"data-slot":"card-title",className:(0,e.cn)("leading-none font-semibold",a),...b})}function i({className:a,...b}){return(0,d.jsx)("div",{"data-slot":"card-description",className:(0,e.cn)("text-muted-foreground text-sm",a),...b})}function j({className:a,...b}){return(0,d.jsx)("div",{"data-slot":"card-content",className:(0,e.cn)("px-6",a),...b})}},40340:(a,b,c)=>{"use strict";c.r(b),c.d(b,{default:()=>k,metadata:()=>j});var d=c(10023),e=c(28103),f=c.n(e),g=c(85355),h=c.n(g),i=c(69187);c(32376);let j={title:{default:"Chorus",template:"%s | Chorus"},description:"AI Agent & Human collaboration platform for the AI-Driven Development Lifecycle",openGraph:{title:"Chorus",description:"AI Agent & Human collaboration platform for the AI-Driven Development Lifecycle",type:"website",siteName:"Chorus"}};function k({children:a}){return(0,d.jsx)("html",{lang:"en",children:(0,d.jsx)("body",{className:`${f().variable} ${h().variable} antialiased`,children:(0,d.jsx)(i.LocaleProvider,{children:a})})})}},42424:(a,b,c)=>{"use strict";c.d(b,{$:()=>i});var d=c(10837);c(21968);var e=c(35973),f=c(11156),g=c(96307);let h=(0,e.F)("inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium cursor-pointer transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",{variants:{variant:{default:"bg-primary text-primary-foreground hover:bg-primary/90",destructive:"bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",outline:"border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",secondary:"bg-secondary text-secondary-foreground hover:bg-secondary/80",ghost:"hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",link:"text-primary underline-offset-4 hover:underline"},size:{default:"h-9 px-4 py-2 has-[>svg]:px-3",xs:"h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",sm:"h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",lg:"h-10 rounded-md px-6 has-[>svg]:px-4",icon:"size-9","icon-xs":"size-6 rounded-md [&_svg:not([class*='size-'])]:size-3","icon-sm":"size-8","icon-lg":"size-10"}},defaultVariants:{variant:"default",size:"default"}});function i({className:a,variant:b="default",size:c="default",asChild:e=!1,...i}){let j=e?f.bL:"button";return(0,d.jsx)(j,{"data-slot":"button","data-variant":b,"data-size":c,className:(0,g.cn)(h({variant:b,size:c,className:a})),...i})}},48415:(a,b,c)=>{Promise.resolve().then(c.t.bind(c,16885,23)),Promise.resolve().then(c.t.bind(c,30892,23)),Promise.resolve().then(c.t.bind(c,89410,23)),Promise.resolve().then(c.t.bind(c,99349,23)),Promise.resolve().then(c.t.bind(c,41105,23)),Promise.resolve().then(c.t.bind(c,89065,23)),Promise.resolve().then(c.t.bind(c,403,23)),Promise.resolve().then(c.t.bind(c,33532,23)),Promise.resolve().then(c.bind(c,41905))},54722:(a,b,c)=>{"use strict";c.d(b,{q:()=>g});let d={debug:0,info:1,warn:2,error:3},e=process.env.NEXT_PUBLIC_LOG_LEVEL||"warn";function f(a){return d[a]>=d[e]}let g={debug:(a,...b)=>{f("debug")&&console.debug(`[Chorus] ${a}`,...b)},info:(a,...b)=>{f("info")&&console.info(`[Chorus] ${a}`,...b)},warn:(a,...b)=>{f("warn")&&console.warn(`[Chorus] ${a}`,...b)},error:(a,...b)=>{f("error")&&console.error(`[Chorus] ${a}`,...b)}}},58857:(a,b,c)=>{"use strict";c.d(b,{LocaleProvider:()=>j,Y:()=>i});var d=c(10837),e=c(21968),f=c(13256),g=c(97553);c(54722);let h=(0,e.createContext)(null);function i(){let a=(0,e.useContext)(h);if(!a)throw Error("useLocale must be used within LocaleProvider");return a}function j({children:a}){let[b,c]=(0,e.useState)(g.q),[i,j]=(0,e.useState)(null),[k,l]=(0,e.useState)(!1),m=(0,e.useCallback)(a=>{c(a),localStorage.setItem("chorus-locale",a),document.cookie=`chorus-locale=${a};path=/;max-age=31536000;SameSite=Lax`,document.documentElement.lang=a},[]);return k&&i?(0,d.jsx)(h.Provider,{value:{locale:b,setLocale:m},children:(0,d.jsx)(f.A,{locale:b,messages:i,children:a})}):null}},69187:(a,b,c)=>{"use strict";c.d(b,{LocaleProvider:()=>e});var d=c(37143);(0,d.registerClientReference)(function(){throw Error("Attempted to call useLocale() from the server but useLocale is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/home/ubuntu/dev/ai-pm/src/contexts/locale-context.tsx","useLocale");let e=(0,d.registerClientReference)(function(){throw Error("Attempted to call LocaleProvider() from the server but LocaleProvider is on the client. It's not possible to invoke a client function from the server, it can only be rendered as a Component or passed to props of a Client Component.")},"/home/ubuntu/dev/ai-pm/src/contexts/locale-context.tsx","LocaleProvider")},93524:(a,b,c)=>{"use strict";c.d(b,{OX:()=>l,aK:()=>k,dv:()=>f,hj:()=>g,ri:()=>n,tm:()=>m,vN:()=>i,w9:()=>h});var d=c(21865),e=c(54722);function f(){return null}function g(a){return(0,d.vh)(a),(0,d.HB)(a)}function h(){}async function i(){let a=f();if(!a)return null;try{return await a.getUser()}catch{return null}}async function j(){let a=await i();return a?a.access_token:null}async function k(a,b){try{return(await fetch("/api/auth/sync-token",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({accessToken:a,refreshToken:b})})).ok}catch{return e.q.error("Failed to sync token to cookie"),!1}}async function l(){try{await fetch("/api/keepalive",{method:"GET",credentials:"same-origin",cache:"no-store"})}catch{}}async function m(a,b={}){let c=await j(),d=new Headers(b.headers);c&&d.set("Authorization",`Bearer ${c}`);let e=await fetch(a,{...b,headers:d});if(401===e.status)if(f());else try{if((await fetch("/api/auth/refresh",{method:"POST"})).ok)return d.delete("Authorization"),fetch(a,{...b,headers:d})}catch{}return e}async function n(){try{await fetch("/api/auth/logout",{method:"POST"})}catch{}let a=f();if(a)try{await a.removeUser()}catch{}h(),(0,d.JK)()}},96307:(a,b,c)=>{"use strict";c.d(b,{cn:()=>f});var d=c(25335),e=c(30393);function f(...a){return(0,e.QP)((0,d.$)(a))}},97553:(a,b,c)=>{"use strict";c.d(b,{AL:()=>g,IB:()=>d,L$:()=>f,q:()=>e});let d=["en","zh"],e="en",f={en:"English",zh:"中文"};function g(){if("undefined"==typeof navigator)return e;let a=navigator.language.split("-")[0];return d.includes(a)?a:e}}};