@openclaw/codex 2026.5.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (599) hide show
  1. package/dist/.boundary-tsc.stamp +1 -0
  2. package/dist/.boundary-tsc.tsbuildinfo +1 -0
  3. package/harness.ts +59 -0
  4. package/index.test.ts +106 -0
  5. package/index.ts +41 -0
  6. package/media-understanding-provider.test.ts +300 -0
  7. package/media-understanding-provider.ts +369 -0
  8. package/openclaw.plugin.json +299 -0
  9. package/package.json +43 -0
  10. package/prompt-overlay-runtime-contract.test.ts +45 -0
  11. package/prompt-overlay.ts +19 -0
  12. package/provider-catalog.ts +83 -0
  13. package/provider-discovery.ts +45 -0
  14. package/provider.test.ts +350 -0
  15. package/provider.ts +223 -0
  16. package/src/app-server/approval-bridge.test.ts +838 -0
  17. package/src/app-server/approval-bridge.ts +731 -0
  18. package/src/app-server/auth-bridge.test.ts +922 -0
  19. package/src/app-server/auth-bridge.ts +345 -0
  20. package/src/app-server/auth-profile-runtime-contract.test.ts +217 -0
  21. package/src/app-server/capabilities.ts +27 -0
  22. package/src/app-server/client-factory.ts +30 -0
  23. package/src/app-server/client.test.ts +414 -0
  24. package/src/app-server/client.ts +575 -0
  25. package/src/app-server/compact.test.ts +452 -0
  26. package/src/app-server/compact.ts +374 -0
  27. package/src/app-server/computer-use.test.ts +790 -0
  28. package/src/app-server/computer-use.ts +682 -0
  29. package/src/app-server/config.test.ts +431 -0
  30. package/src/app-server/config.ts +486 -0
  31. package/src/app-server/context-engine-projection.test.ts +104 -0
  32. package/src/app-server/context-engine-projection.ts +147 -0
  33. package/src/app-server/delivery-no-reply-runtime-contract.test.ts +80 -0
  34. package/src/app-server/dynamic-tools.test.ts +781 -0
  35. package/src/app-server/dynamic-tools.ts +345 -0
  36. package/src/app-server/elicitation-bridge.test.ts +625 -0
  37. package/src/app-server/elicitation-bridge.ts +555 -0
  38. package/src/app-server/event-projector.test.ts +921 -0
  39. package/src/app-server/event-projector.ts +1160 -0
  40. package/src/app-server/managed-binary.test.ts +95 -0
  41. package/src/app-server/managed-binary.ts +121 -0
  42. package/src/app-server/models.test.ts +242 -0
  43. package/src/app-server/models.ts +168 -0
  44. package/src/app-server/native-hook-relay.test.ts +143 -0
  45. package/src/app-server/native-hook-relay.ts +64 -0
  46. package/src/app-server/openclaw-owned-tool-runtime-contract.test.ts +415 -0
  47. package/src/app-server/outcome-fallback-runtime-contract.test.ts +352 -0
  48. package/src/app-server/plugin-approval-roundtrip.ts +122 -0
  49. package/src/app-server/protocol-generated/json/DynamicToolCallParams.json +24 -0
  50. package/src/app-server/protocol-generated/json/v2/ErrorNotification.json +162 -0
  51. package/src/app-server/protocol-generated/json/v2/GetAccountResponse.json +86 -0
  52. package/src/app-server/protocol-generated/json/v2/ModelListResponse.json +162 -0
  53. package/src/app-server/protocol-generated/json/v2/ThreadResumeResponse.json +1994 -0
  54. package/src/app-server/protocol-generated/json/v2/ThreadStartResponse.json +1994 -0
  55. package/src/app-server/protocol-generated/json/v2/TurnCompletedNotification.json +1243 -0
  56. package/src/app-server/protocol-generated/json/v2/TurnStartResponse.json +1240 -0
  57. package/src/app-server/protocol-generated/typescript/AbsolutePathBuf.ts +14 -0
  58. package/src/app-server/protocol-generated/typescript/AgentPath.ts +5 -0
  59. package/src/app-server/protocol-generated/typescript/ApplyPatchApprovalParams.ts +24 -0
  60. package/src/app-server/protocol-generated/typescript/ApplyPatchApprovalResponse.ts +6 -0
  61. package/src/app-server/protocol-generated/typescript/AuthMode.ts +8 -0
  62. package/src/app-server/protocol-generated/typescript/ClientInfo.ts +5 -0
  63. package/src/app-server/protocol-generated/typescript/ClientNotification.ts +5 -0
  64. package/src/app-server/protocol-generated/typescript/ClientRequest.ts +165 -0
  65. package/src/app-server/protocol-generated/typescript/CollaborationMode.ts +10 -0
  66. package/src/app-server/protocol-generated/typescript/ContentItem.ts +9 -0
  67. package/src/app-server/protocol-generated/typescript/ConversationGitInfo.ts +9 -0
  68. package/src/app-server/protocol-generated/typescript/ConversationSummary.ts +19 -0
  69. package/src/app-server/protocol-generated/typescript/ExecCommandApprovalParams.ts +22 -0
  70. package/src/app-server/protocol-generated/typescript/ExecCommandApprovalResponse.ts +6 -0
  71. package/src/app-server/protocol-generated/typescript/ExecPolicyAmendment.ts +12 -0
  72. package/src/app-server/protocol-generated/typescript/FileChange.ts +8 -0
  73. package/src/app-server/protocol-generated/typescript/ForcedLoginMethod.ts +5 -0
  74. package/src/app-server/protocol-generated/typescript/FunctionCallOutputBody.ts +6 -0
  75. package/src/app-server/protocol-generated/typescript/FunctionCallOutputContentItem.ts +12 -0
  76. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchMatchType.ts +5 -0
  77. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchParams.ts +9 -0
  78. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchResponse.ts +6 -0
  79. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchResult.ts +16 -0
  80. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchSessionCompletedNotification.ts +5 -0
  81. package/src/app-server/protocol-generated/typescript/FuzzyFileSearchSessionUpdatedNotification.ts +10 -0
  82. package/src/app-server/protocol-generated/typescript/GetAuthStatusParams.ts +5 -0
  83. package/src/app-server/protocol-generated/typescript/GetAuthStatusResponse.ts +10 -0
  84. package/src/app-server/protocol-generated/typescript/GetConversationSummaryParams.ts +6 -0
  85. package/src/app-server/protocol-generated/typescript/GetConversationSummaryResponse.ts +6 -0
  86. package/src/app-server/protocol-generated/typescript/GhostCommit.ts +13 -0
  87. package/src/app-server/protocol-generated/typescript/GitDiffToRemoteParams.ts +5 -0
  88. package/src/app-server/protocol-generated/typescript/GitDiffToRemoteResponse.ts +6 -0
  89. package/src/app-server/protocol-generated/typescript/GitSha.ts +5 -0
  90. package/src/app-server/protocol-generated/typescript/ImageDetail.ts +5 -0
  91. package/src/app-server/protocol-generated/typescript/InitializeCapabilities.ts +18 -0
  92. package/src/app-server/protocol-generated/typescript/InitializeParams.ts +10 -0
  93. package/src/app-server/protocol-generated/typescript/InitializeResponse.ts +22 -0
  94. package/src/app-server/protocol-generated/typescript/InputModality.ts +8 -0
  95. package/src/app-server/protocol-generated/typescript/LocalShellAction.ts +6 -0
  96. package/src/app-server/protocol-generated/typescript/LocalShellExecAction.ts +11 -0
  97. package/src/app-server/protocol-generated/typescript/LocalShellStatus.ts +5 -0
  98. package/src/app-server/protocol-generated/typescript/MessagePhase.ts +11 -0
  99. package/src/app-server/protocol-generated/typescript/ModeKind.ts +8 -0
  100. package/src/app-server/protocol-generated/typescript/NetworkPolicyAmendment.ts +6 -0
  101. package/src/app-server/protocol-generated/typescript/NetworkPolicyRuleAction.ts +5 -0
  102. package/src/app-server/protocol-generated/typescript/ParsedCommand.ts +20 -0
  103. package/src/app-server/protocol-generated/typescript/Personality.ts +5 -0
  104. package/src/app-server/protocol-generated/typescript/PlanType.ts +17 -0
  105. package/src/app-server/protocol-generated/typescript/RealtimeConversationVersion.ts +5 -0
  106. package/src/app-server/protocol-generated/typescript/RealtimeOutputModality.ts +5 -0
  107. package/src/app-server/protocol-generated/typescript/RealtimeVoice.ts +24 -0
  108. package/src/app-server/protocol-generated/typescript/RealtimeVoicesList.ts +11 -0
  109. package/src/app-server/protocol-generated/typescript/ReasoningEffort.ts +8 -0
  110. package/src/app-server/protocol-generated/typescript/ReasoningItemContent.ts +7 -0
  111. package/src/app-server/protocol-generated/typescript/ReasoningItemReasoningSummary.ts +5 -0
  112. package/src/app-server/protocol-generated/typescript/ReasoningSummary.ts +10 -0
  113. package/src/app-server/protocol-generated/typescript/RequestId.ts +5 -0
  114. package/src/app-server/protocol-generated/typescript/Resource.ts +19 -0
  115. package/src/app-server/protocol-generated/typescript/ResourceContent.ts +27 -0
  116. package/src/app-server/protocol-generated/typescript/ResourceTemplate.ts +16 -0
  117. package/src/app-server/protocol-generated/typescript/ResponseItem.ts +70 -0
  118. package/src/app-server/protocol-generated/typescript/ReviewDecision.ts +17 -0
  119. package/src/app-server/protocol-generated/typescript/ServerNotification.ts +144 -0
  120. package/src/app-server/protocol-generated/typescript/ServerRequest.ts +47 -0
  121. package/src/app-server/protocol-generated/typescript/ServiceTier.ts +5 -0
  122. package/src/app-server/protocol-generated/typescript/SessionSource.ts +13 -0
  123. package/src/app-server/protocol-generated/typescript/Settings.ts +13 -0
  124. package/src/app-server/protocol-generated/typescript/SubAgentSource.ts +20 -0
  125. package/src/app-server/protocol-generated/typescript/ThreadId.ts +5 -0
  126. package/src/app-server/protocol-generated/typescript/ThreadMemoryMode.ts +5 -0
  127. package/src/app-server/protocol-generated/typescript/Tool.ts +18 -0
  128. package/src/app-server/protocol-generated/typescript/Verbosity.ts +9 -0
  129. package/src/app-server/protocol-generated/typescript/WebSearchAction.ts +9 -0
  130. package/src/app-server/protocol-generated/typescript/WebSearchContextSize.ts +5 -0
  131. package/src/app-server/protocol-generated/typescript/WebSearchLocation.ts +10 -0
  132. package/src/app-server/protocol-generated/typescript/WebSearchMode.ts +5 -0
  133. package/src/app-server/protocol-generated/typescript/WebSearchToolConfig.ts +11 -0
  134. package/src/app-server/protocol-generated/typescript/index.ts +80 -0
  135. package/src/app-server/protocol-generated/typescript/serde_json/JsonValue.ts +11 -0
  136. package/src/app-server/protocol-generated/typescript/v2/Account.ts +9 -0
  137. package/src/app-server/protocol-generated/typescript/v2/AccountLoginCompletedNotification.ts +9 -0
  138. package/src/app-server/protocol-generated/typescript/v2/AccountRateLimitsUpdatedNotification.ts +6 -0
  139. package/src/app-server/protocol-generated/typescript/v2/AccountUpdatedNotification.ts +7 -0
  140. package/src/app-server/protocol-generated/typescript/v2/AddCreditsNudgeCreditType.ts +5 -0
  141. package/src/app-server/protocol-generated/typescript/v2/AddCreditsNudgeEmailStatus.ts +5 -0
  142. package/src/app-server/protocol-generated/typescript/v2/AdditionalFileSystemPermissions.ts +18 -0
  143. package/src/app-server/protocol-generated/typescript/v2/AdditionalNetworkPermissions.ts +5 -0
  144. package/src/app-server/protocol-generated/typescript/v2/AdditionalPermissionProfile.ts +10 -0
  145. package/src/app-server/protocol-generated/typescript/v2/AgentMessageDeltaNotification.ts +10 -0
  146. package/src/app-server/protocol-generated/typescript/v2/AnalyticsConfig.ts +14 -0
  147. package/src/app-server/protocol-generated/typescript/v2/AppBranding.ts +15 -0
  148. package/src/app-server/protocol-generated/typescript/v2/AppInfo.ts +32 -0
  149. package/src/app-server/protocol-generated/typescript/v2/AppListUpdatedNotification.ts +9 -0
  150. package/src/app-server/protocol-generated/typescript/v2/AppMetadata.ts +20 -0
  151. package/src/app-server/protocol-generated/typescript/v2/AppReview.ts +5 -0
  152. package/src/app-server/protocol-generated/typescript/v2/AppScreenshot.ts +5 -0
  153. package/src/app-server/protocol-generated/typescript/v2/AppSummary.ts +14 -0
  154. package/src/app-server/protocol-generated/typescript/v2/AppToolApproval.ts +5 -0
  155. package/src/app-server/protocol-generated/typescript/v2/AppToolsConfig.ts +8 -0
  156. package/src/app-server/protocol-generated/typescript/v2/ApprovalsReviewer.ts +12 -0
  157. package/src/app-server/protocol-generated/typescript/v2/AppsConfig.ts +17 -0
  158. package/src/app-server/protocol-generated/typescript/v2/AppsDefaultConfig.ts +9 -0
  159. package/src/app-server/protocol-generated/typescript/v2/AppsListParams.ts +25 -0
  160. package/src/app-server/protocol-generated/typescript/v2/AppsListResponse.ts +16 -0
  161. package/src/app-server/protocol-generated/typescript/v2/AskForApproval.ts +18 -0
  162. package/src/app-server/protocol-generated/typescript/v2/AutoReviewDecisionSource.ts +8 -0
  163. package/src/app-server/protocol-generated/typescript/v2/ByteRange.ts +5 -0
  164. package/src/app-server/protocol-generated/typescript/v2/CancelLoginAccountParams.ts +5 -0
  165. package/src/app-server/protocol-generated/typescript/v2/CancelLoginAccountResponse.ts +6 -0
  166. package/src/app-server/protocol-generated/typescript/v2/CancelLoginAccountStatus.ts +5 -0
  167. package/src/app-server/protocol-generated/typescript/v2/ChatgptAuthTokensRefreshParams.ts +18 -0
  168. package/src/app-server/protocol-generated/typescript/v2/ChatgptAuthTokensRefreshReason.ts +5 -0
  169. package/src/app-server/protocol-generated/typescript/v2/ChatgptAuthTokensRefreshResponse.ts +9 -0
  170. package/src/app-server/protocol-generated/typescript/v2/CodexErrorInfo.ts +27 -0
  171. package/src/app-server/protocol-generated/typescript/v2/CollabAgentState.ts +6 -0
  172. package/src/app-server/protocol-generated/typescript/v2/CollabAgentStatus.ts +12 -0
  173. package/src/app-server/protocol-generated/typescript/v2/CollabAgentTool.ts +5 -0
  174. package/src/app-server/protocol-generated/typescript/v2/CollabAgentToolCallStatus.ts +5 -0
  175. package/src/app-server/protocol-generated/typescript/v2/CollaborationModeMask.ts +15 -0
  176. package/src/app-server/protocol-generated/typescript/v2/CommandAction.ts +10 -0
  177. package/src/app-server/protocol-generated/typescript/v2/CommandExecOutputDeltaNotification.ts +31 -0
  178. package/src/app-server/protocol-generated/typescript/v2/CommandExecOutputStream.ts +8 -0
  179. package/src/app-server/protocol-generated/typescript/v2/CommandExecParams.ts +107 -0
  180. package/src/app-server/protocol-generated/typescript/v2/CommandExecResizeParams.ts +19 -0
  181. package/src/app-server/protocol-generated/typescript/v2/CommandExecResizeResponse.ts +8 -0
  182. package/src/app-server/protocol-generated/typescript/v2/CommandExecResponse.ts +25 -0
  183. package/src/app-server/protocol-generated/typescript/v2/CommandExecTerminalSize.ts +17 -0
  184. package/src/app-server/protocol-generated/typescript/v2/CommandExecTerminateParams.ts +14 -0
  185. package/src/app-server/protocol-generated/typescript/v2/CommandExecTerminateResponse.ts +8 -0
  186. package/src/app-server/protocol-generated/typescript/v2/CommandExecWriteParams.ts +23 -0
  187. package/src/app-server/protocol-generated/typescript/v2/CommandExecWriteResponse.ts +8 -0
  188. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionApprovalDecision.ts +13 -0
  189. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionOutputDeltaNotification.ts +10 -0
  190. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionRequestApprovalParams.ts +62 -0
  191. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionRequestApprovalResponse.ts +8 -0
  192. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionSource.ts +9 -0
  193. package/src/app-server/protocol-generated/typescript/v2/CommandExecutionStatus.ts +5 -0
  194. package/src/app-server/protocol-generated/typescript/v2/Config.ts +54 -0
  195. package/src/app-server/protocol-generated/typescript/v2/ConfigBatchWriteParams.ts +17 -0
  196. package/src/app-server/protocol-generated/typescript/v2/ConfigEdit.ts +7 -0
  197. package/src/app-server/protocol-generated/typescript/v2/ConfigLayer.ts +12 -0
  198. package/src/app-server/protocol-generated/typescript/v2/ConfigLayerMetadata.ts +6 -0
  199. package/src/app-server/protocol-generated/typescript/v2/ConfigLayerSource.ts +27 -0
  200. package/src/app-server/protocol-generated/typescript/v2/ConfigReadParams.ts +13 -0
  201. package/src/app-server/protocol-generated/typescript/v2/ConfigReadResponse.ts +12 -0
  202. package/src/app-server/protocol-generated/typescript/v2/ConfigRequirements.ts +15 -0
  203. package/src/app-server/protocol-generated/typescript/v2/ConfigRequirementsReadResponse.ts +11 -0
  204. package/src/app-server/protocol-generated/typescript/v2/ConfigValueWriteParams.ts +16 -0
  205. package/src/app-server/protocol-generated/typescript/v2/ConfigWarningNotification.ts +23 -0
  206. package/src/app-server/protocol-generated/typescript/v2/ConfigWriteResponse.ts +16 -0
  207. package/src/app-server/protocol-generated/typescript/v2/ConfiguredHookHandler.ts +14 -0
  208. package/src/app-server/protocol-generated/typescript/v2/ConfiguredHookMatcherGroup.ts +9 -0
  209. package/src/app-server/protocol-generated/typescript/v2/ContextCompactedNotification.ts +8 -0
  210. package/src/app-server/protocol-generated/typescript/v2/CreditsSnapshot.ts +5 -0
  211. package/src/app-server/protocol-generated/typescript/v2/DeprecationNoticeNotification.ts +14 -0
  212. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyAlgorithm.ts +8 -0
  213. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyCreateParams.ts +16 -0
  214. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyCreateResponse.ts +18 -0
  215. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyProtectionClass.ts +11 -0
  216. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyProtectionPolicy.ts +8 -0
  217. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyPublicParams.ts +8 -0
  218. package/src/app-server/protocol-generated/typescript/v2/DeviceKeyPublicResponse.ts +18 -0
  219. package/src/app-server/protocol-generated/typescript/v2/DeviceKeySignParams.ts +9 -0
  220. package/src/app-server/protocol-generated/typescript/v2/DeviceKeySignPayload.ts +68 -0
  221. package/src/app-server/protocol-generated/typescript/v2/DeviceKeySignResponse.ts +20 -0
  222. package/src/app-server/protocol-generated/typescript/v2/DynamicToolCallOutputContentItem.ts +7 -0
  223. package/src/app-server/protocol-generated/typescript/v2/DynamicToolCallParams.ts +13 -0
  224. package/src/app-server/protocol-generated/typescript/v2/DynamicToolCallResponse.ts +9 -0
  225. package/src/app-server/protocol-generated/typescript/v2/DynamicToolCallStatus.ts +5 -0
  226. package/src/app-server/protocol-generated/typescript/v2/DynamicToolSpec.ts +12 -0
  227. package/src/app-server/protocol-generated/typescript/v2/ErrorNotification.ts +11 -0
  228. package/src/app-server/protocol-generated/typescript/v2/ExecPolicyAmendment.ts +5 -0
  229. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeature.ts +38 -0
  230. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeatureEnablementSetParams.ts +13 -0
  231. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeatureEnablementSetResponse.ts +10 -0
  232. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeatureListParams.ts +14 -0
  233. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeatureListResponse.ts +13 -0
  234. package/src/app-server/protocol-generated/typescript/v2/ExperimentalFeatureStage.ts +10 -0
  235. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigDetectParams.ts +14 -0
  236. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigDetectResponse.ts +6 -0
  237. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigImportCompletedNotification.ts +5 -0
  238. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigImportParams.ts +8 -0
  239. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigImportResponse.ts +5 -0
  240. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigMigrationItem.ts +15 -0
  241. package/src/app-server/protocol-generated/typescript/v2/ExternalAgentConfigMigrationItemType.ts +10 -0
  242. package/src/app-server/protocol-generated/typescript/v2/FeedbackUploadParams.ts +12 -0
  243. package/src/app-server/protocol-generated/typescript/v2/FeedbackUploadResponse.ts +5 -0
  244. package/src/app-server/protocol-generated/typescript/v2/FileChangeApprovalDecision.ts +5 -0
  245. package/src/app-server/protocol-generated/typescript/v2/FileChangeOutputDeltaNotification.ts +10 -0
  246. package/src/app-server/protocol-generated/typescript/v2/FileChangePatchUpdatedNotification.ts +11 -0
  247. package/src/app-server/protocol-generated/typescript/v2/FileChangeRequestApprovalParams.ts +18 -0
  248. package/src/app-server/protocol-generated/typescript/v2/FileChangeRequestApprovalResponse.ts +6 -0
  249. package/src/app-server/protocol-generated/typescript/v2/FileSystemAccessMode.ts +5 -0
  250. package/src/app-server/protocol-generated/typescript/v2/FileSystemPath.ts +10 -0
  251. package/src/app-server/protocol-generated/typescript/v2/FileSystemSandboxEntry.ts +7 -0
  252. package/src/app-server/protocol-generated/typescript/v2/FileSystemSpecialPath.ts +12 -0
  253. package/src/app-server/protocol-generated/typescript/v2/FileUpdateChange.ts +6 -0
  254. package/src/app-server/protocol-generated/typescript/v2/FsChangedNotification.ts +18 -0
  255. package/src/app-server/protocol-generated/typescript/v2/FsCopyParams.ts +22 -0
  256. package/src/app-server/protocol-generated/typescript/v2/FsCopyResponse.ts +8 -0
  257. package/src/app-server/protocol-generated/typescript/v2/FsCreateDirectoryParams.ts +18 -0
  258. package/src/app-server/protocol-generated/typescript/v2/FsCreateDirectoryResponse.ts +8 -0
  259. package/src/app-server/protocol-generated/typescript/v2/FsGetMetadataParams.ts +14 -0
  260. package/src/app-server/protocol-generated/typescript/v2/FsGetMetadataResponse.ts +29 -0
  261. package/src/app-server/protocol-generated/typescript/v2/FsReadDirectoryEntry.ts +21 -0
  262. package/src/app-server/protocol-generated/typescript/v2/FsReadDirectoryParams.ts +14 -0
  263. package/src/app-server/protocol-generated/typescript/v2/FsReadDirectoryResponse.ts +14 -0
  264. package/src/app-server/protocol-generated/typescript/v2/FsReadFileParams.ts +14 -0
  265. package/src/app-server/protocol-generated/typescript/v2/FsReadFileResponse.ts +13 -0
  266. package/src/app-server/protocol-generated/typescript/v2/FsRemoveParams.ts +22 -0
  267. package/src/app-server/protocol-generated/typescript/v2/FsRemoveResponse.ts +8 -0
  268. package/src/app-server/protocol-generated/typescript/v2/FsUnwatchParams.ts +13 -0
  269. package/src/app-server/protocol-generated/typescript/v2/FsUnwatchResponse.ts +8 -0
  270. package/src/app-server/protocol-generated/typescript/v2/FsWatchParams.ts +18 -0
  271. package/src/app-server/protocol-generated/typescript/v2/FsWatchResponse.ts +14 -0
  272. package/src/app-server/protocol-generated/typescript/v2/FsWriteFileParams.ts +18 -0
  273. package/src/app-server/protocol-generated/typescript/v2/FsWriteFileResponse.ts +8 -0
  274. package/src/app-server/protocol-generated/typescript/v2/GetAccountParams.ts +14 -0
  275. package/src/app-server/protocol-generated/typescript/v2/GetAccountRateLimitsResponse.ts +15 -0
  276. package/src/app-server/protocol-generated/typescript/v2/GetAccountResponse.ts +6 -0
  277. package/src/app-server/protocol-generated/typescript/v2/GitInfo.ts +5 -0
  278. package/src/app-server/protocol-generated/typescript/v2/GrantedPermissionProfile.ts +10 -0
  279. package/src/app-server/protocol-generated/typescript/v2/GuardianApprovalReview.ts +18 -0
  280. package/src/app-server/protocol-generated/typescript/v2/GuardianApprovalReviewAction.ts +34 -0
  281. package/src/app-server/protocol-generated/typescript/v2/GuardianApprovalReviewStatus.ts +13 -0
  282. package/src/app-server/protocol-generated/typescript/v2/GuardianCommandSource.ts +5 -0
  283. package/src/app-server/protocol-generated/typescript/v2/GuardianRiskLevel.ts +8 -0
  284. package/src/app-server/protocol-generated/typescript/v2/GuardianUserAuthorization.ts +8 -0
  285. package/src/app-server/protocol-generated/typescript/v2/GuardianWarningNotification.ts +14 -0
  286. package/src/app-server/protocol-generated/typescript/v2/HookCompletedNotification.ts +10 -0
  287. package/src/app-server/protocol-generated/typescript/v2/HookEventName.ts +11 -0
  288. package/src/app-server/protocol-generated/typescript/v2/HookExecutionMode.ts +5 -0
  289. package/src/app-server/protocol-generated/typescript/v2/HookHandlerType.ts +5 -0
  290. package/src/app-server/protocol-generated/typescript/v2/HookOutputEntry.ts +6 -0
  291. package/src/app-server/protocol-generated/typescript/v2/HookOutputEntryKind.ts +5 -0
  292. package/src/app-server/protocol-generated/typescript/v2/HookPromptFragment.ts +5 -0
  293. package/src/app-server/protocol-generated/typescript/v2/HookRunStatus.ts +5 -0
  294. package/src/app-server/protocol-generated/typescript/v2/HookRunSummary.ts +28 -0
  295. package/src/app-server/protocol-generated/typescript/v2/HookScope.ts +5 -0
  296. package/src/app-server/protocol-generated/typescript/v2/HookSource.ts +13 -0
  297. package/src/app-server/protocol-generated/typescript/v2/HookStartedNotification.ts +10 -0
  298. package/src/app-server/protocol-generated/typescript/v2/ItemCompletedNotification.ts +6 -0
  299. package/src/app-server/protocol-generated/typescript/v2/ItemGuardianApprovalReviewCompletedNotification.ts +36 -0
  300. package/src/app-server/protocol-generated/typescript/v2/ItemGuardianApprovalReviewStartedNotification.ts +34 -0
  301. package/src/app-server/protocol-generated/typescript/v2/ItemStartedNotification.ts +6 -0
  302. package/src/app-server/protocol-generated/typescript/v2/ListMcpServerStatusParams.ts +20 -0
  303. package/src/app-server/protocol-generated/typescript/v2/ListMcpServerStatusResponse.ts +13 -0
  304. package/src/app-server/protocol-generated/typescript/v2/LoginAccountParams.ts +27 -0
  305. package/src/app-server/protocol-generated/typescript/v2/LoginAccountResponse.ts +27 -0
  306. package/src/app-server/protocol-generated/typescript/v2/LogoutAccountResponse.ts +5 -0
  307. package/src/app-server/protocol-generated/typescript/v2/ManagedHooksRequirements.ts +15 -0
  308. package/src/app-server/protocol-generated/typescript/v2/MarketplaceAddParams.ts +9 -0
  309. package/src/app-server/protocol-generated/typescript/v2/MarketplaceAddResponse.ts +10 -0
  310. package/src/app-server/protocol-generated/typescript/v2/MarketplaceInterface.ts +5 -0
  311. package/src/app-server/protocol-generated/typescript/v2/MarketplaceLoadErrorInfo.ts +6 -0
  312. package/src/app-server/protocol-generated/typescript/v2/MarketplaceRemoveParams.ts +5 -0
  313. package/src/app-server/protocol-generated/typescript/v2/MarketplaceRemoveResponse.ts +9 -0
  314. package/src/app-server/protocol-generated/typescript/v2/MarketplaceUpgradeErrorInfo.ts +5 -0
  315. package/src/app-server/protocol-generated/typescript/v2/MarketplaceUpgradeParams.ts +5 -0
  316. package/src/app-server/protocol-generated/typescript/v2/MarketplaceUpgradeResponse.ts +11 -0
  317. package/src/app-server/protocol-generated/typescript/v2/McpAuthStatus.ts +5 -0
  318. package/src/app-server/protocol-generated/typescript/v2/McpElicitationArrayType.ts +5 -0
  319. package/src/app-server/protocol-generated/typescript/v2/McpElicitationBooleanSchema.ts +11 -0
  320. package/src/app-server/protocol-generated/typescript/v2/McpElicitationBooleanType.ts +5 -0
  321. package/src/app-server/protocol-generated/typescript/v2/McpElicitationConstOption.ts +5 -0
  322. package/src/app-server/protocol-generated/typescript/v2/McpElicitationEnumSchema.ts +11 -0
  323. package/src/app-server/protocol-generated/typescript/v2/McpElicitationLegacyTitledEnumSchema.ts +13 -0
  324. package/src/app-server/protocol-generated/typescript/v2/McpElicitationMultiSelectEnumSchema.ts +9 -0
  325. package/src/app-server/protocol-generated/typescript/v2/McpElicitationNumberSchema.ts +13 -0
  326. package/src/app-server/protocol-generated/typescript/v2/McpElicitationNumberType.ts +5 -0
  327. package/src/app-server/protocol-generated/typescript/v2/McpElicitationObjectType.ts +5 -0
  328. package/src/app-server/protocol-generated/typescript/v2/McpElicitationPrimitiveSchema.ts +13 -0
  329. package/src/app-server/protocol-generated/typescript/v2/McpElicitationSchema.ts +18 -0
  330. package/src/app-server/protocol-generated/typescript/v2/McpElicitationSingleSelectEnumSchema.ts +9 -0
  331. package/src/app-server/protocol-generated/typescript/v2/McpElicitationStringFormat.ts +5 -0
  332. package/src/app-server/protocol-generated/typescript/v2/McpElicitationStringSchema.ts +15 -0
  333. package/src/app-server/protocol-generated/typescript/v2/McpElicitationStringType.ts +5 -0
  334. package/src/app-server/protocol-generated/typescript/v2/McpElicitationTitledEnumItems.ts +6 -0
  335. package/src/app-server/protocol-generated/typescript/v2/McpElicitationTitledMultiSelectEnumSchema.ts +15 -0
  336. package/src/app-server/protocol-generated/typescript/v2/McpElicitationTitledSingleSelectEnumSchema.ts +13 -0
  337. package/src/app-server/protocol-generated/typescript/v2/McpElicitationUntitledEnumItems.ts +9 -0
  338. package/src/app-server/protocol-generated/typescript/v2/McpElicitationUntitledMultiSelectEnumSchema.ts +15 -0
  339. package/src/app-server/protocol-generated/typescript/v2/McpElicitationUntitledSingleSelectEnumSchema.ts +12 -0
  340. package/src/app-server/protocol-generated/typescript/v2/McpResourceReadParams.ts +5 -0
  341. package/src/app-server/protocol-generated/typescript/v2/McpResourceReadResponse.ts +6 -0
  342. package/src/app-server/protocol-generated/typescript/v2/McpServerElicitationAction.ts +5 -0
  343. package/src/app-server/protocol-generated/typescript/v2/McpServerElicitationRequestParams.ts +27 -0
  344. package/src/app-server/protocol-generated/typescript/v2/McpServerElicitationRequestResponse.ts +19 -0
  345. package/src/app-server/protocol-generated/typescript/v2/McpServerOauthLoginCompletedNotification.ts +9 -0
  346. package/src/app-server/protocol-generated/typescript/v2/McpServerOauthLoginParams.ts +9 -0
  347. package/src/app-server/protocol-generated/typescript/v2/McpServerOauthLoginResponse.ts +5 -0
  348. package/src/app-server/protocol-generated/typescript/v2/McpServerRefreshResponse.ts +5 -0
  349. package/src/app-server/protocol-generated/typescript/v2/McpServerStartupState.ts +5 -0
  350. package/src/app-server/protocol-generated/typescript/v2/McpServerStatus.ts +15 -0
  351. package/src/app-server/protocol-generated/typescript/v2/McpServerStatusDetail.ts +5 -0
  352. package/src/app-server/protocol-generated/typescript/v2/McpServerStatusUpdatedNotification.ts +10 -0
  353. package/src/app-server/protocol-generated/typescript/v2/McpServerToolCallParams.ts +12 -0
  354. package/src/app-server/protocol-generated/typescript/v2/McpServerToolCallResponse.ts +11 -0
  355. package/src/app-server/protocol-generated/typescript/v2/McpToolCallError.ts +5 -0
  356. package/src/app-server/protocol-generated/typescript/v2/McpToolCallProgressNotification.ts +10 -0
  357. package/src/app-server/protocol-generated/typescript/v2/McpToolCallResult.ts +10 -0
  358. package/src/app-server/protocol-generated/typescript/v2/McpToolCallStatus.ts +5 -0
  359. package/src/app-server/protocol-generated/typescript/v2/MemoryCitation.ts +6 -0
  360. package/src/app-server/protocol-generated/typescript/v2/MemoryCitationEntry.ts +10 -0
  361. package/src/app-server/protocol-generated/typescript/v2/MergeStrategy.ts +5 -0
  362. package/src/app-server/protocol-generated/typescript/v2/MigrationDetails.ts +6 -0
  363. package/src/app-server/protocol-generated/typescript/v2/Model.ts +25 -0
  364. package/src/app-server/protocol-generated/typescript/v2/ModelAvailabilityNux.ts +5 -0
  365. package/src/app-server/protocol-generated/typescript/v2/ModelListParams.ts +18 -0
  366. package/src/app-server/protocol-generated/typescript/v2/ModelListResponse.ts +13 -0
  367. package/src/app-server/protocol-generated/typescript/v2/ModelRerouteReason.ts +5 -0
  368. package/src/app-server/protocol-generated/typescript/v2/ModelReroutedNotification.ts +12 -0
  369. package/src/app-server/protocol-generated/typescript/v2/ModelUpgradeInfo.ts +10 -0
  370. package/src/app-server/protocol-generated/typescript/v2/ModelVerification.ts +5 -0
  371. package/src/app-server/protocol-generated/typescript/v2/ModelVerificationNotification.ts +10 -0
  372. package/src/app-server/protocol-generated/typescript/v2/NetworkAccess.ts +5 -0
  373. package/src/app-server/protocol-generated/typescript/v2/NetworkApprovalContext.ts +6 -0
  374. package/src/app-server/protocol-generated/typescript/v2/NetworkApprovalProtocol.ts +5 -0
  375. package/src/app-server/protocol-generated/typescript/v2/NetworkDomainPermission.ts +5 -0
  376. package/src/app-server/protocol-generated/typescript/v2/NetworkPolicyAmendment.ts +6 -0
  377. package/src/app-server/protocol-generated/typescript/v2/NetworkPolicyRuleAction.ts +5 -0
  378. package/src/app-server/protocol-generated/typescript/v2/NetworkRequirements.ts +40 -0
  379. package/src/app-server/protocol-generated/typescript/v2/NetworkUnixSocketPermission.ts +5 -0
  380. package/src/app-server/protocol-generated/typescript/v2/NonSteerableTurnKind.ts +5 -0
  381. package/src/app-server/protocol-generated/typescript/v2/OverriddenMetadata.ts +11 -0
  382. package/src/app-server/protocol-generated/typescript/v2/PatchApplyStatus.ts +5 -0
  383. package/src/app-server/protocol-generated/typescript/v2/PatchChangeKind.ts +8 -0
  384. package/src/app-server/protocol-generated/typescript/v2/PermissionGrantScope.ts +5 -0
  385. package/src/app-server/protocol-generated/typescript/v2/PermissionProfile.ts +10 -0
  386. package/src/app-server/protocol-generated/typescript/v2/PermissionProfileFileSystemPermissions.ts +9 -0
  387. package/src/app-server/protocol-generated/typescript/v2/PermissionProfileNetworkPermissions.ts +5 -0
  388. package/src/app-server/protocol-generated/typescript/v2/PermissionsRequestApprovalParams.ts +14 -0
  389. package/src/app-server/protocol-generated/typescript/v2/PermissionsRequestApprovalResponse.ts +14 -0
  390. package/src/app-server/protocol-generated/typescript/v2/PlanDeltaNotification.ts +14 -0
  391. package/src/app-server/protocol-generated/typescript/v2/PluginAuthPolicy.ts +5 -0
  392. package/src/app-server/protocol-generated/typescript/v2/PluginDetail.ts +17 -0
  393. package/src/app-server/protocol-generated/typescript/v2/PluginInstallParams.ts +10 -0
  394. package/src/app-server/protocol-generated/typescript/v2/PluginInstallPolicy.ts +5 -0
  395. package/src/app-server/protocol-generated/typescript/v2/PluginInstallResponse.ts +10 -0
  396. package/src/app-server/protocol-generated/typescript/v2/PluginInterface.ts +46 -0
  397. package/src/app-server/protocol-generated/typescript/v2/PluginListParams.ts +12 -0
  398. package/src/app-server/protocol-generated/typescript/v2/PluginListResponse.ts +11 -0
  399. package/src/app-server/protocol-generated/typescript/v2/PluginMarketplaceEntry.ts +17 -0
  400. package/src/app-server/protocol-generated/typescript/v2/PluginReadParams.ts +10 -0
  401. package/src/app-server/protocol-generated/typescript/v2/PluginReadResponse.ts +6 -0
  402. package/src/app-server/protocol-generated/typescript/v2/PluginSource.ts +9 -0
  403. package/src/app-server/protocol-generated/typescript/v2/PluginSummary.ts +18 -0
  404. package/src/app-server/protocol-generated/typescript/v2/PluginUninstallParams.ts +5 -0
  405. package/src/app-server/protocol-generated/typescript/v2/PluginUninstallResponse.ts +5 -0
  406. package/src/app-server/protocol-generated/typescript/v2/PluginsMigration.ts +5 -0
  407. package/src/app-server/protocol-generated/typescript/v2/ProfileV2.ts +38 -0
  408. package/src/app-server/protocol-generated/typescript/v2/RateLimitReachedType.ts +10 -0
  409. package/src/app-server/protocol-generated/typescript/v2/RateLimitSnapshot.ts +17 -0
  410. package/src/app-server/protocol-generated/typescript/v2/RateLimitWindow.ts +9 -0
  411. package/src/app-server/protocol-generated/typescript/v2/RawResponseItemCompletedNotification.ts +10 -0
  412. package/src/app-server/protocol-generated/typescript/v2/ReadOnlyAccess.ts +8 -0
  413. package/src/app-server/protocol-generated/typescript/v2/ReasoningEffortOption.ts +6 -0
  414. package/src/app-server/protocol-generated/typescript/v2/ReasoningSummaryPartAddedNotification.ts +10 -0
  415. package/src/app-server/protocol-generated/typescript/v2/ReasoningSummaryTextDeltaNotification.ts +11 -0
  416. package/src/app-server/protocol-generated/typescript/v2/ReasoningTextDeltaNotification.ts +11 -0
  417. package/src/app-server/protocol-generated/typescript/v2/RemoteControlClientConnectionAudience.ts +8 -0
  418. package/src/app-server/protocol-generated/typescript/v2/RemoteControlClientEnrollmentAudience.ts +8 -0
  419. package/src/app-server/protocol-generated/typescript/v2/RequestPermissionProfile.ts +10 -0
  420. package/src/app-server/protocol-generated/typescript/v2/ResidencyRequirement.ts +5 -0
  421. package/src/app-server/protocol-generated/typescript/v2/ReviewDelivery.ts +5 -0
  422. package/src/app-server/protocol-generated/typescript/v2/ReviewStartParams.ts +15 -0
  423. package/src/app-server/protocol-generated/typescript/v2/ReviewStartResponse.ts +15 -0
  424. package/src/app-server/protocol-generated/typescript/v2/ReviewTarget.ts +16 -0
  425. package/src/app-server/protocol-generated/typescript/v2/SandboxMode.ts +5 -0
  426. package/src/app-server/protocol-generated/typescript/v2/SandboxPolicy.ts +19 -0
  427. package/src/app-server/protocol-generated/typescript/v2/SandboxWorkspaceWrite.ts +10 -0
  428. package/src/app-server/protocol-generated/typescript/v2/SendAddCreditsNudgeEmailParams.ts +6 -0
  429. package/src/app-server/protocol-generated/typescript/v2/SendAddCreditsNudgeEmailResponse.ts +6 -0
  430. package/src/app-server/protocol-generated/typescript/v2/ServerRequestResolvedNotification.ts +6 -0
  431. package/src/app-server/protocol-generated/typescript/v2/SessionSource.ts +13 -0
  432. package/src/app-server/protocol-generated/typescript/v2/SkillDependencies.ts +6 -0
  433. package/src/app-server/protocol-generated/typescript/v2/SkillErrorInfo.ts +5 -0
  434. package/src/app-server/protocol-generated/typescript/v2/SkillInterface.ts +13 -0
  435. package/src/app-server/protocol-generated/typescript/v2/SkillMetadata.ts +21 -0
  436. package/src/app-server/protocol-generated/typescript/v2/SkillScope.ts +5 -0
  437. package/src/app-server/protocol-generated/typescript/v2/SkillSummary.ts +14 -0
  438. package/src/app-server/protocol-generated/typescript/v2/SkillToolDependency.ts +12 -0
  439. package/src/app-server/protocol-generated/typescript/v2/SkillsChangedNotification.ts +11 -0
  440. package/src/app-server/protocol-generated/typescript/v2/SkillsConfigWriteParams.ts +16 -0
  441. package/src/app-server/protocol-generated/typescript/v2/SkillsConfigWriteResponse.ts +5 -0
  442. package/src/app-server/protocol-generated/typescript/v2/SkillsListEntry.ts +11 -0
  443. package/src/app-server/protocol-generated/typescript/v2/SkillsListExtraRootsForCwd.ts +5 -0
  444. package/src/app-server/protocol-generated/typescript/v2/SkillsListParams.ts +19 -0
  445. package/src/app-server/protocol-generated/typescript/v2/SkillsListResponse.ts +6 -0
  446. package/src/app-server/protocol-generated/typescript/v2/SortDirection.ts +5 -0
  447. package/src/app-server/protocol-generated/typescript/v2/TerminalInteractionNotification.ts +11 -0
  448. package/src/app-server/protocol-generated/typescript/v2/TextElement.ts +15 -0
  449. package/src/app-server/protocol-generated/typescript/v2/TextPosition.ts +14 -0
  450. package/src/app-server/protocol-generated/typescript/v2/TextRange.ts +6 -0
  451. package/src/app-server/protocol-generated/typescript/v2/Thread.ts +79 -0
  452. package/src/app-server/protocol-generated/typescript/v2/ThreadActiveFlag.ts +5 -0
  453. package/src/app-server/protocol-generated/typescript/v2/ThreadApproveGuardianDeniedActionParams.ts +12 -0
  454. package/src/app-server/protocol-generated/typescript/v2/ThreadApproveGuardianDeniedActionResponse.ts +5 -0
  455. package/src/app-server/protocol-generated/typescript/v2/ThreadArchiveParams.ts +5 -0
  456. package/src/app-server/protocol-generated/typescript/v2/ThreadArchiveResponse.ts +5 -0
  457. package/src/app-server/protocol-generated/typescript/v2/ThreadArchivedNotification.ts +5 -0
  458. package/src/app-server/protocol-generated/typescript/v2/ThreadClosedNotification.ts +5 -0
  459. package/src/app-server/protocol-generated/typescript/v2/ThreadCompactStartParams.ts +5 -0
  460. package/src/app-server/protocol-generated/typescript/v2/ThreadCompactStartResponse.ts +5 -0
  461. package/src/app-server/protocol-generated/typescript/v2/ThreadForkParams.ts +55 -0
  462. package/src/app-server/protocol-generated/typescript/v2/ThreadForkResponse.ts +41 -0
  463. package/src/app-server/protocol-generated/typescript/v2/ThreadInjectItemsParams.ts +12 -0
  464. package/src/app-server/protocol-generated/typescript/v2/ThreadInjectItemsResponse.ts +5 -0
  465. package/src/app-server/protocol-generated/typescript/v2/ThreadItem.ts +156 -0
  466. package/src/app-server/protocol-generated/typescript/v2/ThreadListParams.ts +55 -0
  467. package/src/app-server/protocol-generated/typescript/v2/ThreadListResponse.ts +20 -0
  468. package/src/app-server/protocol-generated/typescript/v2/ThreadLoadedListParams.ts +14 -0
  469. package/src/app-server/protocol-generated/typescript/v2/ThreadLoadedListResponse.ts +15 -0
  470. package/src/app-server/protocol-generated/typescript/v2/ThreadMetadataGitInfoUpdateParams.ts +21 -0
  471. package/src/app-server/protocol-generated/typescript/v2/ThreadMetadataUpdateParams.ts +14 -0
  472. package/src/app-server/protocol-generated/typescript/v2/ThreadMetadataUpdateResponse.ts +6 -0
  473. package/src/app-server/protocol-generated/typescript/v2/ThreadNameUpdatedNotification.ts +5 -0
  474. package/src/app-server/protocol-generated/typescript/v2/ThreadReadParams.ts +11 -0
  475. package/src/app-server/protocol-generated/typescript/v2/ThreadReadResponse.ts +6 -0
  476. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeAudioChunk.ts +14 -0
  477. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeClosedNotification.ts +8 -0
  478. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeErrorNotification.ts +8 -0
  479. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeItemAddedNotification.ts +9 -0
  480. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeOutputAudioDeltaNotification.ts +12 -0
  481. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeSdpNotification.ts +8 -0
  482. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeStartTransport.ts +17 -0
  483. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeStartedNotification.ts +13 -0
  484. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeTranscriptDeltaNotification.ts +16 -0
  485. package/src/app-server/protocol-generated/typescript/v2/ThreadRealtimeTranscriptDoneNotification.ts +16 -0
  486. package/src/app-server/protocol-generated/typescript/v2/ThreadResumeParams.ts +64 -0
  487. package/src/app-server/protocol-generated/typescript/v2/ThreadResumeResponse.ts +41 -0
  488. package/src/app-server/protocol-generated/typescript/v2/ThreadRollbackParams.ts +14 -0
  489. package/src/app-server/protocol-generated/typescript/v2/ThreadRollbackResponse.ts +15 -0
  490. package/src/app-server/protocol-generated/typescript/v2/ThreadSetNameParams.ts +5 -0
  491. package/src/app-server/protocol-generated/typescript/v2/ThreadSetNameResponse.ts +5 -0
  492. package/src/app-server/protocol-generated/typescript/v2/ThreadShellCommandParams.ts +14 -0
  493. package/src/app-server/protocol-generated/typescript/v2/ThreadShellCommandResponse.ts +5 -0
  494. package/src/app-server/protocol-generated/typescript/v2/ThreadSortKey.ts +5 -0
  495. package/src/app-server/protocol-generated/typescript/v2/ThreadSourceKind.ts +15 -0
  496. package/src/app-server/protocol-generated/typescript/v2/ThreadStartParams.ts +43 -0
  497. package/src/app-server/protocol-generated/typescript/v2/ThreadStartResponse.ts +41 -0
  498. package/src/app-server/protocol-generated/typescript/v2/ThreadStartSource.ts +5 -0
  499. package/src/app-server/protocol-generated/typescript/v2/ThreadStartedNotification.ts +6 -0
  500. package/src/app-server/protocol-generated/typescript/v2/ThreadStatus.ts +10 -0
  501. package/src/app-server/protocol-generated/typescript/v2/ThreadStatusChangedNotification.ts +6 -0
  502. package/src/app-server/protocol-generated/typescript/v2/ThreadTokenUsage.ts +10 -0
  503. package/src/app-server/protocol-generated/typescript/v2/ThreadTokenUsageUpdatedNotification.ts +10 -0
  504. package/src/app-server/protocol-generated/typescript/v2/ThreadTurnsListParams.ts +20 -0
  505. package/src/app-server/protocol-generated/typescript/v2/ThreadTurnsListResponse.ts +20 -0
  506. package/src/app-server/protocol-generated/typescript/v2/ThreadUnarchiveParams.ts +5 -0
  507. package/src/app-server/protocol-generated/typescript/v2/ThreadUnarchiveResponse.ts +6 -0
  508. package/src/app-server/protocol-generated/typescript/v2/ThreadUnarchivedNotification.ts +5 -0
  509. package/src/app-server/protocol-generated/typescript/v2/ThreadUnsubscribeParams.ts +5 -0
  510. package/src/app-server/protocol-generated/typescript/v2/ThreadUnsubscribeResponse.ts +6 -0
  511. package/src/app-server/protocol-generated/typescript/v2/ThreadUnsubscribeStatus.ts +5 -0
  512. package/src/app-server/protocol-generated/typescript/v2/TokenUsageBreakdown.ts +11 -0
  513. package/src/app-server/protocol-generated/typescript/v2/ToolRequestUserInputAnswer.ts +8 -0
  514. package/src/app-server/protocol-generated/typescript/v2/ToolRequestUserInputOption.ts +8 -0
  515. package/src/app-server/protocol-generated/typescript/v2/ToolRequestUserInputParams.ts +14 -0
  516. package/src/app-server/protocol-generated/typescript/v2/ToolRequestUserInputQuestion.ts +16 -0
  517. package/src/app-server/protocol-generated/typescript/v2/ToolRequestUserInputResponse.ts +11 -0
  518. package/src/app-server/protocol-generated/typescript/v2/ToolsV2.ts +6 -0
  519. package/src/app-server/protocol-generated/typescript/v2/Turn.ts +33 -0
  520. package/src/app-server/protocol-generated/typescript/v2/TurnCompletedNotification.ts +6 -0
  521. package/src/app-server/protocol-generated/typescript/v2/TurnDiffUpdatedNotification.ts +9 -0
  522. package/src/app-server/protocol-generated/typescript/v2/TurnEnvironmentParams.ts +6 -0
  523. package/src/app-server/protocol-generated/typescript/v2/TurnError.ts +10 -0
  524. package/src/app-server/protocol-generated/typescript/v2/TurnInterruptParams.ts +5 -0
  525. package/src/app-server/protocol-generated/typescript/v2/TurnInterruptResponse.ts +5 -0
  526. package/src/app-server/protocol-generated/typescript/v2/TurnPlanStep.ts +6 -0
  527. package/src/app-server/protocol-generated/typescript/v2/TurnPlanStepStatus.ts +5 -0
  528. package/src/app-server/protocol-generated/typescript/v2/TurnPlanUpdatedNotification.ts +11 -0
  529. package/src/app-server/protocol-generated/typescript/v2/TurnStartParams.ts +62 -0
  530. package/src/app-server/protocol-generated/typescript/v2/TurnStartResponse.ts +6 -0
  531. package/src/app-server/protocol-generated/typescript/v2/TurnStartedNotification.ts +6 -0
  532. package/src/app-server/protocol-generated/typescript/v2/TurnStatus.ts +5 -0
  533. package/src/app-server/protocol-generated/typescript/v2/TurnSteerParams.ts +13 -0
  534. package/src/app-server/protocol-generated/typescript/v2/TurnSteerResponse.ts +5 -0
  535. package/src/app-server/protocol-generated/typescript/v2/UserInput.ts +18 -0
  536. package/src/app-server/protocol-generated/typescript/v2/WarningNotification.ts +14 -0
  537. package/src/app-server/protocol-generated/typescript/v2/WebSearchAction.ts +9 -0
  538. package/src/app-server/protocol-generated/typescript/v2/WindowsSandboxSetupCompletedNotification.ts +10 -0
  539. package/src/app-server/protocol-generated/typescript/v2/WindowsSandboxSetupMode.ts +5 -0
  540. package/src/app-server/protocol-generated/typescript/v2/WindowsSandboxSetupStartParams.ts +10 -0
  541. package/src/app-server/protocol-generated/typescript/v2/WindowsSandboxSetupStartResponse.ts +5 -0
  542. package/src/app-server/protocol-generated/typescript/v2/WindowsWorldWritableWarningNotification.ts +9 -0
  543. package/src/app-server/protocol-generated/typescript/v2/WriteStatus.ts +5 -0
  544. package/src/app-server/protocol-generated/typescript/v2/index.ts +410 -0
  545. package/src/app-server/protocol-validators.ts +167 -0
  546. package/src/app-server/protocol.ts +110 -0
  547. package/src/app-server/request.ts +45 -0
  548. package/src/app-server/run-attempt.context-engine.test.ts +420 -0
  549. package/src/app-server/run-attempt.test.ts +2225 -0
  550. package/src/app-server/run-attempt.ts +1641 -0
  551. package/src/app-server/run-attempt.vision-tools.test.ts +35 -0
  552. package/src/app-server/schema-normalization-runtime-contract.test.ts +168 -0
  553. package/src/app-server/session-binding.test.ts +52 -0
  554. package/src/app-server/session-binding.ts +131 -0
  555. package/src/app-server/session-history.ts +44 -0
  556. package/src/app-server/shared-client.test.ts +368 -0
  557. package/src/app-server/shared-client.ts +157 -0
  558. package/src/app-server/test-support.ts +48 -0
  559. package/src/app-server/thread-lifecycle.test.ts +59 -0
  560. package/src/app-server/thread-lifecycle.ts +311 -0
  561. package/src/app-server/timeout.ts +22 -0
  562. package/src/app-server/trajectory.test.ts +155 -0
  563. package/src/app-server/trajectory.ts +433 -0
  564. package/src/app-server/transcript-mirror.test.ts +265 -0
  565. package/src/app-server/transcript-mirror.ts +308 -0
  566. package/src/app-server/transcript-repair-runtime-contract.test.ts +44 -0
  567. package/src/app-server/transport-stdio.test.ts +171 -0
  568. package/src/app-server/transport-stdio.ts +107 -0
  569. package/src/app-server/transport-websocket.test.ts +71 -0
  570. package/src/app-server/transport-websocket.ts +90 -0
  571. package/src/app-server/transport.ts +117 -0
  572. package/src/app-server/user-input-bridge.test.ts +137 -0
  573. package/src/app-server/user-input-bridge.ts +294 -0
  574. package/src/app-server/version.ts +3 -0
  575. package/src/app-server/vision-tools.ts +12 -0
  576. package/src/command-formatters.ts +204 -0
  577. package/src/command-handlers.ts +1559 -0
  578. package/src/command-rpc.test.ts +16 -0
  579. package/src/command-rpc.ts +97 -0
  580. package/src/commands.test.ts +1716 -0
  581. package/src/commands.ts +32 -0
  582. package/src/conversation-binding-data.ts +72 -0
  583. package/src/conversation-binding.test.ts +76 -0
  584. package/src/conversation-binding.ts +351 -0
  585. package/src/conversation-control.test.ts +50 -0
  586. package/src/conversation-control.ts +255 -0
  587. package/src/conversation-turn-collector.test.ts +154 -0
  588. package/src/conversation-turn-collector.ts +158 -0
  589. package/src/conversation-turn-input.test.ts +44 -0
  590. package/src/conversation-turn-input.ts +80 -0
  591. package/src/manifest.test.ts +20 -0
  592. package/src/migration/apply.ts +43 -0
  593. package/src/migration/helpers.ts +60 -0
  594. package/src/migration/plan.ts +148 -0
  595. package/src/migration/provider.test.ts +219 -0
  596. package/src/migration/provider.ts +28 -0
  597. package/src/migration/source.ts +176 -0
  598. package/src/migration/targets.ts +25 -0
  599. package/tsconfig.json +16 -0
