chainlesschain 0.166.46 → 0.166.47

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 (232) hide show
  1. package/README.md +6 -4
  2. package/package.json +3 -1
  3. package/src/assets/web-panel/assets/{AIOps-Cp8KiMmv.js → AIOps-CDpT0WXV.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-B4d9BGsm.js → ActionButton-BNfUljWx.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-Cf9yXTkv.js → Analytics-C9323DEx.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-40UkmxtP.js → AppLayout-CRrjb2db.js} +4 -4
  7. package/src/assets/web-panel/assets/{Artifacts-Ckkb4oOA.js → Artifacts-Db-DGqJS.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-Dnf569k5.js → Audit-CJdHRJ0W.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-CA6bLem4.js → BackgroundAgents-_RSCnTYs.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-ChQU0I_Q.js → Backup-8GFQi-dM.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BHeZXnNN.js → BaseInput-C87NtgUZ.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-DcFVYB2E.js → Chat-DcTMWS8G.js} +5 -5
  13. package/src/assets/web-panel/assets/ChatBubbleRenderer-5ZDPTW_H.js +1 -0
  14. package/src/assets/web-panel/assets/{Checkbox-2PDbejv2.js → Checkbox-DM9EmYee.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-DJBn1RAu.js → Codegen-M2YqDRNN.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CPRq7n6y.js → Col-oB1LLxyf.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-axvINFMi.js → Community-TcVZK5tF.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-_OX4Qbe0.js → Compact-XKmq-3K_.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-DWAkvzRL.js → Compliance-C6CBiDRy.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-CUIJ25oj.js → Cowork-Dz9CGqXH.js} +4 -4
  21. package/src/assets/web-panel/assets/{Cron-nR0m7H3_.js → Cron-CxJDgDVL.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-CgTLXzh_.js → Crosschain-DMP0JsaQ.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-BllzRB6Q.js → DID-I5IQeogb.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-C9f0IIBN.js → Dashboard-BfLW2MXj.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-BkahetiO.js → Dropdown-BK8EjAlh.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-rartVC-k.js → EmailListRenderer-C3iqSv5I.js} +1 -1
  27. package/src/assets/web-panel/assets/EvolutionSettings-BJmBzpW6.js +1 -0
  28. package/src/assets/web-panel/assets/{FamilyGuardDashboard-DgUdFvw1.js → FamilyGuardDashboard-T19rnqm-.js} +1 -1
  29. package/src/assets/web-panel/assets/{Federation-oFpIQU6g.js → Federation-CozrbPMy.js} +1 -1
  30. package/src/assets/web-panel/assets/{FormItemContext-BUH5xlyP.js → FormItemContext-k1dCLNNJ.js} +1 -1
  31. package/src/assets/web-panel/assets/{GenericCardRenderer-ilRa-ueg.js → GenericCardRenderer-DbaW_Jvt.js} +1 -1
  32. package/src/assets/web-panel/assets/{Git-DlHHSF8j.js → Git-CQHRyEIt.js} +2 -2
  33. package/src/assets/web-panel/assets/{Governance-yQiiXIWe.js → Governance-BMJYZgMy.js} +1 -1
  34. package/src/assets/web-panel/assets/{Inference-BUDRVGOP.js → Inference-B7mpNU7V.js} +1 -1
  35. package/src/assets/web-panel/assets/{KnowledgeGraph-BaGyIoN1.js → KnowledgeGraph-Br0Blg6B.js} +1 -1
  36. package/src/assets/web-panel/assets/{Logs-C1cO6zE0.js → Logs-HIdk0S6M.js} +2 -2
  37. package/src/assets/web-panel/assets/{MarkdownRenderer-_z_Iaoes.js → MarkdownRenderer-HxeocBHy.js} +1 -1
  38. package/src/assets/web-panel/assets/{Marketplace-CM8cGL0K.js → Marketplace-Bkfq69UP.js} +1 -1
  39. package/src/assets/web-panel/assets/{McpTools-CDqAMU0U.js → McpTools-dtmx0bHO.js} +3 -3
  40. package/src/assets/web-panel/assets/{Memory-CfB0-Ofv.js → Memory-CrRlOuEf.js} +2 -2
  41. package/src/assets/web-panel/assets/{MobileBridge-BL18ytvB.js → MobileBridge-D4ZIZiAH.js} +1 -1
  42. package/src/assets/web-panel/assets/{MobileProjects-PIyyxbuJ.js → MobileProjects-C90Rw7GE.js} +1 -1
  43. package/src/assets/web-panel/assets/{Mtc-BTTrFCol.js → Mtc-CC6k_uh4.js} +4 -4
  44. package/src/assets/web-panel/assets/{MtcAudit-JHZn037c.js → MtcAudit-T_CUIYUy.js} +4 -4
  45. package/src/assets/web-panel/assets/{Multisig-CMf9NEdB.js → Multisig-N_e0RRWk.js} +3 -3
  46. package/src/assets/web-panel/assets/{NLProgramming-Dhk3qaus.js → NLProgramming-Iq3VzEv0.js} +1 -1
  47. package/src/assets/web-panel/assets/Notes-B5sKJUJt.js +7 -0
  48. package/src/assets/web-panel/assets/{NotificationSettings-gxdaVEit.js → NotificationSettings-K8UVp8qQ.js} +1 -1
  49. package/src/assets/web-panel/assets/OrderTableRenderer-BWNyM-6m.js +1 -0
  50. package/src/assets/web-panel/assets/{Organization-CzvHcDNO.js → Organization-DMO1NJxI.js} +4 -4
  51. package/src/assets/web-panel/assets/{Overflow-Dclx30Vv.js → Overflow-BBY6b31Z.js} +1 -1
  52. package/src/assets/web-panel/assets/{P2P-CM0Wm9vZ.js → P2P-DmsL0y0v.js} +2 -2
  53. package/src/assets/web-panel/assets/{PdhVaultBrowser-fYmQzmTA.js → PdhVaultBrowser-C65AX_RY.js} +3 -3
  54. package/src/assets/web-panel/assets/{Permissions-B_oATenG.js → Permissions-CWcpRtWa.js} +4 -4
  55. package/src/assets/web-panel/assets/{PersonalDataHub-CptiVBh5.js → PersonalDataHub--ZNDIdsn.js} +3 -3
  56. package/src/assets/web-panel/assets/{Pipeline-DkHzgyyI.js → Pipeline-DCdj5NGb.js} +1 -1
  57. package/src/assets/web-panel/assets/{Privacy-DzZvwcKh.js → Privacy-BRKu7Q3E.js} +1 -1
  58. package/src/assets/web-panel/assets/{ProjectInit-CzkCs-5G.js → ProjectInit-BJ7ozIAb.js} +2 -2
  59. package/src/assets/web-panel/assets/{ProjectSettings-BB8dcxRD.js → ProjectSettings-B2cVLiXW.js} +2 -2
  60. package/src/assets/web-panel/assets/{Projects-B7PW9CFY.js → Projects-Ce9r_W-p.js} +1 -1
  61. package/src/assets/web-panel/assets/{Providers-Tfx-ti1V.js → Providers-BDespvGZ.js} +1 -1
  62. package/src/assets/web-panel/assets/{QrScannerModal-EvWlNhfX.js → QrScannerModal-BUNO4fDU.js} +1 -1
  63. package/src/assets/web-panel/assets/{QuickAsk-CUspgbNC.js → QuickAsk-D2TUqeBV.js} +1 -1
  64. package/src/assets/web-panel/assets/{Recommend-COY2g77J.js → Recommend-BNNQPHBH.js} +1 -1
  65. package/src/assets/web-panel/assets/{RemoteSession-BzMBU6E6.js → RemoteSession-D2t1V_Cm.js} +2 -2
  66. package/src/assets/web-panel/assets/{Reputation-RtYRF4YP.js → Reputation-DVK5hEqI.js} +1 -1
  67. package/src/assets/web-panel/assets/{Row-Bva3l9Lc.js → Row-BB9RTwmE.js} +1 -1
  68. package/src/assets/web-panel/assets/{RssFeed-BhjzLvW3.js → RssFeed-DmEO7liZ.js} +2 -2
  69. package/src/assets/web-panel/assets/{Search-BYzT2PTR.js → Search-UwEYjnDB.js} +1 -1
  70. package/src/assets/web-panel/assets/{Security-Bcl4-KTv.js → Security-DebqSsMp.js} +4 -4
  71. package/src/assets/web-panel/assets/{Services-BRRa4A0f.js → Services-CuuPts3C.js} +2 -2
  72. package/src/assets/web-panel/assets/{Skeleton-DYQ016qy.js → Skeleton-DbWpmbgf.js} +1 -1
  73. package/src/assets/web-panel/assets/{Skills-CAUKL9JA.js → Skills-BaK7SxzT.js} +1 -1
  74. package/src/assets/web-panel/assets/{Sla-BTyUqfuD.js → Sla-BFHv51Jb.js} +1 -1
  75. package/src/assets/web-panel/assets/{SpeechSettings-CUjMihrs.js → SpeechSettings-B9b1R6uK.js} +1 -1
  76. package/src/assets/web-panel/assets/{SyncSettings-CvfOSupr.js → SyncSettings-BnO0CN02.js} +2 -2
  77. package/src/assets/web-panel/assets/{Tasks-EvjNXrWH.js → Tasks-Dy01Vq49.js} +1 -1
  78. package/src/assets/web-panel/assets/{Templates-wPAp0EoM.js → Templates-BCy2AMHc.js} +1 -1
  79. package/src/assets/web-panel/assets/{Tenant-CP0D-q9Q.js → Tenant-4Tx9y_Y1.js} +1 -1
  80. package/src/assets/web-panel/assets/{Terminal-CQ0f2iFg.js → Terminal-CPOoUH8J.js} +2 -2
  81. package/src/assets/web-panel/assets/{TimelineRenderer-D3JrGSPb.js → TimelineRenderer-C85GlJzu.js} +1 -1
  82. package/src/assets/web-panel/assets/{Tokens-yXf2PPp9.js → Tokens-_xneFBGF.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trigger-DfmMOoZ2.js → Trigger-BizG4rTr.js} +1 -1
  84. package/src/assets/web-panel/assets/{Trust-BTCyMgf1.js → Trust-Dh8EIn3n.js} +1 -1
  85. package/src/assets/web-panel/assets/{UkeySign-DKCwIBHi.js → UkeySign-C5NWoHWQ.js} +1 -1
  86. package/src/assets/web-panel/assets/{VideoEditing-CQpDSzLO.js → VideoEditing-0YfrySL6.js} +1 -1
  87. package/src/assets/web-panel/assets/{Wallet-CyFrzC3K.js → Wallet-BZVH8Ags.js} +4 -4
  88. package/src/assets/web-panel/assets/{WebAuthn-D6Nr_1kd.js → WebAuthn-kNofa6vm.js} +3 -3
  89. package/src/assets/web-panel/assets/{WorkflowEditor-CgR9tNrB.js → WorkflowEditor-BNO34_j0.js} +1 -1
  90. package/src/assets/web-panel/assets/{chat-CRG_IeyP.js → chat-ifQ2FlwD.js} +1 -1
  91. package/src/assets/web-panel/assets/{colors-MdE3TStI.js → colors-1TL8H6EH.js} +1 -1
  92. package/src/assets/web-panel/assets/{compact-item-B0LRuiNV.js → compact-item-DBfRjs_T.js} +1 -1
  93. package/src/assets/web-panel/assets/{createContext-B_V5fjcX.js → createContext-Bs6V4iBf.js} +1 -1
  94. package/src/assets/web-panel/assets/devWarning-BHnbnuYL.js +1 -0
  95. package/src/assets/web-panel/assets/{hasIn-DSL-c_hr.js → hasIn-Bz6LMY_0.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DE7bSYmW.js → index--WvT2Il3.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DBwBZwio.js → index-84mi5rsv.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BsAEd-Do.js → index-B5SJCUZ7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-CdHMc9LQ.js → index-B6RClMZl.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-B3iW-mhn.js → index-BB1Af7Fv.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-C67dPW0L.js → index-BQL_tldd.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BpwGsAA-.js → index-BXJXMaZe.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-CZICtYJ0.js → index-BhPDhmvN.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-BbZZ7P_c.js → index-BiZ7Nz5-.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Didyuq2Z.js → index-BoCTDcn6.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-BNhWoD6d.js → index-BxqC3LiO.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-DRRY44XP.js → index-C6c0j-Ki.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-D3pJBTvo.js → index-C7d6QlfR.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CdplnAS9.js → index-C9QYxq61.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CPYshcic.js → index-CBh48_Mh.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-CkN53KiJ.js → index-CNKHoXWI.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-OBhlHSMN.js → index-CXVBQ82k.js} +3 -3
  113. package/src/assets/web-panel/assets/{index-DtOTppk8.js → index-CXtiLagi.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CNytQuFq.js → index-Cn7EIWmI.js} +1 -1
  115. package/src/assets/web-panel/assets/index-CsRz9-ZH.js +1 -0
  116. package/src/assets/web-panel/assets/{index-BIDCNwy6.js → index-CtB45Hok.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BQAsL19t.js → index-Cu_V6ZJH.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-c8OqO3rq.js → index-Cykt9BbN.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-Bq8-2Ibv.js → index-DDhy4eCS.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-CXMeisJK.js → index-DEPPzM8I.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BD0w-888.js → index-DMVktlxG.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-CVDi4rS9.js → index-DOww5Qny.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-Tg3yl8wc.js → index-DWPp-d07.js} +1 -1
  124. package/src/assets/web-panel/assets/index-DnjMFf3m.js +1 -0
  125. package/src/assets/web-panel/assets/{index-DVgTf7CZ.js → index-DvM2xBUA.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-SmQfEHjp.js → index-EK3XtMxA.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C5dqieYX.js → index-XJIV7HEw.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-0200QxC4.js → index-cNwsev6n.js} +1 -1
  129. package/src/assets/web-panel/assets/{index-C-aKWR2v.js → index-f_7L9GvP.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-KaSejHjf.js → index-lUL3bD2z.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CS3bOhV7.js → index-pOUFfRdN.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-CEOAZm5j.js → index-rGhS02f8.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-BMm8tO2X.js → index-vHtnQsnV.js} +1 -1
  134. package/src/assets/web-panel/assets/{index-BHp1nSur.js → index-vb1XIxEB.js} +1 -1
  135. package/src/assets/web-panel/assets/{initDefaultProps-P8SBN6Di.js → initDefaultProps-DpRIjwTt.js} +1 -1
  136. package/src/assets/web-panel/assets/{motion-BubbjUT_.js → motion-CG4_9o-Z.js} +1 -1
  137. package/src/assets/web-panel/assets/{move-Chcykv0I.js → move-Dku1fXtf.js} +1 -1
  138. package/src/assets/web-panel/assets/{mtc-parser-DyiYuGDu.js → mtc-parser-BvAVeX-3.js} +1 -1
  139. package/src/assets/web-panel/assets/{omit-Rdwb_Neb.js → omit-DFu0x78D.js} +1 -1
  140. package/src/assets/web-panel/assets/{pickAttrs-CCr0MYHl.js → pickAttrs-CmI-FNcK.js} +1 -1
  141. package/src/assets/web-panel/assets/{placementArrow-B1ZXz3bb.js → placementArrow-CGERYewB.js} +1 -1
  142. package/src/assets/web-panel/assets/{responsiveObserve-BGEdwej6.js → responsiveObserve-D7G8bF5J.js} +1 -1
  143. package/src/assets/web-panel/assets/{slide-CpnsUMaT.js → slide-B2sXwWXy.js} +1 -1
  144. package/src/assets/web-panel/assets/{statusUtils-C7x-TVp_.js → statusUtils-DI5yh0n3.js} +1 -1
  145. package/src/assets/web-panel/assets/{styleChecker-CC5k_791.js → styleChecker-deGJ5dp_.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFlexGapSupport-BpZFKsJW.js → useFlexGapSupport-OqHBXEmi.js} +1 -1
  147. package/src/assets/web-panel/assets/{useFs-CVq5HySy.js → useFs-Dqgx0Qv_.js} +1 -1
  148. package/src/assets/web-panel/assets/{usePersonalDataHub-BGkdLryu.js → usePersonalDataHub-Be-lveh2.js} +1 -1
  149. package/src/assets/web-panel/assets/{vnode-uLtOUEK-.js → vnode-CFxB876W.js} +1 -1
  150. package/src/assets/web-panel/assets/{zoom-BfVC9iUs.js → zoom-BFOYrl4e.js} +1 -1
  151. package/src/assets/web-panel/index.html +1 -1
  152. package/src/command-help-index.json +3 -3
  153. package/src/commands/agent.js +8 -0
  154. package/src/commands/background-session.js +31 -3
  155. package/src/commands/chat.js +5 -1
  156. package/src/commands/eval.js +86 -28
  157. package/src/commands/llm.js +104 -1
  158. package/src/commands/plugin.js +130 -1
  159. package/src/commands/sandbox.js +121 -2
  160. package/src/commands/serve.js +11 -2
  161. package/src/commands/skill.js +29 -0
  162. package/src/data/changelog.json +13 -0
  163. package/src/gateways/ws/session-protocol.js +13 -0
  164. package/src/gateways/ws/ws-agent-handler.js +17 -0
  165. package/src/gateways/ws/ws-server.js +3 -0
  166. package/src/gateways/ws/ws-session-state.js +131 -0
  167. package/src/generated/cc-agent-protocol.schema.json +4 -0
  168. package/src/lazy-dispatch.js +8 -1
  169. package/src/lib/agent-sandbox.js +337 -53
  170. package/src/lib/app-server/cli-agent-kernel-adapter.js +28 -0
  171. package/src/lib/app-server/server.js +93 -0
  172. package/src/lib/background-agent-keeper-client.js +2 -0
  173. package/src/lib/background-agent-keeper-protocol.js +7 -0
  174. package/src/lib/background-agent-supervisor.js +194 -47
  175. package/src/lib/codex-app-server-adapter.js +81 -27
  176. package/src/lib/context-memory-kernel/durable-memory-port.js +31 -8
  177. package/src/lib/context-memory-kernel/provider-context.js +53 -19
  178. package/src/lib/deferred-question-context.js +122 -0
  179. package/src/lib/eval/evidence.js +202 -64
  180. package/src/lib/eval/plugin-suite.js +917 -0
  181. package/src/lib/eval/runner.js +10 -3
  182. package/src/lib/evolution/agent-evolution-runtime-composition.js +236 -1
  183. package/src/lib/evolution/agent-model-projection.js +97 -1
  184. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +154 -1
  185. package/src/lib/evolution/evolution-artifact-ports.js +6 -0
  186. package/src/lib/evolution/evolution-deployment-loader.js +87 -3
  187. package/src/lib/evolution/evolution-eval-gate.js +8 -0
  188. package/src/lib/evolution/evolution-eval-runtime-composition.js +255 -0
  189. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +58 -2
  190. package/src/lib/evolution/evolution-ledger-file-backend.js +23 -2
  191. package/src/lib/evolution/evolution-ledger-manifest-chain.js +223 -7
  192. package/src/lib/evolution/evolution-ledger-v2-journal.js +525 -0
  193. package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +46 -2
  194. package/src/lib/evolution/evolution-ledger.js +57 -1
  195. package/src/lib/evolution/evolution-run-ledger-adapter.js +93 -0
  196. package/src/lib/evolution/evolution-run-wiki-evidence-resolver.js +259 -0
  197. package/src/lib/evolution/governed-hub-llm.js +238 -5
  198. package/src/lib/evolution/governed-hub-resolver.js +82 -4
  199. package/src/lib/evolution/governed-hub-skill.js +11 -5
  200. package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +10 -1
  201. package/src/lib/evolution/skill-runtime-revalidation-ledger-adapter.js +254 -0
  202. package/src/lib/evolution/skill-runtime-revalidation.js +530 -0
  203. package/src/lib/evolution/skill-target-matrix-eval.js +4 -4
  204. package/src/lib/evolution/wiki-evidence-commit-guard.js +366 -0
  205. package/src/lib/evolution/wiki-informed-skill-proposer.js +13 -1
  206. package/src/lib/goal-context.js +14 -5
  207. package/src/lib/interaction-adapter.js +166 -1
  208. package/src/lib/model-capabilities.js +271 -0
  209. package/src/lib/model-context-catalog.js +105 -0
  210. package/src/lib/model-context-window.js +4 -49
  211. package/src/lib/openai-responses.js +428 -0
  212. package/src/lib/plugin-runtime/scopes.js +7 -1
  213. package/src/lib/remote-read-loop-guard.js +2 -2
  214. package/src/lib/runtime-usage-ledger.js +16 -0
  215. package/src/lib/skill-loader.js +12 -0
  216. package/src/lib/with-file-lock.js +3 -1
  217. package/src/repl/agent-repl.js +5 -1
  218. package/src/runtime/agent-core.js +348 -23
  219. package/src/runtime/agent-runtime.js +7 -0
  220. package/src/runtime/coding-agent-contract-shared.cjs +13 -1
  221. package/src/runtime/headless-runner.js +4 -1
  222. package/src/runtime/headless-stream.js +133 -44
  223. package/src/runtime/runtime-factory.js +26 -2
  224. package/src/workers/background-agent-keeper.js +23 -13
  225. package/src/workers/background-agent-worker.js +27 -13
  226. package/src/assets/web-panel/assets/ChatBubbleRenderer-Bi2SY9E_.js +0 -1
  227. package/src/assets/web-panel/assets/EvolutionSettings-BqpG3XHV.js +0 -1
  228. package/src/assets/web-panel/assets/Notes-6F8EarNN.js +0 -7
  229. package/src/assets/web-panel/assets/OrderTableRenderer-gVgXQWdc.js +0 -1
  230. package/src/assets/web-panel/assets/devWarning-BtFLt3qI.js +0 -1
  231. package/src/assets/web-panel/assets/index-BhuWQHeG.js +0 -1
  232. package/src/assets/web-panel/assets/index-D-4tFek3.js +0 -1
