factory-factory 0.3.4 → 0.3.6

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 (313) hide show
  1. package/README.md +17 -0
  2. package/dist/client/assets/_basePickBy-CzLmZHC2.js +1 -0
  3. package/dist/client/assets/{_baseUniq-DisTPj-I.js → _baseUniq-B2vXSgXW.js} +1 -1
  4. package/dist/client/assets/arc-Dq49BrkB.js +1 -0
  5. package/dist/client/assets/architectureDiagram-VXUJARFQ-BGubq_o9.js +36 -0
  6. package/dist/client/assets/{blockDiagram-VD42YOAC-DXpmez0y.js → blockDiagram-VD42YOAC-CJ7hB7tD.js} +4 -4
  7. package/dist/client/assets/{c4Diagram-YG6GDRKO-C6sDxR49.js → c4Diagram-YG6GDRKO-C9-3yIl7.js} +3 -3
  8. package/dist/client/assets/channel-DMaON2vY.js +1 -0
  9. package/dist/client/assets/{chunk-4BX2VUAB-CQxlRbYu.js → chunk-4BX2VUAB-LA-tkKjD.js} +1 -1
  10. package/dist/client/assets/{chunk-55IACEB6-DxubFDep.js → chunk-55IACEB6-D8Jq3_Ne.js} +1 -1
  11. package/dist/client/assets/{chunk-B4BG7PRW-BtTQ0im9.js → chunk-B4BG7PRW-BbB1Mssd.js} +1 -1
  12. package/dist/client/assets/{chunk-DI55MBZ5-C2tNYDZj.js → chunk-DI55MBZ5-CpO3WS5m.js} +1 -1
  13. package/dist/client/assets/{chunk-FMBD7UC4-kRQRN6vP.js → chunk-FMBD7UC4-zaxR2CzR.js} +1 -1
  14. package/dist/client/assets/chunk-QN33PNHL-BMmRcEqx.js +1 -0
  15. package/dist/client/assets/{chunk-QZHKN3VN-BfxMFZDN.js → chunk-QZHKN3VN-CbCQqsiw.js} +1 -1
  16. package/dist/client/assets/chunk-TZMSLE5B-BhRzmVVB.js +1 -0
  17. package/dist/client/assets/classDiagram-2ON5EDUG-BKODUnN9.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-WZHVMYZB-BKODUnN9.js +1 -0
  19. package/dist/client/assets/clone-d5fivKBG.js +1 -0
  20. package/dist/client/assets/{cose-bilkent-S5V4N54A-QPsUxyAe.js → cose-bilkent-S5V4N54A-BZtg5BPr.js} +1 -1
  21. package/dist/client/assets/{dagre-6UL2VRFP-BAmsmcnI.js → dagre-6UL2VRFP-DiejfPfI.js} +2 -2
  22. package/dist/client/assets/diagram-PSM6KHXK-CRE7v2e7.js +24 -0
  23. package/dist/client/assets/diagram-QEK2KX5R-DpAOzBlm.js +43 -0
  24. package/dist/client/assets/diagram-S2PKOQOG-BOJvuhJd.js +24 -0
  25. package/dist/client/assets/{erDiagram-Q2GNP2WA-DMKIVzp4.js → erDiagram-Q2GNP2WA-IM11uKRy.js} +4 -4
  26. package/dist/client/assets/{flowDiagram-NV44I4VS-op3xA_nS.js → flowDiagram-NV44I4VS-DTbQf_7g.js} +5 -5
  27. package/dist/client/assets/{ganttDiagram-JELNMOA3-nrnzVCJK.js → ganttDiagram-JELNMOA3-Bqd3rUDW.js} +2 -2
  28. package/dist/client/assets/gitGraphDiagram-V2S2FVAM-Cd1j3VMN.js +65 -0
  29. package/dist/client/assets/{graph-D_lEcPMk.js → graph-D667hHAk.js} +1 -1
  30. package/dist/client/assets/{index-C2qHRaOH.js → index-BXRnTMjJ.js} +170 -171
  31. package/dist/client/assets/index-DllMnbgu.css +1 -0
  32. package/dist/client/assets/infoDiagram-HS3SLOUP-CzpZPq7Y.js +2 -0
  33. package/dist/client/assets/{journeyDiagram-XKPGCS4Q-Bnhe4U1x.js → journeyDiagram-XKPGCS4Q-Br2GUbhE.js} +1 -1
  34. package/dist/client/assets/{kanban-definition-3W4ZIXB7-DpTaTXko.js → kanban-definition-3W4ZIXB7-D0SM1ldH.js} +6 -6
  35. package/dist/client/assets/{layout-HT5qr--Q.js → layout-BPE9Dzp9.js} +1 -1
  36. package/dist/client/assets/{linear-CDmQ8HG8.js → linear-5exZFNBA.js} +1 -1
  37. package/dist/client/assets/{mindmap-definition-VGOIOE7T-zDr7s4o2.js → mindmap-definition-VGOIOE7T-D1W9h2lc.js} +3 -3
  38. package/dist/client/assets/mobile-baseline-BHP-K4kd.js +10 -0
  39. package/dist/client/assets/pieDiagram-ADFJNKIX-DG-umOTY.js +30 -0
  40. package/dist/client/assets/{quadrantDiagram-AYHSOK5B-DzdJzkAJ.js → quadrantDiagram-AYHSOK5B-Cb0ciONq.js} +1 -1
  41. package/dist/client/assets/{requirementDiagram-UZGBJVZJ-ChrLzx8S.js → requirementDiagram-UZGBJVZJ-BKYaJZ83.js} +1 -1
  42. package/dist/client/assets/sankeyDiagram-TZEHDZUN-DkR9L4Vw.js +10 -0
  43. package/dist/client/assets/{sequenceDiagram-WL72ISMW-BHxEpSCU.js → sequenceDiagram-WL72ISMW-Cys03qhv.js} +6 -6
  44. package/dist/client/assets/stateDiagram-FKZM4ZOC-DQWCRtS7.js +1 -0
  45. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-DoWi8XaX.js +1 -0
  46. package/dist/client/assets/terminal-instance-CpEqekOB.js +1 -0
  47. package/dist/client/assets/{timeline-definition-IT6M3QCI-BcCmIxix.js → timeline-definition-IT6M3QCI-C0mVV_zL.js} +4 -4
  48. package/dist/client/assets/treemap-GDKQZRPO-DK-Wlxvy.js +160 -0
  49. package/dist/client/assets/{xychartDiagram-PRI3JC2R-DrgKBPUM.js → xychartDiagram-PRI3JC2R-CFg2MfK9.js} +4 -4
  50. package/dist/client/index.html +3 -3
  51. package/dist/prisma/generated/commonInputTypes.d.ts +69 -39
  52. package/dist/prisma/generated/commonInputTypes.d.ts.map +1 -1
  53. package/dist/prisma/generated/enums.d.ts +6 -0
  54. package/dist/prisma/generated/enums.d.ts.map +1 -1
  55. package/dist/prisma/generated/enums.js +6 -1
  56. package/dist/prisma/generated/enums.js.map +1 -1
  57. package/dist/prisma/generated/internal/class.d.ts.map +1 -1
  58. package/dist/prisma/generated/internal/class.js +12 -4
  59. package/dist/prisma/generated/internal/class.js.map +1 -1
  60. package/dist/prisma/generated/internal/prismaNamespace.d.ts +13 -7
  61. package/dist/prisma/generated/internal/prismaNamespace.d.ts.map +1 -1
  62. package/dist/prisma/generated/internal/prismaNamespace.js +11 -6
  63. package/dist/prisma/generated/internal/prismaNamespace.js.map +1 -1
  64. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts +10 -5
  65. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts.map +1 -1
  66. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js +9 -4
  67. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  68. package/dist/prisma/generated/models/Project.d.ts +63 -1
  69. package/dist/prisma/generated/models/Project.d.ts.map +1 -1
  70. package/dist/prisma/generated/models/Workspace.d.ts +130 -1
  71. package/dist/prisma/generated/models/Workspace.d.ts.map +1 -1
  72. package/dist/prompts/quick-actions/screenshots.md +12 -6
  73. package/dist/prompts/ratchet/dispatch.md +4 -0
  74. package/dist/src/backend/domains/linear/index.d.ts +3 -0
  75. package/dist/src/backend/domains/linear/index.d.ts.map +1 -0
  76. package/dist/src/backend/domains/linear/index.js +3 -0
  77. package/dist/src/backend/domains/linear/index.js.map +1 -0
  78. package/dist/src/backend/domains/linear/linear-client.service.d.ts +40 -0
  79. package/dist/src/backend/domains/linear/linear-client.service.d.ts.map +1 -0
  80. package/dist/src/backend/domains/linear/linear-client.service.js +134 -0
  81. package/dist/src/backend/domains/linear/linear-client.service.js.map +1 -0
  82. package/dist/src/backend/domains/linear/linear-state-sync.service.d.ts +7 -0
  83. package/dist/src/backend/domains/linear/linear-state-sync.service.d.ts.map +1 -0
  84. package/dist/src/backend/domains/linear/linear-state-sync.service.js +29 -0
  85. package/dist/src/backend/domains/linear/linear-state-sync.service.js.map +1 -0
  86. package/dist/src/backend/domains/ratchet/bridges.d.ts +5 -1
  87. package/dist/src/backend/domains/ratchet/bridges.d.ts.map +1 -1
  88. package/dist/src/backend/domains/ratchet/ratchet.service.d.ts +2 -0
  89. package/dist/src/backend/domains/ratchet/ratchet.service.d.ts.map +1 -1
  90. package/dist/src/backend/domains/ratchet/ratchet.service.js +43 -53
  91. package/dist/src/backend/domains/ratchet/ratchet.service.js.map +1 -1
  92. package/dist/src/backend/domains/ratchet/ratchet.service.test.js +61 -18
  93. package/dist/src/backend/domains/ratchet/ratchet.service.test.js.map +1 -1
  94. package/dist/src/backend/domains/session/acp/acp-runtime-manager.d.ts.map +1 -1
  95. package/dist/src/backend/domains/session/acp/acp-runtime-manager.js +5 -1
  96. package/dist/src/backend/domains/session/acp/acp-runtime-manager.js.map +1 -1
  97. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts +13 -2
  98. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts.map +1 -1
  99. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js +839 -106
  100. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js.map +1 -1
  101. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.test.js +735 -8
  102. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-app-server-acp-adapter.test.js.map +1 -1
  103. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.d.ts +20 -0
  104. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.d.ts.map +1 -1
  105. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.js +16 -0
  106. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.js.map +1 -1
  107. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.test.js +15 -0
  108. package/dist/src/backend/domains/session/acp/codex-app-server-adapter/codex-zod.test.js.map +1 -1
  109. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.d.ts +2 -2
  110. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
  111. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.js +32 -1
  112. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
  113. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.test.js +79 -9
  114. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/load-session.handler.test.js.map +1 -1
  115. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.d.ts +2 -2
  116. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.d.ts.map +1 -1
  117. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.js +3 -1
  118. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.js.map +1 -1
  119. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.test.js +10 -1
  120. package/dist/src/backend/domains/session/chat/chat-message-handlers/handlers/stop.handler.test.js.map +1 -1
  121. package/dist/src/backend/domains/session/chat/chat-message-handlers/registry.js +2 -2
  122. package/dist/src/backend/domains/session/chat/chat-message-handlers/registry.js.map +1 -1
  123. package/dist/src/backend/domains/session/chat/chat-message-handlers/types.d.ts +1 -0
  124. package/dist/src/backend/domains/session/chat/chat-message-handlers/types.d.ts.map +1 -1
  125. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.d.ts +5 -1
  126. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.d.ts.map +1 -1
  127. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.js +39 -13
  128. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.js.map +1 -1
  129. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.test.js +81 -0
  130. package/dist/src/backend/domains/session/chat/chat-message-handlers.service.test.js.map +1 -1
  131. package/dist/src/backend/domains/session/data/codex-session-history-loader.service.d.ts.map +1 -1
  132. package/dist/src/backend/domains/session/data/codex-session-history-loader.service.js +12 -59
  133. package/dist/src/backend/domains/session/data/codex-session-history-loader.service.js.map +1 -1
  134. package/dist/src/backend/domains/session/data/codex-session-history-loader.service.test.js +11 -21
  135. package/dist/src/backend/domains/session/data/codex-session-history-loader.service.test.js.map +1 -1
  136. package/dist/src/backend/domains/session/data/session-data.service.d.ts +6 -0
  137. package/dist/src/backend/domains/session/data/session-data.service.d.ts.map +1 -1
  138. package/dist/src/backend/domains/session/lifecycle/session.service.d.ts +7 -0
  139. package/dist/src/backend/domains/session/lifecycle/session.service.d.ts.map +1 -1
  140. package/dist/src/backend/domains/session/lifecycle/session.service.js +105 -6
  141. package/dist/src/backend/domains/session/lifecycle/session.service.js.map +1 -1
  142. package/dist/src/backend/domains/session/lifecycle/session.service.test.js +84 -0
  143. package/dist/src/backend/domains/session/lifecycle/session.service.test.js.map +1 -1
  144. package/dist/src/backend/domains/session/session-domain.service.d.ts +8 -1
  145. package/dist/src/backend/domains/session/session-domain.service.d.ts.map +1 -1
  146. package/dist/src/backend/domains/session/session-domain.service.js +43 -2
  147. package/dist/src/backend/domains/session/session-domain.service.js.map +1 -1
  148. package/dist/src/backend/domains/session/session-domain.service.test.js +21 -0
  149. package/dist/src/backend/domains/session/session-domain.service.test.js.map +1 -1
  150. package/dist/src/backend/domains/session/store/session-store.types.d.ts +1 -1
  151. package/dist/src/backend/domains/session/store/session-store.types.d.ts.map +1 -1
  152. package/dist/src/backend/domains/session/store/session-transcript.d.ts +1 -0
  153. package/dist/src/backend/domains/session/store/session-transcript.d.ts.map +1 -1
  154. package/dist/src/backend/domains/session/store/session-transcript.js +8 -0
  155. package/dist/src/backend/domains/session/store/session-transcript.js.map +1 -1
  156. package/dist/src/backend/domains/workspace/lifecycle/creation.service.d.ts +9 -0
  157. package/dist/src/backend/domains/workspace/lifecycle/creation.service.d.ts.map +1 -1
  158. package/dist/src/backend/domains/workspace/lifecycle/creation.service.js +18 -0
  159. package/dist/src/backend/domains/workspace/lifecycle/creation.service.js.map +1 -1
  160. package/dist/src/backend/domains/workspace/lifecycle/creation.service.test.js +46 -0
  161. package/dist/src/backend/domains/workspace/lifecycle/creation.service.test.js.map +1 -1
  162. package/dist/src/backend/domains/workspace/lifecycle/data.service.d.ts +25 -0
  163. package/dist/src/backend/domains/workspace/lifecycle/data.service.d.ts.map +1 -1
  164. package/dist/src/backend/domains/workspace/query/project-management.service.d.ts +16 -0
  165. package/dist/src/backend/domains/workspace/query/project-management.service.d.ts.map +1 -1
  166. package/dist/src/backend/domains/workspace/query/project-management.service.js.map +1 -1
  167. package/dist/src/backend/domains/workspace/query/workspace-query.service.d.ts +6 -0
  168. package/dist/src/backend/domains/workspace/query/workspace-query.service.d.ts.map +1 -1
  169. package/dist/src/backend/interceptors/pr-detection.interceptor.d.ts.map +1 -1
  170. package/dist/src/backend/interceptors/pr-detection.interceptor.js +5 -1
  171. package/dist/src/backend/interceptors/pr-detection.interceptor.js.map +1 -1
  172. package/dist/src/backend/interceptors/pr-detection.interceptor.test.js +14 -0
  173. package/dist/src/backend/interceptors/pr-detection.interceptor.test.js.map +1 -1
  174. package/dist/src/backend/migrate.d.ts.map +1 -1
  175. package/dist/src/backend/migrate.js +13 -3
  176. package/dist/src/backend/migrate.js.map +1 -1
  177. package/dist/src/backend/migrate.test.js +44 -0
  178. package/dist/src/backend/migrate.test.js.map +1 -1
  179. package/dist/src/backend/orchestration/cli-health.service.d.ts +1 -0
  180. package/dist/src/backend/orchestration/cli-health.service.d.ts.map +1 -1
  181. package/dist/src/backend/orchestration/cli-health.service.js +16 -1
  182. package/dist/src/backend/orchestration/cli-health.service.js.map +1 -1
  183. package/dist/src/backend/orchestration/cli-health.service.test.js +23 -0
  184. package/dist/src/backend/orchestration/cli-health.service.test.js.map +1 -1
  185. package/dist/src/backend/orchestration/data-backup.service.d.ts.map +1 -1
  186. package/dist/src/backend/orchestration/data-backup.service.js +21 -1
  187. package/dist/src/backend/orchestration/data-backup.service.js.map +1 -1
  188. package/dist/src/backend/orchestration/data-backup.service.test.js +19 -9
  189. package/dist/src/backend/orchestration/data-backup.service.test.js.map +1 -1
  190. package/dist/src/backend/orchestration/event-collector.orchestrator.d.ts.map +1 -1
  191. package/dist/src/backend/orchestration/event-collector.orchestrator.js +63 -3
  192. package/dist/src/backend/orchestration/event-collector.orchestrator.js.map +1 -1
  193. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js +49 -3
  194. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js.map +1 -1
  195. package/dist/src/backend/orchestration/linear-config.helper.d.ts +9 -0
  196. package/dist/src/backend/orchestration/linear-config.helper.d.ts.map +1 -0
  197. package/dist/src/backend/orchestration/linear-config.helper.js +23 -0
  198. package/dist/src/backend/orchestration/linear-config.helper.js.map +1 -0
  199. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts +3 -0
  200. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts.map +1 -1
  201. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts.map +1 -1
  202. package/dist/src/backend/orchestration/workspace-init.orchestrator.js +228 -3
  203. package/dist/src/backend/orchestration/workspace-init.orchestrator.js.map +1 -1
  204. package/dist/src/backend/prompts/ratchet-dispatch.d.ts +8 -1
  205. package/dist/src/backend/prompts/ratchet-dispatch.d.ts.map +1 -1
  206. package/dist/src/backend/prompts/ratchet-dispatch.js +20 -3
  207. package/dist/src/backend/prompts/ratchet-dispatch.js.map +1 -1
  208. package/dist/src/backend/resource_accessors/project.accessor.d.ts +7 -1
  209. package/dist/src/backend/resource_accessors/project.accessor.d.ts.map +1 -1
  210. package/dist/src/backend/resource_accessors/project.accessor.js +8 -1
  211. package/dist/src/backend/resource_accessors/project.accessor.js.map +1 -1
  212. package/dist/src/backend/resource_accessors/workspace.accessor.d.ts +4 -1
  213. package/dist/src/backend/resource_accessors/workspace.accessor.d.ts.map +1 -1
  214. package/dist/src/backend/resource_accessors/workspace.accessor.js +3 -0
  215. package/dist/src/backend/resource_accessors/workspace.accessor.js.map +1 -1
  216. package/dist/src/backend/server.d.ts.map +1 -1
  217. package/dist/src/backend/server.js +11 -9
  218. package/dist/src/backend/server.js.map +1 -1
  219. package/dist/src/backend/services/constants.d.ts +1 -0
  220. package/dist/src/backend/services/constants.d.ts.map +1 -1
  221. package/dist/src/backend/services/constants.js +1 -0
  222. package/dist/src/backend/services/constants.js.map +1 -1
  223. package/dist/src/backend/services/crypto.service.d.ts +10 -0
  224. package/dist/src/backend/services/crypto.service.d.ts.map +1 -0
  225. package/dist/src/backend/services/crypto.service.js +61 -0
  226. package/dist/src/backend/services/crypto.service.js.map +1 -0
  227. package/dist/src/backend/trpc/admin.trpc.d.ts +14 -4
  228. package/dist/src/backend/trpc/admin.trpc.d.ts.map +1 -1
  229. package/dist/src/backend/trpc/index.d.ts +169 -9
  230. package/dist/src/backend/trpc/index.d.ts.map +1 -1
  231. package/dist/src/backend/trpc/index.js +2 -0
  232. package/dist/src/backend/trpc/index.js.map +1 -1
  233. package/dist/src/backend/trpc/linear.trpc.d.ts +44 -0
  234. package/dist/src/backend/trpc/linear.trpc.d.ts.map +1 -0
  235. package/dist/src/backend/trpc/linear.trpc.js +69 -0
  236. package/dist/src/backend/trpc/linear.trpc.js.map +1 -0
  237. package/dist/src/backend/trpc/project.trpc.d.ts +50 -5
  238. package/dist/src/backend/trpc/project.trpc.d.ts.map +1 -1
  239. package/dist/src/backend/trpc/project.trpc.js +46 -18
  240. package/dist/src/backend/trpc/project.trpc.js.map +1 -1
  241. package/dist/src/backend/trpc/session.trpc.d.ts +13 -0
  242. package/dist/src/backend/trpc/session.trpc.d.ts.map +1 -1
  243. package/dist/src/backend/trpc/session.trpc.js +5 -1
  244. package/dist/src/backend/trpc/session.trpc.js.map +1 -1
  245. package/dist/src/backend/trpc/workspace/git.trpc.d.ts +10 -0
  246. package/dist/src/backend/trpc/workspace/git.trpc.d.ts.map +1 -1
  247. package/dist/src/backend/trpc/workspace/git.trpc.js +25 -0
  248. package/dist/src/backend/trpc/workspace/git.trpc.js.map +1 -1
  249. package/dist/src/backend/trpc/workspace/init.trpc.d.ts +3 -0
  250. package/dist/src/backend/trpc/workspace/init.trpc.d.ts.map +1 -1
  251. package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts +8 -0
  252. package/dist/src/backend/trpc/workspace/workspace-helpers.d.ts.map +1 -1
  253. package/dist/src/backend/trpc/workspace.trpc.d.ts +49 -0
  254. package/dist/src/backend/trpc/workspace.trpc.d.ts.map +1 -1
  255. package/dist/src/backend/trpc/workspace.trpc.js +20 -4
  256. package/dist/src/backend/trpc/workspace.trpc.js.map +1 -1
  257. package/dist/src/cli/index.js +17 -91
  258. package/dist/src/cli/index.js.map +1 -1
  259. package/dist/src/cli/proxy.d.ts +80 -0
  260. package/dist/src/cli/proxy.d.ts.map +1 -0
  261. package/dist/src/cli/proxy.js +1054 -0
  262. package/dist/src/cli/proxy.js.map +1 -0
  263. package/dist/src/cli/proxy.test.d.ts +2 -0
  264. package/dist/src/cli/proxy.test.d.ts.map +1 -0
  265. package/dist/src/cli/proxy.test.js +249 -0
  266. package/dist/src/cli/proxy.test.js.map +1 -0
  267. package/dist/src/cli/runtime-utils.d.ts +9 -0
  268. package/dist/src/cli/runtime-utils.d.ts.map +1 -0
  269. package/dist/src/cli/runtime-utils.js +86 -0
  270. package/dist/src/cli/runtime-utils.js.map +1 -0
  271. package/dist/src/shared/core/enums.d.ts +6 -0
  272. package/dist/src/shared/core/enums.d.ts.map +1 -1
  273. package/dist/src/shared/core/enums.js +5 -0
  274. package/dist/src/shared/core/enums.js.map +1 -1
  275. package/dist/src/shared/core/index.d.ts +1 -1
  276. package/dist/src/shared/core/index.d.ts.map +1 -1
  277. package/dist/src/shared/core/index.js +1 -1
  278. package/dist/src/shared/core/index.js.map +1 -1
  279. package/dist/src/shared/schemas/export-data.schema.d.ts +19 -1
  280. package/dist/src/shared/schemas/export-data.schema.d.ts.map +1 -1
  281. package/dist/src/shared/schemas/export-data.schema.js +8 -2
  282. package/dist/src/shared/schemas/export-data.schema.js.map +1 -1
  283. package/dist/src/shared/schemas/issue-tracker-config.schema.d.ts +35 -0
  284. package/dist/src/shared/schemas/issue-tracker-config.schema.d.ts.map +1 -0
  285. package/dist/src/shared/schemas/issue-tracker-config.schema.js +29 -0
  286. package/dist/src/shared/schemas/issue-tracker-config.schema.js.map +1 -0
  287. package/dist/tsconfig.backend.tsbuildinfo +1 -1
  288. package/package.json +40 -36
  289. package/prisma/migrations/20260217190642_add_linear_integration/migration.sql +34 -0
  290. package/prisma/schema.prisma +13 -0
  291. package/prompts/quick-actions/screenshots.md +12 -6
  292. package/prompts/ratchet/dispatch.md +4 -0
  293. package/dist/client/assets/_basePickBy-kS1IrKFQ.js +0 -1
  294. package/dist/client/assets/arc-CWpVTRns.js +0 -1
  295. package/dist/client/assets/architectureDiagram-VXUJARFQ-Dp1AYPMt.js +0 -36
  296. package/dist/client/assets/channel-D5BCeybW.js +0 -1
  297. package/dist/client/assets/chunk-QN33PNHL-DP72lCp6.js +0 -1
  298. package/dist/client/assets/chunk-TZMSLE5B-G3wPktxK.js +0 -1
  299. package/dist/client/assets/classDiagram-2ON5EDUG-BUS1JDwr.js +0 -1
  300. package/dist/client/assets/classDiagram-v2-WZHVMYZB-BUS1JDwr.js +0 -1
  301. package/dist/client/assets/clone-C5xhPZw8.js +0 -1
  302. package/dist/client/assets/diagram-PSM6KHXK-QA-3O0wr.js +0 -24
  303. package/dist/client/assets/diagram-QEK2KX5R-VZkRJLXU.js +0 -43
  304. package/dist/client/assets/diagram-S2PKOQOG-DwLmogjW.js +0 -24
  305. package/dist/client/assets/gitGraphDiagram-NY62KEGX-CK2ytvyZ.js +0 -65
  306. package/dist/client/assets/index-D5ywoSOH.css +0 -1
  307. package/dist/client/assets/infoDiagram-WHAUD3N6-DO5X8HkW.js +0 -2
  308. package/dist/client/assets/pieDiagram-ADFJNKIX-BVBJCvYL.js +0 -30
  309. package/dist/client/assets/sankeyDiagram-TZEHDZUN-GIzxIQQB.js +0 -10
  310. package/dist/client/assets/stateDiagram-FKZM4ZOC-ByvdYZKl.js +0 -1
  311. package/dist/client/assets/stateDiagram-v2-4FDKWEC3-ynQtAOKH.js +0 -1
  312. package/dist/client/assets/terminal-instance-xdECz0dx.js +0 -1
  313. package/dist/client/assets/treemap-KMMF4GRG-CVpLLzhu.js +0 -128