@@ -0,0 +1,2225 @@
1
+ import fs from "node:fs/promises";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { SessionManager } from "@mariozechner/pi-coding-agent";
5
+ import {
6
+ abortAgentHarnessRun,
7
+ queueAgentHarnessMessage,
8
+ type EmbeddedRunAttemptParams,
9
+ } from "openclaw/plugin-sdk/agent-harness";
10
+ import {
11
+ buildAgentRuntimePlan,
12
+ embeddedAgentLog,
13
+ nativeHookRelayTesting,
14
+ onAgentEvent,
15
+ resetAgentEventsForTest,
16
+ type AgentEventPayload,
17
+ } from "openclaw/plugin-sdk/agent-harness-runtime";
18
+ import {
19
+ initializeGlobalHookRunner,
20
+ resetGlobalHookRunner,
21
+ } from "openclaw/plugin-sdk/hook-runtime";
22
+ import { createMockPluginRegistry } from "openclaw/plugin-sdk/plugin-test-runtime";
23
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
24
+ import { CODEX_GPT5_BEHAVIOR_CONTRACT } from "../../prompt-overlay.js";
25
+ import * as elicitationBridge from "./elicitation-bridge.js";
26
+ import type { CodexServerNotification } from "./protocol.js";
27
+ import { runCodexAppServerAttempt, __testing } from "./run-attempt.js";
28
+ import { writeCodexAppServerBinding } from "./session-binding.js";
29
+ import { createCodexTestModel } from "./test-support.js";
30
+ import {
31
+ buildThreadResumeParams,
32
+ buildTurnStartParams,
33
+ startOrResumeThread,
34
+ } from "./thread-lifecycle.js";
35
+
36
+ let tempDir: string;
37
+
38
+ function createParams(sessionFile: string, workspaceDir: string): EmbeddedRunAttemptParams {
39
+ return {
40
+ prompt: "hello",
41
+ sessionId: "session-1",
42
+ sessionKey: "agent:main:session-1",
43
+ sessionFile,
44
+ workspaceDir,
45
+ runId: "run-1",
46
+ provider: "codex",
47
+ modelId: "gpt-5.4-codex",
48
+ model: createCodexTestModel("codex"),
49
+ thinkLevel: "medium",
50
+ disableTools: true,
51
+ timeoutMs: 5_000,
52
+ authStorage: {} as never,
53
+ modelRegistry: {} as never,
54
+ } as EmbeddedRunAttemptParams;
55
+ }
56
+
57
+ function createParamsWithRuntimePlan(
58
+ sessionFile: string,
59
+ workspaceDir: string,
60
+ ): EmbeddedRunAttemptParams {
61
+ const params = createParams(sessionFile, workspaceDir);
62
+ return {
63
+ ...params,
64
+ runtimePlan: buildAgentRuntimePlan({
65
+ provider: params.provider,
66
+ modelId: params.modelId,
67
+ model: params.model,
68
+ modelApi: params.model.api,
69
+ harnessId: "codex",
70
+ harnessRuntime: "codex",
71
+ config: params.config,
72
+ workspaceDir,
73
+ agentDir: tempDir,
74
+ thinkingLevel: params.thinkLevel,
75
+ }),
76
+ } as EmbeddedRunAttemptParams;
77
+ }
78
+
79
+ function threadStartResult(threadId = "thread-1") {
80
+ return {
81
+ thread: {
82
+ id: threadId,
83
+ forkedFromId: null,
84
+ preview: "",
85
+ ephemeral: false,
86
+ modelProvider: "openai",
87
+ createdAt: 1,
88
+ updatedAt: 1,
89
+ status: { type: "idle" },
90
+ path: null,
91
+ cwd: tempDir || "/tmp/openclaw-codex-test",
92
+ cliVersion: "0.125.0",
93
+ source: "unknown",
94
+ agentNickname: null,
95
+ agentRole: null,
96
+ gitInfo: null,
97
+ name: null,
98
+ turns: [],
99
+ },
100
+ model: "gpt-5.4-codex",
101
+ modelProvider: "openai",
102
+ serviceTier: null,
103
+ cwd: tempDir || "/tmp/openclaw-codex-test",
104
+ instructionSources: [],
105
+ approvalPolicy: "never",
106
+ approvalsReviewer: "user",
107
+ sandbox: { type: "dangerFullAccess" },
108
+ permissionProfile: null,
109
+ reasoningEffort: null,
110
+ };
111
+ }
112
+
113
+ function turnStartResult(turnId = "turn-1", status = "inProgress") {
114
+ return {
115
+ turn: {
116
+ id: turnId,
117
+ status,
118
+ items: [],
119
+ error: null,
120
+ startedAt: null,
121
+ completedAt: null,
122
+ durationMs: null,
123
+ },
124
+ };
125
+ }
126
+
127
+ function assistantMessage(text: string, timestamp: number) {
128
+ return {
129
+ role: "assistant" as const,
130
+ content: [{ type: "text" as const, text }],
131
+ api: "openai-codex-responses",
132
+ provider: "openai-codex",
133
+ model: "gpt-5.4-codex",
134
+ usage: {
135
+ input: 0,
136
+ output: 0,
137
+ cacheRead: 0,
138
+ cacheWrite: 0,
139
+ totalTokens: 0,
140
+ cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
141
+ },
142
+ stopReason: "stop" as const,
143
+ timestamp,
144
+ };
145
+ }
146
+
147
+ function createAppServerHarness(
148
+ requestImpl: (method: string, params: unknown) => Promise<unknown>,
149
+ options: {
150
+ onStart?: (authProfileId: string | undefined, agentDir: string | undefined) => void;
151
+ } = {},
152
+ ) {
153
+ const requests: Array<{ method: string; params: unknown }> = [];
154
+ let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
155
+ const request = vi.fn(async (method: string, params?: unknown) => {
156
+ requests.push({ method, params });
157
+ return requestImpl(method, params);
158
+ });
159
+
160
+ __testing.setCodexAppServerClientFactoryForTests(
161
+ async (_startOptions, authProfileId, agentDir) => {
162
+ options.onStart?.(authProfileId, agentDir);
163
+ return {
164
+ request,
165
+ addNotificationHandler: (handler: typeof notify) => {
166
+ notify = handler;
167
+ return () => undefined;
168
+ },
169
+ addRequestHandler: () => () => undefined,
170
+ } as never;
171
+ },
172
+ );
173
+
174
+ return {
175
+ request,
176
+ requests,
177
+ async waitForMethod(method: string) {
178
+ await vi.waitFor(
179
+ () => {
180
+ if (!requests.some((entry) => entry.method === method)) {
181
+ const mockMethods = request.mock.calls.map((call) => call[0]);
182
+ throw new Error(
183
+ `expected app-server method ${method}; saw ${requests
184
+ .map((entry) => entry.method)
185
+ .join(", ")}; mock saw ${mockMethods.join(", ")}`,
186
+ );
187
+ }
188
+ },
189
+ { interval: 1, timeout: 30_000 },
190
+ );
191
+ },
192
+ async notify(notification: CodexServerNotification) {
193
+ await notify(notification);
194
+ },
195
+ async completeTurn(params: { threadId: string; turnId: string }) {
196
+ await notify({
197
+ method: "turn/completed",
198
+ params: {
199
+ threadId: params.threadId,
200
+ turnId: params.turnId,
201
+ turn: { id: params.turnId, status: "completed" },
202
+ },
203
+ });
204
+ },
205
+ };
206
+ }
207
+
208
+ function createStartedThreadHarness(
209
+ requestImpl: (method: string, params: unknown) => Promise<unknown> = async () => undefined,
210
+ options: {
211
+ onStart?: (authProfileId: string | undefined, agentDir: string | undefined) => void;
212
+ } = {},
213
+ ) {
214
+ return createAppServerHarness(async (method, params) => {
215
+ const override = await requestImpl(method, params);
216
+ if (override !== undefined) {
217
+ return override;
218
+ }
219
+ if (method === "thread/start") {
220
+ return threadStartResult();
221
+ }
222
+ if (method === "turn/start") {
223
+ return turnStartResult();
224
+ }
225
+ return {};
226
+ }, options);
227
+ }
228
+
229
+ function expectResumeRequest(
230
+ requests: Array<{ method: string; params: unknown }>,
231
+ params: Record<string, unknown>,
232
+ ) {
233
+ expect(requests).toEqual(
234
+ expect.arrayContaining([
235
+ {
236
+ method: "thread/resume",
237
+ params: expect.objectContaining(params),
238
+ },
239
+ ]),
240
+ );
241
+ }
242
+
243
+ function createResumeHarness() {
244
+ return createAppServerHarness(async (method) => {
245
+ if (method === "thread/resume") {
246
+ return threadStartResult("thread-existing");
247
+ }
248
+ if (method === "turn/start") {
249
+ return turnStartResult();
250
+ }
251
+ return {};
252
+ });
253
+ }
254
+
255
+ async function writeExistingBinding(
256
+ sessionFile: string,
257
+ workspaceDir: string,
258
+ overrides: Partial<Parameters<typeof writeCodexAppServerBinding>[1]> = {},
259
+ ) {
260
+ await writeCodexAppServerBinding(sessionFile, {
261
+ threadId: "thread-existing",
262
+ cwd: workspaceDir,
263
+ model: "gpt-5.4-codex",
264
+ modelProvider: "openai",
265
+ ...overrides,
266
+ });
267
+ }
268
+
269
+ function createThreadLifecycleAppServerOptions(): Parameters<
270
+ typeof startOrResumeThread
271
+ >[0]["appServer"] {
272
+ return {
273
+ start: {
274
+ transport: "stdio",
275
+ command: "codex",
276
+ args: ["app-server"],
277
+ headers: {},
278
+ },
279
+ requestTimeoutMs: 60_000,
280
+ approvalPolicy: "never",
281
+ approvalsReviewer: "user",
282
+ sandbox: "workspace-write",
283
+ };
284
+ }
285
+
286
+ function createMessageDynamicTool(
287
+ description: string,
288
+ actions: string[] = ["send"],
289
+ ): Parameters<typeof startOrResumeThread>[0]["dynamicTools"][number] {
290
+ return {
291
+ name: "message",
292
+ description,
293
+ inputSchema: {
294
+ type: "object",
295
+ properties: {
296
+ action: {
297
+ type: "string",
298
+ enum: actions,
299
+ },
300
+ },
301
+ required: ["action"],
302
+ additionalProperties: false,
303
+ },
304
+ };
305
+ }
306
+
307
+ function createNamedDynamicTool(
308
+ name: string,
309
+ ): Parameters<typeof startOrResumeThread>[0]["dynamicTools"][number] {
310
+ return {
311
+ name,
312
+ description: `${name} test tool`,
313
+ inputSchema: {
314
+ type: "object",
315
+ properties: {},
316
+ additionalProperties: false,
317
+ },
318
+ };
319
+ }
320
+
321
+ function extractRelayIdFromThreadRequest(params: unknown): string {
322
+ const command = (
323
+ params as {
324
+ config?: {
325
+ "hooks.PreToolUse"?: Array<{ hooks?: Array<{ command?: string }> }>;
326
+ };
327
+ }
328
+ ).config?.["hooks.PreToolUse"]?.[0]?.hooks?.[0]?.command;
329
+ const match = command?.match(/--relay-id ([^ ]+)/);
330
+ if (!match?.[1]) {
331
+ throw new Error(`relay id missing from command: ${command}`);
332
+ }
333
+ return match[1];
334
+ }
335
+
336
+ describe("runCodexAppServerAttempt", () => {
337
+ beforeEach(async () => {
338
+ resetAgentEventsForTest();
339
+ tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-codex-run-"));
340
+ });
341
+
342
+ afterEach(async () => {
343
+ __testing.resetCodexAppServerClientFactoryForTests();
344
+ nativeHookRelayTesting.clearNativeHookRelaysForTests();
345
+ resetAgentEventsForTest();
346
+ resetGlobalHookRunner();
347
+ vi.useRealTimers();
348
+ vi.restoreAllMocks();
349
+ await fs.rm(tempDir, { recursive: true, force: true });
350
+ });
351
+
352
+ it("defaults Codex dynamic tools to the native-first profile", () => {
353
+ const tools = [
354
+ "read",
355
+ "write",
356
+ "edit",
357
+ "apply_patch",
358
+ "exec",
359
+ "process",
360
+ "update_plan",
361
+ "web_search",
362
+ "message",
363
+ "heartbeat_respond",
364
+ "sessions_spawn",
365
+ ].map((name) => ({ name }));
366
+
367
+ expect(__testing.applyCodexDynamicToolProfile(tools, {}).map((tool) => tool.name)).toEqual([
368
+ "web_search",
369
+ "message",
370
+ "heartbeat_respond",
371
+ "sessions_spawn",
372
+ ]);
373
+ });
374
+
375
+ it("allows Codex dynamic tool filtering to opt back into OpenClaw compatibility", () => {
376
+ const tools = ["read", "exec", "message", "custom_tool"].map((name) => ({ name }));
377
+
378
+ expect(
379
+ __testing
380
+ .applyCodexDynamicToolProfile(tools, {
381
+ codexDynamicToolsProfile: "openclaw-compat",
382
+ codexDynamicToolsExclude: ["custom_tool"],
383
+ })
384
+ .map((tool) => tool.name),
385
+ ).toEqual(["read", "exec", "message"]);
386
+ });
387
+
388
+ it("starts Codex threads without duplicate OpenClaw workspace tools by default", async () => {
389
+ const sessionFile = path.join(tempDir, "session.jsonl");
390
+ const workspaceDir = path.join(tempDir, "workspace");
391
+ const appServer = createThreadLifecycleAppServerOptions();
392
+ const request = vi.fn(async (method: string, _params: unknown) => {
393
+ if (method === "thread/start") {
394
+ return threadStartResult();
395
+ }
396
+ throw new Error(`unexpected method: ${method}`);
397
+ });
398
+ const dynamicTools = __testing.applyCodexDynamicToolProfile(
399
+ [
400
+ "read",
401
+ "write",
402
+ "edit",
403
+ "apply_patch",
404
+ "exec",
405
+ "process",
406
+ "update_plan",
407
+ "web_search",
408
+ "message",
409
+ ].map(createNamedDynamicTool),
410
+ {},
411
+ );
412
+
413
+ await startOrResumeThread({
414
+ client: { request } as never,
415
+ params: createParams(sessionFile, workspaceDir),
416
+ cwd: workspaceDir,
417
+ dynamicTools,
418
+ appServer,
419
+ });
420
+
421
+ const startRequest = request.mock.calls.find(([method]) => method === "thread/start");
422
+ const dynamicToolNames = (
423
+ (startRequest?.[1] as { dynamicTools?: Array<{ name: string }> } | undefined)?.dynamicTools ??
424
+ []
425
+ ).map((tool) => tool.name);
426
+
427
+ expect(dynamicToolNames).toContain("message");
428
+ expect(dynamicToolNames).toContain("web_search");
429
+ expect(dynamicToolNames).not.toEqual(
430
+ expect.arrayContaining([
431
+ "read",
432
+ "write",
433
+ "edit",
434
+ "apply_patch",
435
+ "exec",
436
+ "process",
437
+ "update_plan",
438
+ ]),
439
+ );
440
+ });
441
+
442
+ it("returns a failed dynamic tool response when an app-server tool call exceeds the deadline", async () => {
443
+ vi.useFakeTimers();
444
+ let capturedSignal: AbortSignal | undefined;
445
+ const onTimeout = vi.fn();
446
+ const response = __testing.handleDynamicToolCallWithTimeout({
447
+ call: {
448
+ threadId: "thread-1",
449
+ turnId: "turn-1",
450
+ callId: "call-timeout",
451
+ namespace: null,
452
+ tool: "message",
453
+ arguments: { action: "send", text: "hello" },
454
+ },
455
+ toolBridge: {
456
+ handleToolCall: vi.fn((_call, options) => {
457
+ capturedSignal = options?.signal;
458
+ return new Promise<never>(() => undefined);
459
+ }),
460
+ },
461
+ signal: new AbortController().signal,
462
+ timeoutMs: 1,
463
+ onTimeout,
464
+ });
465
+
466
+ await vi.advanceTimersByTimeAsync(1);
467
+
468
+ await expect(response).resolves.toEqual({
469
+ success: false,
470
+ contentItems: [
471
+ {
472
+ type: "inputText",
473
+ text: "OpenClaw dynamic tool call timed out after 1ms while running tool message.",
474
+ },
475
+ ],
476
+ });
477
+ expect(capturedSignal?.aborted).toBe(true);
478
+ expect(onTimeout).toHaveBeenCalledTimes(1);
479
+ });
480
+
481
+ it("logs process poll timeout context separately from session idle", async () => {
482
+ vi.useFakeTimers();
483
+ const warn = vi.spyOn(embeddedAgentLog, "warn").mockImplementation(() => undefined);
484
+ const response = __testing.handleDynamicToolCallWithTimeout({
485
+ call: {
486
+ threadId: "thread-1",
487
+ turnId: "turn-1",
488
+ callId: "call-timeout",
489
+ namespace: null,
490
+ tool: "process",
491
+ arguments: { action: "poll", sessionId: "rapid-crustacean", timeout: 30_000 },
492
+ },
493
+ toolBridge: {
494
+ handleToolCall: vi.fn(() => new Promise<never>(() => undefined)),
495
+ },
496
+ signal: new AbortController().signal,
497
+ timeoutMs: 1,
498
+ });
499
+
500
+ await vi.advanceTimersByTimeAsync(1);
501
+
502
+ await expect(response).resolves.toEqual({
503
+ success: false,
504
+ contentItems: [
505
+ {
506
+ type: "inputText",
507
+ text: "OpenClaw dynamic tool call timed out after 1ms while waiting for process action=poll sessionId=rapid-crustacean. This is a tool RPC timeout, not a session idle timeout.",
508
+ },
509
+ ],
510
+ });
511
+ expect(warn).toHaveBeenCalledWith("codex dynamic tool call timed out", {
512
+ tool: "process",
513
+ toolCallId: "call-timeout",
514
+ threadId: "thread-1",
515
+ turnId: "turn-1",
516
+ timeoutMs: 1,
517
+ timeoutKind: "codex_dynamic_tool_rpc",
518
+ processAction: "poll",
519
+ processSessionId: "rapid-crustacean",
520
+ processRequestedTimeoutMs: 30_000,
521
+ consoleMessage:
522
+ "codex process tool timeout: action=poll sessionId=rapid-crustacean toolTimeoutMs=1 requestedWaitMs=30000; per-tool-call watchdog, not session idle; repeated lines usually mean process-poll retry churn, not model progress",
523
+ });
524
+ });
525
+
526
+ it("releases the session when Codex never completes after a dynamic tool response", async () => {
527
+ let handleRequest:
528
+ | ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
529
+ | undefined;
530
+ const request = vi.fn(async (method: string) => {
531
+ if (method === "thread/start") {
532
+ return threadStartResult("thread-1");
533
+ }
534
+ if (method === "turn/start") {
535
+ return turnStartResult("turn-1", "inProgress");
536
+ }
537
+ return {};
538
+ });
539
+ __testing.setCodexAppServerClientFactoryForTests(
540
+ async () =>
541
+ ({
542
+ request,
543
+ addNotificationHandler: () => () => undefined,
544
+ addRequestHandler: (
545
+ handler: (request: {
546
+ id: string;
547
+ method: string;
548
+ params?: unknown;
549
+ }) => Promise<unknown>,
550
+ ) => {
551
+ handleRequest = handler;
552
+ return () => undefined;
553
+ },
554
+ }) as never,
555
+ );
556
+ const params = createParams(
557
+ path.join(tempDir, "session.jsonl"),
558
+ path.join(tempDir, "workspace"),
559
+ );
560
+ params.timeoutMs = 60_000;
561
+
562
+ const run = runCodexAppServerAttempt(params, { turnCompletionIdleTimeoutMs: 5 });
563
+ await vi.waitFor(() => expect(handleRequest).toBeTypeOf("function"), { interval: 1 });
564
+
565
+ await expect(
566
+ handleRequest?.({
567
+ id: "request-tool-1",
568
+ method: "item/tool/call",
569
+ params: {
570
+ threadId: "thread-1",
571
+ turnId: "turn-1",
572
+ callId: "call-1",
573
+ namespace: null,
574
+ tool: "message",
575
+ arguments: { action: "send", text: "already sent" },
576
+ },
577
+ }),
578
+ ).resolves.toMatchObject({
579
+ success: false,
580
+ contentItems: [{ type: "inputText", text: "Unknown OpenClaw tool: message" }],
581
+ });
582
+
583
+ await expect(run).resolves.toMatchObject({
584
+ aborted: true,
585
+ timedOut: true,
586
+ promptError: "codex app-server turn idle timed out waiting for turn/completed",
587
+ });
588
+ await vi.waitFor(
589
+ () =>
590
+ expect(request).toHaveBeenCalledWith("turn/interrupt", {
591
+ threadId: "thread-1",
592
+ turnId: "turn-1",
593
+ }),
594
+ { interval: 1 },
595
+ );
596
+ expect(queueAgentHarnessMessage("session-1", "after timeout")).toBe(false);
597
+ });
598
+
599
+ it("releases the session when Codex accepts a turn but never sends progress", async () => {
600
+ const harness = createStartedThreadHarness();
601
+ const params = createParams(
602
+ path.join(tempDir, "session.jsonl"),
603
+ path.join(tempDir, "workspace"),
604
+ );
605
+ params.timeoutMs = 60_000;
606
+
607
+ const run = runCodexAppServerAttempt(params, { turnTerminalIdleTimeoutMs: 5 });
608
+ await harness.waitForMethod("turn/start");
609
+
610
+ await expect(run).resolves.toMatchObject({
611
+ aborted: true,
612
+ timedOut: true,
613
+ promptError: "codex app-server turn idle timed out waiting for turn/completed",
614
+ });
615
+ await vi.waitFor(
616
+ () =>
617
+ expect(harness.request).toHaveBeenCalledWith("turn/interrupt", {
618
+ threadId: "thread-1",
619
+ turnId: "turn-1",
620
+ }),
621
+ { interval: 1 },
622
+ );
623
+ expect(queueAgentHarnessMessage("session-1", "after silent turn")).toBe(false);
624
+ });
625
+
626
+ it("applies before_prompt_build to Codex developer instructions and turn input", async () => {
627
+ const beforePromptBuild = vi.fn(async () => ({
628
+ systemPrompt: "custom codex system",
629
+ prependSystemContext: "pre system",
630
+ appendSystemContext: "post system",
631
+ prependContext: "queued context",
632
+ }));
633
+ initializeGlobalHookRunner(
634
+ createMockPluginRegistry([{ hookName: "before_prompt_build", handler: beforePromptBuild }]),
635
+ );
636
+ const sessionFile = path.join(tempDir, "session.jsonl");
637
+ const workspaceDir = path.join(tempDir, "workspace");
638
+ const sessionManager = SessionManager.open(sessionFile);
639
+ sessionManager.appendMessage(assistantMessage("previous turn", Date.now()));
640
+ const harness = createStartedThreadHarness();
641
+
642
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir));
643
+ await harness.waitForMethod("turn/start");
644
+ await new Promise<void>((resolve) => setImmediate(resolve));
645
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
646
+ await run;
647
+
648
+ expect(beforePromptBuild).toHaveBeenCalledWith(
649
+ {
650
+ prompt: "hello",
651
+ messages: [expect.objectContaining({ role: "assistant" })],
652
+ },
653
+ expect.objectContaining({
654
+ runId: "run-1",
655
+ sessionId: "session-1",
656
+ }),
657
+ );
658
+ expect(harness.requests).toEqual(
659
+ expect.arrayContaining([
660
+ {
661
+ method: "thread/start",
662
+ params: expect.objectContaining({
663
+ developerInstructions: expect.stringContaining("pre system\n\ncustom codex system"),
664
+ }),
665
+ },
666
+ {
667
+ method: "turn/start",
668
+ params: expect.objectContaining({
669
+ input: [{ type: "text", text: "queued context\n\nhello", text_elements: [] }],
670
+ }),
671
+ },
672
+ ]),
673
+ );
674
+ });
675
+
676
+ it("fires llm_input, llm_output, and agent_end hooks for codex turns", async () => {
677
+ const llmInput = vi.fn();
678
+ const llmOutput = vi.fn();
679
+ const agentEnd = vi.fn();
680
+ const onRunAgentEvent = vi.fn();
681
+ const globalAgentEvents: AgentEventPayload[] = [];
682
+ onAgentEvent((event) => globalAgentEvents.push(event));
683
+ initializeGlobalHookRunner(
684
+ createMockPluginRegistry([
685
+ { hookName: "llm_input", handler: llmInput },
686
+ { hookName: "llm_output", handler: llmOutput },
687
+ { hookName: "agent_end", handler: agentEnd },
688
+ ]),
689
+ );
690
+ const sessionFile = path.join(tempDir, "session.jsonl");
691
+ const workspaceDir = path.join(tempDir, "workspace");
692
+ const sessionManager = SessionManager.open(sessionFile);
693
+ sessionManager.appendMessage(assistantMessage("existing context", Date.now()));
694
+ const harness = createStartedThreadHarness();
695
+
696
+ const params = createParamsWithRuntimePlan(sessionFile, workspaceDir);
697
+ params.onAgentEvent = onRunAgentEvent;
698
+ const run = runCodexAppServerAttempt(params);
699
+ await harness.waitForMethod("turn/start");
700
+ await vi.waitFor(() => expect(llmInput).toHaveBeenCalledTimes(1), { interval: 1 });
701
+
702
+ expect(llmInput).toHaveBeenCalledWith(
703
+ expect.objectContaining({
704
+ runId: "run-1",
705
+ sessionId: "session-1",
706
+ provider: "codex",
707
+ model: "gpt-5.4-codex",
708
+ prompt: "hello",
709
+ imagesCount: 0,
710
+ historyMessages: [expect.objectContaining({ role: "assistant" })],
711
+ systemPrompt: expect.stringContaining(CODEX_GPT5_BEHAVIOR_CONTRACT),
712
+ }),
713
+ expect.objectContaining({
714
+ runId: "run-1",
715
+ sessionId: "session-1",
716
+ sessionKey: "agent:main:session-1",
717
+ }),
718
+ );
719
+
720
+ await harness.notify({
721
+ method: "item/agentMessage/delta",
722
+ params: {
723
+ threadId: "thread-1",
724
+ turnId: "turn-1",
725
+ itemId: "msg-1",
726
+ delta: "hello back",
727
+ },
728
+ });
729
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
730
+ const result = await run;
731
+
732
+ expect(result.assistantTexts).toEqual(["hello back"]);
733
+ await vi.waitFor(() => expect(llmOutput).toHaveBeenCalledTimes(1), { interval: 1 });
734
+ await vi.waitFor(() => expect(agentEnd).toHaveBeenCalledTimes(1), { interval: 1 });
735
+ const agentEvents = onRunAgentEvent.mock.calls.map(([event]) => event);
736
+ expect(agentEvents).toEqual(
737
+ expect.arrayContaining([
738
+ {
739
+ stream: "lifecycle",
740
+ data: expect.objectContaining({
741
+ phase: "start",
742
+ startedAt: expect.any(Number),
743
+ }),
744
+ },
745
+ {
746
+ stream: "assistant",
747
+ data: { text: "hello back" },
748
+ },
749
+ {
750
+ stream: "lifecycle",
751
+ data: expect.objectContaining({
752
+ phase: "end",
753
+ startedAt: expect.any(Number),
754
+ endedAt: expect.any(Number),
755
+ }),
756
+ },
757
+ ]),
758
+ );
759
+ const startIndex = agentEvents.findIndex(
760
+ (event) => event.stream === "lifecycle" && event.data.phase === "start",
761
+ );
762
+ const assistantIndex = agentEvents.findIndex((event) => event.stream === "assistant");
763
+ const endIndex = agentEvents.findIndex(
764
+ (event) => event.stream === "lifecycle" && event.data.phase === "end",
765
+ );
766
+ expect(startIndex).toBeGreaterThanOrEqual(0);
767
+ expect(assistantIndex).toBeGreaterThan(startIndex);
768
+ expect(endIndex).toBeGreaterThan(assistantIndex);
769
+ expect(globalAgentEvents).toEqual(
770
+ expect.arrayContaining([
771
+ expect.objectContaining({
772
+ runId: "run-1",
773
+ sessionKey: "agent:main:session-1",
774
+ stream: "assistant",
775
+ data: { text: "hello back" },
776
+ }),
777
+ expect.objectContaining({
778
+ runId: "run-1",
779
+ sessionKey: "agent:main:session-1",
780
+ stream: "lifecycle",
781
+ data: expect.objectContaining({ phase: "end" }),
782
+ }),
783
+ ]),
784
+ );
785
+
786
+ expect(llmOutput).toHaveBeenCalledWith(
787
+ expect.objectContaining({
788
+ runId: "run-1",
789
+ sessionId: "session-1",
790
+ provider: "codex",
791
+ model: "gpt-5.4-codex",
792
+ resolvedRef: "codex/gpt-5.4-codex",
793
+ harnessId: "codex",
794
+ assistantTexts: ["hello back"],
795
+ lastAssistant: expect.objectContaining({
796
+ role: "assistant",
797
+ }),
798
+ }),
799
+ expect.objectContaining({
800
+ runId: "run-1",
801
+ sessionId: "session-1",
802
+ }),
803
+ );
804
+ expect(agentEnd).toHaveBeenCalledWith(
805
+ expect.objectContaining({
806
+ success: true,
807
+ messages: expect.arrayContaining([
808
+ expect.objectContaining({ role: "user" }),
809
+ expect.objectContaining({ role: "assistant" }),
810
+ ]),
811
+ }),
812
+ expect.objectContaining({
813
+ runId: "run-1",
814
+ sessionId: "session-1",
815
+ }),
816
+ );
817
+ });
818
+
819
+ it("forwards Codex app-server verbose tool summaries and completed output", async () => {
820
+ const onToolResult = vi.fn();
821
+ const sessionFile = path.join(tempDir, "session.jsonl");
822
+ const workspaceDir = path.join(tempDir, "workspace");
823
+ const harness = createStartedThreadHarness();
824
+ const params = createParams(sessionFile, workspaceDir);
825
+ params.verboseLevel = "full";
826
+ params.onToolResult = onToolResult;
827
+
828
+ const run = runCodexAppServerAttempt(params);
829
+ await harness.waitForMethod("turn/start");
830
+ await harness.notify({
831
+ method: "item/started",
832
+ params: {
833
+ threadId: "thread-1",
834
+ turnId: "turn-1",
835
+ item: {
836
+ type: "dynamicToolCall",
837
+ id: "tool-1",
838
+ namespace: null,
839
+ tool: "read",
840
+ arguments: { path: "README.md" },
841
+ status: "inProgress",
842
+ contentItems: null,
843
+ success: null,
844
+ durationMs: null,
845
+ },
846
+ },
847
+ });
848
+ await harness.notify({
849
+ method: "item/completed",
850
+ params: {
851
+ threadId: "thread-1",
852
+ turnId: "turn-1",
853
+ item: {
854
+ type: "dynamicToolCall",
855
+ id: "tool-1",
856
+ namespace: null,
857
+ tool: "read",
858
+ arguments: { path: "README.md" },
859
+ status: "completed",
860
+ contentItems: [{ type: "inputText", text: "file contents" }],
861
+ success: true,
862
+ durationMs: 12,
863
+ },
864
+ },
865
+ });
866
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
867
+ await run;
868
+
869
+ expect(onToolResult).toHaveBeenCalledTimes(2);
870
+ expect(onToolResult).toHaveBeenNthCalledWith(1, {
871
+ text: "📖 Read: `from README.md`",
872
+ });
873
+ expect(onToolResult).toHaveBeenNthCalledWith(2, {
874
+ text: "📖 Read: `from README.md`\n```txt\nfile contents\n```",
875
+ });
876
+ });
877
+
878
+ it("registers native hook relay config for an enabled Codex turn and cleans it up", async () => {
879
+ const sessionFile = path.join(tempDir, "session.jsonl");
880
+ const workspaceDir = path.join(tempDir, "workspace");
881
+ const harness = createStartedThreadHarness();
882
+
883
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
884
+ nativeHookRelay: {
885
+ enabled: true,
886
+ events: ["pre_tool_use"],
887
+ gatewayTimeoutMs: 4321,
888
+ hookTimeoutSec: 9,
889
+ },
890
+ });
891
+ await harness.waitForMethod("turn/start");
892
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
893
+ await run;
894
+
895
+ const startRequest = harness.requests.find((request) => request.method === "thread/start");
896
+ expect(startRequest?.params).toEqual(
897
+ expect.objectContaining({
898
+ config: expect.objectContaining({
899
+ "features.codex_hooks": true,
900
+ "hooks.PreToolUse": [
901
+ expect.objectContaining({
902
+ hooks: [
903
+ expect.objectContaining({
904
+ type: "command",
905
+ timeout: 9,
906
+ command: expect.stringContaining("--event pre_tool_use --timeout 4321"),
907
+ }),
908
+ ],
909
+ }),
910
+ ],
911
+ }),
912
+ }),
913
+ );
914
+ const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
915
+ expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
916
+ });
917
+
918
+ it("reuses the Codex native hook relay id across runs for the same session", async () => {
919
+ const sessionFile = path.join(tempDir, "session.jsonl");
920
+ const workspaceDir = path.join(tempDir, "workspace");
921
+ const firstHarness = createStartedThreadHarness();
922
+
923
+ const firstRun = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
924
+ nativeHookRelay: {
925
+ enabled: true,
926
+ events: ["pre_tool_use"],
927
+ },
928
+ });
929
+ await firstHarness.waitForMethod("turn/start");
930
+ await firstHarness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
931
+ await firstRun;
932
+
933
+ const firstStartRequest = firstHarness.requests.find(
934
+ (request) => request.method === "thread/start",
935
+ );
936
+ const firstRelayId = extractRelayIdFromThreadRequest(firstStartRequest?.params);
937
+ expect(
938
+ nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(firstRelayId),
939
+ ).toBeUndefined();
940
+
941
+ const secondHarness = createResumeHarness();
942
+ const secondParams = createParams(sessionFile, workspaceDir);
943
+ secondParams.runId = "run-2";
944
+ const secondRun = runCodexAppServerAttempt(secondParams, {
945
+ nativeHookRelay: {
946
+ enabled: true,
947
+ events: ["pre_tool_use"],
948
+ },
949
+ });
950
+ await secondHarness.waitForMethod("turn/start");
951
+
952
+ const resumeRequest = secondHarness.requests.find(
953
+ (request) => request.method === "thread/resume",
954
+ );
955
+ const secondRelayId = extractRelayIdFromThreadRequest(resumeRequest?.params);
956
+ expect(secondRelayId).toBe(firstRelayId);
957
+ expect(
958
+ nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(firstRelayId),
959
+ ).toMatchObject({
960
+ runId: "run-2",
961
+ allowedEvents: ["pre_tool_use"],
962
+ });
963
+
964
+ await secondHarness.completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
965
+ await secondRun;
966
+ expect(
967
+ nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(firstRelayId),
968
+ ).toBeUndefined();
969
+ });
970
+
971
+ it("builds deterministic opaque Codex native hook relay ids", () => {
972
+ const relayId = __testing.buildCodexNativeHookRelayId({
973
+ agentId: "dev-codex",
974
+ sessionId: "cu-pr-relay-smoke",
975
+ sessionKey: "agent:dev-codex:cu-pr-relay-smoke",
976
+ });
977
+
978
+ expect(relayId).toBe("codex-8810b5252975550c887ff0def512b25e944bac39");
979
+ expect(relayId).not.toContain("dev-codex");
980
+ expect(relayId).not.toContain("cu-pr-relay-smoke");
981
+ });
982
+
983
+ it("sends clearing Codex native hook config when the relay is disabled", async () => {
984
+ const sessionFile = path.join(tempDir, "session.jsonl");
985
+ const workspaceDir = path.join(tempDir, "workspace");
986
+ const harness = createStartedThreadHarness();
987
+
988
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
989
+ nativeHookRelay: { enabled: false },
990
+ });
991
+ await harness.waitForMethod("turn/start");
992
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
993
+ await run;
994
+
995
+ const startRequest = harness.requests.find((request) => request.method === "thread/start");
996
+ expect(startRequest?.params).toEqual(
997
+ expect.objectContaining({
998
+ config: {
999
+ "features.codex_hooks": false,
1000
+ "hooks.PreToolUse": [],
1001
+ "hooks.PostToolUse": [],
1002
+ "hooks.PermissionRequest": [],
1003
+ "hooks.Stop": [],
1004
+ },
1005
+ }),
1006
+ );
1007
+ });
1008
+
1009
+ it("cleans up native hook relay state when turn/start fails", async () => {
1010
+ const sessionFile = path.join(tempDir, "session.jsonl");
1011
+ const workspaceDir = path.join(tempDir, "workspace");
1012
+ const harness = createStartedThreadHarness(async (method) => {
1013
+ if (method === "turn/start") {
1014
+ throw new Error("turn start exploded");
1015
+ }
1016
+ return undefined;
1017
+ });
1018
+
1019
+ await expect(
1020
+ runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
1021
+ nativeHookRelay: { enabled: true },
1022
+ }),
1023
+ ).rejects.toThrow("turn start exploded");
1024
+
1025
+ const startRequest = harness.requests.find((request) => request.method === "thread/start");
1026
+ const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
1027
+ expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
1028
+ });
1029
+
1030
+ it("cleans up native hook relay state when the Codex turn aborts", async () => {
1031
+ const sessionFile = path.join(tempDir, "session.jsonl");
1032
+ const workspaceDir = path.join(tempDir, "workspace");
1033
+ const harness = createStartedThreadHarness();
1034
+
1035
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
1036
+ nativeHookRelay: { enabled: true },
1037
+ });
1038
+ await harness.waitForMethod("turn/start");
1039
+ const startRequest = harness.requests.find((request) => request.method === "thread/start");
1040
+ const relayId = extractRelayIdFromThreadRequest(startRequest?.params);
1041
+ expect(abortAgentHarnessRun("session-1")).toBe(true);
1042
+
1043
+ const result = await run;
1044
+
1045
+ expect(result.aborted).toBe(true);
1046
+ expect(nativeHookRelayTesting.getNativeHookRelayRegistrationForTests(relayId)).toBeUndefined();
1047
+ });
1048
+
1049
+ it("fires agent_end with failure metadata when the codex turn fails", async () => {
1050
+ const agentEnd = vi.fn();
1051
+ const onRunAgentEvent = vi.fn();
1052
+ initializeGlobalHookRunner(
1053
+ createMockPluginRegistry([{ hookName: "agent_end", handler: agentEnd }]),
1054
+ );
1055
+ const sessionFile = path.join(tempDir, "session.jsonl");
1056
+ const workspaceDir = path.join(tempDir, "workspace");
1057
+ const harness = createStartedThreadHarness();
1058
+
1059
+ const params = createParams(sessionFile, workspaceDir);
1060
+ params.onAgentEvent = onRunAgentEvent;
1061
+ const run = runCodexAppServerAttempt(params);
1062
+ await harness.waitForMethod("turn/start");
1063
+ await harness.notify({
1064
+ method: "turn/completed",
1065
+ params: {
1066
+ threadId: "thread-1",
1067
+ turnId: "turn-1",
1068
+ turn: {
1069
+ id: "turn-1",
1070
+ status: "failed",
1071
+ error: { message: "codex exploded" },
1072
+ },
1073
+ },
1074
+ });
1075
+
1076
+ const result = await run;
1077
+
1078
+ expect(result.promptError).toBe("codex exploded");
1079
+ await vi.waitFor(() => expect(agentEnd).toHaveBeenCalledTimes(1), { interval: 1 });
1080
+ const agentEvents = onRunAgentEvent.mock.calls.map(([event]) => event);
1081
+ expect(agentEvents).toEqual(
1082
+ expect.arrayContaining([
1083
+ {
1084
+ stream: "lifecycle",
1085
+ data: expect.objectContaining({ phase: "start", startedAt: expect.any(Number) }),
1086
+ },
1087
+ {
1088
+ stream: "lifecycle",
1089
+ data: expect.objectContaining({
1090
+ phase: "error",
1091
+ startedAt: expect.any(Number),
1092
+ endedAt: expect.any(Number),
1093
+ error: "codex exploded",
1094
+ }),
1095
+ },
1096
+ ]),
1097
+ );
1098
+ expect(agentEvents.some((event) => event.stream === "assistant")).toBe(false);
1099
+ expect(agentEnd).toHaveBeenCalledWith(
1100
+ expect.objectContaining({
1101
+ success: false,
1102
+ error: "codex exploded",
1103
+ }),
1104
+ expect.objectContaining({
1105
+ runId: "run-1",
1106
+ sessionId: "session-1",
1107
+ }),
1108
+ );
1109
+ });
1110
+
1111
+ it("fires llm_output and agent_end when turn/start fails", async () => {
1112
+ const llmInput = vi.fn();
1113
+ const llmOutput = vi.fn();
1114
+ const agentEnd = vi.fn();
1115
+ initializeGlobalHookRunner(
1116
+ createMockPluginRegistry([
1117
+ { hookName: "llm_input", handler: llmInput },
1118
+ { hookName: "llm_output", handler: llmOutput },
1119
+ { hookName: "agent_end", handler: agentEnd },
1120
+ ]),
1121
+ );
1122
+ const sessionFile = path.join(tempDir, "session.jsonl");
1123
+ const workspaceDir = path.join(tempDir, "workspace");
1124
+ SessionManager.open(sessionFile).appendMessage(
1125
+ assistantMessage("existing context", Date.now()),
1126
+ );
1127
+ createStartedThreadHarness(async (method) => {
1128
+ if (method === "turn/start") {
1129
+ throw new Error("turn start exploded");
1130
+ }
1131
+ return undefined;
1132
+ });
1133
+
1134
+ await expect(
1135
+ runCodexAppServerAttempt(createParamsWithRuntimePlan(sessionFile, workspaceDir)),
1136
+ ).rejects.toThrow("turn start exploded");
1137
+
1138
+ await vi.waitFor(() => expect(llmInput).toHaveBeenCalledTimes(1), { interval: 1 });
1139
+ await vi.waitFor(() => expect(llmOutput).toHaveBeenCalledTimes(1), { interval: 1 });
1140
+ await vi.waitFor(() => expect(agentEnd).toHaveBeenCalledTimes(1), { interval: 1 });
1141
+ expect(llmOutput).toHaveBeenCalledWith(
1142
+ expect.objectContaining({
1143
+ assistantTexts: [],
1144
+ model: "gpt-5.4-codex",
1145
+ provider: "codex",
1146
+ resolvedRef: "codex/gpt-5.4-codex",
1147
+ harnessId: "codex",
1148
+ runId: "run-1",
1149
+ sessionId: "session-1",
1150
+ }),
1151
+ expect.any(Object),
1152
+ );
1153
+ expect(agentEnd).toHaveBeenCalledWith(
1154
+ expect.objectContaining({
1155
+ success: false,
1156
+ error: "turn start exploded",
1157
+ messages: expect.arrayContaining([
1158
+ expect.objectContaining({ role: "assistant" }),
1159
+ expect.objectContaining({ role: "user" }),
1160
+ ]),
1161
+ }),
1162
+ expect.any(Object),
1163
+ );
1164
+ });
1165
+
1166
+ it("fires agent_end with success false when the codex turn is aborted", async () => {
1167
+ const agentEnd = vi.fn();
1168
+ initializeGlobalHookRunner(
1169
+ createMockPluginRegistry([{ hookName: "agent_end", handler: agentEnd }]),
1170
+ );
1171
+ const { waitForMethod } = createStartedThreadHarness();
1172
+ const run = runCodexAppServerAttempt(
1173
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1174
+ );
1175
+
1176
+ await waitForMethod("turn/start");
1177
+ expect(abortAgentHarnessRun("session-1")).toBe(true);
1178
+
1179
+ const result = await run;
1180
+ expect(result.aborted).toBe(true);
1181
+ await vi.waitFor(() => expect(agentEnd).toHaveBeenCalledTimes(1), { interval: 1 });
1182
+ expect(agentEnd).toHaveBeenCalledWith(
1183
+ expect.objectContaining({
1184
+ success: false,
1185
+ }),
1186
+ expect.any(Object),
1187
+ );
1188
+ });
1189
+
1190
+ it("forwards queued user input and aborts the active app-server turn", async () => {
1191
+ const { requests, waitForMethod } = createStartedThreadHarness();
1192
+
1193
+ const run = runCodexAppServerAttempt(
1194
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1195
+ );
1196
+ await waitForMethod("turn/start");
1197
+
1198
+ expect(queueAgentHarnessMessage("session-1", "more context")).toBe(true);
1199
+ await vi.waitFor(
1200
+ () => expect(requests.some((entry) => entry.method === "turn/steer")).toBe(true),
1201
+ { interval: 1 },
1202
+ );
1203
+ expect(abortAgentHarnessRun("session-1")).toBe(true);
1204
+ await vi.waitFor(
1205
+ () => expect(requests.some((entry) => entry.method === "turn/interrupt")).toBe(true),
1206
+ { interval: 1 },
1207
+ );
1208
+
1209
+ const result = await run;
1210
+ expect(result.aborted).toBe(true);
1211
+ expect(requests).toEqual(
1212
+ expect.arrayContaining([
1213
+ {
1214
+ method: "thread/start",
1215
+ params: expect.objectContaining({
1216
+ model: "gpt-5.4-codex",
1217
+ approvalPolicy: "never",
1218
+ sandbox: "danger-full-access",
1219
+ approvalsReviewer: "user",
1220
+ developerInstructions: expect.stringContaining(CODEX_GPT5_BEHAVIOR_CONTRACT),
1221
+ }),
1222
+ },
1223
+ {
1224
+ method: "turn/steer",
1225
+ params: {
1226
+ threadId: "thread-1",
1227
+ expectedTurnId: "turn-1",
1228
+ input: [{ type: "text", text: "more context", text_elements: [] }],
1229
+ },
1230
+ },
1231
+ {
1232
+ method: "turn/interrupt",
1233
+ params: { threadId: "thread-1", turnId: "turn-1" },
1234
+ },
1235
+ ]),
1236
+ );
1237
+ });
1238
+
1239
+ it("batches default queued steering before sending turn/steer", async () => {
1240
+ const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
1241
+
1242
+ const run = runCodexAppServerAttempt(
1243
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1244
+ );
1245
+ await waitForMethod("turn/start");
1246
+
1247
+ expect(queueAgentHarnessMessage("session-1", "first", { debounceMs: 5 })).toBe(true);
1248
+ expect(queueAgentHarnessMessage("session-1", "second", { debounceMs: 5 })).toBe(true);
1249
+
1250
+ await vi.waitFor(
1251
+ () =>
1252
+ expect(requests.filter((entry) => entry.method === "turn/steer")).toEqual([
1253
+ {
1254
+ method: "turn/steer",
1255
+ params: {
1256
+ threadId: "thread-1",
1257
+ expectedTurnId: "turn-1",
1258
+ input: [
1259
+ { type: "text", text: "first", text_elements: [] },
1260
+ { type: "text", text: "second", text_elements: [] },
1261
+ ],
1262
+ },
1263
+ },
1264
+ ]),
1265
+ { interval: 1 },
1266
+ );
1267
+
1268
+ await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1269
+ await run;
1270
+ });
1271
+
1272
+ it("flushes pending default queued steering during normal turn cleanup", async () => {
1273
+ const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
1274
+
1275
+ const run = runCodexAppServerAttempt(
1276
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1277
+ );
1278
+ await waitForMethod("turn/start");
1279
+
1280
+ expect(queueAgentHarnessMessage("session-1", "late steer", { debounceMs: 30_000 })).toBe(true);
1281
+
1282
+ await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1283
+ await run;
1284
+
1285
+ expect(requests.filter((entry) => entry.method === "turn/steer")).toEqual([
1286
+ {
1287
+ method: "turn/steer",
1288
+ params: {
1289
+ threadId: "thread-1",
1290
+ expectedTurnId: "turn-1",
1291
+ input: [{ type: "text", text: "late steer", text_elements: [] }],
1292
+ },
1293
+ },
1294
+ ]);
1295
+ });
1296
+
1297
+ it("keeps legacy queue steering as separate turn/steer requests", async () => {
1298
+ const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
1299
+
1300
+ const run = runCodexAppServerAttempt(
1301
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1302
+ );
1303
+ await waitForMethod("turn/start");
1304
+
1305
+ expect(queueAgentHarnessMessage("session-1", "first", { steeringMode: "one-at-a-time" })).toBe(
1306
+ true,
1307
+ );
1308
+ expect(queueAgentHarnessMessage("session-1", "second", { steeringMode: "one-at-a-time" })).toBe(
1309
+ true,
1310
+ );
1311
+
1312
+ await vi.waitFor(
1313
+ () =>
1314
+ expect(requests.filter((entry) => entry.method === "turn/steer")).toEqual([
1315
+ {
1316
+ method: "turn/steer",
1317
+ params: {
1318
+ threadId: "thread-1",
1319
+ expectedTurnId: "turn-1",
1320
+ input: [{ type: "text", text: "first", text_elements: [] }],
1321
+ },
1322
+ },
1323
+ {
1324
+ method: "turn/steer",
1325
+ params: {
1326
+ threadId: "thread-1",
1327
+ expectedTurnId: "turn-1",
1328
+ input: [{ type: "text", text: "second", text_elements: [] }],
1329
+ },
1330
+ },
1331
+ ]),
1332
+ { interval: 1 },
1333
+ );
1334
+
1335
+ await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1336
+ await run;
1337
+ });
1338
+
1339
+ it("routes MCP approval elicitations through the native bridge", async () => {
1340
+ let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
1341
+ let handleRequest:
1342
+ | ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
1343
+ | undefined;
1344
+ const bridgeSpy = vi
1345
+ .spyOn(elicitationBridge, "handleCodexAppServerElicitationRequest")
1346
+ .mockResolvedValue({
1347
+ action: "accept",
1348
+ content: null,
1349
+ _meta: null,
1350
+ });
1351
+ const request = vi.fn(async (method: string) => {
1352
+ if (method === "thread/start") {
1353
+ return threadStartResult();
1354
+ }
1355
+ if (method === "turn/start") {
1356
+ return turnStartResult();
1357
+ }
1358
+ return {};
1359
+ });
1360
+ __testing.setCodexAppServerClientFactoryForTests(
1361
+ async () =>
1362
+ ({
1363
+ request,
1364
+ addNotificationHandler: (handler: typeof notify) => {
1365
+ notify = handler;
1366
+ return () => undefined;
1367
+ },
1368
+ addRequestHandler: (
1369
+ handler: (request: {
1370
+ id: string;
1371
+ method: string;
1372
+ params?: unknown;
1373
+ }) => Promise<unknown>,
1374
+ ) => {
1375
+ handleRequest = handler;
1376
+ return () => undefined;
1377
+ },
1378
+ }) as never,
1379
+ );
1380
+
1381
+ const run = runCodexAppServerAttempt(
1382
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1383
+ );
1384
+ await vi.waitFor(() => expect(handleRequest).toBeTypeOf("function"), { interval: 1 });
1385
+
1386
+ const result = await handleRequest?.({
1387
+ id: "request-elicitation-1",
1388
+ method: "mcpServer/elicitation/request",
1389
+ params: {
1390
+ threadId: "thread-1",
1391
+ turnId: "turn-1",
1392
+ serverName: "codex_apps__github",
1393
+ mode: "form",
1394
+ },
1395
+ });
1396
+
1397
+ expect(result).toEqual({
1398
+ action: "accept",
1399
+ content: null,
1400
+ _meta: null,
1401
+ });
1402
+ expect(bridgeSpy).toHaveBeenCalledWith(
1403
+ expect.objectContaining({
1404
+ threadId: "thread-1",
1405
+ turnId: "turn-1",
1406
+ }),
1407
+ );
1408
+
1409
+ await notify({
1410
+ method: "turn/completed",
1411
+ params: {
1412
+ threadId: "thread-1",
1413
+ turnId: "turn-1",
1414
+ turn: { id: "turn-1", status: "completed" },
1415
+ },
1416
+ });
1417
+ await run;
1418
+ });
1419
+
1420
+ it("routes request_user_input prompts through the active run follow-up queue", async () => {
1421
+ let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
1422
+ let handleRequest:
1423
+ | ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
1424
+ | undefined;
1425
+ const request = vi.fn(async (method: string) => {
1426
+ if (method === "thread/start") {
1427
+ return threadStartResult();
1428
+ }
1429
+ if (method === "turn/start") {
1430
+ return turnStartResult();
1431
+ }
1432
+ return {};
1433
+ });
1434
+ __testing.setCodexAppServerClientFactoryForTests(
1435
+ async () =>
1436
+ ({
1437
+ request,
1438
+ addNotificationHandler: (handler: typeof notify) => {
1439
+ notify = handler;
1440
+ return () => undefined;
1441
+ },
1442
+ addRequestHandler: (
1443
+ handler: (request: {
1444
+ id: string;
1445
+ method: string;
1446
+ params?: unknown;
1447
+ }) => Promise<unknown>,
1448
+ ) => {
1449
+ handleRequest = handler;
1450
+ return () => undefined;
1451
+ },
1452
+ }) as never,
1453
+ );
1454
+
1455
+ const params = createParams(
1456
+ path.join(tempDir, "session.jsonl"),
1457
+ path.join(tempDir, "workspace"),
1458
+ );
1459
+ params.onBlockReply = vi.fn();
1460
+ const run = runCodexAppServerAttempt(params);
1461
+ await vi.waitFor(
1462
+ () => expect(request.mock.calls.some(([method]) => method === "turn/start")).toBe(true),
1463
+ { interval: 1 },
1464
+ );
1465
+ await vi.waitFor(() => expect(handleRequest).toBeTypeOf("function"), { interval: 1 });
1466
+
1467
+ const response = handleRequest?.({
1468
+ id: "request-input-1",
1469
+ method: "item/tool/requestUserInput",
1470
+ params: {
1471
+ threadId: "thread-1",
1472
+ turnId: "turn-1",
1473
+ itemId: "ask-1",
1474
+ questions: [
1475
+ {
1476
+ id: "mode",
1477
+ header: "Mode",
1478
+ question: "Pick a mode",
1479
+ isOther: false,
1480
+ isSecret: false,
1481
+ options: [
1482
+ { label: "Fast", description: "Use less reasoning" },
1483
+ { label: "Deep", description: "Use more reasoning" },
1484
+ ],
1485
+ },
1486
+ ],
1487
+ },
1488
+ });
1489
+
1490
+ await vi.waitFor(() => expect(params.onBlockReply).toHaveBeenCalledTimes(1), { interval: 1 });
1491
+ expect(queueAgentHarnessMessage("session-1", "2")).toBe(true);
1492
+ await expect(response).resolves.toEqual({
1493
+ answers: { mode: { answers: ["Deep"] } },
1494
+ });
1495
+ expect(request).not.toHaveBeenCalledWith(
1496
+ "turn/steer",
1497
+ expect.objectContaining({ expectedTurnId: "turn-1" }),
1498
+ );
1499
+
1500
+ await notify({
1501
+ method: "turn/completed",
1502
+ params: {
1503
+ threadId: "thread-1",
1504
+ turnId: "turn-1",
1505
+ turn: { id: "turn-1", status: "completed" },
1506
+ },
1507
+ });
1508
+ await run;
1509
+ });
1510
+
1511
+ it("does not leak unhandled rejections when shutdown closes before interrupt", async () => {
1512
+ const unhandledRejections: unknown[] = [];
1513
+ const onUnhandledRejection = (reason: unknown) => {
1514
+ unhandledRejections.push(reason);
1515
+ };
1516
+ process.on("unhandledRejection", onUnhandledRejection);
1517
+ try {
1518
+ const { waitForMethod } = createStartedThreadHarness(async (method) => {
1519
+ if (method === "turn/interrupt") {
1520
+ throw new Error("codex app-server client is closed");
1521
+ }
1522
+ });
1523
+ const abortController = new AbortController();
1524
+ const params = createParams(
1525
+ path.join(tempDir, "session.jsonl"),
1526
+ path.join(tempDir, "workspace"),
1527
+ );
1528
+ params.abortSignal = abortController.signal;
1529
+
1530
+ const run = runCodexAppServerAttempt(params);
1531
+ await waitForMethod("turn/start");
1532
+ abortController.abort("shutdown");
1533
+
1534
+ await expect(run).resolves.toMatchObject({ aborted: true });
1535
+ await new Promise((resolve) => setImmediate(resolve));
1536
+ expect(unhandledRejections).toEqual([]);
1537
+ } finally {
1538
+ process.off("unhandledRejection", onUnhandledRejection);
1539
+ }
1540
+ });
1541
+
1542
+ it("forwards image attachments to the app-server turn input", async () => {
1543
+ const { requests, waitForMethod, completeTurn } = createStartedThreadHarness();
1544
+ const params = createParams(
1545
+ path.join(tempDir, "session.jsonl"),
1546
+ path.join(tempDir, "workspace"),
1547
+ );
1548
+ params.model = createCodexTestModel("codex", ["text", "image"]);
1549
+ params.images = [
1550
+ {
1551
+ type: "image",
1552
+ mimeType: "image/png",
1553
+ data: "aW1hZ2UtYnl0ZXM=",
1554
+ },
1555
+ ];
1556
+
1557
+ const run = runCodexAppServerAttempt(params);
1558
+ await waitForMethod("turn/start");
1559
+ await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1560
+ await run;
1561
+
1562
+ expect(requests).toEqual(
1563
+ expect.arrayContaining([
1564
+ {
1565
+ method: "turn/start",
1566
+ params: expect.objectContaining({
1567
+ input: [
1568
+ { type: "text", text: "hello", text_elements: [] },
1569
+ { type: "image", url: "data:image/png;base64,aW1hZ2UtYnl0ZXM=" },
1570
+ ],
1571
+ }),
1572
+ },
1573
+ ]),
1574
+ );
1575
+ });
1576
+
1577
+ it("does not drop turn completion notifications emitted while turn/start is in flight", async () => {
1578
+ let harness: ReturnType<typeof createAppServerHarness>;
1579
+ harness = createAppServerHarness(async (method) => {
1580
+ if (method === "thread/start") {
1581
+ return threadStartResult();
1582
+ }
1583
+ if (method === "turn/start") {
1584
+ await harness.completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1585
+ return turnStartResult("turn-1", "completed");
1586
+ }
1587
+ return {};
1588
+ });
1589
+
1590
+ await expect(
1591
+ runCodexAppServerAttempt(
1592
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1593
+ ),
1594
+ ).resolves.toMatchObject({
1595
+ aborted: false,
1596
+ timedOut: false,
1597
+ });
1598
+ });
1599
+
1600
+ it("completes when turn/start returns a terminal turn without a follow-up notification", async () => {
1601
+ const harness = createAppServerHarness(async (method) => {
1602
+ if (method === "thread/start") {
1603
+ return threadStartResult();
1604
+ }
1605
+ if (method === "turn/start") {
1606
+ return {
1607
+ turn: {
1608
+ id: "turn-1",
1609
+ status: "completed",
1610
+ items: [{ type: "agentMessage", id: "msg-1", text: "done from response" }],
1611
+ },
1612
+ };
1613
+ }
1614
+ return {};
1615
+ });
1616
+
1617
+ const result = await runCodexAppServerAttempt(
1618
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1619
+ );
1620
+
1621
+ expect(harness.requests.map((entry) => entry.method)).toContain("turn/start");
1622
+ expect(result).toMatchObject({
1623
+ assistantTexts: ["done from response"],
1624
+ aborted: false,
1625
+ timedOut: false,
1626
+ });
1627
+ });
1628
+
1629
+ it("does not complete on unscoped turn/completed notifications", async () => {
1630
+ const harness = createStartedThreadHarness();
1631
+ const run = runCodexAppServerAttempt(
1632
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1633
+ );
1634
+ let resolved = false;
1635
+ void run.then(() => {
1636
+ resolved = true;
1637
+ });
1638
+
1639
+ await harness.waitForMethod("turn/start");
1640
+ await harness.notify({
1641
+ method: "turn/completed",
1642
+ params: {
1643
+ turn: {
1644
+ id: "turn-1",
1645
+ status: "completed",
1646
+ items: [{ type: "agentMessage", id: "msg-wrong", text: "wrong completion" }],
1647
+ },
1648
+ },
1649
+ });
1650
+ await new Promise((resolve) => setTimeout(resolve, 25));
1651
+ expect(resolved).toBe(false);
1652
+
1653
+ await harness.notify({
1654
+ method: "turn/completed",
1655
+ params: {
1656
+ threadId: "thread-1",
1657
+ turn: {
1658
+ id: "turn-1",
1659
+ status: "completed",
1660
+ items: [{ type: "agentMessage", id: "msg-right", text: "final completion" }],
1661
+ },
1662
+ },
1663
+ });
1664
+
1665
+ await expect(run).resolves.toMatchObject({
1666
+ assistantTexts: ["final completion"],
1667
+ aborted: false,
1668
+ timedOut: false,
1669
+ });
1670
+ });
1671
+
1672
+ it("releases completion when a projector callback throws during turn/completed", async () => {
1673
+ // Regression for openclaw/openclaw#67996: a throw inside the projector's
1674
+ // turn/completed handler must not strand resolveCompletion, otherwise the
1675
+ // gateway session lane stays locked and every follow-up message queues
1676
+ // behind a run that will never resolve.
1677
+ let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
1678
+ const request = vi.fn(async (method: string) => {
1679
+ if (method === "thread/start") {
1680
+ return threadStartResult("thread-1");
1681
+ }
1682
+ if (method === "turn/start") {
1683
+ return turnStartResult("turn-1", "inProgress");
1684
+ }
1685
+ return {};
1686
+ });
1687
+ __testing.setCodexAppServerClientFactoryForTests(
1688
+ async () =>
1689
+ ({
1690
+ request,
1691
+ addNotificationHandler: (handler: typeof notify) => {
1692
+ notify = handler;
1693
+ return () => undefined;
1694
+ },
1695
+ addRequestHandler: () => () => undefined,
1696
+ }) as never,
1697
+ );
1698
+ const params = createParams(
1699
+ path.join(tempDir, "session.jsonl"),
1700
+ path.join(tempDir, "workspace"),
1701
+ );
1702
+ params.onAgentEvent = () => {
1703
+ throw new Error("downstream consumer exploded");
1704
+ };
1705
+ const run = runCodexAppServerAttempt(params);
1706
+ await vi.waitFor(() =>
1707
+ expect(request.mock.calls.some(([method]) => method === "turn/start")).toBe(true),
1708
+ );
1709
+ await notify({
1710
+ method: "turn/completed",
1711
+ params: {
1712
+ threadId: "thread-1",
1713
+ turn: {
1714
+ id: "turn-1",
1715
+ status: "completed",
1716
+ items: [{ id: "plan-1", type: "plan", text: "step one\nstep two" }],
1717
+ },
1718
+ },
1719
+ });
1720
+ await expect(run).resolves.toMatchObject({
1721
+ aborted: false,
1722
+ timedOut: false,
1723
+ });
1724
+ });
1725
+
1726
+ it("routes MCP approval elicitations through the native bridge", async () => {
1727
+ let notify: (notification: CodexServerNotification) => Promise<void> = async () => undefined;
1728
+ let handleRequest:
1729
+ | ((request: { id: string; method: string; params?: unknown }) => Promise<unknown>)
1730
+ | undefined;
1731
+ const bridgeSpy = vi
1732
+ .spyOn(elicitationBridge, "handleCodexAppServerElicitationRequest")
1733
+ .mockResolvedValue({
1734
+ action: "accept",
1735
+ content: { approve: true },
1736
+ _meta: null,
1737
+ });
1738
+ const request = vi.fn(async (method: string) => {
1739
+ if (method === "thread/start") {
1740
+ return threadStartResult("thread-1");
1741
+ }
1742
+ if (method === "turn/start") {
1743
+ return turnStartResult("turn-1", "inProgress");
1744
+ }
1745
+ return {};
1746
+ });
1747
+ __testing.setCodexAppServerClientFactoryForTests(
1748
+ async () =>
1749
+ ({
1750
+ request,
1751
+ addNotificationHandler: (handler: typeof notify) => {
1752
+ notify = handler;
1753
+ return () => undefined;
1754
+ },
1755
+ addRequestHandler: (
1756
+ handler: (request: {
1757
+ id: string;
1758
+ method: string;
1759
+ params?: unknown;
1760
+ }) => Promise<unknown>,
1761
+ ) => {
1762
+ handleRequest = handler;
1763
+ return () => undefined;
1764
+ },
1765
+ }) as never,
1766
+ );
1767
+
1768
+ const run = runCodexAppServerAttempt(
1769
+ createParams(path.join(tempDir, "session.jsonl"), path.join(tempDir, "workspace")),
1770
+ );
1771
+ await vi.waitFor(() => expect(handleRequest).toBeTypeOf("function"));
1772
+
1773
+ const result = await handleRequest?.({
1774
+ id: "request-elicitation-1",
1775
+ method: "mcpServer/elicitation/request",
1776
+ params: {
1777
+ threadId: "thread-1",
1778
+ turnId: "turn-1",
1779
+ serverName: "codex_apps__github",
1780
+ mode: "form",
1781
+ },
1782
+ });
1783
+
1784
+ expect(result).toEqual({
1785
+ action: "accept",
1786
+ content: { approve: true },
1787
+ _meta: null,
1788
+ });
1789
+ expect(bridgeSpy).toHaveBeenCalledWith(
1790
+ expect.objectContaining({
1791
+ threadId: "thread-1",
1792
+ turnId: "turn-1",
1793
+ }),
1794
+ );
1795
+
1796
+ await notify({
1797
+ method: "turn/completed",
1798
+ params: {
1799
+ threadId: "thread-1",
1800
+ turnId: "turn-1",
1801
+ turn: { id: "turn-1", status: "completed" },
1802
+ },
1803
+ });
1804
+ await run;
1805
+ });
1806
+
1807
+ it("times out app-server startup before thread setup can hang forever", async () => {
1808
+ __testing.setCodexAppServerClientFactoryForTests(() => new Promise<never>(() => undefined));
1809
+ const params = createParams(
1810
+ path.join(tempDir, "session.jsonl"),
1811
+ path.join(tempDir, "workspace"),
1812
+ );
1813
+ params.timeoutMs = 1;
1814
+
1815
+ await expect(runCodexAppServerAttempt(params, { startupTimeoutFloorMs: 1 })).rejects.toThrow(
1816
+ "codex app-server startup timed out",
1817
+ );
1818
+ expect(queueAgentHarnessMessage("session-1", "after timeout")).toBe(false);
1819
+ });
1820
+
1821
+ it("passes the selected auth profile into app-server startup", async () => {
1822
+ const seenAuthProfileIds: Array<string | undefined> = [];
1823
+ const seenAgentDirs: Array<string | undefined> = [];
1824
+ const { requests, waitForMethod, completeTurn } = createStartedThreadHarness(undefined, {
1825
+ onStart: (authProfileId, agentDir) => {
1826
+ seenAuthProfileIds.push(authProfileId);
1827
+ seenAgentDirs.push(agentDir);
1828
+ },
1829
+ });
1830
+ const params = createParams(
1831
+ path.join(tempDir, "session.jsonl"),
1832
+ path.join(tempDir, "workspace"),
1833
+ );
1834
+ params.authProfileId = "openai-codex:work";
1835
+ params.agentDir = path.join(tempDir, "agent");
1836
+
1837
+ const run = runCodexAppServerAttempt(params);
1838
+ await vi.waitFor(() => expect(seenAuthProfileIds).toEqual(["openai-codex:work"]), {
1839
+ interval: 1,
1840
+ });
1841
+ await waitForMethod("turn/start");
1842
+ await new Promise<void>((resolve) => setImmediate(resolve));
1843
+ await completeTurn({ threadId: "thread-1", turnId: "turn-1" });
1844
+ await run;
1845
+
1846
+ expect(seenAuthProfileIds).toEqual(["openai-codex:work"]);
1847
+ expect(seenAgentDirs).toEqual([path.join(tempDir, "agent")]);
1848
+ expect(requests.map((entry) => entry.method)).toContain("turn/start");
1849
+ });
1850
+
1851
+ it("times out turn start before the active run handle is installed", async () => {
1852
+ const request = vi.fn(
1853
+ async (method: string, _params?: unknown, options?: { timeoutMs?: number }) => {
1854
+ if (method === "thread/start") {
1855
+ return threadStartResult("thread-1");
1856
+ }
1857
+ if (method === "turn/start") {
1858
+ return await new Promise<never>((_, reject) => {
1859
+ setTimeout(() => reject(new Error("turn/start timed out")), options?.timeoutMs ?? 0);
1860
+ });
1861
+ }
1862
+ return {};
1863
+ },
1864
+ );
1865
+ __testing.setCodexAppServerClientFactoryForTests(
1866
+ async () =>
1867
+ ({
1868
+ request,
1869
+ addNotificationHandler: () => () => undefined,
1870
+ addRequestHandler: () => () => undefined,
1871
+ }) as never,
1872
+ );
1873
+ const params = createParams(
1874
+ path.join(tempDir, "session.jsonl"),
1875
+ path.join(tempDir, "workspace"),
1876
+ );
1877
+ params.timeoutMs = 1;
1878
+
1879
+ await expect(runCodexAppServerAttempt(params)).rejects.toThrow("turn/start timed out");
1880
+ expect(queueAgentHarnessMessage("session-1", "after timeout")).toBe(false);
1881
+ });
1882
+
1883
+ it("keeps extended history enabled when resuming a bound Codex thread", async () => {
1884
+ const sessionFile = path.join(tempDir, "session.jsonl");
1885
+ const workspaceDir = path.join(tempDir, "workspace");
1886
+ await writeExistingBinding(sessionFile, workspaceDir, { dynamicToolsFingerprint: "[]" });
1887
+ const { requests, waitForMethod, completeTurn } = createResumeHarness();
1888
+
1889
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir));
1890
+ await waitForMethod("turn/start");
1891
+ await completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
1892
+ await run;
1893
+
1894
+ expectResumeRequest(requests, {
1895
+ threadId: "thread-existing",
1896
+ model: "gpt-5.4-codex",
1897
+ approvalPolicy: "never",
1898
+ approvalsReviewer: "user",
1899
+ sandbox: "danger-full-access",
1900
+ developerInstructions: expect.stringContaining(CODEX_GPT5_BEHAVIOR_CONTRACT),
1901
+ persistExtendedHistory: true,
1902
+ });
1903
+ });
1904
+
1905
+ it("resumes a bound Codex thread when only dynamic tool descriptions change", async () => {
1906
+ const sessionFile = path.join(tempDir, "session.jsonl");
1907
+ const workspaceDir = path.join(tempDir, "workspace");
1908
+ const params = createParams(sessionFile, workspaceDir);
1909
+ const appServer = createThreadLifecycleAppServerOptions();
1910
+ const request = vi.fn(async (method: string) => {
1911
+ if (method === "thread/start") {
1912
+ return threadStartResult("thread-existing");
1913
+ }
1914
+ if (method === "thread/resume") {
1915
+ return threadStartResult("thread-existing");
1916
+ }
1917
+ throw new Error(`unexpected method: ${method}`);
1918
+ });
1919
+
1920
+ await startOrResumeThread({
1921
+ client: { request } as never,
1922
+ params,
1923
+ cwd: workspaceDir,
1924
+ dynamicTools: [
1925
+ createMessageDynamicTool("Send and manage messages for the current Slack thread."),
1926
+ ],
1927
+ appServer,
1928
+ });
1929
+ const binding = await startOrResumeThread({
1930
+ client: { request } as never,
1931
+ params,
1932
+ cwd: workspaceDir,
1933
+ dynamicTools: [
1934
+ createMessageDynamicTool("Send and manage messages for the current Discord channel."),
1935
+ ],
1936
+ appServer,
1937
+ });
1938
+
1939
+ expect(binding.threadId).toBe("thread-existing");
1940
+ expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start", "thread/resume"]);
1941
+ });
1942
+
1943
+ it("passes native hook relay config on thread start and resume", async () => {
1944
+ const sessionFile = path.join(tempDir, "session.jsonl");
1945
+ const workspaceDir = path.join(tempDir, "workspace");
1946
+ const params = createParams(sessionFile, workspaceDir);
1947
+ const appServer = createThreadLifecycleAppServerOptions();
1948
+ const request = vi.fn(async (method: string) => {
1949
+ if (method === "thread/start") {
1950
+ return threadStartResult("thread-existing");
1951
+ }
1952
+ if (method === "thread/resume") {
1953
+ return threadStartResult("thread-existing");
1954
+ }
1955
+ throw new Error(`unexpected method: ${method}`);
1956
+ });
1957
+ const config = {
1958
+ "features.codex_hooks": true,
1959
+ "hooks.PreToolUse": [],
1960
+ };
1961
+
1962
+ await startOrResumeThread({
1963
+ client: { request } as never,
1964
+ params,
1965
+ cwd: workspaceDir,
1966
+ dynamicTools: [],
1967
+ appServer,
1968
+ config,
1969
+ });
1970
+ await startOrResumeThread({
1971
+ client: { request } as never,
1972
+ params,
1973
+ cwd: workspaceDir,
1974
+ dynamicTools: [],
1975
+ appServer,
1976
+ config,
1977
+ });
1978
+
1979
+ expect(request.mock.calls).toEqual([
1980
+ [
1981
+ "thread/start",
1982
+ expect.objectContaining({
1983
+ config,
1984
+ }),
1985
+ ],
1986
+ [
1987
+ "thread/resume",
1988
+ expect.objectContaining({
1989
+ config,
1990
+ }),
1991
+ ],
1992
+ ]);
1993
+ });
1994
+
1995
+ it("starts a new Codex thread when dynamic tool schemas change", async () => {
1996
+ const sessionFile = path.join(tempDir, "session.jsonl");
1997
+ const workspaceDir = path.join(tempDir, "workspace");
1998
+ const params = createParams(sessionFile, workspaceDir);
1999
+ const appServer = createThreadLifecycleAppServerOptions();
2000
+ let nextThread = 1;
2001
+ const request = vi.fn(async (method: string) => {
2002
+ if (method === "thread/start") {
2003
+ return threadStartResult(`thread-${nextThread++}`);
2004
+ }
2005
+ throw new Error(`unexpected method: ${method}`);
2006
+ });
2007
+
2008
+ await startOrResumeThread({
2009
+ client: { request } as never,
2010
+ params,
2011
+ cwd: workspaceDir,
2012
+ dynamicTools: [createMessageDynamicTool("Send and manage messages.", ["send"])],
2013
+ appServer,
2014
+ });
2015
+ const binding = await startOrResumeThread({
2016
+ client: { request } as never,
2017
+ params,
2018
+ cwd: workspaceDir,
2019
+ dynamicTools: [createMessageDynamicTool("Send and manage messages.", ["send", "read"])],
2020
+ appServer,
2021
+ });
2022
+
2023
+ expect(binding.threadId).toBe("thread-2");
2024
+ expect(request.mock.calls.map(([method]) => method)).toEqual(["thread/start", "thread/start"]);
2025
+ });
2026
+
2027
+ it("passes configured app-server policy, sandbox, service tier, and model on resume", async () => {
2028
+ const sessionFile = path.join(tempDir, "session.jsonl");
2029
+ const workspaceDir = path.join(tempDir, "workspace");
2030
+ await writeExistingBinding(sessionFile, workspaceDir, { model: "gpt-5.2" });
2031
+ const { requests, waitForMethod, completeTurn } = createResumeHarness();
2032
+
2033
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
2034
+ pluginConfig: {
2035
+ appServer: {
2036
+ approvalPolicy: "on-request",
2037
+ approvalsReviewer: "guardian_subagent",
2038
+ sandbox: "danger-full-access",
2039
+ serviceTier: "fast",
2040
+ },
2041
+ },
2042
+ });
2043
+ await waitForMethod("turn/start");
2044
+ await completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
2045
+ await run;
2046
+
2047
+ expectResumeRequest(requests, {
2048
+ threadId: "thread-existing",
2049
+ model: "gpt-5.4-codex",
2050
+ approvalPolicy: "on-request",
2051
+ approvalsReviewer: "guardian_subagent",
2052
+ sandbox: "danger-full-access",
2053
+ serviceTier: "fast",
2054
+ developerInstructions: expect.stringContaining(CODEX_GPT5_BEHAVIOR_CONTRACT),
2055
+ persistExtendedHistory: true,
2056
+ });
2057
+ expect(requests).toEqual(
2058
+ expect.arrayContaining([
2059
+ {
2060
+ method: "turn/start",
2061
+ params: expect.objectContaining({
2062
+ approvalPolicy: "on-request",
2063
+ approvalsReviewer: "guardian_subagent",
2064
+ sandboxPolicy: { type: "dangerFullAccess" },
2065
+ serviceTier: "fast",
2066
+ model: "gpt-5.4-codex",
2067
+ }),
2068
+ },
2069
+ ]),
2070
+ );
2071
+ });
2072
+
2073
+ it("drops invalid legacy service tiers before app-server resume and turn requests", async () => {
2074
+ const sessionFile = path.join(tempDir, "session.jsonl");
2075
+ const workspaceDir = path.join(tempDir, "workspace");
2076
+ await writeExistingBinding(sessionFile, workspaceDir, { model: "gpt-5.2" });
2077
+ const { requests, waitForMethod, completeTurn } = createResumeHarness();
2078
+
2079
+ const run = runCodexAppServerAttempt(createParams(sessionFile, workspaceDir), {
2080
+ pluginConfig: {
2081
+ appServer: {
2082
+ approvalPolicy: "on-request",
2083
+ sandbox: "danger-full-access",
2084
+ serviceTier: "priority",
2085
+ },
2086
+ },
2087
+ });
2088
+ await waitForMethod("turn/start");
2089
+ await completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
2090
+ await run;
2091
+
2092
+ const resumeRequest = requests.find((request) => request.method === "thread/resume");
2093
+ expect(resumeRequest?.params).toEqual(
2094
+ expect.not.objectContaining({ serviceTier: expect.anything() }),
2095
+ );
2096
+ const turnRequest = requests.find((request) => request.method === "turn/start");
2097
+ expect(turnRequest?.params).toEqual(
2098
+ expect.not.objectContaining({ serviceTier: expect.anything() }),
2099
+ );
2100
+ });
2101
+
2102
+ it("builds resume and turn params from the currently selected OpenClaw model", () => {
2103
+ const params = createParams("/tmp/session.jsonl", "/tmp/workspace");
2104
+ const appServer = {
2105
+ start: {
2106
+ transport: "stdio" as const,
2107
+ command: "codex",
2108
+ args: ["app-server", "--listen", "stdio://"],
2109
+ headers: {},
2110
+ },
2111
+ requestTimeoutMs: 60_000,
2112
+ approvalPolicy: "on-request" as const,
2113
+ approvalsReviewer: "guardian_subagent" as const,
2114
+ sandbox: "danger-full-access" as const,
2115
+ serviceTier: "flex" as const,
2116
+ };
2117
+
2118
+ expect(buildThreadResumeParams(params, { threadId: "thread-1", appServer })).toEqual({
2119
+ threadId: "thread-1",
2120
+ model: "gpt-5.4-codex",
2121
+ approvalPolicy: "on-request",
2122
+ approvalsReviewer: "guardian_subagent",
2123
+ sandbox: "danger-full-access",
2124
+ serviceTier: "flex",
2125
+ developerInstructions: expect.stringContaining(CODEX_GPT5_BEHAVIOR_CONTRACT),
2126
+ persistExtendedHistory: true,
2127
+ });
2128
+ expect(
2129
+ buildTurnStartParams(params, { threadId: "thread-1", cwd: "/tmp/workspace", appServer }),
2130
+ ).toEqual(
2131
+ expect.objectContaining({
2132
+ threadId: "thread-1",
2133
+ cwd: "/tmp/workspace",
2134
+ model: "gpt-5.4-codex",
2135
+ approvalPolicy: "on-request",
2136
+ approvalsReviewer: "guardian_subagent",
2137
+ sandboxPolicy: { type: "dangerFullAccess" },
2138
+ serviceTier: "flex",
2139
+ }),
2140
+ );
2141
+ });
2142
+
2143
+ it("preserves the bound auth profile when resume params omit authProfileId", async () => {
2144
+ const sessionFile = path.join(tempDir, "session.jsonl");
2145
+ const workspaceDir = path.join(tempDir, "workspace");
2146
+ await writeExistingBinding(sessionFile, workspaceDir, {
2147
+ authProfileId: "openai-codex:bound",
2148
+ });
2149
+ const params = createParams(sessionFile, workspaceDir);
2150
+ delete params.authProfileId;
2151
+ params.agentDir = path.join(tempDir, "agent");
2152
+
2153
+ const binding = await startOrResumeThread({
2154
+ client: {
2155
+ request: async (method: string) => {
2156
+ if (method === "thread/resume") {
2157
+ return threadStartResult("thread-existing");
2158
+ }
2159
+ throw new Error(`unexpected method: ${method}`);
2160
+ },
2161
+ } as never,
2162
+ params,
2163
+ cwd: workspaceDir,
2164
+ dynamicTools: [],
2165
+ appServer: {
2166
+ start: {
2167
+ transport: "stdio",
2168
+ command: "codex",
2169
+ args: ["app-server"],
2170
+ headers: {},
2171
+ },
2172
+ requestTimeoutMs: 60_000,
2173
+ approvalPolicy: "never",
2174
+ approvalsReviewer: "user",
2175
+ sandbox: "workspace-write",
2176
+ },
2177
+ });
2178
+
2179
+ expect(binding.authProfileId).toBe("openai-codex:bound");
2180
+ });
2181
+
2182
+ it("reuses the bound auth profile for app-server startup when params omit it", async () => {
2183
+ const sessionFile = path.join(tempDir, "session.jsonl");
2184
+ const workspaceDir = path.join(tempDir, "workspace");
2185
+ await writeExistingBinding(sessionFile, workspaceDir, {
2186
+ authProfileId: "openai-codex:bound",
2187
+ dynamicToolsFingerprint: "[]",
2188
+ });
2189
+ const seenAuthProfileIds: Array<string | undefined> = [];
2190
+ const seenAgentDirs: Array<string | undefined> = [];
2191
+ const { requests, waitForMethod, completeTurn } = createAppServerHarness(
2192
+ async (method: string) => {
2193
+ if (method === "thread/resume") {
2194
+ return threadStartResult("thread-existing");
2195
+ }
2196
+ if (method === "turn/start") {
2197
+ return turnStartResult();
2198
+ }
2199
+ throw new Error(`unexpected method: ${method}`);
2200
+ },
2201
+ {
2202
+ onStart: (authProfileId, agentDir) => {
2203
+ seenAuthProfileIds.push(authProfileId);
2204
+ seenAgentDirs.push(agentDir);
2205
+ },
2206
+ },
2207
+ );
2208
+ const params = createParams(sessionFile, workspaceDir);
2209
+ delete params.authProfileId;
2210
+ params.agentDir = path.join(tempDir, "agent");
2211
+
2212
+ const run = runCodexAppServerAttempt(params);
2213
+ await vi.waitFor(() => expect(seenAuthProfileIds).toEqual(["openai-codex:bound"]), {
2214
+ interval: 1,
2215
+ });
2216
+ await waitForMethod("turn/start");
2217
+ await new Promise<void>((resolve) => setImmediate(resolve));
2218
+ await completeTurn({ threadId: "thread-existing", turnId: "turn-1" });
2219
+ await run;
2220
+
2221
+ expect(seenAuthProfileIds).toEqual(["openai-codex:bound"]);
2222
+ expect(seenAgentDirs).toEqual([path.join(tempDir, "agent")]);
2223
+ expect(requests.map((entry) => entry.method)).toContain("turn/start");
2224
+ });
2225
+ });