@@ -2,6 +2,7 @@ import { PassThrough } from "node:stream";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import {
4
4
  assertSandboxAvailable,
5
+ assertSandboxCapabilities,
5
6
  normalizeAgentSandboxMode,
6
7
  } from "../agent-sandbox.js";
7
8
 
@@ -127,6 +128,7 @@ export class CliAgentKernelAdapter {
127
128
  },
128
129
  },
129
130
  );
131
+ assertSandboxCapabilities(sandbox);
130
132
  assertSandboxAvailable(sandbox);
131
133
 
132
134
  const input = new PassThrough();
@@ -166,6 +168,26 @@ export class CliAgentKernelAdapter {
166
168
  binding: event.binding,
167
169
  });
168
170
  }
171
+ if (event.type === "question_request") {
172
+ const answerQuestion = async () => {
173
+ let answer = null;
174
+ try {
175
+ answer = await current.requestQuestion(event);
176
+ } catch {
177
+ // A missing/rejecting host is cancellation, never implicit consent.
178
+ }
179
+ writeLine(input, {
180
+ type: "answer",
181
+ id: event.id,
182
+ answer,
183
+ binding: event.binding,
184
+ });
185
+ };
186
+ // The Headless runtime owns blocking semantics and its timeout. Keep
187
+ // the transport event chain free so a timed-out question can emit
188
+ // question_resolved/result even while a slow host request is pending.
189
+ void answerQuestion().catch(() => {});
190
+ }
169
191
  if (event.type === "result") {
170
192
  session.current = null;
171
193
  current.resolve(event);
@@ -194,6 +216,7 @@ export class CliAgentKernelAdapter {
194
216
  permissionMode,
195
217
  sandbox,
196
218
  interactiveApprovals: true,
219
+ interactiveQuestions: turnOptions.interactiveQuestions === true,
197
220
  includePartialMessages: true,
198
221
  ephemeral: false,
199
222
  evolutionIngress:
@@ -263,6 +286,7 @@ export class CliAgentKernelAdapter {
263
286
  options = {},
264
287
  emit,
265
288
  requestApproval,
289
+ requestQuestion,
266
290
  }) {
267
291
  const session = await this._session(threadId, options);
268
292
  if (session.current) {
@@ -283,6 +307,10 @@ export class CliAgentKernelAdapter {
283
307
  turnId,
284
308
  emit,
285
309
  requestApproval,
310
+ requestQuestion:
311
+ typeof requestQuestion === "function"
312
+ ? requestQuestion
313
+ : async () => null,
286
314
  resolve,
287
315
  reject,
288
316
  };
@@ -10,6 +10,7 @@ import { AppServerGraphRuntime } from "./graph-runtime.js";
10
10
  import { captureAgentSkillOutcomeIndex } from "../evolution/agent-evolution-runtime-composition-brand.js";
11
11
  import { captureSkillVectorAuthority } from "../skill-vector-authority.js";
12
12
  import { captureSkillRetrievalRevocationReader } from "../evolution/skill-retrieval-revocation-authority.js";
13
+ import { sameInteractionBinding } from "../interaction-binding.js";
13
14
  import { compileGraphDefinition } from "../graph-kernel/compiler.js";
14
15
  import { createCliContextMemoryRuntime } from "../context-memory-kernel/runtime.js";
15
16
  import { isEvolutionWorkbenchCliHost } from "../evolution/evolution-workbench-cli-host.js";
@@ -882,6 +883,10 @@ export class CcAppServer {
882
883
  turnOptions.skillRetrievalRevocationReader =
883
884
  skillRetrievalRevocationReader;
884
885
  }
886
+ if (this.negotiated) {
887
+ turnOptions.interactiveQuestions =
888
+ this.negotiated.features.includes("deferred_questions");
889
+ }
885
890
  const result = await this.kernel.startTurn({
886
891
  threadId,
887
892
  turnId: attempt.id,
@@ -896,6 +901,7 @@ export class CcAppServer {
896
901
  event,
897
902
  }),
898
903
  requestApproval: (event) => this._requestApproval(turn, event),
904
+ requestQuestion: (event) => this._requestQuestion(turn, event),
899
905
  });
900
906
  const failed = result?.is_error === true || result?.subtype === "error";
901
907
  const output = typeof result?.result === "string" ? result.result : "";
@@ -1137,6 +1143,8 @@ export class CcAppServer {
1137
1143
  turnOptions.skillRetrievalRevocationReader =
1138
1144
  this.skillRetrievalRevocationReader;
1139
1145
  }
1146
+ turnOptions.interactiveQuestions =
1147
+ this.negotiated?.features.includes("deferred_questions") === true;
1140
1148
  const result = await this.kernel.startTurn({
1141
1149
  threadId: turn.threadId,
1142
1150
  turnId: turn.id,
@@ -1144,6 +1152,7 @@ export class CcAppServer {
1144
1152
  options: turnOptions,
1145
1153
  emit: (event) => this._emitKernelEvent(turn, event),
1146
1154
  requestApproval: (event) => this._requestApproval(turn, event),
1155
+ requestQuestion: (event) => this._requestQuestion(turn, event),
1147
1156
  });
1148
1157
  if (turn.interruptRequested) {
1149
1158
  await this._completeTurn(turn, "interrupted", result);
@@ -1321,6 +1330,48 @@ export class CcAppServer {
1321
1330
  );
1322
1331
  return;
1323
1332
  }
1333
+ if (event?.type === "question_request") {
1334
+ if (!this.negotiated?.features.includes("deferred_questions")) return;
1335
+ const blocking = event.blocking !== false && event.mode !== "deferred";
1336
+ if (blocking) turn.status = "waiting_input";
1337
+ turn.revision += 1;
1338
+ await this._notify(
1339
+ "question/requested",
1340
+ { request: this._questionRequest(turn, event) },
1341
+ {
1342
+ threadId: turn.threadId,
1343
+ turnId: turn.id,
1344
+ itemId: `${turn.id}:question:${safeId(event.id, "question")}`,
1345
+ traceId,
1346
+ idempotencyKey: `question-requested:${turn.id}:${event.id}`,
1347
+ },
1348
+ );
1349
+ return;
1350
+ }
1351
+ if (event?.type === "question_resolved") {
1352
+ if (!this.negotiated?.features.includes("deferred_questions")) return;
1353
+ if (event.blocking !== false && event.mode !== "deferred") {
1354
+ turn.status = "running";
1355
+ }
1356
+ turn.revision += 1;
1357
+ await this._notify(
1358
+ "question/resolved",
1359
+ {
1360
+ threadId: turn.threadId,
1361
+ turnId: turn.id,
1362
+ questionId: safeId(event.id, `${turn.id}:question`),
1363
+ mode: event.mode === "deferred" ? "deferred" : "blocking",
1364
+ via: event.via || "unknown",
1365
+ },
1366
+ {
1367
+ threadId: turn.threadId,
1368
+ turnId: turn.id,
1369
+ traceId,
1370
+ idempotencyKey: `question-resolved:${turn.id}:${event.id}:${event.via}`,
1371
+ },
1372
+ );
1373
+ return;
1374
+ }
1324
1375
  if (event?.type === "plan_update") {
1325
1376
  await this._notify(
1326
1377
  "item/completed",
@@ -1394,6 +1445,48 @@ export class CcAppServer {
1394
1445
  return result;
1395
1446
  }
1396
1447
 
1448
+ _questionRequest(turn, event) {
1449
+ const id = safeId(event.id, `${turn.id}:question`);
1450
+ return {
1451
+ id,
1452
+ threadId: turn.threadId,
1453
+ turnId: turn.id,
1454
+ binding: event.binding || null,
1455
+ question: String(event.question || "").slice(0, 16_384),
1456
+ options: Array.isArray(event.options)
1457
+ ? event.options.slice(0, 128)
1458
+ : null,
1459
+ multiSelect: event.multiSelect === true,
1460
+ mode: event.mode === "deferred" ? "deferred" : "blocking",
1461
+ blocking: event.blocking !== false && event.mode !== "deferred",
1462
+ purpose: event.purpose || "decision",
1463
+ contextRevision: Number.isSafeInteger(event.context_revision)
1464
+ ? event.context_revision
1465
+ : null,
1466
+ expiresAt: new Date(this.now() + this.requestTimeoutMs).toISOString(),
1467
+ };
1468
+ }
1469
+
1470
+ async _requestQuestion(turn, event) {
1471
+ if (!this.negotiated?.features.includes("deferred_questions")) return null;
1472
+ const request = this._questionRequest(turn, event);
1473
+ const result = requireObject(
1474
+ await this._requestClient("question/answer", { request }),
1475
+ "question answer",
1476
+ );
1477
+ if (
1478
+ result.questionId !== request.id ||
1479
+ !request.binding ||
1480
+ !sameInteractionBinding(request.binding, result.binding)
1481
+ ) {
1482
+ throw new JsonRpcError(
1483
+ JSON_RPC_ERROR.INVALID_PARAMS,
1484
+ "client returned an invalid or stale question answer",
1485
+ );
1486
+ }
1487
+ return Object.hasOwn(result, "answer") ? result.answer : null;
1488
+ }
1489
+
1397
1490
  async _requestHumanTask({ task }) {
1398
1491
  const result = await this._requestClient("humanTask/decide", { task });
1399
1492
  const response = requireObject(result, "humanTask decision");
@@ -4,6 +4,7 @@ import { createNdjsonReader } from "./background-session-transport.js";
4
4
  import {
5
5
  BACKGROUND_AGENT_KEEPER_ARM,
6
6
  BACKGROUND_AGENT_KEEPER_ARMED,
7
+ BACKGROUND_AGENT_KEEPER_ARM_TIMEOUT_MS,
7
8
  BACKGROUND_AGENT_KEEPER_HELLO,
8
9
  BACKGROUND_AGENT_KEEPER_HEARTBEAT,
9
10
  BACKGROUND_AGENT_KEEPER_HEARTBEAT_INTERVAL_MS,
@@ -244,6 +245,7 @@ export async function connectBackgroundAgentKeeper(options = {}) {
244
245
  BACKGROUND_AGENT_KEEPER_ARM,
245
246
  BACKGROUND_AGENT_KEEPER_ARMED,
246
247
  normalized,
248
+ BACKGROUND_AGENT_KEEPER_ARM_TIMEOUT_MS,
247
249
  );
248
250
  activeTurn = normalized;
249
251
  return normalized;
@@ -50,6 +50,13 @@ export const BACKGROUND_AGENT_KEEPER_IDENTITY_PROBE_TIMEOUT_MS =
50
50
  export const BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS = 5_000;
51
51
  export const BACKGROUND_AGENT_KEEPER_PERSIST_RETRY_TIMEOUT_MS =
52
52
  3 * BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS;
53
+ // ARM durably binds a newly spawned turn. It can contend with a strict state
54
+ // lock and, on Windows, an incumbent identity probe. Keep this deadline
55
+ // independent from short HELLO requests and long destructive RETIRE cleanup.
56
+ export const BACKGROUND_AGENT_KEEPER_ARM_TIMEOUT_MS =
57
+ BACKGROUND_AGENT_KEEPER_IDENTITY_PROBE_TIMEOUT_MS +
58
+ BACKGROUND_AGENT_KEEPER_STATE_LOCK_TIMEOUT_MS +
59
+ 10_000;
53
60
  // A 20-Agent macOS formal run can leave successfully SIGKILLed process groups
54
61
  // observable for several seconds while launchd reaps their orphaned leaders.
55
62
  // Two seconds made that bounded kernel/reaper delay look like an escaped tree.
@@ -63,6 +63,8 @@ import {
63
63
 
64
64
  export const DEFAULT_HEARTBEAT_INTERVAL_MS = 5000;
65
65
  export const DEFAULT_HEARTBEAT_STALE_MS = 120000;
66
+ export const DEFAULT_BACKGROUND_AGENT_PAGE_LIMIT = 50;
67
+ export const MAX_BACKGROUND_AGENT_PAGE_LIMIT = 200;
66
68
 
67
69
  /**
68
70
  * Pid identity tolerance (Gap 1: OS pid reuse). A pid alone does not identify
@@ -1370,7 +1372,14 @@ export function mutateBackgroundAgentState(id, updater, options = {}) {
1370
1372
  // A live numeric pid can belong to a later process after the lock owner
1371
1373
  // was killed. Use the lock acquisition timestamp as a one-sided identity
1372
1374
  // fence so only an owner which existed before acquisition still blocks.
1373
- _isOwnerAlive: isBackgroundAgentStateLockOwnerAlive,
1375
+ // Periodic telemetry may request a strict try-lock. In that mode an
1376
+ // incumbent is deliberately retained without a Windows PID/start-time
1377
+ // probe: a heartbeat must never synchronously stall a control channel
1378
+ // merely to determine whether it could reclaim a lock.
1379
+ _isOwnerAlive:
1380
+ options.probeLockOwner === false
1381
+ ? () => true
1382
+ : isBackgroundAgentStateLockOwnerAlive,
1374
1383
  },
1375
1384
  );
1376
1385
  }
@@ -1458,57 +1467,65 @@ export async function deliverBackgroundNeedsInputNotification(
1458
1467
  * the only authority to start or continue a turn: a terminal, deleted,
1459
1468
  * corrupt, differently-generated or already-owned record is never revived.
1460
1469
  */
1461
- export function claimBackgroundAgentHeartbeat(id, heartbeat = {}) {
1470
+ export function claimBackgroundAgentHeartbeat(
1471
+ id,
1472
+ heartbeat = {},
1473
+ options = {},
1474
+ ) {
1462
1475
  const workerPid = Number(heartbeat.workerPid ?? heartbeat.pid);
1463
1476
  const workerGeneration = heartbeat.workerGeneration;
1464
1477
  if (!Number.isInteger(workerPid) || workerPid <= 0) {
1465
1478
  throw new Error(`Invalid background worker pid: ${heartbeat.workerPid}`);
1466
1479
  }
1467
- return mutateBackgroundAgentState(id, (current) => {
1468
- if (!current || current.status !== "running") return null;
1469
- if (current.stopRequestedAt) return null;
1470
- if (
1471
- current.workerGeneration &&
1472
- current.workerGeneration !== workerGeneration
1473
- ) {
1474
- return null;
1475
- }
1476
- const claimedWorkerPid = Number(current.workerClaimedPid);
1477
- if (
1478
- current.workerClaimedPid != null &&
1479
- Number.isInteger(claimedWorkerPid) &&
1480
- claimedWorkerPid > 0 &&
1481
- claimedWorkerPid !== workerPid
1482
- ) {
1483
- return null;
1484
- }
1485
- const currentWorkerPid = Number(current.workerPid ?? current.pid);
1486
- if (
1487
- !current.workerGeneration &&
1488
- Number.isInteger(currentWorkerPid) &&
1489
- currentWorkerPid > 0 &&
1490
- currentWorkerPid !== workerPid &&
1491
- current.launchFinalizationUncertain !== true
1492
- ) {
1493
- return null;
1494
- }
1495
- return {
1496
- ...current,
1497
- ...heartbeat,
1498
- id,
1499
- pid: workerPid,
1500
- workerPid,
1501
- workerClaimedPid:
1480
+ return mutateBackgroundAgentState(
1481
+ id,
1482
+ (current) => {
1483
+ if (!current || current.status !== "running") return null;
1484
+ if (current.stopRequestedAt) return null;
1485
+ if (
1486
+ current.workerGeneration &&
1487
+ current.workerGeneration !== workerGeneration
1488
+ ) {
1489
+ return null;
1490
+ }
1491
+ const claimedWorkerPid = Number(current.workerClaimedPid);
1492
+ if (
1502
1493
  current.workerClaimedPid != null &&
1503
1494
  Number.isInteger(claimedWorkerPid) &&
1504
- claimedWorkerPid > 0
1505
- ? claimedWorkerPid
1506
- : workerPid,
1507
- workerClaimedAt: current.workerClaimedAt || Date.now(),
1508
- status: "running",
1509
- heartbeatAt: heartbeat.heartbeatAt ?? Date.now(),
1510
- };
1511
- });
1495
+ claimedWorkerPid > 0 &&
1496
+ claimedWorkerPid !== workerPid
1497
+ ) {
1498
+ return null;
1499
+ }
1500
+ const currentWorkerPid = Number(current.workerPid ?? current.pid);
1501
+ if (
1502
+ !current.workerGeneration &&
1503
+ Number.isInteger(currentWorkerPid) &&
1504
+ currentWorkerPid > 0 &&
1505
+ currentWorkerPid !== workerPid &&
1506
+ current.launchFinalizationUncertain !== true
1507
+ ) {
1508
+ return null;
1509
+ }
1510
+ return {
1511
+ ...current,
1512
+ ...heartbeat,
1513
+ id,
1514
+ pid: workerPid,
1515
+ workerPid,
1516
+ workerClaimedPid:
1517
+ current.workerClaimedPid != null &&
1518
+ Number.isInteger(claimedWorkerPid) &&
1519
+ claimedWorkerPid > 0
1520
+ ? claimedWorkerPid
1521
+ : workerPid,
1522
+ workerClaimedAt: current.workerClaimedAt || Date.now(),
1523
+ status: "running",
1524
+ heartbeatAt: heartbeat.heartbeatAt ?? Date.now(),
1525
+ };
1526
+ },
1527
+ options,
1528
+ );
1512
1529
  }
1513
1530
 
1514
1531
  function deleteBackgroundAgentState(id, predicate = () => true) {
@@ -2038,7 +2055,21 @@ export function effectiveBackgroundAgentState(state, options = {}) {
2038
2055
  return next;
2039
2056
  }
2040
2057
 
2041
- export function listBackgroundAgents(options = {}) {
2058
+ function backgroundAgentStartedAt(state) {
2059
+ const startedAt = Number(state?.startedAt);
2060
+ return Number.isFinite(startedAt) && startedAt >= 0 ? startedAt : 0;
2061
+ }
2062
+
2063
+ function compareBackgroundAgentListEntries(a, b) {
2064
+ const newestFirst = backgroundAgentStartedAt(b) - backgroundAgentStartedAt(a);
2065
+ if (newestFirst !== 0) return newestFirst;
2066
+ return String(a.id).localeCompare(String(b.id));
2067
+ }
2068
+
2069
+ function listBackgroundAgentEntries(
2070
+ options = {},
2071
+ { stableTieBreak = false } = {},
2072
+ ) {
2042
2073
  return (
2043
2074
  readdirSync(backgroundAgentsDir())
2044
2075
  .filter((name) => name.endsWith(".json") && !name.includes(".job."))
@@ -2046,7 +2077,11 @@ export function listBackgroundAgents(options = {}) {
2046
2077
  .filter(Boolean)
2047
2078
  .map((state) => effectiveBackgroundAgentState(state, options))
2048
2079
  .filter((state) => options.all || state.status === "running")
2049
- .sort((a, b) => (b.startedAt || 0) - (a.startedAt || 0))
2080
+ .sort(
2081
+ stableTieBreak
2082
+ ? compareBackgroundAgentListEntries
2083
+ : (a, b) => (b.startedAt || 0) - (a.startedAt || 0),
2084
+ )
2050
2085
  // Display-only enrichment: attach the canonical unified lifecycle state.
2051
2086
  // This is a list feed (never written back to disk), so the derived field
2052
2087
  // cannot leak into the on-disk schema the way it would in the mutate paths.
@@ -2054,6 +2089,118 @@ export function listBackgroundAgents(options = {}) {
2054
2089
  );
2055
2090
  }
2056
2091
 
2092
+ function normalizeBackgroundAgentPageLimit(value) {
2093
+ if (value === undefined || value === null) {
2094
+ return DEFAULT_BACKGROUND_AGENT_PAGE_LIMIT;
2095
+ }
2096
+ const limit =
2097
+ typeof value === "string" && /^\d+$/u.test(value) ? Number(value) : value;
2098
+ if (
2099
+ !Number.isSafeInteger(limit) ||
2100
+ limit < 1 ||
2101
+ limit > MAX_BACKGROUND_AGENT_PAGE_LIMIT
2102
+ ) {
2103
+ throw new TypeError(
2104
+ `background agent page limit must be an integer from 1 to ${MAX_BACKGROUND_AGENT_PAGE_LIMIT}`,
2105
+ );
2106
+ }
2107
+ return limit;
2108
+ }
2109
+
2110
+ function encodeBackgroundAgentPageCursor(state, all) {
2111
+ const payload = JSON.stringify({
2112
+ version: 1,
2113
+ all: all === true,
2114
+ startedAt: backgroundAgentStartedAt(state),
2115
+ id: safeId(state?.id),
2116
+ });
2117
+ return `bg1.${Buffer.from(payload).toString("base64url")}`;
2118
+ }
2119
+
2120
+ function decodeBackgroundAgentPageCursor(value) {
2121
+ if (value === undefined || value === null) return null;
2122
+ if (
2123
+ typeof value !== "string" ||
2124
+ value.length > 768 ||
2125
+ !/^bg1\.[A-Za-z0-9_-]+$/u.test(value)
2126
+ ) {
2127
+ throw new TypeError("background agent page cursor is invalid");
2128
+ }
2129
+ let payload;
2130
+ try {
2131
+ const encoded = value.slice(4);
2132
+ const decoded = Buffer.from(encoded, "base64url");
2133
+ if (decoded.toString("base64url") !== encoded) {
2134
+ throw new Error("cursor encoding is not canonical");
2135
+ }
2136
+ payload = JSON.parse(decoded.toString("utf8"));
2137
+ } catch {
2138
+ throw new TypeError("background agent page cursor is invalid");
2139
+ }
2140
+ if (
2141
+ !payload ||
2142
+ typeof payload !== "object" ||
2143
+ Array.isArray(payload) ||
2144
+ Object.keys(payload).length !== 4 ||
2145
+ payload.version !== 1 ||
2146
+ typeof payload.all !== "boolean" ||
2147
+ !Number.isSafeInteger(payload.startedAt) ||
2148
+ payload.startedAt < 0
2149
+ ) {
2150
+ throw new TypeError("background agent page cursor is invalid");
2151
+ }
2152
+ let id;
2153
+ try {
2154
+ id = safeId(payload.id);
2155
+ } catch {
2156
+ throw new TypeError("background agent page cursor is invalid");
2157
+ }
2158
+ return { all: payload.all, startedAt: payload.startedAt, id };
2159
+ }
2160
+
2161
+ function isBackgroundAgentAfterCursor(state, cursor) {
2162
+ const startedAt = backgroundAgentStartedAt(state);
2163
+ if (startedAt !== cursor.startedAt) return startedAt < cursor.startedAt;
2164
+ return String(state.id).localeCompare(cursor.id) > 0;
2165
+ }
2166
+
2167
+ /**
2168
+ * Return one stable, bounded page from the newest-first background session
2169
+ * feed. The cursor is an opaque ordering fence, not an authority token.
2170
+ *
2171
+ * State files are still read to preserve the current authoritative lifecycle
2172
+ * reconciliation. A future read-only index may reduce that I/O, but this API
2173
+ * immediately bounds the object graph and protocol response retained by a
2174
+ * caller.
2175
+ */
2176
+ export function listBackgroundAgentsPage(options = {}) {
2177
+ const limit = normalizeBackgroundAgentPageLimit(options.limit);
2178
+ const cursor = decodeBackgroundAgentPageCursor(options.cursor);
2179
+ if (cursor && cursor.all !== (options.all === true)) {
2180
+ throw new TypeError(
2181
+ "background agent page cursor does not match the requested filter",
2182
+ );
2183
+ }
2184
+ const eligible = cursor
2185
+ ? listBackgroundAgentEntries(options, { stableTieBreak: true }).filter(
2186
+ (state) => isBackgroundAgentAfterCursor(state, cursor),
2187
+ )
2188
+ : listBackgroundAgentEntries(options, { stableTieBreak: true });
2189
+ const sessions = eligible.slice(0, limit);
2190
+ const nextCursor =
2191
+ eligible.length > sessions.length
2192
+ ? encodeBackgroundAgentPageCursor(sessions.at(-1), options.all)
2193
+ : null;
2194
+ return Object.freeze({
2195
+ sessions: Object.freeze(sessions),
2196
+ nextCursor,
2197
+ });
2198
+ }
2199
+
2200
+ export function listBackgroundAgents(options = {}) {
2201
+ return listBackgroundAgentEntries(options);
2202
+ }
2203
+
2057
2204
  function sleepSyncMs(ms) {
2058
2205
  try {
2059
2206
  Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
@@ -66,18 +66,40 @@ function projectNotification(notification) {
66
66
  };
67
67
  }
68
68
  if (method === "turn/completed" || method === "turn/failed") {
69
- const failed = method === "turn/failed";
69
+ const turn = params.turn || {};
70
+ const reportedStatus =
71
+ method === "turn/failed" ? "failed" : String(turn.status || "");
72
+ const terminalStatus = ["completed", "failed", "interrupted"].includes(
73
+ reportedStatus,
74
+ )
75
+ ? reportedStatus
76
+ : "failed";
77
+ const protocolError =
78
+ terminalStatus === "failed" && reportedStatus !== "failed"
79
+ ? {
80
+ code: "CC_CODEX_APP_SERVER_INVALID_TERMINAL_STATUS",
81
+ message:
82
+ "Codex App Server emitted turn/completed without a supported terminal status",
83
+ }
84
+ : null;
85
+ const terminalError =
86
+ terminalStatus === "failed"
87
+ ? turn.error ||
88
+ params.error ||
89
+ protocolError ||
90
+ "Codex App Server turn failed"
91
+ : turn.error || params.error || null;
70
92
  return {
71
93
  method: "turn/completed",
72
94
  params: {
73
95
  turn: {
74
- ...params.turn,
75
- status: failed ? "failed" : "completed",
96
+ ...turn,
97
+ status: terminalStatus,
76
98
  },
77
99
  },
78
- terminal: failed ? "failed" : "completed",
79
- error: failed ? params.error || "Codex App Server turn failed" : null,
80
- usage: params.usage || null,
100
+ terminal: terminalStatus,
101
+ error: terminalError,
102
+ usage: params.usage || turn.usage || null,
81
103
  };
82
104
  }
83
105
  return { unknownMethod: String(method || "<missing>") };
@@ -138,6 +160,10 @@ export class CodexAppServerAdapter extends EventEmitter {
138
160
  const notifications = [];
139
161
  const unknownMethods = new Set();
140
162
  let output = "";
163
+ let activeThreadId = threadId;
164
+ let submissionStarted = false;
165
+ let admissionObserved = false;
166
+ let observedTerminal = null;
141
167
  let terminalResolve;
142
168
  let terminalReject;
143
169
  const terminal = new Promise((resolve, reject) => {
@@ -162,16 +188,38 @@ export class CodexAppServerAdapter extends EventEmitter {
162
188
  notifications.push(projected);
163
189
  this.emit("notification", projected);
164
190
  }
191
+ if (
192
+ submissionStarted &&
193
+ (projected.method === "turn/started" ||
194
+ projected.method?.startsWith("item/") ||
195
+ Boolean(projected.terminal))
196
+ ) {
197
+ admissionObserved = true;
198
+ }
165
199
  if (projected.output) output = projected.output;
166
- if (projected.terminal) terminalResolve(projected);
200
+ if (projected.terminal) {
201
+ observedTerminal = projected;
202
+ terminalResolve(projected);
203
+ }
167
204
  };
168
205
  this.client.on?.("notification", onNotification);
169
- let admitted = false;
206
+ const buildResult = (terminalEvent) =>
207
+ Object.freeze({
208
+ protocol: CODEX_APP_SERVER_PROTOCOL,
209
+ threadId: activeThreadId,
210
+ terminal: terminalEvent.terminal,
211
+ output,
212
+ error: terminalEvent.error || null,
213
+ usage: terminalEvent.usage || null,
214
+ notifications: Object.freeze(notifications),
215
+ unknownMethods: Object.freeze([...unknownMethods].sort()),
216
+ fallback: false,
217
+ authoritative: false,
218
+ });
170
219
  try {
171
220
  if (!this.client.running && typeof this.client.start === "function") {
172
221
  await this.client.start();
173
222
  }
174
- let activeThreadId = threadId;
175
223
  if (!activeThreadId) {
176
224
  const started = await this.client.request("thread/start", {
177
225
  ephemeral: false,
@@ -185,31 +233,37 @@ export class CodexAppServerAdapter extends EventEmitter {
185
233
  "Codex App Server did not return a thread identity",
186
234
  );
187
235
  }
236
+ // Once turn/start begins, a rejected client promise cannot prove that
237
+ // the server did not accept the input. Never launch a second execution
238
+ // path from that ambiguous state.
239
+ submissionStarted = true;
188
240
  const started = await this.client.request("turn/start", {
189
241
  threadId: activeThreadId,
190
242
  input: [{ type: "text", text: String(prompt || "") }],
191
243
  });
192
- admitted = true;
193
- const terminalEvent =
194
- started?.turn?.status === "completed"
195
- ? { terminal: "completed", usage: started.turn.usage || null }
196
- : await terminal;
197
- return Object.freeze({
198
- protocol: CODEX_APP_SERVER_PROTOCOL,
199
- threadId: activeThreadId,
200
- terminal: terminalEvent.terminal,
201
- output,
202
- error: terminalEvent.error || null,
203
- usage: terminalEvent.usage || null,
204
- notifications: Object.freeze(notifications),
205
- unknownMethods: Object.freeze([...unknownMethods].sort()),
206
- fallback: false,
207
- authoritative: false,
208
- });
244
+ admissionObserved = true;
245
+ const initialStatus = String(started?.turn?.status || "");
246
+ const terminalEvent = ["completed", "failed", "interrupted"].includes(
247
+ initialStatus,
248
+ )
249
+ ? projectNotification({
250
+ method: "turn/completed",
251
+ params: { turn: started.turn, usage: started.turn.usage || null },
252
+ })
253
+ : await terminal;
254
+ return buildResult(terminalEvent);
209
255
  } catch (error) {
210
- if (!admitted) {
256
+ if (!submissionStarted) {
211
257
  return this._fallback(prompt, options, error?.code || "startup_failed");
212
258
  }
259
+ if (observedTerminal) return buildResult(observedTerminal);
260
+ if (!admissionObserved) {
261
+ throw adapterError(
262
+ "CC_CODEX_APP_SERVER_SUBMISSION_UNKNOWN",
263
+ "Codex App Server turn submission outcome is unknown; fallback was suppressed to prevent duplicate effects",
264
+ { cause: error },
265
+ );
266
+ }
213
267
  throw adapterError(
214
268
  "CC_CODEX_APP_SERVER_FAILED_AFTER_ADMISSION",
215
269
  "Codex App Server failed after the turn was admitted; fallback was suppressed to prevent duplicate effects",