@@ -6,6 +6,25 @@ const PENDING_TURN_ID = '__pending_turn__';
6
6
  const MAX_CLOSE_WATCHER_ATTACH_RETRIES = 50;
7
7
  const DEFAULT_APPROVAL_POLICIES = ['on-failure', 'on-request', 'never'];
8
8
  const DEFAULT_SANDBOX_MODES = ['read-only', 'workspace-write', 'danger-full-access'];
9
+ const SHAPE_DRIFT_DETAILS_LIMIT = 700;
10
+ const COMMAND_EXECUTION_SESSION_HANDOFF_LINE_PATTERN = /^process running with session id(?:\s*[:#-]?\s*[a-z0-9_-]+)?\.?$/i;
11
+ function toTurnItemKey(turnId, itemId) {
12
+ return `${turnId}:${itemId}`;
13
+ }
14
+ function toShapeDriftDetails(value) {
15
+ try {
16
+ const text = JSON.stringify(value);
17
+ if (typeof text !== 'string') {
18
+ return '[unserializable]';
19
+ }
20
+ return text.length > SHAPE_DRIFT_DETAILS_LIMIT
21
+ ? `${text.slice(0, SHAPE_DRIFT_DETAILS_LIMIT)}...`
22
+ : text;
23
+ }
24
+ catch {
25
+ return '[unserializable]';
26
+ }
27
+ }
9
28
  function isNonEmptyString(value) {
10
29
  return typeof value === 'string' && value.trim().length > 0;
11
30
  }
@@ -107,8 +126,21 @@ function toSessionId(threadId) {
107
126
  function toThreadId(sessionId) {
108
127
  return sessionId.startsWith('sess_') ? sessionId.slice('sess_'.length) : sessionId;
109
128
  }
110
- function createToolCallId(threadId, turnId, itemId) {
111
- return `codex:${threadId}:${turnId}:${itemId}`;
129
+ function extractToolCallIdFromUnknown(value) {
130
+ if (!isRecord(value)) {
131
+ return null;
132
+ }
133
+ return asString(value.callId) ?? asString(value.call_id) ?? null;
134
+ }
135
+ function resolveToolCallId(params) {
136
+ return extractToolCallIdFromUnknown(params.source) ?? params.itemId;
137
+ }
138
+ function isCommandExecutionSessionHandoffOutput(output) {
139
+ const trimmedOutput = output.trim();
140
+ if (trimmedOutput.length === 0 || /[\r\n]/.test(trimmedOutput)) {
141
+ return false;
142
+ }
143
+ return COMMAND_EXECUTION_SESSION_HANDOFF_LINE_PATTERN.test(trimmedOutput);
112
144
  }
113
145
  function isPlanLikeMode(mode) {
114
146
  return /plan/i.test(mode);
@@ -123,6 +155,16 @@ const PLAN_TEXT_PREFERRED_KEYS = [
123
155
  'value',
124
156
  'message',
125
157
  ];
158
+ const REASONING_TEXT_MAX_DEPTH = 8;
159
+ const REASONING_TEXT_PREFERRED_KEYS = [
160
+ 'summary',
161
+ 'summaryText',
162
+ 'text',
163
+ 'delta',
164
+ 'message',
165
+ 'content',
166
+ 'reasoning',
167
+ ];
126
168
  function extractFirstPlanText(values, depth) {
127
169
  for (const entry of values) {
128
170
  const extracted = extractPlanTextLocal(entry, depth + 1);
@@ -155,6 +197,37 @@ function extractPlanTextLocal(value, depth = 0) {
155
197
  }
156
198
  return extractPlanTextFromRecord(value, depth);
157
199
  }
200
+ function collectReasoningText(values, value, depth = 0) {
201
+ if (depth > REASONING_TEXT_MAX_DEPTH) {
202
+ return;
203
+ }
204
+ if (typeof value === 'string') {
205
+ if (value.trim().length > 0) {
206
+ values.push(value);
207
+ }
208
+ return;
209
+ }
210
+ if (Array.isArray(value)) {
211
+ for (const entry of value) {
212
+ collectReasoningText(values, entry, depth + 1);
213
+ }
214
+ return;
215
+ }
216
+ if (!isRecord(value)) {
217
+ return;
218
+ }
219
+ for (const key of REASONING_TEXT_PREFERRED_KEYS) {
220
+ collectReasoningText(values, value[key], depth + 1);
221
+ }
222
+ }
223
+ function extractReasoningTextLocal(value) {
224
+ const collected = [];
225
+ collectReasoningText(collected, value);
226
+ if (collected.length === 0) {
227
+ return null;
228
+ }
229
+ return dedupeStrings(collected).join('\n\n');
230
+ }
158
231
  function toMcpEnvRecord(envVars) {
159
232
  const env = {};
160
233
  for (const entry of envVars) {
@@ -293,6 +366,307 @@ function parseToolUserInputAnswersFromPermissionMeta(params) {
293
366
  function asString(value) {
294
367
  return typeof value === 'string' ? value : null;
295
368
  }
369
+ function trimOptionalQuotes(token) {
370
+ if (token.length >= 2 && token.startsWith('"') && token.endsWith('"')) {
371
+ return { text: token.slice(1, -1), quote: '"' };
372
+ }
373
+ if (token.length >= 2 && token.startsWith("'") && token.endsWith("'")) {
374
+ return { text: token.slice(1, -1), quote: "'" };
375
+ }
376
+ return { text: token, quote: null };
377
+ }
378
+ function unescapeCommandToken(token, quote) {
379
+ if (quote === "'") {
380
+ return token.replace(/\\'/g, "'").replace(/\\\\/g, '\\');
381
+ }
382
+ return token.replace(/\\(["'\\\s|&;])/g, '$1');
383
+ }
384
+ function sanitizeCommandToken(token) {
385
+ const trimmed = token.trim();
386
+ const { text, quote } = trimOptionalQuotes(trimmed);
387
+ return unescapeCommandToken(text, quote).trim();
388
+ }
389
+ function tokenizeCommand(command) {
390
+ return (command.match(/"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|\S+/g) ?? [])
391
+ .map((token) => sanitizeCommandToken(token))
392
+ .filter((token) => token.length > 0);
393
+ }
394
+ function commandName(token) {
395
+ const unix = token.split('/').at(-1) ?? token;
396
+ return (unix.split('\\').at(-1) ?? unix).toLowerCase();
397
+ }
398
+ function isChainSeparatorChar(char) {
399
+ return char === ';' || char === '\n' || char === '|' || char === '&';
400
+ }
401
+ function readChainSeparatorLength(command, index) {
402
+ const char = command[index];
403
+ if (!(char && isChainSeparatorChar(char))) {
404
+ return 0;
405
+ }
406
+ const next = command[index + 1];
407
+ if ((char === '|' || char === '&') && next === char) {
408
+ return 2;
409
+ }
410
+ return 1;
411
+ }
412
+ function isLikelyPathToken(token) {
413
+ if (!token || token.startsWith('-')) {
414
+ return false;
415
+ }
416
+ if (token.startsWith('./') ||
417
+ token.startsWith('../') ||
418
+ token.startsWith('/') ||
419
+ token.startsWith('~')) {
420
+ return true;
421
+ }
422
+ return (token.includes('/') ||
423
+ token.includes('\\') ||
424
+ token.endsWith('.') ||
425
+ /\.[a-z0-9]+$/i.test(token));
426
+ }
427
+ function normalizeLocationPath(pathToken, cwd) {
428
+ if (pathToken.startsWith('/')) {
429
+ return pathToken;
430
+ }
431
+ if (pathToken.startsWith('~')) {
432
+ return pathToken;
433
+ }
434
+ const base = cwd.endsWith('/') ? cwd.slice(0, -1) : cwd;
435
+ return `${base}/${pathToken}`;
436
+ }
437
+ const READ_COMMANDS = new Set(['cat', 'head', 'tail', 'less', 'more']);
438
+ const LIST_OR_FIND_COMMANDS = new Set(['ls', 'tree', 'find', 'fd']);
439
+ const GREP_LIKE_COMMANDS = new Set(['rg', 'ripgrep', 'grep', 'ag']);
440
+ const SHELL_META_COMMANDS = new Set(['cd', 'export', 'set', 'unset', 'alias', 'unalias', 'source']);
441
+ function pushCommandChainPart(state) {
442
+ const trimmed = state.current.trim();
443
+ if (trimmed.length > 0) {
444
+ state.parts.push(trimmed);
445
+ }
446
+ state.current = '';
447
+ }
448
+ function consumeEscapedCommandChar(state, char) {
449
+ if (!state.escaped) {
450
+ return false;
451
+ }
452
+ state.current += char;
453
+ state.escaped = false;
454
+ return true;
455
+ }
456
+ function consumeEscapeInitiator(state, char) {
457
+ if (!(char === '\\' && state.quote !== "'")) {
458
+ return false;
459
+ }
460
+ state.current += char;
461
+ state.escaped = true;
462
+ return true;
463
+ }
464
+ function hasOddTrailingBackslashes(value) {
465
+ let count = 0;
466
+ for (let index = value.length - 1; index >= 0; index -= 1) {
467
+ if (value[index] !== '\\') {
468
+ break;
469
+ }
470
+ count += 1;
471
+ }
472
+ return count % 2 === 1;
473
+ }
474
+ function consumeQuotedCommandChar(state, char) {
475
+ if (!state.quote) {
476
+ return false;
477
+ }
478
+ const escapedSingleQuote = state.quote === "'" && char === "'" && hasOddTrailingBackslashes(state.current);
479
+ state.current += char;
480
+ if (char === state.quote && !escapedSingleQuote) {
481
+ state.quote = null;
482
+ }
483
+ return true;
484
+ }
485
+ function consumeQuoteStart(state, char) {
486
+ if (!(char === '"' || char === "'")) {
487
+ return false;
488
+ }
489
+ state.quote = char;
490
+ state.current += char;
491
+ return true;
492
+ }
493
+ function consumeCommandChainSeparator(state, command, index) {
494
+ const separatorLength = readChainSeparatorLength(command, index);
495
+ if (separatorLength <= 0) {
496
+ return 0;
497
+ }
498
+ pushCommandChainPart(state);
499
+ return separatorLength;
500
+ }
501
+ function splitCommandChain(command) {
502
+ const state = {
503
+ parts: [],
504
+ current: '',
505
+ quote: null,
506
+ escaped: false,
507
+ };
508
+ for (let index = 0; index < command.length; index += 1) {
509
+ const char = command[index];
510
+ if (!char) {
511
+ continue;
512
+ }
513
+ if (consumeEscapedCommandChar(state, char)) {
514
+ continue;
515
+ }
516
+ if (consumeEscapeInitiator(state, char)) {
517
+ continue;
518
+ }
519
+ if (consumeQuotedCommandChar(state, char)) {
520
+ continue;
521
+ }
522
+ if (consumeQuoteStart(state, char)) {
523
+ continue;
524
+ }
525
+ const consumedSeparatorLength = consumeCommandChainSeparator(state, command, index);
526
+ if (consumedSeparatorLength > 0) {
527
+ index += consumedSeparatorLength - 1;
528
+ continue;
529
+ }
530
+ state.current += char;
531
+ }
532
+ pushCommandChainPart(state);
533
+ return state.parts;
534
+ }
535
+ function buildCommandDisplayContexts(rawCommand, cwd) {
536
+ const command = rawCommand?.trim();
537
+ if (!command) {
538
+ return [];
539
+ }
540
+ const contexts = [];
541
+ const segments = splitCommandChain(command);
542
+ for (const segment of segments) {
543
+ const tokens = tokenizeCommand(segment);
544
+ if (tokens.length === 0) {
545
+ continue;
546
+ }
547
+ const [firstToken] = tokens;
548
+ if (!firstToken) {
549
+ continue;
550
+ }
551
+ const firstCommand = commandName(firstToken);
552
+ const nonFlagArgs = tokens.slice(1).filter((token) => !token.startsWith('-'));
553
+ const pathArg = nonFlagArgs.find(isLikelyPathToken) ?? null;
554
+ const commandPath = pathArg ? normalizeLocationPath(pathArg, cwd) : null;
555
+ const locations = commandPath ? [{ path: commandPath }] : [];
556
+ contexts.push({
557
+ command: segment,
558
+ firstCommand,
559
+ nonFlagArgs,
560
+ pathArg,
561
+ cwd,
562
+ locations,
563
+ isShellMeta: SHELL_META_COMMANDS.has(firstCommand),
564
+ });
565
+ }
566
+ return contexts;
567
+ }
568
+ function chooseActionableCommandContexts(contexts) {
569
+ const actionable = contexts.filter((context) => !context.isShellMeta);
570
+ return actionable.length > 0 ? actionable : contexts;
571
+ }
572
+ function selectCombinedCommandKind(displays) {
573
+ const firstNonExecute = displays.find((display) => display.kind !== 'execute');
574
+ return firstNonExecute?.kind ?? 'execute';
575
+ }
576
+ function resolveCommandDisplay(params) {
577
+ const contexts = chooseActionableCommandContexts(buildCommandDisplayContexts(params.command, params.cwd));
578
+ if (contexts.length === 0) {
579
+ return { title: 'commandExecution', kind: 'execute', locations: [] };
580
+ }
581
+ const [singleContext] = contexts;
582
+ if (singleContext && contexts.length === 1) {
583
+ return resolveCommandDisplayFromContext(singleContext);
584
+ }
585
+ const displays = contexts.map((context) => resolveCommandDisplayFromContext(context));
586
+ const title = dedupeStrings(displays.map((display) => display.title)).join(', ');
587
+ const kind = selectCombinedCommandKind(displays);
588
+ const locations = dedupeLocations(displays.flatMap((display) => display.locations)).map((location) => ({ path: location.path }));
589
+ return { title, kind, locations };
590
+ }
591
+ function resolveCommandDisplayFromContext(context) {
592
+ if (READ_COMMANDS.has(context.firstCommand)) {
593
+ const label = context.pathArg ?? context.command;
594
+ return { title: `Read ${label}`, kind: 'read', locations: context.locations };
595
+ }
596
+ if (LIST_OR_FIND_COMMANDS.has(context.firstCommand)) {
597
+ const target = context.pathArg
598
+ ? normalizeLocationPath(context.pathArg, context.cwd)
599
+ : context.cwd;
600
+ const title = context.firstCommand === 'find' || context.firstCommand === 'fd'
601
+ ? `Search ${target}`
602
+ : `List ${target}`;
603
+ return { title, kind: 'search', locations: context.locations };
604
+ }
605
+ if (GREP_LIKE_COMMANDS.has(context.firstCommand)) {
606
+ const query = context.nonFlagArgs.find((token) => !isLikelyPathToken(token));
607
+ const target = context.pathArg ? ` in ${context.pathArg}` : '';
608
+ const title = query ? `Search ${query}${target}` : `Search ${context.command}`;
609
+ return { title, kind: 'search', locations: context.locations };
610
+ }
611
+ return { title: context.command, kind: 'execute', locations: context.locations };
612
+ }
613
+ function normalizeCwdForScope(cwd) {
614
+ if (cwd.length > 1 && cwd.endsWith('/')) {
615
+ return cwd.slice(0, -1);
616
+ }
617
+ return cwd;
618
+ }
619
+ function normalizeScopeToken(token) {
620
+ return token.replace(/\s+/g, ' ').trim();
621
+ }
622
+ function buildCommandApprovalScopeKey(params) {
623
+ const contexts = buildCommandDisplayContexts(params.command, params.cwd);
624
+ if (contexts.length === 0) {
625
+ return null;
626
+ }
627
+ let scopeCwd = normalizeCwdForScope(params.cwd);
628
+ const segments = [];
629
+ for (const context of contexts) {
630
+ if (context.firstCommand === 'cd') {
631
+ const rawTarget = context.nonFlagArgs[0];
632
+ const target = rawTarget ? normalizeScopeToken(rawTarget) : '';
633
+ if (target.length > 0) {
634
+ scopeCwd = normalizeCwdForScope(normalizeLocationPath(target, scopeCwd));
635
+ segments.push(`cd ${target} -> ${scopeCwd}`);
636
+ }
637
+ else {
638
+ segments.push('cd');
639
+ }
640
+ continue;
641
+ }
642
+ const args = context.nonFlagArgs.map(normalizeScopeToken).filter((arg) => arg.length > 0);
643
+ const normalized = normalizeScopeToken([context.firstCommand, ...args].join(' '));
644
+ if (normalized.length === 0) {
645
+ continue;
646
+ }
647
+ segments.push(`[cwd=${scopeCwd}] ${normalized}`);
648
+ }
649
+ const normalizedSegments = segments
650
+ .map(normalizeScopeToken)
651
+ .filter((segment) => segment.length > 0);
652
+ if (normalizedSegments.length === 0) {
653
+ return null;
654
+ }
655
+ return `cwd=${normalizeCwdForScope(params.cwd)}|${normalizedSegments.join(' && ')}`;
656
+ }
657
+ function dedupeLocations(locations) {
658
+ const seen = new Set();
659
+ const result = [];
660
+ for (const location of locations) {
661
+ const key = `${location.path}:${location.line ?? ''}`;
662
+ if (seen.has(key)) {
663
+ continue;
664
+ }
665
+ seen.add(key);
666
+ result.push(location);
667
+ }
668
+ return result;
669
+ }
296
670
  function parseTextFromPromptBlock(block) {
297
671
  if (block.type === 'text') {
298
672
  return block.text;
@@ -430,6 +804,7 @@ export class CodexAppServerAcpAdapter {
430
804
  collaborationModes = [];
431
805
  mcpServersByThreadId = new Map();
432
806
  appliedMcpServerConfigJson = '{}';
807
+ shapeDriftCounts = new Map();
433
808
  constructor(connection, codexClient) {
434
809
  this.connection = connection;
435
810
  this.codex =
@@ -542,10 +917,14 @@ export class CodexAppServerAcpAdapter {
542
917
  },
543
918
  activeTurn: null,
544
919
  toolCallsByItemId: new Map(),
920
+ syntheticallyCompletedToolItemIds: new Set(),
921
+ reasoningDeltaItemIds: new Set(),
545
922
  planTextByItemId: new Map(),
546
923
  planApprovalRequestedByTurnId: new Set(),
547
924
  pendingPlanApprovalsByTurnId: new Map(),
548
925
  pendingTurnCompletionsByTurnId: new Map(),
926
+ commandApprovalScopes: new Set(),
927
+ replayedTurnItemKeys: new Set(),
549
928
  };
550
929
  this.sessions.set(sessionId, session);
551
930
  this.sessionIdByThreadId.set(session.threadId, sessionId);
@@ -587,10 +966,14 @@ export class CodexAppServerAcpAdapter {
587
966
  },
588
967
  activeTurn: null,
589
968
  toolCallsByItemId: new Map(),
969
+ syntheticallyCompletedToolItemIds: new Set(),
970
+ reasoningDeltaItemIds: new Set(),
590
971
  planTextByItemId: new Map(),
591
972
  planApprovalRequestedByTurnId: new Set(),
592
973
  pendingPlanApprovalsByTurnId: new Map(),
593
974
  pendingTurnCompletionsByTurnId: new Map(),
975
+ commandApprovalScopes: new Set(),
976
+ replayedTurnItemKeys: new Set(),
594
977
  };
595
978
  this.sessions.set(session.sessionId, session);
596
979
  this.sessionIdByThreadId.set(session.threadId, session.sessionId);
@@ -1202,8 +1585,27 @@ export class CodexAppServerAcpAdapter {
1202
1585
  });
1203
1586
  }
1204
1587
  async replayToolLikeHistoryItem(session, sessionId, turnId, item) {
1588
+ if (item.type === 'reasoning') {
1589
+ await this.emitSessionUpdate(sessionId, {
1590
+ sessionUpdate: 'tool_call',
1591
+ toolCallId: resolveToolCallId({ itemId: item.id, source: item }),
1592
+ title: 'reasoning',
1593
+ kind: 'think',
1594
+ status: 'completed',
1595
+ rawInput: item,
1596
+ rawOutput: item,
1597
+ });
1598
+ await this.emitReasoningThoughtChunkFromItem(sessionId, item);
1599
+ session.replayedTurnItemKeys.add(toTurnItemKey(turnId, item.id));
1600
+ return;
1601
+ }
1205
1602
  const toolInfo = this.buildToolCallState(session, item, turnId);
1206
1603
  if (!toolInfo) {
1604
+ this.reportShapeDrift('unhandled_replay_item', {
1605
+ turnId,
1606
+ itemType: item.type,
1607
+ itemId: item.id,
1608
+ });
1207
1609
  return;
1208
1610
  }
1209
1611
  await this.emitSessionUpdate(sessionId, {
@@ -1215,11 +1617,15 @@ export class CodexAppServerAcpAdapter {
1215
1617
  rawInput: item,
1216
1618
  rawOutput: item,
1217
1619
  });
1620
+ session.replayedTurnItemKeys.add(toTurnItemKey(turnId, item.id));
1218
1621
  }
1219
1622
  async handleCodexNotification(method, params) {
1220
1623
  const parsed = knownCodexNotificationSchema.safeParse({ method, params });
1221
1624
  if (!parsed.success) {
1222
- process.stderr.write(`[codex-app-server-acp] dropped malformed notification: ${method}\n`);
1625
+ this.reportShapeDrift('malformed_notification', {
1626
+ method,
1627
+ issues: parsed.error.issues.slice(0, 3).map((issue) => issue.message),
1628
+ });
1223
1629
  return;
1224
1630
  }
1225
1631
  const notification = parsed.data;
@@ -1248,16 +1654,20 @@ export class CodexAppServerAcpAdapter {
1248
1654
  await this.handlePlanDelta(sessionId, session, notification.params.itemId, notification.params.delta);
1249
1655
  return;
1250
1656
  }
1657
+ if (notification.method === 'item/reasoning/summaryTextDelta') {
1658
+ await this.emitReasoningThoughtDelta(sessionId, session, notification.params.turnId, notification.params.itemId, notification.params.delta);
1659
+ return;
1660
+ }
1251
1661
  if (notification.method === 'item/commandExecution/outputDelta') {
1252
- await this.emitToolCallProgress(sessionId, session, notification.params.itemId, notification.params.delta);
1662
+ await this.emitToolCallProgress(sessionId, session, notification.params.turnId, notification.params.itemId, notification.params.delta, 'commandExecution');
1253
1663
  return;
1254
1664
  }
1255
1665
  if (notification.method === 'item/fileChange/outputDelta') {
1256
- await this.emitToolCallProgress(sessionId, session, notification.params.itemId, notification.params.delta);
1666
+ await this.emitToolCallProgress(sessionId, session, notification.params.turnId, notification.params.itemId, notification.params.delta, 'fileChange');
1257
1667
  return;
1258
1668
  }
1259
1669
  if (notification.method === 'item/mcpToolCall/progress') {
1260
- await this.emitToolCallProgress(sessionId, session, notification.params.itemId, notification.params.message);
1670
+ await this.emitToolCallProgress(sessionId, session, notification.params.turnId, notification.params.itemId, notification.params.message, 'mcpToolCall');
1261
1671
  return;
1262
1672
  }
1263
1673
  if (notification.method === 'item/started') {
@@ -1287,15 +1697,92 @@ export class CodexAppServerAcpAdapter {
1287
1697
  ],
1288
1698
  });
1289
1699
  }
1290
- async emitToolCallProgress(sessionId, session, itemId, output) {
1700
+ async emitReasoningThoughtChunkFromItem(sessionId, item) {
1701
+ const text = extractReasoningTextLocal(item);
1702
+ if (!text) {
1703
+ return;
1704
+ }
1705
+ await this.emitSessionUpdate(sessionId, {
1706
+ sessionUpdate: 'agent_thought_chunk',
1707
+ content: { type: 'text', text },
1708
+ });
1709
+ }
1710
+ isReplayedTurnItem(session, turnId, itemId) {
1711
+ return session.replayedTurnItemKeys.has(toTurnItemKey(turnId, itemId));
1712
+ }
1713
+ async emitReasoningThoughtDelta(sessionId, session, turnId, itemId, delta) {
1714
+ if (this.isReplayedTurnItem(session, turnId, itemId)) {
1715
+ return;
1716
+ }
1717
+ if (delta.length === 0) {
1718
+ return;
1719
+ }
1720
+ const existing = session.toolCallsByItemId.get(itemId);
1721
+ if (!existing) {
1722
+ const toolCallId = resolveToolCallId({
1723
+ itemId,
1724
+ });
1725
+ session.toolCallsByItemId.set(itemId, {
1726
+ toolCallId,
1727
+ title: 'reasoning',
1728
+ kind: 'think',
1729
+ locations: [],
1730
+ });
1731
+ await this.emitSessionUpdate(sessionId, {
1732
+ sessionUpdate: 'tool_call',
1733
+ toolCallId,
1734
+ title: 'reasoning',
1735
+ kind: 'think',
1736
+ status: 'pending',
1737
+ rawInput: {
1738
+ type: 'reasoning',
1739
+ id: itemId,
1740
+ },
1741
+ });
1742
+ await this.emitSessionUpdate(sessionId, {
1743
+ sessionUpdate: 'tool_call_update',
1744
+ toolCallId,
1745
+ title: 'reasoning',
1746
+ kind: 'think',
1747
+ status: 'in_progress',
1748
+ });
1749
+ }
1750
+ session.reasoningDeltaItemIds.add(itemId);
1751
+ await this.emitSessionUpdate(sessionId, {
1752
+ sessionUpdate: 'agent_thought_chunk',
1753
+ content: { type: 'text', text: delta },
1754
+ });
1755
+ }
1756
+ async emitToolCallProgress(sessionId, session, turnId, itemId, output, source) {
1757
+ if (this.isReplayedTurnItem(session, turnId, itemId)) {
1758
+ return;
1759
+ }
1291
1760
  const toolCall = session.toolCallsByItemId.get(itemId);
1292
1761
  if (!toolCall) {
1762
+ this.reportShapeDrift('tool_progress_without_tool_call', { turnId, itemId });
1763
+ return;
1764
+ }
1765
+ if (session.syntheticallyCompletedToolItemIds.has(itemId)) {
1766
+ return;
1767
+ }
1768
+ if (source === 'commandExecution' && isCommandExecutionSessionHandoffOutput(output)) {
1769
+ session.syntheticallyCompletedToolItemIds.add(itemId);
1770
+ await this.emitSessionUpdate(sessionId, {
1771
+ sessionUpdate: 'tool_call_update',
1772
+ toolCallId: toolCall.toolCallId,
1773
+ status: 'completed',
1774
+ kind: toolCall.kind,
1775
+ title: toolCall.title,
1776
+ ...(toolCall.locations.length > 0 ? { locations: toolCall.locations } : {}),
1777
+ rawOutput: output,
1778
+ });
1293
1779
  return;
1294
1780
  }
1295
1781
  await this.emitSessionUpdate(sessionId, {
1296
1782
  sessionUpdate: 'tool_call_update',
1297
1783
  toolCallId: toolCall.toolCallId,
1298
1784
  status: 'in_progress',
1785
+ ...(toolCall.locations.length > 0 ? { locations: toolCall.locations } : {}),
1299
1786
  rawOutput: output,
1300
1787
  });
1301
1788
  }
@@ -1338,10 +1825,52 @@ export class CodexAppServerAcpAdapter {
1338
1825
  this.settleTurn(session, session.activeTurn?.cancelRequested ? 'cancelled' : 'end_turn');
1339
1826
  }
1340
1827
  async handleItemStarted(session, item, turnId) {
1828
+ if (this.isReplayedTurnItem(session, turnId, item.id)) {
1829
+ return;
1830
+ }
1831
+ if (item.type === 'reasoning') {
1832
+ const existing = session.toolCallsByItemId.get(item.id);
1833
+ const toolInfo = existing ??
1834
+ {
1835
+ toolCallId: resolveToolCallId({
1836
+ itemId: item.id,
1837
+ source: item,
1838
+ }),
1839
+ title: 'reasoning',
1840
+ kind: 'think',
1841
+ locations: [],
1842
+ };
1843
+ if (!existing) {
1844
+ session.syntheticallyCompletedToolItemIds.delete(item.id);
1845
+ session.toolCallsByItemId.set(item.id, toolInfo);
1846
+ await this.emitSessionUpdate(session.sessionId, {
1847
+ sessionUpdate: 'tool_call',
1848
+ toolCallId: toolInfo.toolCallId,
1849
+ title: toolInfo.title,
1850
+ kind: toolInfo.kind,
1851
+ status: 'pending',
1852
+ rawInput: item,
1853
+ });
1854
+ }
1855
+ await this.emitSessionUpdate(session.sessionId, {
1856
+ sessionUpdate: 'tool_call_update',
1857
+ toolCallId: toolInfo.toolCallId,
1858
+ title: toolInfo.title,
1859
+ kind: toolInfo.kind,
1860
+ status: 'in_progress',
1861
+ });
1862
+ return;
1863
+ }
1341
1864
  const toolInfo = this.buildToolCallState(session, item, turnId);
1342
1865
  if (!toolInfo) {
1866
+ this.reportShapeDrift('unhandled_item_started_type', {
1867
+ turnId,
1868
+ itemType: item.type,
1869
+ itemId: item.id,
1870
+ });
1343
1871
  return;
1344
1872
  }
1873
+ session.syntheticallyCompletedToolItemIds.delete(item.id);
1345
1874
  session.toolCallsByItemId.set(item.id, toolInfo);
1346
1875
  await this.emitSessionUpdate(session.sessionId, {
1347
1876
  sessionUpdate: 'tool_call',
@@ -1349,6 +1878,7 @@ export class CodexAppServerAcpAdapter {
1349
1878
  title: toolInfo.title,
1350
1879
  kind: toolInfo.kind,
1351
1880
  status: 'pending',
1881
+ ...(toolInfo.locations.length > 0 ? { locations: toolInfo.locations } : {}),
1352
1882
  rawInput: item,
1353
1883
  });
1354
1884
  const itemStatus = toToolStatus(item.status);
@@ -1361,8 +1891,53 @@ export class CodexAppServerAcpAdapter {
1361
1891
  }
1362
1892
  }
1363
1893
  async handleItemCompleted(session, item, turnId) {
1894
+ if (this.isReplayedTurnItem(session, turnId, item.id)) {
1895
+ return;
1896
+ }
1897
+ if (item.type === 'reasoning') {
1898
+ const existing = session.toolCallsByItemId.get(item.id);
1899
+ const reasoningToolCallId = existing?.toolCallId ??
1900
+ resolveToolCallId({
1901
+ itemId: item.id,
1902
+ source: item,
1903
+ });
1904
+ if (existing) {
1905
+ await this.emitSessionUpdate(session.sessionId, {
1906
+ sessionUpdate: 'tool_call_update',
1907
+ toolCallId: reasoningToolCallId,
1908
+ title: existing.title,
1909
+ kind: existing.kind,
1910
+ status: toToolStatus(item.status) ?? 'completed',
1911
+ rawOutput: item,
1912
+ });
1913
+ }
1914
+ else {
1915
+ await this.emitSessionUpdate(session.sessionId, {
1916
+ sessionUpdate: 'tool_call',
1917
+ toolCallId: reasoningToolCallId,
1918
+ title: 'reasoning',
1919
+ kind: 'think',
1920
+ status: toToolStatus(item.status) ?? 'completed',
1921
+ rawInput: item,
1922
+ rawOutput: item,
1923
+ });
1924
+ }
1925
+ session.toolCallsByItemId.delete(item.id);
1926
+ session.syntheticallyCompletedToolItemIds.delete(item.id);
1927
+ const sawDelta = session.reasoningDeltaItemIds.has(item.id);
1928
+ session.reasoningDeltaItemIds.delete(item.id);
1929
+ if (!sawDelta) {
1930
+ await this.emitReasoningThoughtChunkFromItem(session.sessionId, item);
1931
+ }
1932
+ return;
1933
+ }
1364
1934
  const existing = session.toolCallsByItemId.get(item.id);
1365
1935
  if (!existing) {
1936
+ this.reportShapeDrift('item_completed_without_started_state', {
1937
+ turnId,
1938
+ itemType: item.type,
1939
+ itemId: item.id,
1940
+ });
1366
1941
  return;
1367
1942
  }
1368
1943
  const shouldHoldTurnForPlanApproval = item.type === 'plan' &&
@@ -1374,7 +1949,8 @@ export class CodexAppServerAcpAdapter {
1374
1949
  }
1375
1950
  const statusFromItem = toToolStatus(item.status);
1376
1951
  const status = statusFromItem ?? 'completed';
1377
- const locations = this.extractLocations(item);
1952
+ const completionLocations = this.extractLocations(item);
1953
+ const locations = dedupeLocations([...existing.locations, ...completionLocations]);
1378
1954
  await this.emitSessionUpdate(session.sessionId, {
1379
1955
  sessionUpdate: 'tool_call_update',
1380
1956
  toolCallId: existing.toolCallId,
@@ -1385,6 +1961,7 @@ export class CodexAppServerAcpAdapter {
1385
1961
  rawOutput: item,
1386
1962
  });
1387
1963
  session.toolCallsByItemId.delete(item.id);
1964
+ session.syntheticallyCompletedToolItemIds.delete(item.id);
1388
1965
  await this.maybeRequestPlanApproval(session, item, turnId, existing);
1389
1966
  }
1390
1967
  buildPlanApprovalInput(planText, sourceItemId) {
@@ -1411,28 +1988,43 @@ export class CodexAppServerAcpAdapter {
1411
1988
  }
1412
1989
  return null;
1413
1990
  }
1414
- isPlanApprovalSelected(optionId) {
1415
- if (!optionId) {
1416
- return false;
1991
+ getPlanExitModePriority(modeId) {
1992
+ const normalized = modeId.toLowerCase();
1993
+ const index = PLAN_EXIT_MODE_PREFERENCE.findIndex((preferred) => preferred.toLowerCase() === normalized);
1994
+ return index >= 0 ? index : PLAN_EXIT_MODE_PREFERENCE.length;
1995
+ }
1996
+ comparePlanExitModePreference(left, right) {
1997
+ const priorityDiff = this.getPlanExitModePriority(left) - this.getPlanExitModePriority(right);
1998
+ if (priorityDiff !== 0) {
1999
+ return priorityDiff;
1417
2000
  }
1418
- return optionId.startsWith('allow') || optionId === 'default';
2001
+ return left.localeCompare(right);
1419
2002
  }
1420
- resolvePostPlanApprovalMode(currentMode) {
1421
- if (!isPlanLikeMode(currentMode)) {
1422
- return null;
2003
+ resolveCollaborationModeLabel(modeId) {
2004
+ const entry = this.collaborationModes.find((candidate) => candidate.mode === modeId);
2005
+ if (entry?.name) {
2006
+ return entry.name;
1423
2007
  }
2008
+ return sanitizeModeName(modeId);
2009
+ }
2010
+ buildPlanApprovalOptions(session) {
2011
+ const currentMode = session.defaults.collaborationMode;
1424
2012
  const availableModes = this.getCollaborationModeValues(currentMode);
1425
- for (const preferred of PLAN_EXIT_MODE_PREFERENCE) {
1426
- const match = availableModes.find((mode) => mode.toLowerCase() === preferred.toLowerCase());
1427
- if (match) {
1428
- return match;
1429
- }
1430
- }
1431
- const firstNonPlan = availableModes.find((mode) => !isPlanLikeMode(mode));
1432
- if (firstNonPlan) {
1433
- return firstNonPlan;
1434
- }
1435
- return currentMode.toLowerCase() === 'default' ? null : 'default';
2013
+ const nonPlanModes = availableModes
2014
+ .filter((modeId) => !isPlanLikeMode(modeId))
2015
+ .sort((left, right) => this.comparePlanExitModePreference(left, right));
2016
+ const approvableModeIds = new Set(nonPlanModes);
2017
+ const options = nonPlanModes.map((modeId) => ({
2018
+ optionId: modeId,
2019
+ name: `Approve and switch to ${this.resolveCollaborationModeLabel(modeId)}`,
2020
+ kind: 'allow_once',
2021
+ }));
2022
+ options.push({
2023
+ optionId: currentMode,
2024
+ name: 'Keep planning',
2025
+ kind: 'reject_once',
2026
+ });
2027
+ return { options, approvableModeIds };
1436
2028
  }
1437
2029
  holdTurnUntilPlanApprovalResolves(session, turnId) {
1438
2030
  if (this.hasPendingPlanApprovals(session, turnId)) {
@@ -1494,6 +2086,18 @@ export class CodexAppServerAcpAdapter {
1494
2086
  rawInput: approvalInput,
1495
2087
  });
1496
2088
  try {
2089
+ const { options: planApprovalOptions, approvableModeIds } = this.buildPlanApprovalOptions(session);
2090
+ if (approvableModeIds.size === 0) {
2091
+ await this.emitSessionUpdate(session.sessionId, {
2092
+ sessionUpdate: 'tool_call_update',
2093
+ toolCallId: approvalToolCallId,
2094
+ kind: 'switch_mode',
2095
+ title: 'ExitPlanMode',
2096
+ status: 'failed',
2097
+ rawOutput: 'Plan proposed, but no non-plan collaboration mode is available.',
2098
+ });
2099
+ return;
2100
+ }
1497
2101
  const permissionResult = await this.connection.requestPermission({
1498
2102
  sessionId: session.sessionId,
1499
2103
  toolCall: {
@@ -1503,30 +2107,19 @@ export class CodexAppServerAcpAdapter {
1503
2107
  status: 'pending',
1504
2108
  rawInput: approvalInput,
1505
2109
  },
1506
- options: [
1507
- {
1508
- optionId: 'allow_once',
1509
- name: 'Approve plan',
1510
- kind: 'allow_once',
1511
- },
1512
- {
1513
- optionId: 'reject_once',
1514
- name: 'Request changes',
1515
- kind: 'reject_once',
1516
- },
1517
- ],
2110
+ options: planApprovalOptions,
1518
2111
  });
1519
- const selectedOptionId = permissionResult.outcome.outcome === 'selected' ? permissionResult.outcome.optionId : null;
1520
- const approved = this.isPlanApprovalSelected(selectedOptionId);
1521
- if (approved) {
1522
- const postApprovalMode = this.resolvePostPlanApprovalMode(session.defaults.collaborationMode);
1523
- if (postApprovalMode) {
1524
- session.defaults.collaborationMode = postApprovalMode;
1525
- await this.emitSessionUpdate(session.sessionId, {
1526
- sessionUpdate: 'config_option_update',
1527
- configOptions: this.buildConfigOptions(session),
1528
- });
1529
- }
2112
+ const selectedMode = permissionResult.outcome.outcome === 'selected' &&
2113
+ approvableModeIds.has(permissionResult.outcome.optionId)
2114
+ ? permissionResult.outcome.optionId
2115
+ : null;
2116
+ const approved = selectedMode !== null;
2117
+ if (selectedMode && session.defaults.collaborationMode !== selectedMode) {
2118
+ session.defaults.collaborationMode = selectedMode;
2119
+ await this.emitSessionUpdate(session.sessionId, {
2120
+ sessionUpdate: 'config_option_update',
2121
+ configOptions: this.buildConfigOptions(session),
2122
+ });
1530
2123
  }
1531
2124
  await this.emitSessionUpdate(session.sessionId, {
1532
2125
  sessionUpdate: 'tool_call_update',
@@ -1573,26 +2166,32 @@ export class CodexAppServerAcpAdapter {
1573
2166
  }
1574
2167
  return locations;
1575
2168
  }
1576
- buildToolCallState(session, item, turnId) {
2169
+ buildToolCallState(session, item, _turnId) {
1577
2170
  const kindByType = {
1578
2171
  commandExecution: 'execute',
1579
2172
  fileChange: 'edit',
1580
2173
  mcpToolCall: 'fetch',
1581
2174
  webSearch: 'search',
1582
2175
  plan: 'think',
1583
- reasoning: 'think',
1584
2176
  };
1585
2177
  const kind = kindByType[item.type];
1586
2178
  if (!kind) {
1587
2179
  return null;
1588
2180
  }
1589
2181
  let title = item.type;
2182
+ let kindToEmit = kind;
2183
+ let locations = [];
1590
2184
  if (item.type === 'commandExecution') {
1591
2185
  const command = asString(item.command);
1592
- title = command ?? 'commandExecution';
2186
+ const cwd = asString(item.cwd) ?? session.cwd;
2187
+ const parsed = resolveCommandDisplay({ command, cwd });
2188
+ title = parsed.title;
2189
+ kindToEmit = parsed.kind;
2190
+ locations = parsed.locations;
1593
2191
  }
1594
2192
  else if (item.type === 'fileChange') {
1595
2193
  title = 'fileChange';
2194
+ locations = this.extractLocations(item);
1596
2195
  }
1597
2196
  else if (item.type === 'mcpToolCall') {
1598
2197
  const server = asString(item.server) ?? 'mcp';
@@ -1604,15 +2203,81 @@ export class CodexAppServerAcpAdapter {
1604
2203
  title = query ? `webSearch:${query}` : 'webSearch';
1605
2204
  }
1606
2205
  return {
1607
- toolCallId: createToolCallId(session.threadId, turnId, item.id),
1608
- kind,
2206
+ toolCallId: resolveToolCallId({
2207
+ itemId: item.id,
2208
+ source: item,
2209
+ }),
2210
+ kind: kindToEmit,
1609
2211
  title,
2212
+ locations,
1610
2213
  };
1611
2214
  }
2215
+ buildPermissionOptions(params) {
2216
+ if (params.method === 'item/tool/requestUserInput') {
2217
+ return buildToolUserInputPermissionOptions(params.questions);
2218
+ }
2219
+ if (params.method === 'item/commandExecution/requestApproval') {
2220
+ return [
2221
+ {
2222
+ optionId: 'allow_always',
2223
+ name: 'Allow for session',
2224
+ kind: 'allow_always',
2225
+ },
2226
+ {
2227
+ optionId: 'allow_once',
2228
+ name: 'Allow once',
2229
+ kind: 'allow_once',
2230
+ },
2231
+ {
2232
+ optionId: 'reject_once',
2233
+ name: 'Reject',
2234
+ kind: 'reject_once',
2235
+ },
2236
+ ];
2237
+ }
2238
+ return [
2239
+ {
2240
+ optionId: 'allow_once',
2241
+ name: 'Allow once',
2242
+ kind: 'allow_once',
2243
+ },
2244
+ {
2245
+ optionId: 'reject_once',
2246
+ name: 'Reject',
2247
+ kind: 'reject_once',
2248
+ },
2249
+ ];
2250
+ }
2251
+ async maybeAutoApproveCommandRequest(params) {
2252
+ if (params.request.method !== 'item/commandExecution/requestApproval') {
2253
+ return false;
2254
+ }
2255
+ const scopeKey = buildCommandApprovalScopeKey({
2256
+ command: asString(params.request.params.command),
2257
+ cwd: asString(params.request.params.cwd) ?? params.session.cwd,
2258
+ });
2259
+ if (!(scopeKey && params.session.commandApprovalScopes.has(scopeKey))) {
2260
+ return false;
2261
+ }
2262
+ await this.respondToCodexPermissionRequest({
2263
+ request: params.request,
2264
+ permission: {
2265
+ outcome: { outcome: 'selected', optionId: 'allow_always' },
2266
+ },
2267
+ session: params.session,
2268
+ toolCallId: params.toolCallId,
2269
+ questions: [],
2270
+ });
2271
+ return true;
2272
+ }
1612
2273
  async handleCodexServerRequest(request) {
1613
2274
  try {
1614
2275
  const parsed = knownCodexServerRequestSchema.safeParse(request);
1615
2276
  if (!parsed.success) {
2277
+ this.reportShapeDrift('malformed_server_request', {
2278
+ method: request.method,
2279
+ issues: parsed.error.issues.slice(0, 3).map((issue) => issue.message),
2280
+ });
1616
2281
  this.codex.respondError(request.id, {
1617
2282
  code: -32_602,
1618
2283
  message: 'Unsupported codex server request payload',
@@ -1622,6 +2287,11 @@ export class CodexAppServerAcpAdapter {
1622
2287
  const typed = parsed.data;
1623
2288
  const sessionId = this.sessionIdByThreadId.get(typed.params.threadId);
1624
2289
  if (!sessionId) {
2290
+ this.reportShapeDrift('request_for_unknown_thread', {
2291
+ method: typed.method,
2292
+ threadId: typed.params.threadId,
2293
+ itemId: typed.params.itemId,
2294
+ });
1625
2295
  this.codex.respondError(typed.id, {
1626
2296
  code: -32_603,
1627
2297
  message: 'No ACP session mapped for this thread',
@@ -1630,6 +2300,11 @@ export class CodexAppServerAcpAdapter {
1630
2300
  }
1631
2301
  const session = this.sessions.get(sessionId);
1632
2302
  if (!session) {
2303
+ this.reportShapeDrift('request_for_missing_session', {
2304
+ method: typed.method,
2305
+ sessionId,
2306
+ itemId: typed.params.itemId,
2307
+ });
1633
2308
  this.codex.respondError(typed.id, {
1634
2309
  code: -32_603,
1635
2310
  message: 'No ACP session mapped for this thread',
@@ -1647,24 +2322,30 @@ export class CodexAppServerAcpAdapter {
1647
2322
  title: toolCall.title,
1648
2323
  kind: toolCall.kind,
1649
2324
  status: 'pending',
2325
+ ...(toolCall.locations.length > 0 ? { locations: toolCall.locations } : {}),
1650
2326
  rawInput: typed.params,
1651
2327
  });
1652
2328
  }
2329
+ if (await this.maybeAutoApproveCommandRequest({
2330
+ request: typed,
2331
+ session,
2332
+ toolCallId: toolCall.toolCallId,
2333
+ })) {
2334
+ return;
2335
+ }
1653
2336
  const questions = typed.method === 'item/tool/requestUserInput' ? typed.params.questions : [];
1654
- const permissionOptions = typed.method === 'item/tool/requestUserInput'
1655
- ? buildToolUserInputPermissionOptions(questions)
1656
- : [
1657
- {
1658
- optionId: 'allow_once',
1659
- name: 'Allow once',
1660
- kind: 'allow_once',
1661
- },
1662
- {
1663
- optionId: 'reject_once',
1664
- name: 'Reject',
1665
- kind: 'reject_once',
1666
- },
1667
- ];
2337
+ const permissionOptions = this.buildPermissionOptions({
2338
+ method: typed.method,
2339
+ questions,
2340
+ });
2341
+ await this.emitSessionUpdate(sessionId, {
2342
+ sessionUpdate: 'tool_call_update',
2343
+ toolCallId: toolCall.toolCallId,
2344
+ title: toolCall.title,
2345
+ kind: toolCall.kind,
2346
+ status: 'pending',
2347
+ ...(toolCall.locations.length > 0 ? { locations: toolCall.locations } : {}),
2348
+ });
1668
2349
  const permissionResult = await this.connection.requestPermission({
1669
2350
  sessionId,
1670
2351
  toolCall: {
@@ -1693,67 +2374,110 @@ export class CodexAppServerAcpAdapter {
1693
2374
  });
1694
2375
  }
1695
2376
  }
1696
- buildApprovalToolCallState(session, method, itemId, turnId, params) {
2377
+ buildApprovalToolCallState(session, method, itemId, _turnId, params) {
1697
2378
  if (method === 'item/commandExecution/requestApproval') {
1698
2379
  const command = asString(params.command);
2380
+ const cwd = asString(params.cwd) ?? session.cwd;
2381
+ const parsed = resolveCommandDisplay({ command, cwd });
1699
2382
  return {
1700
- toolCallId: createToolCallId(session.threadId, turnId, itemId),
1701
- title: command ? command : 'commandExecution',
1702
- kind: 'execute',
2383
+ toolCallId: resolveToolCallId({
2384
+ itemId,
2385
+ source: params,
2386
+ }),
2387
+ title: parsed.title,
2388
+ kind: parsed.kind,
2389
+ locations: parsed.locations,
1703
2390
  };
1704
2391
  }
1705
2392
  if (method === 'item/fileChange/requestApproval') {
2393
+ const grantRoot = asString(params.grantRoot);
1706
2394
  return {
1707
- toolCallId: createToolCallId(session.threadId, turnId, itemId),
2395
+ toolCallId: resolveToolCallId({
2396
+ itemId,
2397
+ source: params,
2398
+ }),
1708
2399
  title: 'fileChange',
1709
2400
  kind: 'edit',
2401
+ locations: grantRoot ? [{ path: grantRoot }] : [],
1710
2402
  };
1711
2403
  }
1712
2404
  return {
1713
- toolCallId: createToolCallId(session.threadId, turnId, itemId),
2405
+ toolCallId: resolveToolCallId({
2406
+ itemId,
2407
+ source: params,
2408
+ }),
1714
2409
  title: 'item/tool/requestUserInput',
1715
2410
  kind: 'other',
2411
+ locations: [],
1716
2412
  };
1717
2413
  }
1718
- async respondToCodexPermissionRequest(params) {
1719
- const selected = params.permission.outcome.outcome === 'selected' ? params.permission.outcome.optionId : null;
1720
- const allow = selected === 'allow_once';
1721
- if (params.request.method === 'item/tool/requestUserInput') {
1722
- const rejected = selected === null || selected === 'reject_once';
1723
- let answers;
1724
- try {
1725
- answers = buildToolUserInputAnswers({
1726
- questions: params.questions,
1727
- permission: params.permission,
1728
- selectedOptionId: selected,
1729
- });
1730
- }
1731
- catch (error) {
1732
- const message = error instanceof Error ? error.message : String(error);
1733
- this.codex.respondError(params.request.id, {
1734
- code: -32_602,
1735
- message: 'Failed to map requestUserInput answers',
1736
- data: { error: message },
1737
- });
1738
- await this.emitSessionUpdate(params.session.sessionId, {
1739
- sessionUpdate: 'tool_call_update',
1740
- toolCallId: params.toolCallId,
1741
- status: 'failed',
1742
- rawOutput: { error: message },
1743
- });
1744
- return;
1745
- }
1746
- this.codex.respondSuccess(params.request.id, {
1747
- answers,
2414
+ isAllowedPermissionSelection(selectedOptionId) {
2415
+ return selectedOptionId === 'allow_once' || selectedOptionId === 'allow_always';
2416
+ }
2417
+ async handleToolUserInputPermissionResponse(params) {
2418
+ if (params.request.method !== 'item/tool/requestUserInput') {
2419
+ return false;
2420
+ }
2421
+ const rejected = params.selectedOptionId === null || params.selectedOptionId === 'reject_once';
2422
+ let answers;
2423
+ try {
2424
+ answers = buildToolUserInputAnswers({
2425
+ questions: params.questions,
2426
+ permission: params.permission,
2427
+ selectedOptionId: params.selectedOptionId,
2428
+ });
2429
+ }
2430
+ catch (error) {
2431
+ const message = error instanceof Error ? error.message : String(error);
2432
+ this.codex.respondError(params.request.id, {
2433
+ code: -32_602,
2434
+ message: 'Failed to map requestUserInput answers',
2435
+ data: { error: message },
1748
2436
  });
1749
2437
  await this.emitSessionUpdate(params.session.sessionId, {
1750
2438
  sessionUpdate: 'tool_call_update',
1751
2439
  toolCallId: params.toolCallId,
1752
- status: rejected ? 'failed' : 'completed',
1753
- rawOutput: rejected ? { outcome: 'rejected' } : { answers },
2440
+ status: 'failed',
2441
+ rawOutput: { error: message },
1754
2442
  });
2443
+ return true;
2444
+ }
2445
+ this.codex.respondSuccess(params.request.id, {
2446
+ answers,
2447
+ });
2448
+ await this.emitSessionUpdate(params.session.sessionId, {
2449
+ sessionUpdate: 'tool_call_update',
2450
+ toolCallId: params.toolCallId,
2451
+ status: rejected ? 'failed' : 'completed',
2452
+ rawOutput: rejected ? { outcome: 'rejected' } : { answers },
2453
+ });
2454
+ return true;
2455
+ }
2456
+ async respondToCodexPermissionRequest(params) {
2457
+ const selected = params.permission.outcome.outcome === 'selected' ? params.permission.outcome.optionId : null;
2458
+ if (await this.handleToolUserInputPermissionResponse({
2459
+ ...params,
2460
+ selectedOptionId: selected,
2461
+ })) {
1755
2462
  return;
1756
2463
  }
2464
+ if (params.request.method === 'item/commandExecution/requestApproval' &&
2465
+ selected === 'allow_always') {
2466
+ const scopeKey = buildCommandApprovalScopeKey({
2467
+ command: asString(params.request.params.command),
2468
+ cwd: asString(params.request.params.cwd) ?? params.session.cwd,
2469
+ });
2470
+ if (scopeKey) {
2471
+ params.session.commandApprovalScopes.add(scopeKey);
2472
+ }
2473
+ else {
2474
+ this.reportShapeDrift('allow_always_without_scope_key', {
2475
+ itemId: params.request.params.itemId,
2476
+ command: params.request.params.command,
2477
+ });
2478
+ }
2479
+ }
2480
+ const allow = this.isAllowedPermissionSelection(selected);
1757
2481
  this.codex.respondSuccess(params.request.id, {
1758
2482
  decision: allow ? 'accept' : 'decline',
1759
2483
  });
@@ -1775,8 +2499,17 @@ export class CodexAppServerAcpAdapter {
1775
2499
  session.planApprovalRequestedByTurnId.clear();
1776
2500
  session.pendingPlanApprovalsByTurnId.clear();
1777
2501
  session.toolCallsByItemId.clear();
2502
+ session.syntheticallyCompletedToolItemIds.clear();
2503
+ session.reasoningDeltaItemIds.clear();
1778
2504
  session.pendingTurnCompletionsByTurnId.clear();
1779
2505
  }
2506
+ reportShapeDrift(event, details) {
2507
+ const count = (this.shapeDriftCounts.get(event) ?? 0) + 1;
2508
+ this.shapeDriftCounts.set(event, count);
2509
+ const includeDetails = details !== undefined && (count <= 5 || count % 50 === 0);
2510
+ const detailsSuffix = includeDetails ? ` details=${toShapeDriftDetails(details)}` : '';
2511
+ process.stderr.write(`[codex-app-server-acp] shape-drift event=${event} count=${count}${detailsSuffix}\n`);
2512
+ }
1780
2513
  async emitSessionUpdate(sessionId, update) {
1781
2514
  try {
1782
2515
  await this.connection.sessionUpdate({ sessionId, update });