@companyhelm/runner 0.0.12 → 0.0.13

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 (486) hide show
  1. package/dist/cli.js +57 -0
  2. package/dist/commands/global-options.js +20 -0
  3. package/dist/commands/register-commands.js +15 -0
  4. package/dist/commands/root.js +2627 -0
  5. package/dist/commands/runner/common.js +18 -0
  6. package/dist/commands/runner/register-runner-commands.js +12 -0
  7. package/dist/commands/runner/start.js +30 -0
  8. package/dist/commands/runner/stop.js +55 -0
  9. package/dist/commands/sdk/codex/auth.js +200 -0
  10. package/dist/commands/sdk/codex/register-codex-sdk-commands.js +12 -0
  11. package/dist/commands/sdk/codex/use-dedicated-auth.js +18 -0
  12. package/dist/commands/sdk/codex/use-host-auth.js +19 -0
  13. package/dist/commands/sdk/list.js +76 -0
  14. package/dist/commands/sdk/refresh-models.js +25 -0
  15. package/dist/commands/sdk/register-sdk-commands.js +14 -0
  16. package/dist/commands/shell.js +90 -0
  17. package/dist/commands/startup.js +137 -0
  18. package/dist/commands/status.js +32 -0
  19. package/dist/commands/thread/docker.js +77 -0
  20. package/dist/commands/thread/list.js +37 -0
  21. package/dist/commands/thread/register-thread-commands.js +12 -0
  22. package/dist/config.js +105 -0
  23. package/dist/generated/codex-app-server/AbsolutePathBuf.js +3 -0
  24. package/dist/generated/codex-app-server/AddConversationListenerParams.js +3 -0
  25. package/dist/generated/codex-app-server/AddConversationSubscriptionResponse.js +3 -0
  26. package/dist/generated/codex-app-server/AgentMessageContent.js +3 -0
  27. package/dist/generated/codex-app-server/AgentMessageContentDeltaEvent.js +3 -0
  28. package/dist/generated/codex-app-server/AgentMessageDeltaEvent.js +3 -0
  29. package/dist/generated/codex-app-server/AgentMessageEvent.js +3 -0
  30. package/dist/generated/codex-app-server/AgentMessageItem.js +3 -0
  31. package/dist/generated/codex-app-server/AgentReasoningDeltaEvent.js +3 -0
  32. package/dist/generated/codex-app-server/AgentReasoningEvent.js +3 -0
  33. package/dist/generated/codex-app-server/AgentReasoningRawContentDeltaEvent.js +3 -0
  34. package/dist/generated/codex-app-server/AgentReasoningRawContentEvent.js +3 -0
  35. package/dist/generated/codex-app-server/AgentReasoningSectionBreakEvent.js +3 -0
  36. package/dist/generated/codex-app-server/AgentStatus.js +3 -0
  37. package/dist/generated/codex-app-server/ApplyPatchApprovalParams.js +3 -0
  38. package/dist/generated/codex-app-server/ApplyPatchApprovalRequestEvent.js +3 -0
  39. package/dist/generated/codex-app-server/ApplyPatchApprovalResponse.js +3 -0
  40. package/dist/generated/codex-app-server/ArchiveConversationParams.js +3 -0
  41. package/dist/generated/codex-app-server/ArchiveConversationResponse.js +3 -0
  42. package/dist/generated/codex-app-server/AskForApproval.js +3 -0
  43. package/dist/generated/codex-app-server/AuthMode.js +3 -0
  44. package/dist/generated/codex-app-server/AuthStatusChangeNotification.js +3 -0
  45. package/dist/generated/codex-app-server/BackgroundEventEvent.js +3 -0
  46. package/dist/generated/codex-app-server/ByteRange.js +3 -0
  47. package/dist/generated/codex-app-server/CallToolResult.js +3 -0
  48. package/dist/generated/codex-app-server/CancelLoginChatGptParams.js +3 -0
  49. package/dist/generated/codex-app-server/CancelLoginChatGptResponse.js +3 -0
  50. package/dist/generated/codex-app-server/ClientInfo.js +3 -0
  51. package/dist/generated/codex-app-server/ClientNotification.js +3 -0
  52. package/dist/generated/codex-app-server/ClientRequest.js +3 -0
  53. package/dist/generated/codex-app-server/CodexErrorInfo.js +3 -0
  54. package/dist/generated/codex-app-server/CollabAgentInteractionBeginEvent.js +3 -0
  55. package/dist/generated/codex-app-server/CollabAgentInteractionEndEvent.js +3 -0
  56. package/dist/generated/codex-app-server/CollabAgentSpawnBeginEvent.js +3 -0
  57. package/dist/generated/codex-app-server/CollabAgentSpawnEndEvent.js +3 -0
  58. package/dist/generated/codex-app-server/CollabCloseBeginEvent.js +3 -0
  59. package/dist/generated/codex-app-server/CollabCloseEndEvent.js +3 -0
  60. package/dist/generated/codex-app-server/CollabResumeBeginEvent.js +3 -0
  61. package/dist/generated/codex-app-server/CollabResumeEndEvent.js +3 -0
  62. package/dist/generated/codex-app-server/CollabWaitingBeginEvent.js +3 -0
  63. package/dist/generated/codex-app-server/CollabWaitingEndEvent.js +3 -0
  64. package/dist/generated/codex-app-server/CollaborationMode.js +3 -0
  65. package/dist/generated/codex-app-server/CollaborationModeMask.js +3 -0
  66. package/dist/generated/codex-app-server/ContentItem.js +3 -0
  67. package/dist/generated/codex-app-server/ContextCompactedEvent.js +3 -0
  68. package/dist/generated/codex-app-server/ContextCompactionItem.js +3 -0
  69. package/dist/generated/codex-app-server/ConversationGitInfo.js +3 -0
  70. package/dist/generated/codex-app-server/ConversationSummary.js +3 -0
  71. package/dist/generated/codex-app-server/CreditsSnapshot.js +3 -0
  72. package/dist/generated/codex-app-server/CustomPrompt.js +3 -0
  73. package/dist/generated/codex-app-server/DeprecationNoticeEvent.js +3 -0
  74. package/dist/generated/codex-app-server/DynamicToolCallRequest.js +3 -0
  75. package/dist/generated/codex-app-server/ElicitationRequestEvent.js +3 -0
  76. package/dist/generated/codex-app-server/ErrorEvent.js +3 -0
  77. package/dist/generated/codex-app-server/EventMsg.js +3 -0
  78. package/dist/generated/codex-app-server/ExecApprovalRequestEvent.js +3 -0
  79. package/dist/generated/codex-app-server/ExecCommandApprovalParams.js +3 -0
  80. package/dist/generated/codex-app-server/ExecCommandApprovalResponse.js +3 -0
  81. package/dist/generated/codex-app-server/ExecCommandBeginEvent.js +3 -0
  82. package/dist/generated/codex-app-server/ExecCommandEndEvent.js +3 -0
  83. package/dist/generated/codex-app-server/ExecCommandOutputDeltaEvent.js +3 -0
  84. package/dist/generated/codex-app-server/ExecCommandSource.js +3 -0
  85. package/dist/generated/codex-app-server/ExecCommandStatus.js +3 -0
  86. package/dist/generated/codex-app-server/ExecOneOffCommandParams.js +3 -0
  87. package/dist/generated/codex-app-server/ExecOneOffCommandResponse.js +3 -0
  88. package/dist/generated/codex-app-server/ExecOutputStream.js +3 -0
  89. package/dist/generated/codex-app-server/ExecPolicyAmendment.js +3 -0
  90. package/dist/generated/codex-app-server/ExitedReviewModeEvent.js +3 -0
  91. package/dist/generated/codex-app-server/FileChange.js +3 -0
  92. package/dist/generated/codex-app-server/ForcedLoginMethod.js +3 -0
  93. package/dist/generated/codex-app-server/ForkConversationParams.js +3 -0
  94. package/dist/generated/codex-app-server/ForkConversationResponse.js +3 -0
  95. package/dist/generated/codex-app-server/FunctionCallOutputBody.js +3 -0
  96. package/dist/generated/codex-app-server/FunctionCallOutputContentItem.js +3 -0
  97. package/dist/generated/codex-app-server/FunctionCallOutputPayload.js +3 -0
  98. package/dist/generated/codex-app-server/FuzzyFileSearchParams.js +3 -0
  99. package/dist/generated/codex-app-server/FuzzyFileSearchResponse.js +3 -0
  100. package/dist/generated/codex-app-server/FuzzyFileSearchResult.js +3 -0
  101. package/dist/generated/codex-app-server/FuzzyFileSearchSessionCompletedNotification.js +3 -0
  102. package/dist/generated/codex-app-server/FuzzyFileSearchSessionUpdatedNotification.js +3 -0
  103. package/dist/generated/codex-app-server/GetAuthStatusParams.js +3 -0
  104. package/dist/generated/codex-app-server/GetAuthStatusResponse.js +3 -0
  105. package/dist/generated/codex-app-server/GetConversationSummaryParams.js +3 -0
  106. package/dist/generated/codex-app-server/GetConversationSummaryResponse.js +3 -0
  107. package/dist/generated/codex-app-server/GetHistoryEntryResponseEvent.js +3 -0
  108. package/dist/generated/codex-app-server/GetUserAgentResponse.js +3 -0
  109. package/dist/generated/codex-app-server/GetUserSavedConfigResponse.js +3 -0
  110. package/dist/generated/codex-app-server/GhostCommit.js +3 -0
  111. package/dist/generated/codex-app-server/GitDiffToRemoteParams.js +3 -0
  112. package/dist/generated/codex-app-server/GitDiffToRemoteResponse.js +3 -0
  113. package/dist/generated/codex-app-server/GitSha.js +3 -0
  114. package/dist/generated/codex-app-server/HistoryEntry.js +3 -0
  115. package/dist/generated/codex-app-server/InitializeCapabilities.js +3 -0
  116. package/dist/generated/codex-app-server/InitializeParams.js +3 -0
  117. package/dist/generated/codex-app-server/InitializeResponse.js +3 -0
  118. package/dist/generated/codex-app-server/InputItem.js +3 -0
  119. package/dist/generated/codex-app-server/InputModality.js +3 -0
  120. package/dist/generated/codex-app-server/InterruptConversationParams.js +3 -0
  121. package/dist/generated/codex-app-server/InterruptConversationResponse.js +3 -0
  122. package/dist/generated/codex-app-server/ItemCompletedEvent.js +3 -0
  123. package/dist/generated/codex-app-server/ItemStartedEvent.js +3 -0
  124. package/dist/generated/codex-app-server/ListConversationsParams.js +3 -0
  125. package/dist/generated/codex-app-server/ListConversationsResponse.js +3 -0
  126. package/dist/generated/codex-app-server/ListCustomPromptsResponseEvent.js +3 -0
  127. package/dist/generated/codex-app-server/ListRemoteSkillsResponseEvent.js +3 -0
  128. package/dist/generated/codex-app-server/ListSkillsResponseEvent.js +3 -0
  129. package/dist/generated/codex-app-server/LocalShellAction.js +3 -0
  130. package/dist/generated/codex-app-server/LocalShellExecAction.js +3 -0
  131. package/dist/generated/codex-app-server/LocalShellStatus.js +3 -0
  132. package/dist/generated/codex-app-server/LoginApiKeyParams.js +3 -0
  133. package/dist/generated/codex-app-server/LoginApiKeyResponse.js +3 -0
  134. package/dist/generated/codex-app-server/LoginChatGptCompleteNotification.js +3 -0
  135. package/dist/generated/codex-app-server/LoginChatGptResponse.js +3 -0
  136. package/dist/generated/codex-app-server/LogoutChatGptResponse.js +3 -0
  137. package/dist/generated/codex-app-server/McpAuthStatus.js +3 -0
  138. package/dist/generated/codex-app-server/McpInvocation.js +3 -0
  139. package/dist/generated/codex-app-server/McpListToolsResponseEvent.js +3 -0
  140. package/dist/generated/codex-app-server/McpStartupCompleteEvent.js +3 -0
  141. package/dist/generated/codex-app-server/McpStartupFailure.js +3 -0
  142. package/dist/generated/codex-app-server/McpStartupStatus.js +3 -0
  143. package/dist/generated/codex-app-server/McpStartupUpdateEvent.js +3 -0
  144. package/dist/generated/codex-app-server/McpToolCallBeginEvent.js +3 -0
  145. package/dist/generated/codex-app-server/McpToolCallEndEvent.js +3 -0
  146. package/dist/generated/codex-app-server/MessagePhase.js +3 -0
  147. package/dist/generated/codex-app-server/ModeKind.js +3 -0
  148. package/dist/generated/codex-app-server/ModelRerouteEvent.js +3 -0
  149. package/dist/generated/codex-app-server/ModelRerouteReason.js +3 -0
  150. package/dist/generated/codex-app-server/NetworkAccess.js +3 -0
  151. package/dist/generated/codex-app-server/NetworkApprovalContext.js +3 -0
  152. package/dist/generated/codex-app-server/NetworkApprovalProtocol.js +3 -0
  153. package/dist/generated/codex-app-server/NewConversationParams.js +3 -0
  154. package/dist/generated/codex-app-server/NewConversationResponse.js +3 -0
  155. package/dist/generated/codex-app-server/ParsedCommand.js +3 -0
  156. package/dist/generated/codex-app-server/PatchApplyBeginEvent.js +3 -0
  157. package/dist/generated/codex-app-server/PatchApplyEndEvent.js +3 -0
  158. package/dist/generated/codex-app-server/PatchApplyStatus.js +3 -0
  159. package/dist/generated/codex-app-server/Personality.js +3 -0
  160. package/dist/generated/codex-app-server/PlanDeltaEvent.js +3 -0
  161. package/dist/generated/codex-app-server/PlanItem.js +3 -0
  162. package/dist/generated/codex-app-server/PlanItemArg.js +3 -0
  163. package/dist/generated/codex-app-server/PlanType.js +3 -0
  164. package/dist/generated/codex-app-server/Profile.js +3 -0
  165. package/dist/generated/codex-app-server/RateLimitSnapshot.js +3 -0
  166. package/dist/generated/codex-app-server/RateLimitWindow.js +3 -0
  167. package/dist/generated/codex-app-server/RawResponseItemEvent.js +3 -0
  168. package/dist/generated/codex-app-server/ReadOnlyAccess.js +3 -0
  169. package/dist/generated/codex-app-server/ReasoningContentDeltaEvent.js +3 -0
  170. package/dist/generated/codex-app-server/ReasoningEffort.js +3 -0
  171. package/dist/generated/codex-app-server/ReasoningItem.js +3 -0
  172. package/dist/generated/codex-app-server/ReasoningItemContent.js +3 -0
  173. package/dist/generated/codex-app-server/ReasoningItemReasoningSummary.js +3 -0
  174. package/dist/generated/codex-app-server/ReasoningRawContentDeltaEvent.js +3 -0
  175. package/dist/generated/codex-app-server/ReasoningSummary.js +3 -0
  176. package/dist/generated/codex-app-server/RemoteSkillDownloadedEvent.js +3 -0
  177. package/dist/generated/codex-app-server/RemoteSkillSummary.js +3 -0
  178. package/dist/generated/codex-app-server/RemoveConversationListenerParams.js +3 -0
  179. package/dist/generated/codex-app-server/RemoveConversationSubscriptionResponse.js +3 -0
  180. package/dist/generated/codex-app-server/RequestId.js +3 -0
  181. package/dist/generated/codex-app-server/RequestUserInputEvent.js +3 -0
  182. package/dist/generated/codex-app-server/RequestUserInputQuestion.js +3 -0
  183. package/dist/generated/codex-app-server/RequestUserInputQuestionOption.js +3 -0
  184. package/dist/generated/codex-app-server/Resource.js +3 -0
  185. package/dist/generated/codex-app-server/ResourceTemplate.js +3 -0
  186. package/dist/generated/codex-app-server/ResponseItem.js +3 -0
  187. package/dist/generated/codex-app-server/ResumeConversationParams.js +3 -0
  188. package/dist/generated/codex-app-server/ResumeConversationResponse.js +3 -0
  189. package/dist/generated/codex-app-server/ReviewCodeLocation.js +3 -0
  190. package/dist/generated/codex-app-server/ReviewDecision.js +3 -0
  191. package/dist/generated/codex-app-server/ReviewFinding.js +3 -0
  192. package/dist/generated/codex-app-server/ReviewLineRange.js +3 -0
  193. package/dist/generated/codex-app-server/ReviewOutputEvent.js +3 -0
  194. package/dist/generated/codex-app-server/ReviewRequest.js +3 -0
  195. package/dist/generated/codex-app-server/ReviewTarget.js +3 -0
  196. package/dist/generated/codex-app-server/SandboxMode.js +3 -0
  197. package/dist/generated/codex-app-server/SandboxPolicy.js +3 -0
  198. package/dist/generated/codex-app-server/SandboxSettings.js +3 -0
  199. package/dist/generated/codex-app-server/SendUserMessageParams.js +3 -0
  200. package/dist/generated/codex-app-server/SendUserMessageResponse.js +3 -0
  201. package/dist/generated/codex-app-server/SendUserTurnParams.js +3 -0
  202. package/dist/generated/codex-app-server/SendUserTurnResponse.js +3 -0
  203. package/dist/generated/codex-app-server/ServerNotification.js +3 -0
  204. package/dist/generated/codex-app-server/ServerRequest.js +3 -0
  205. package/dist/generated/codex-app-server/SessionConfiguredEvent.js +3 -0
  206. package/dist/generated/codex-app-server/SessionConfiguredNotification.js +3 -0
  207. package/dist/generated/codex-app-server/SessionNetworkProxyRuntime.js +3 -0
  208. package/dist/generated/codex-app-server/SessionSource.js +3 -0
  209. package/dist/generated/codex-app-server/SetDefaultModelParams.js +3 -0
  210. package/dist/generated/codex-app-server/SetDefaultModelResponse.js +3 -0
  211. package/dist/generated/codex-app-server/Settings.js +3 -0
  212. package/dist/generated/codex-app-server/SkillDependencies.js +3 -0
  213. package/dist/generated/codex-app-server/SkillErrorInfo.js +3 -0
  214. package/dist/generated/codex-app-server/SkillInterface.js +3 -0
  215. package/dist/generated/codex-app-server/SkillMetadata.js +3 -0
  216. package/dist/generated/codex-app-server/SkillScope.js +3 -0
  217. package/dist/generated/codex-app-server/SkillToolDependency.js +3 -0
  218. package/dist/generated/codex-app-server/SkillsListEntry.js +3 -0
  219. package/dist/generated/codex-app-server/StepStatus.js +3 -0
  220. package/dist/generated/codex-app-server/StreamErrorEvent.js +3 -0
  221. package/dist/generated/codex-app-server/SubAgentSource.js +3 -0
  222. package/dist/generated/codex-app-server/TerminalInteractionEvent.js +3 -0
  223. package/dist/generated/codex-app-server/TextElement.js +3 -0
  224. package/dist/generated/codex-app-server/ThreadId.js +3 -0
  225. package/dist/generated/codex-app-server/ThreadNameUpdatedEvent.js +3 -0
  226. package/dist/generated/codex-app-server/ThreadRolledBackEvent.js +3 -0
  227. package/dist/generated/codex-app-server/TokenCountEvent.js +3 -0
  228. package/dist/generated/codex-app-server/TokenUsage.js +3 -0
  229. package/dist/generated/codex-app-server/TokenUsageInfo.js +3 -0
  230. package/dist/generated/codex-app-server/Tool.js +3 -0
  231. package/dist/generated/codex-app-server/Tools.js +3 -0
  232. package/dist/generated/codex-app-server/TurnAbortReason.js +3 -0
  233. package/dist/generated/codex-app-server/TurnAbortedEvent.js +3 -0
  234. package/dist/generated/codex-app-server/TurnCompleteEvent.js +3 -0
  235. package/dist/generated/codex-app-server/TurnDiffEvent.js +3 -0
  236. package/dist/generated/codex-app-server/TurnItem.js +3 -0
  237. package/dist/generated/codex-app-server/TurnStartedEvent.js +3 -0
  238. package/dist/generated/codex-app-server/UndoCompletedEvent.js +3 -0
  239. package/dist/generated/codex-app-server/UndoStartedEvent.js +3 -0
  240. package/dist/generated/codex-app-server/UpdatePlanArgs.js +3 -0
  241. package/dist/generated/codex-app-server/UserInfoResponse.js +3 -0
  242. package/dist/generated/codex-app-server/UserInput.js +3 -0
  243. package/dist/generated/codex-app-server/UserMessageEvent.js +3 -0
  244. package/dist/generated/codex-app-server/UserMessageItem.js +3 -0
  245. package/dist/generated/codex-app-server/UserSavedConfig.js +3 -0
  246. package/dist/generated/codex-app-server/Verbosity.js +3 -0
  247. package/dist/generated/codex-app-server/ViewImageToolCallEvent.js +3 -0
  248. package/dist/generated/codex-app-server/WarningEvent.js +3 -0
  249. package/dist/generated/codex-app-server/WebSearchAction.js +3 -0
  250. package/dist/generated/codex-app-server/WebSearchBeginEvent.js +3 -0
  251. package/dist/generated/codex-app-server/WebSearchEndEvent.js +3 -0
  252. package/dist/generated/codex-app-server/WebSearchItem.js +3 -0
  253. package/dist/generated/codex-app-server/WebSearchMode.js +3 -0
  254. package/dist/generated/codex-app-server/index.js +38 -0
  255. package/dist/generated/codex-app-server/serde_json/JsonValue.js +3 -0
  256. package/dist/generated/codex-app-server/v2/Account.js +3 -0
  257. package/dist/generated/codex-app-server/v2/AccountLoginCompletedNotification.js +3 -0
  258. package/dist/generated/codex-app-server/v2/AccountRateLimitsUpdatedNotification.js +3 -0
  259. package/dist/generated/codex-app-server/v2/AccountUpdatedNotification.js +3 -0
  260. package/dist/generated/codex-app-server/v2/AgentMessageDeltaNotification.js +3 -0
  261. package/dist/generated/codex-app-server/v2/AnalyticsConfig.js +3 -0
  262. package/dist/generated/codex-app-server/v2/AppBranding.js +3 -0
  263. package/dist/generated/codex-app-server/v2/AppDisabledReason.js +3 -0
  264. package/dist/generated/codex-app-server/v2/AppInfo.js +3 -0
  265. package/dist/generated/codex-app-server/v2/AppListUpdatedNotification.js +3 -0
  266. package/dist/generated/codex-app-server/v2/AppMetadata.js +3 -0
  267. package/dist/generated/codex-app-server/v2/AppReview.js +3 -0
  268. package/dist/generated/codex-app-server/v2/AppScreenshot.js +3 -0
  269. package/dist/generated/codex-app-server/v2/AppsConfig.js +3 -0
  270. package/dist/generated/codex-app-server/v2/AppsListParams.js +3 -0
  271. package/dist/generated/codex-app-server/v2/AppsListResponse.js +3 -0
  272. package/dist/generated/codex-app-server/v2/AskForApproval.js +3 -0
  273. package/dist/generated/codex-app-server/v2/ByteRange.js +3 -0
  274. package/dist/generated/codex-app-server/v2/CancelLoginAccountParams.js +3 -0
  275. package/dist/generated/codex-app-server/v2/CancelLoginAccountResponse.js +3 -0
  276. package/dist/generated/codex-app-server/v2/CancelLoginAccountStatus.js +3 -0
  277. package/dist/generated/codex-app-server/v2/ChatgptAuthTokensRefreshParams.js +3 -0
  278. package/dist/generated/codex-app-server/v2/ChatgptAuthTokensRefreshReason.js +3 -0
  279. package/dist/generated/codex-app-server/v2/ChatgptAuthTokensRefreshResponse.js +3 -0
  280. package/dist/generated/codex-app-server/v2/CodexErrorInfo.js +3 -0
  281. package/dist/generated/codex-app-server/v2/CollabAgentState.js +3 -0
  282. package/dist/generated/codex-app-server/v2/CollabAgentStatus.js +3 -0
  283. package/dist/generated/codex-app-server/v2/CollabAgentTool.js +3 -0
  284. package/dist/generated/codex-app-server/v2/CollabAgentToolCallStatus.js +3 -0
  285. package/dist/generated/codex-app-server/v2/CommandAction.js +3 -0
  286. package/dist/generated/codex-app-server/v2/CommandExecParams.js +3 -0
  287. package/dist/generated/codex-app-server/v2/CommandExecResponse.js +3 -0
  288. package/dist/generated/codex-app-server/v2/CommandExecutionApprovalDecision.js +3 -0
  289. package/dist/generated/codex-app-server/v2/CommandExecutionOutputDeltaNotification.js +3 -0
  290. package/dist/generated/codex-app-server/v2/CommandExecutionRequestApprovalParams.js +3 -0
  291. package/dist/generated/codex-app-server/v2/CommandExecutionRequestApprovalResponse.js +3 -0
  292. package/dist/generated/codex-app-server/v2/CommandExecutionStatus.js +3 -0
  293. package/dist/generated/codex-app-server/v2/Config.js +3 -0
  294. package/dist/generated/codex-app-server/v2/ConfigBatchWriteParams.js +3 -0
  295. package/dist/generated/codex-app-server/v2/ConfigEdit.js +3 -0
  296. package/dist/generated/codex-app-server/v2/ConfigLayer.js +3 -0
  297. package/dist/generated/codex-app-server/v2/ConfigLayerMetadata.js +3 -0
  298. package/dist/generated/codex-app-server/v2/ConfigLayerSource.js +3 -0
  299. package/dist/generated/codex-app-server/v2/ConfigReadParams.js +3 -0
  300. package/dist/generated/codex-app-server/v2/ConfigReadResponse.js +3 -0
  301. package/dist/generated/codex-app-server/v2/ConfigRequirements.js +3 -0
  302. package/dist/generated/codex-app-server/v2/ConfigRequirementsReadResponse.js +3 -0
  303. package/dist/generated/codex-app-server/v2/ConfigValueWriteParams.js +3 -0
  304. package/dist/generated/codex-app-server/v2/ConfigWarningNotification.js +3 -0
  305. package/dist/generated/codex-app-server/v2/ConfigWriteResponse.js +3 -0
  306. package/dist/generated/codex-app-server/v2/ContextCompactedNotification.js +3 -0
  307. package/dist/generated/codex-app-server/v2/CreditsSnapshot.js +3 -0
  308. package/dist/generated/codex-app-server/v2/DeprecationNoticeNotification.js +3 -0
  309. package/dist/generated/codex-app-server/v2/DynamicToolCallOutputContentItem.js +3 -0
  310. package/dist/generated/codex-app-server/v2/DynamicToolCallParams.js +3 -0
  311. package/dist/generated/codex-app-server/v2/DynamicToolCallResponse.js +3 -0
  312. package/dist/generated/codex-app-server/v2/DynamicToolSpec.js +3 -0
  313. package/dist/generated/codex-app-server/v2/ErrorNotification.js +3 -0
  314. package/dist/generated/codex-app-server/v2/ExecPolicyAmendment.js +3 -0
  315. package/dist/generated/codex-app-server/v2/ExperimentalFeature.js +3 -0
  316. package/dist/generated/codex-app-server/v2/ExperimentalFeatureListParams.js +3 -0
  317. package/dist/generated/codex-app-server/v2/ExperimentalFeatureListResponse.js +3 -0
  318. package/dist/generated/codex-app-server/v2/ExperimentalFeatureStage.js +3 -0
  319. package/dist/generated/codex-app-server/v2/FeedbackUploadParams.js +3 -0
  320. package/dist/generated/codex-app-server/v2/FeedbackUploadResponse.js +3 -0
  321. package/dist/generated/codex-app-server/v2/FileChangeApprovalDecision.js +3 -0
  322. package/dist/generated/codex-app-server/v2/FileChangeOutputDeltaNotification.js +3 -0
  323. package/dist/generated/codex-app-server/v2/FileChangeRequestApprovalParams.js +3 -0
  324. package/dist/generated/codex-app-server/v2/FileChangeRequestApprovalResponse.js +3 -0
  325. package/dist/generated/codex-app-server/v2/FileUpdateChange.js +3 -0
  326. package/dist/generated/codex-app-server/v2/GetAccountParams.js +3 -0
  327. package/dist/generated/codex-app-server/v2/GetAccountRateLimitsResponse.js +3 -0
  328. package/dist/generated/codex-app-server/v2/GetAccountResponse.js +3 -0
  329. package/dist/generated/codex-app-server/v2/GitInfo.js +3 -0
  330. package/dist/generated/codex-app-server/v2/HazelnutScope.js +3 -0
  331. package/dist/generated/codex-app-server/v2/ItemCompletedNotification.js +3 -0
  332. package/dist/generated/codex-app-server/v2/ItemStartedNotification.js +3 -0
  333. package/dist/generated/codex-app-server/v2/ListMcpServerStatusParams.js +3 -0
  334. package/dist/generated/codex-app-server/v2/ListMcpServerStatusResponse.js +3 -0
  335. package/dist/generated/codex-app-server/v2/LoginAccountParams.js +3 -0
  336. package/dist/generated/codex-app-server/v2/LoginAccountResponse.js +3 -0
  337. package/dist/generated/codex-app-server/v2/LogoutAccountResponse.js +3 -0
  338. package/dist/generated/codex-app-server/v2/McpAuthStatus.js +3 -0
  339. package/dist/generated/codex-app-server/v2/McpServerOauthLoginCompletedNotification.js +3 -0
  340. package/dist/generated/codex-app-server/v2/McpServerOauthLoginParams.js +3 -0
  341. package/dist/generated/codex-app-server/v2/McpServerOauthLoginResponse.js +3 -0
  342. package/dist/generated/codex-app-server/v2/McpServerRefreshResponse.js +3 -0
  343. package/dist/generated/codex-app-server/v2/McpServerStatus.js +3 -0
  344. package/dist/generated/codex-app-server/v2/McpToolCallError.js +3 -0
  345. package/dist/generated/codex-app-server/v2/McpToolCallProgressNotification.js +3 -0
  346. package/dist/generated/codex-app-server/v2/McpToolCallResult.js +3 -0
  347. package/dist/generated/codex-app-server/v2/McpToolCallStatus.js +3 -0
  348. package/dist/generated/codex-app-server/v2/MergeStrategy.js +3 -0
  349. package/dist/generated/codex-app-server/v2/Model.js +3 -0
  350. package/dist/generated/codex-app-server/v2/ModelListParams.js +3 -0
  351. package/dist/generated/codex-app-server/v2/ModelListResponse.js +3 -0
  352. package/dist/generated/codex-app-server/v2/ModelRerouteReason.js +3 -0
  353. package/dist/generated/codex-app-server/v2/ModelReroutedNotification.js +3 -0
  354. package/dist/generated/codex-app-server/v2/NetworkAccess.js +3 -0
  355. package/dist/generated/codex-app-server/v2/NetworkRequirements.js +3 -0
  356. package/dist/generated/codex-app-server/v2/OverriddenMetadata.js +3 -0
  357. package/dist/generated/codex-app-server/v2/PatchApplyStatus.js +3 -0
  358. package/dist/generated/codex-app-server/v2/PatchChangeKind.js +3 -0
  359. package/dist/generated/codex-app-server/v2/PlanDeltaNotification.js +3 -0
  360. package/dist/generated/codex-app-server/v2/ProductSurface.js +3 -0
  361. package/dist/generated/codex-app-server/v2/ProfileV2.js +3 -0
  362. package/dist/generated/codex-app-server/v2/RateLimitSnapshot.js +3 -0
  363. package/dist/generated/codex-app-server/v2/RateLimitWindow.js +3 -0
  364. package/dist/generated/codex-app-server/v2/RawResponseItemCompletedNotification.js +3 -0
  365. package/dist/generated/codex-app-server/v2/ReadOnlyAccess.js +3 -0
  366. package/dist/generated/codex-app-server/v2/ReasoningEffortOption.js +3 -0
  367. package/dist/generated/codex-app-server/v2/ReasoningSummaryPartAddedNotification.js +3 -0
  368. package/dist/generated/codex-app-server/v2/ReasoningSummaryTextDeltaNotification.js +3 -0
  369. package/dist/generated/codex-app-server/v2/ReasoningTextDeltaNotification.js +3 -0
  370. package/dist/generated/codex-app-server/v2/RemoteSkillSummary.js +3 -0
  371. package/dist/generated/codex-app-server/v2/ResidencyRequirement.js +3 -0
  372. package/dist/generated/codex-app-server/v2/ReviewDelivery.js +3 -0
  373. package/dist/generated/codex-app-server/v2/ReviewStartParams.js +3 -0
  374. package/dist/generated/codex-app-server/v2/ReviewStartResponse.js +3 -0
  375. package/dist/generated/codex-app-server/v2/ReviewTarget.js +3 -0
  376. package/dist/generated/codex-app-server/v2/SandboxMode.js +3 -0
  377. package/dist/generated/codex-app-server/v2/SandboxPolicy.js +3 -0
  378. package/dist/generated/codex-app-server/v2/SandboxWorkspaceWrite.js +3 -0
  379. package/dist/generated/codex-app-server/v2/SessionSource.js +3 -0
  380. package/dist/generated/codex-app-server/v2/SkillDependencies.js +3 -0
  381. package/dist/generated/codex-app-server/v2/SkillErrorInfo.js +3 -0
  382. package/dist/generated/codex-app-server/v2/SkillInterface.js +3 -0
  383. package/dist/generated/codex-app-server/v2/SkillMetadata.js +3 -0
  384. package/dist/generated/codex-app-server/v2/SkillScope.js +3 -0
  385. package/dist/generated/codex-app-server/v2/SkillToolDependency.js +3 -0
  386. package/dist/generated/codex-app-server/v2/SkillsConfigWriteParams.js +3 -0
  387. package/dist/generated/codex-app-server/v2/SkillsConfigWriteResponse.js +3 -0
  388. package/dist/generated/codex-app-server/v2/SkillsListEntry.js +3 -0
  389. package/dist/generated/codex-app-server/v2/SkillsListExtraRootsForCwd.js +3 -0
  390. package/dist/generated/codex-app-server/v2/SkillsListParams.js +3 -0
  391. package/dist/generated/codex-app-server/v2/SkillsListResponse.js +3 -0
  392. package/dist/generated/codex-app-server/v2/SkillsRemoteReadParams.js +3 -0
  393. package/dist/generated/codex-app-server/v2/SkillsRemoteReadResponse.js +3 -0
  394. package/dist/generated/codex-app-server/v2/SkillsRemoteWriteParams.js +3 -0
  395. package/dist/generated/codex-app-server/v2/SkillsRemoteWriteResponse.js +3 -0
  396. package/dist/generated/codex-app-server/v2/TerminalInteractionNotification.js +3 -0
  397. package/dist/generated/codex-app-server/v2/TextElement.js +3 -0
  398. package/dist/generated/codex-app-server/v2/TextPosition.js +3 -0
  399. package/dist/generated/codex-app-server/v2/TextRange.js +3 -0
  400. package/dist/generated/codex-app-server/v2/Thread.js +3 -0
  401. package/dist/generated/codex-app-server/v2/ThreadArchiveParams.js +3 -0
  402. package/dist/generated/codex-app-server/v2/ThreadArchiveResponse.js +3 -0
  403. package/dist/generated/codex-app-server/v2/ThreadArchivedNotification.js +3 -0
  404. package/dist/generated/codex-app-server/v2/ThreadCompactStartParams.js +3 -0
  405. package/dist/generated/codex-app-server/v2/ThreadCompactStartResponse.js +3 -0
  406. package/dist/generated/codex-app-server/v2/ThreadForkParams.js +3 -0
  407. package/dist/generated/codex-app-server/v2/ThreadForkResponse.js +3 -0
  408. package/dist/generated/codex-app-server/v2/ThreadItem.js +3 -0
  409. package/dist/generated/codex-app-server/v2/ThreadListParams.js +3 -0
  410. package/dist/generated/codex-app-server/v2/ThreadListResponse.js +3 -0
  411. package/dist/generated/codex-app-server/v2/ThreadLoadedListParams.js +3 -0
  412. package/dist/generated/codex-app-server/v2/ThreadLoadedListResponse.js +3 -0
  413. package/dist/generated/codex-app-server/v2/ThreadNameUpdatedNotification.js +3 -0
  414. package/dist/generated/codex-app-server/v2/ThreadReadParams.js +3 -0
  415. package/dist/generated/codex-app-server/v2/ThreadReadResponse.js +3 -0
  416. package/dist/generated/codex-app-server/v2/ThreadResumeParams.js +3 -0
  417. package/dist/generated/codex-app-server/v2/ThreadResumeResponse.js +3 -0
  418. package/dist/generated/codex-app-server/v2/ThreadRollbackParams.js +3 -0
  419. package/dist/generated/codex-app-server/v2/ThreadRollbackResponse.js +3 -0
  420. package/dist/generated/codex-app-server/v2/ThreadSetNameParams.js +3 -0
  421. package/dist/generated/codex-app-server/v2/ThreadSetNameResponse.js +3 -0
  422. package/dist/generated/codex-app-server/v2/ThreadSortKey.js +3 -0
  423. package/dist/generated/codex-app-server/v2/ThreadSourceKind.js +3 -0
  424. package/dist/generated/codex-app-server/v2/ThreadStartParams.js +3 -0
  425. package/dist/generated/codex-app-server/v2/ThreadStartResponse.js +3 -0
  426. package/dist/generated/codex-app-server/v2/ThreadStartedNotification.js +3 -0
  427. package/dist/generated/codex-app-server/v2/ThreadTokenUsage.js +3 -0
  428. package/dist/generated/codex-app-server/v2/ThreadTokenUsageUpdatedNotification.js +3 -0
  429. package/dist/generated/codex-app-server/v2/ThreadUnarchiveParams.js +3 -0
  430. package/dist/generated/codex-app-server/v2/ThreadUnarchiveResponse.js +3 -0
  431. package/dist/generated/codex-app-server/v2/ThreadUnarchivedNotification.js +3 -0
  432. package/dist/generated/codex-app-server/v2/TokenUsageBreakdown.js +3 -0
  433. package/dist/generated/codex-app-server/v2/ToolRequestUserInputAnswer.js +3 -0
  434. package/dist/generated/codex-app-server/v2/ToolRequestUserInputOption.js +3 -0
  435. package/dist/generated/codex-app-server/v2/ToolRequestUserInputParams.js +3 -0
  436. package/dist/generated/codex-app-server/v2/ToolRequestUserInputQuestion.js +3 -0
  437. package/dist/generated/codex-app-server/v2/ToolRequestUserInputResponse.js +3 -0
  438. package/dist/generated/codex-app-server/v2/ToolsV2.js +3 -0
  439. package/dist/generated/codex-app-server/v2/Turn.js +3 -0
  440. package/dist/generated/codex-app-server/v2/TurnCompletedNotification.js +3 -0
  441. package/dist/generated/codex-app-server/v2/TurnDiffUpdatedNotification.js +3 -0
  442. package/dist/generated/codex-app-server/v2/TurnError.js +3 -0
  443. package/dist/generated/codex-app-server/v2/TurnInterruptParams.js +3 -0
  444. package/dist/generated/codex-app-server/v2/TurnInterruptResponse.js +3 -0
  445. package/dist/generated/codex-app-server/v2/TurnPlanStep.js +3 -0
  446. package/dist/generated/codex-app-server/v2/TurnPlanStepStatus.js +3 -0
  447. package/dist/generated/codex-app-server/v2/TurnPlanUpdatedNotification.js +3 -0
  448. package/dist/generated/codex-app-server/v2/TurnStartParams.js +3 -0
  449. package/dist/generated/codex-app-server/v2/TurnStartResponse.js +3 -0
  450. package/dist/generated/codex-app-server/v2/TurnStartedNotification.js +3 -0
  451. package/dist/generated/codex-app-server/v2/TurnStatus.js +3 -0
  452. package/dist/generated/codex-app-server/v2/TurnSteerParams.js +3 -0
  453. package/dist/generated/codex-app-server/v2/TurnSteerResponse.js +3 -0
  454. package/dist/generated/codex-app-server/v2/UserInput.js +3 -0
  455. package/dist/generated/codex-app-server/v2/WebSearchAction.js +3 -0
  456. package/dist/generated/codex-app-server/v2/WindowsWorldWritableWarningNotification.js +3 -0
  457. package/dist/generated/codex-app-server/v2/WriteStatus.js +3 -0
  458. package/dist/generated/codex-app-server/v2/index.js +3 -0
  459. package/dist/service/app_server.js +441 -0
  460. package/dist/service/buffered_client_message_sender.js +89 -0
  461. package/dist/service/companyhelm_api_client.js +337 -0
  462. package/dist/service/docker/app_server_container.js +319 -0
  463. package/dist/service/docker/dind.js +114 -0
  464. package/dist/service/docker/runtime_app_server_exec.js +97 -0
  465. package/dist/service/host.js +15 -0
  466. package/dist/service/runtime_bashrc.js +57 -0
  467. package/dist/service/runtime_shell.js +23 -0
  468. package/dist/service/sdk/refresh_models.js +91 -0
  469. package/dist/service/thread_lifecycle.js +693 -0
  470. package/dist/service/thread_runtime.js +15 -0
  471. package/dist/service/thread_turn_state.js +46 -0
  472. package/dist/service/thread_user_message_request_store.js +136 -0
  473. package/dist/service/workspace_agents.js +82 -0
  474. package/dist/state/daemon_state.js +83 -0
  475. package/dist/state/db.js +63 -0
  476. package/dist/state/schema.js +59 -0
  477. package/dist/templates/app_server_bootstrap.sh.j2 +52 -0
  478. package/dist/templates/runtime_agents.md.j2 +50 -0
  479. package/dist/templates/runtime_bashrc.j2 +19 -0
  480. package/dist/utils/async_queue.js +102 -0
  481. package/dist/utils/daemon.js +15 -0
  482. package/dist/utils/logger.js +96 -0
  483. package/dist/utils/path.js +10 -0
  484. package/dist/utils/process.js +83 -0
  485. package/dist/utils/terminal.js +34 -0
  486. package/package.json +1 -1
@@ -0,0 +1,2627 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.toErrorMessage = toErrorMessage;
37
+ exports.isRetryableApiConnectionError = isRetryableApiConnectionError;
38
+ exports.formatApiConnectionFailureMessage = formatApiConnectionFailureMessage;
39
+ exports.formatApiConnectionFailureDiagnostics = formatApiConnectionFailureDiagnostics;
40
+ exports.shouldUseTurnSteer = shouldUseTurnSteer;
41
+ exports.isNoActiveTurnSteerError = isNoActiveTurnSteerError;
42
+ exports.isNoRunningTurnInterruptError = isNoRunningTurnInterruptError;
43
+ exports.normalizeThreadAgentApiUrlForRuntime = normalizeThreadAgentApiUrlForRuntime;
44
+ exports.extractThreadNameUpdateFromNotification = extractThreadNameUpdateFromNotification;
45
+ exports.extractServerMessageRequestId = extractServerMessageRequestId;
46
+ exports.runCommandLoop = runCommandLoop;
47
+ exports.isInternalDaemonChildProcess = isInternalDaemonChildProcess;
48
+ exports.runDetachedDaemonProcess = runDetachedDaemonProcess;
49
+ exports.sendDaemonParentMessage = sendDaemonParentMessage;
50
+ exports.runRootCommand = runRootCommand;
51
+ exports.buildRootConfig = buildRootConfig;
52
+ const protobuf_1 = require("@bufbuild/protobuf");
53
+ const protos_1 = require("@companyhelm/protos");
54
+ const drizzle_orm_1 = require("drizzle-orm");
55
+ const grpc = __importStar(require("@grpc/grpc-js"));
56
+ const node_child_process_1 = require("node:child_process");
57
+ const node_crypto_1 = require("node:crypto");
58
+ const node_fs_1 = require("node:fs");
59
+ const node_path_1 = require("node:path");
60
+ const config_js_1 = require("../config.js");
61
+ const startup_js_1 = require("./startup.js");
62
+ const companyhelm_api_client_js_1 = require("../service/companyhelm_api_client.js");
63
+ const buffered_client_message_sender_js_1 = require("../service/buffered_client_message_sender.js");
64
+ const host_js_1 = require("../service/host.js");
65
+ const refresh_models_js_1 = require("../service/sdk/refresh_models.js");
66
+ const app_server_js_1 = require("../service/app_server.js");
67
+ const runtime_app_server_exec_js_1 = require("../service/docker/runtime_app_server_exec.js");
68
+ const thread_runtime_js_1 = require("../service/thread_runtime.js");
69
+ const thread_turn_state_js_1 = require("../service/thread_turn_state.js");
70
+ const thread_user_message_request_store_js_1 = require("../service/thread_user_message_request_store.js");
71
+ const thread_lifecycle_js_1 = require("../service/thread_lifecycle.js");
72
+ const daemon_state_js_1 = require("../state/daemon_state.js");
73
+ const db_js_1 = require("../state/db.js");
74
+ const schema_js_1 = require("../state/schema.js");
75
+ const daemon_js_1 = require("../utils/daemon.js");
76
+ const logger_js_1 = require("../utils/logger.js");
77
+ const path_js_1 = require("../utils/path.js");
78
+ const terminal_js_1 = require("../utils/terminal.js");
79
+ const workspace_agents_js_1 = require("../service/workspace_agents.js");
80
+ const COMMAND_CHANNEL_CONNECT_RETRY_DELAY_MS = 1000;
81
+ const COMMAND_CHANNEL_OPEN_TIMEOUT_MS = 5000;
82
+ const TURN_COMPLETION_TIMEOUT_MS = 2 * 60 * 60000;
83
+ const GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS = 5 * 60000;
84
+ const GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS = 30000;
85
+ const GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS = 15 * 60000;
86
+ const WORKSPACE_INSTALLATIONS_DIRECTORY = ".companyhelm";
87
+ const WORKSPACE_INSTALLATIONS_FILENAME = "installations.json";
88
+ const THREAD_GIT_SKILLS_CONFIG_FILENAME = "thread-git-skills.json";
89
+ const THREAD_MCP_CONFIG_FILENAME = "thread-mcp.json";
90
+ const THREAD_AGENT_CLI_CONFIG_FILENAME = "thread-agent-cli.json";
91
+ const THREAD_MCP_BEARER_TOKEN_ENV_PREFIX = "COMPANYHELM_MCP_TOKEN_";
92
+ const THREAD_MCP_AUTH_TYPE_BEARER_TOKEN = 2;
93
+ const THREAD_MCP_STARTUP_TIMEOUT_SECONDS = 60;
94
+ const YOLO_APPROVAL_POLICY = "never";
95
+ const YOLO_SANDBOX_MODE = "danger-full-access";
96
+ const YOLO_SANDBOX_POLICY = { type: "dangerFullAccess" };
97
+ const DOCKER_INTERNAL_HOSTNAME = "host.docker.internal";
98
+ const LOCALHOST_HOSTNAMES = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1"]);
99
+ const DAEMON_STARTUP_TIMEOUT_MS = 15000;
100
+ class RootCommandInterruptedError extends Error {
101
+ constructor(message = "Root command interrupted.") {
102
+ super(message);
103
+ this.name = "RootCommandInterruptedError";
104
+ }
105
+ }
106
+ const threadAppServerSessions = new Map();
107
+ const threadRolloutPaths = new Map();
108
+ function rememberThreadRolloutPath(threadId, rolloutPath) {
109
+ if (rolloutPath && rolloutPath.trim().length > 0) {
110
+ threadRolloutPaths.set(threadId, rolloutPath);
111
+ }
112
+ }
113
+ async function getOrCreateThreadAppServerSession(threadId, runtimeContainer, appServerEnv, clientName, logger) {
114
+ const existingSession = threadAppServerSessions.get(threadId);
115
+ if (existingSession && existingSession.runtimeContainer === runtimeContainer) {
116
+ return existingSession;
117
+ }
118
+ if (existingSession && existingSession.runtimeContainer !== runtimeContainer) {
119
+ await stopThreadAppServerSession(threadId);
120
+ }
121
+ const appServer = new app_server_js_1.AppServerService(new runtime_app_server_exec_js_1.RuntimeContainerAppServerTransport(runtimeContainer, undefined, appServerEnv), clientName, logger, () => ({
122
+ threadId,
123
+ sdkThreadId: threadAppServerSessions.get(threadId)?.sdkThreadId ?? null,
124
+ }));
125
+ const newSession = {
126
+ runtimeContainer,
127
+ appServer,
128
+ appServerEnv,
129
+ sdkThreadId: null,
130
+ rolloutPath: threadRolloutPaths.get(threadId) ?? null,
131
+ started: false,
132
+ };
133
+ threadAppServerSessions.set(threadId, newSession);
134
+ return newSession;
135
+ }
136
+ async function ensureThreadAppServerSessionStarted(session) {
137
+ if (session.started) {
138
+ return;
139
+ }
140
+ await session.appServer.start();
141
+ session.started = true;
142
+ }
143
+ async function stopThreadAppServerSession(threadId) {
144
+ const session = threadAppServerSessions.get(threadId);
145
+ if (!session) {
146
+ return;
147
+ }
148
+ threadAppServerSessions.delete(threadId);
149
+ if (!session.started) {
150
+ return;
151
+ }
152
+ await session.appServer.stop().catch(() => undefined);
153
+ session.started = false;
154
+ }
155
+ async function stopAllThreadAppServerSessions() {
156
+ const threadIds = [...threadAppServerSessions.keys()];
157
+ for (const threadId of threadIds) {
158
+ await stopThreadAppServerSession(threadId);
159
+ }
160
+ }
161
+ async function stopAllThreadContainers(cfg, logger) {
162
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
163
+ let containers = [];
164
+ try {
165
+ containers = await db
166
+ .select({
167
+ runtimeContainer: schema_js_1.threads.runtimeContainer,
168
+ dindContainer: schema_js_1.threads.dindContainer,
169
+ })
170
+ .from(schema_js_1.threads)
171
+ .all();
172
+ }
173
+ finally {
174
+ client.close();
175
+ }
176
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
177
+ for (const container of containers) {
178
+ await containerService.stopContainer(container.runtimeContainer).catch((error) => {
179
+ logger.warn(`Failed to stop runtime container '${container.runtimeContainer}': ${toErrorMessage(error)}`);
180
+ });
181
+ if (container.dindContainer && container.dindContainer.trim().length > 0) {
182
+ await containerService.stopContainer(container.dindContainer).catch((error) => {
183
+ logger.warn(`Failed to stop DinD container '${container.dindContainer}': ${toErrorMessage(error)}`);
184
+ });
185
+ }
186
+ }
187
+ }
188
+ async function reconcileTrackedRunningThreadsOnStartup(cfg, logger) {
189
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
190
+ let runningThreads = [];
191
+ try {
192
+ runningThreads = await db
193
+ .select({
194
+ id: schema_js_1.threads.id,
195
+ sdkThreadId: schema_js_1.threads.sdkThreadId,
196
+ currentSdkTurnId: schema_js_1.threads.currentSdkTurnId,
197
+ runtimeContainer: schema_js_1.threads.runtimeContainer,
198
+ })
199
+ .from(schema_js_1.threads)
200
+ .where((0, drizzle_orm_1.eq)(schema_js_1.threads.isCurrentTurnRunning, true))
201
+ .all();
202
+ }
203
+ finally {
204
+ client.close();
205
+ }
206
+ if (runningThreads.length === 0) {
207
+ return;
208
+ }
209
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
210
+ for (const thread of runningThreads) {
211
+ if (!thread.sdkThreadId || !thread.currentSdkTurnId) {
212
+ await (0, thread_turn_state_js_1.updateThreadTurnState)(cfg.state_db_path, thread.id, {
213
+ isCurrentTurnRunning: false,
214
+ });
215
+ logger.warn(`Cleared stale running state for thread '${thread.id}' during startup because the tracked SDK thread/turn identifiers were incomplete.`);
216
+ continue;
217
+ }
218
+ let runtimeRunning = false;
219
+ try {
220
+ runtimeRunning = await containerService.isContainerRunning(thread.runtimeContainer);
221
+ }
222
+ catch (error) {
223
+ logger.warn(`Failed checking runtime container '${thread.runtimeContainer}' for thread '${thread.id}' during startup reconciliation: ${toErrorMessage(error)}`);
224
+ continue;
225
+ }
226
+ if (!runtimeRunning) {
227
+ await (0, thread_turn_state_js_1.updateThreadTurnState)(cfg.state_db_path, thread.id, {
228
+ isCurrentTurnRunning: false,
229
+ });
230
+ logger.info(`Cleared stale running state for thread '${thread.id}' during startup because runtime container '${thread.runtimeContainer}' is not running.`);
231
+ }
232
+ }
233
+ }
234
+ const SUPPORTED_REASONING_EFFORTS = new Set([
235
+ "none",
236
+ "minimal",
237
+ "low",
238
+ "medium",
239
+ "high",
240
+ "xhigh",
241
+ ]);
242
+ function normalizeReasoningLevels(value) {
243
+ if (Array.isArray(value)) {
244
+ return value.filter((item) => typeof item === "string");
245
+ }
246
+ if (typeof value === "string" && value.trim().length > 0) {
247
+ try {
248
+ const parsed = JSON.parse(value);
249
+ if (Array.isArray(parsed)) {
250
+ return parsed.filter((item) => typeof item === "string");
251
+ }
252
+ }
253
+ catch {
254
+ return [];
255
+ }
256
+ }
257
+ return [];
258
+ }
259
+ function toErrorMessage(error) {
260
+ return error instanceof Error ? error.message : String(error);
261
+ }
262
+ function getGrpcStatusCode(error) {
263
+ if (!error || typeof error !== "object" || !("code" in error)) {
264
+ return undefined;
265
+ }
266
+ const { code } = error;
267
+ return typeof code === "number" ? code : undefined;
268
+ }
269
+ function getGrpcStatusName(error) {
270
+ const code = getGrpcStatusCode(error);
271
+ if (code === undefined) {
272
+ return undefined;
273
+ }
274
+ const statusName = grpc.status[code];
275
+ return typeof statusName === "string" ? statusName : undefined;
276
+ }
277
+ function isRetryableApiConnectionError(error) {
278
+ return getGrpcStatusCode(error) !== grpc.status.UNAUTHENTICATED;
279
+ }
280
+ function formatGrpcMetadataForLog(metadata) {
281
+ if (!metadata) {
282
+ return undefined;
283
+ }
284
+ const rawEntries = metadata.getMap();
285
+ const entries = Object.entries(rawEntries);
286
+ if (entries.length === 0) {
287
+ return undefined;
288
+ }
289
+ const normalizedEntries = Object.fromEntries(entries.map(([key, value]) => [
290
+ key,
291
+ Buffer.isBuffer(value) ? value.toString("base64") : String(value),
292
+ ]));
293
+ return JSON.stringify(normalizedEntries);
294
+ }
295
+ function formatApiConnectionFailureMessage(error, apiUrl, secret) {
296
+ const statusCode = getGrpcStatusCode(error);
297
+ const statusName = getGrpcStatusName(error);
298
+ const serviceError = isGrpcServiceError(error) ? error : undefined;
299
+ const baseMessage = serviceError && typeof serviceError.details === "string" && serviceError.details.trim().length > 0
300
+ ? serviceError.details.trim()
301
+ : toErrorMessage(error);
302
+ let message = baseMessage;
303
+ if (statusCode !== undefined) {
304
+ message = `gRPC ${statusName ?? "UNKNOWN"} (${statusCode}): ${baseMessage}`;
305
+ }
306
+ message += ` [endpoint=${apiUrl}]`;
307
+ if (statusCode === grpc.status.UNAUTHENTICATED && (!secret || secret.trim().length === 0)) {
308
+ message += " Provide --secret <secret> to authenticate.";
309
+ }
310
+ return message;
311
+ }
312
+ function formatApiConnectionFailureDiagnostics(error) {
313
+ if (!isGrpcServiceError(error)) {
314
+ return error instanceof Error && typeof error.stack === "string" ? error.stack : undefined;
315
+ }
316
+ const diagnostics = [];
317
+ const statusCode = getGrpcStatusCode(error);
318
+ const statusName = getGrpcStatusName(error);
319
+ if (statusCode !== undefined) {
320
+ diagnostics.push(`code=${statusCode}`);
321
+ }
322
+ if (statusName) {
323
+ diagnostics.push(`status=${statusName}`);
324
+ }
325
+ if (typeof error.details === "string" && error.details.trim().length > 0) {
326
+ diagnostics.push(`details=${JSON.stringify(error.details.trim())}`);
327
+ }
328
+ const metadata = formatGrpcMetadataForLog(error.metadata);
329
+ if (metadata) {
330
+ diagnostics.push(`metadata=${metadata}`);
331
+ }
332
+ if (typeof error.stack === "string" && error.stack.trim().length > 0) {
333
+ diagnostics.push(`stack=${JSON.stringify(error.stack)}`);
334
+ }
335
+ return diagnostics.length > 0 ? diagnostics.join(" ") : undefined;
336
+ }
337
+ function shouldUseTurnSteer(allowSteer, startedFromIdle) {
338
+ return allowSteer && !startedFromIdle;
339
+ }
340
+ function isNoActiveTurnSteerError(error) {
341
+ return /no active turn to steer/i.test(toErrorMessage(error));
342
+ }
343
+ function isNoRunningTurnInterruptError(error) {
344
+ return /no running turn to interrupt/i.test(toErrorMessage(error));
345
+ }
346
+ function isTurnCompletionTimeoutError(error) {
347
+ return /timed out waiting for completion of turn/i.test(toErrorMessage(error));
348
+ }
349
+ function isRecord(value) {
350
+ return typeof value === "object" && value !== null;
351
+ }
352
+ function normalizeNonEmptyString(value) {
353
+ if (typeof value !== "string") {
354
+ return undefined;
355
+ }
356
+ const trimmed = value.trim();
357
+ return trimmed.length > 0 ? trimmed : undefined;
358
+ }
359
+ function rewriteLocalTargetForDockerRuntime(target) {
360
+ const trimmed = target.trim();
361
+ if (!trimmed) {
362
+ return trimmed;
363
+ }
364
+ if (trimmed.startsWith("[")) {
365
+ const closingBracketIndex = trimmed.indexOf("]");
366
+ if (closingBracketIndex > 0) {
367
+ const host = trimmed.slice(1, closingBracketIndex).toLowerCase();
368
+ if (LOCALHOST_HOSTNAMES.has(host)) {
369
+ return `${DOCKER_INTERNAL_HOSTNAME}${trimmed.slice(closingBracketIndex + 1)}`;
370
+ }
371
+ }
372
+ return trimmed;
373
+ }
374
+ const colonIndex = trimmed.indexOf(":");
375
+ const host = (colonIndex >= 0 ? trimmed.slice(0, colonIndex) : trimmed).toLowerCase();
376
+ if (!LOCALHOST_HOSTNAMES.has(host)) {
377
+ return trimmed;
378
+ }
379
+ return `${DOCKER_INTERNAL_HOSTNAME}${colonIndex >= 0 ? trimmed.slice(colonIndex) : ""}`;
380
+ }
381
+ function normalizeThreadAgentApiUrlForRuntime(agentApiUrl) {
382
+ const trimmed = agentApiUrl.trim();
383
+ if (!trimmed) {
384
+ return trimmed;
385
+ }
386
+ if (trimmed.includes("://")) {
387
+ try {
388
+ const parsed = new URL(trimmed);
389
+ if (LOCALHOST_HOSTNAMES.has(parsed.hostname.toLowerCase())) {
390
+ parsed.hostname = DOCKER_INTERNAL_HOSTNAME;
391
+ }
392
+ const pathname = parsed.pathname === "/" ? "" : parsed.pathname;
393
+ return `${parsed.protocol}//${parsed.host}${pathname}${parsed.search}${parsed.hash}`;
394
+ }
395
+ catch {
396
+ return trimmed;
397
+ }
398
+ }
399
+ const firstSlashIndex = trimmed.indexOf("/");
400
+ const target = firstSlashIndex >= 0 ? trimmed.slice(0, firstSlashIndex) : trimmed;
401
+ const pathSuffix = firstSlashIndex >= 0 ? trimmed.slice(firstSlashIndex) : "";
402
+ const rewrittenTarget = rewriteLocalTargetForDockerRuntime(target);
403
+ if (rewrittenTarget !== target) {
404
+ return `http://${rewrittenTarget}${pathSuffix}`;
405
+ }
406
+ return `${rewrittenTarget}${pathSuffix}`;
407
+ }
408
+ function extractThreadNameUpdateFromNotification(notification) {
409
+ if (notification.method === "thread/name/updated") {
410
+ const rawParams = notification.params;
411
+ const sdkThreadId = normalizeNonEmptyString(rawParams.threadId) ??
412
+ normalizeNonEmptyString(rawParams.thread_id) ??
413
+ normalizeNonEmptyString(rawParams.conversationId) ??
414
+ normalizeNonEmptyString(rawParams.conversation_id);
415
+ if (!sdkThreadId) {
416
+ return null;
417
+ }
418
+ return {
419
+ sdkThreadId,
420
+ threadName: normalizeNonEmptyString(rawParams.threadName) ??
421
+ normalizeNonEmptyString(rawParams.thread_name),
422
+ };
423
+ }
424
+ const rawNotification = notification;
425
+ if (rawNotification.method !== "codex/event/thread_name_updated") {
426
+ return null;
427
+ }
428
+ if (!isRecord(rawNotification.params)) {
429
+ return null;
430
+ }
431
+ const params = rawNotification.params;
432
+ const msg = isRecord(params.msg) ? params.msg : undefined;
433
+ const sdkThreadId = normalizeNonEmptyString(msg?.thread_id) ??
434
+ normalizeNonEmptyString(msg?.threadId) ??
435
+ normalizeNonEmptyString(params.threadId) ??
436
+ normalizeNonEmptyString(params.thread_id) ??
437
+ normalizeNonEmptyString(params.conversationId) ??
438
+ normalizeNonEmptyString(params.conversation_id);
439
+ if (!sdkThreadId) {
440
+ return null;
441
+ }
442
+ const threadName = normalizeNonEmptyString(msg?.thread_name) ??
443
+ normalizeNonEmptyString(msg?.threadName) ??
444
+ normalizeNonEmptyString(params.threadName) ??
445
+ normalizeNonEmptyString(params.thread_name);
446
+ return { sdkThreadId, threadName };
447
+ }
448
+ function isByte(value) {
449
+ return typeof value === "number" && Number.isInteger(value) && value >= 0 && value <= 255;
450
+ }
451
+ function decodeLengthDelimitedPayload(bytes) {
452
+ let index = 0;
453
+ let shift = 0;
454
+ let length = 0;
455
+ while (index < bytes.length) {
456
+ const current = bytes[index];
457
+ length |= (current & 0x7f) << shift;
458
+ index += 1;
459
+ if ((current & 0x80) === 0) {
460
+ break;
461
+ }
462
+ shift += 7;
463
+ if (shift > 28) {
464
+ return null;
465
+ }
466
+ }
467
+ if (index === 0) {
468
+ return null;
469
+ }
470
+ if (index + length !== bytes.length) {
471
+ return null;
472
+ }
473
+ return bytes.subarray(index);
474
+ }
475
+ function toUint8Array(data) {
476
+ if (data instanceof Uint8Array) {
477
+ return data;
478
+ }
479
+ if (Buffer.isBuffer(data)) {
480
+ return new Uint8Array(data);
481
+ }
482
+ if (Array.isArray(data) && data.every(isByte)) {
483
+ return Uint8Array.from(data);
484
+ }
485
+ if (data &&
486
+ typeof data === "object" &&
487
+ "type" in data &&
488
+ data.type === "Buffer" &&
489
+ "data" in data &&
490
+ Array.isArray(data.data)) {
491
+ const values = data.data;
492
+ if (values.every(isByte)) {
493
+ return Uint8Array.from(values);
494
+ }
495
+ }
496
+ return null;
497
+ }
498
+ function extractServerMessageRequestId(serverMessage) {
499
+ if (!serverMessage || typeof serverMessage !== "object") {
500
+ return undefined;
501
+ }
502
+ const typedMessage = serverMessage;
503
+ if (typeof typedMessage.requestId === "string" && typedMessage.requestId.length > 0) {
504
+ return typedMessage.requestId;
505
+ }
506
+ if (!Array.isArray(typedMessage.$unknown)) {
507
+ return undefined;
508
+ }
509
+ for (const field of typedMessage.$unknown) {
510
+ if (field?.no !== 1 || field.wireType !== 2) {
511
+ continue;
512
+ }
513
+ const bytes = toUint8Array(field.data);
514
+ if (!bytes || bytes.length === 0) {
515
+ continue;
516
+ }
517
+ const payload = decodeLengthDelimitedPayload(bytes) ?? bytes;
518
+ return Buffer.from(payload).toString("utf8");
519
+ }
520
+ return undefined;
521
+ }
522
+ function isGrpcServiceError(error) {
523
+ return Boolean(error && typeof error === "object" && "code" in error);
524
+ }
525
+ function isUnimplementedGrpcMethod(error) {
526
+ return isGrpcServiceError(error) && error.code === grpc.status.UNIMPLEMENTED;
527
+ }
528
+ function normalizeAccessTokenExpiration(accessTokenExpiresUnixTimeMs) {
529
+ const rawUnixTimeMs = Number(accessTokenExpiresUnixTimeMs);
530
+ const expirationUnixTimeMs = Number.isFinite(rawUnixTimeMs) && rawUnixTimeMs > 0
531
+ ? Math.floor(rawUnixTimeMs)
532
+ : Date.now() + 60 * 60000;
533
+ return {
534
+ accessTokenExpiresUnixTimeMs: expirationUnixTimeMs.toString(),
535
+ accessTokenExpiration: new Date(expirationUnixTimeMs).toISOString(),
536
+ };
537
+ }
538
+ async function loadRuntimeGithubInstallations(apiClient, options, logger) {
539
+ let installationIds = [];
540
+ try {
541
+ const listResponse = await apiClient.listGithubInstallationsForRunner(options);
542
+ installationIds = listResponse.installations.map((installation) => installation.installationId);
543
+ }
544
+ catch (error) {
545
+ const warning = isUnimplementedGrpcMethod(error)
546
+ ? "CompanyHelm API does not implement listGithubInstallationsForRunner yet."
547
+ : `Failed to fetch GitHub installations: ${toErrorMessage(error)}`;
548
+ logger.warn(warning);
549
+ return [];
550
+ }
551
+ const installationDetails = [];
552
+ for (const installationId of installationIds) {
553
+ try {
554
+ const accessTokenResponse = await apiClient.getGithubInstallationAccessTokenForRunner(installationId, options);
555
+ const accessToken = accessTokenResponse.accessToken.trim();
556
+ if (!accessToken) {
557
+ logger.warn(`Received empty GitHub access token for installation ${installationId.toString()}; skipping.`);
558
+ continue;
559
+ }
560
+ const expiration = normalizeAccessTokenExpiration(accessTokenResponse.accessTokenExpiresUnixTimeMs);
561
+ const repositories = [...new Set(accessTokenResponse.repositories.filter((repository) => repository.trim().length > 0))]
562
+ .sort((left, right) => left.localeCompare(right));
563
+ installationDetails.push({
564
+ installationId: accessTokenResponse.installationId.toString(),
565
+ accessToken,
566
+ accessTokenExpiresUnixTimeMs: expiration.accessTokenExpiresUnixTimeMs,
567
+ accessTokenExpiration: expiration.accessTokenExpiration,
568
+ repositories,
569
+ });
570
+ }
571
+ catch (error) {
572
+ const warning = isUnimplementedGrpcMethod(error)
573
+ ? "CompanyHelm API does not implement getGithubInstallationAccessTokenForRunner yet."
574
+ : `Failed to fetch GitHub access token for installation ${installationId.toString()}: ${toErrorMessage(error)}`;
575
+ logger.warn(warning);
576
+ }
577
+ }
578
+ return installationDetails;
579
+ }
580
+ function buildWorkspaceGithubInstallationsPayload(installations) {
581
+ return {
582
+ synced_at: new Date().toISOString(),
583
+ installations: installations.map((installation) => ({
584
+ installation_id: installation.installationId,
585
+ access_token: installation.accessToken,
586
+ access_token_expires_unix_time_ms: installation.accessTokenExpiresUnixTimeMs,
587
+ access_token_expiration: installation.accessTokenExpiration,
588
+ repositories: installation.repositories,
589
+ })),
590
+ };
591
+ }
592
+ function writeWorkspaceGithubInstallationsPayload(workspaceDirectory, payload, logger) {
593
+ const installationsDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
594
+ const installationsPath = (0, node_path_1.join)(installationsDirectory, WORKSPACE_INSTALLATIONS_FILENAME);
595
+ const temporaryPath = `${installationsPath}.tmp`;
596
+ const serializedPayload = `${JSON.stringify(payload, null, 2)}\n`;
597
+ try {
598
+ (0, node_fs_1.mkdirSync)(installationsDirectory, { recursive: true });
599
+ (0, node_fs_1.writeFileSync)(temporaryPath, serializedPayload, "utf8");
600
+ (0, node_fs_1.renameSync)(temporaryPath, installationsPath);
601
+ }
602
+ catch (error) {
603
+ logger.warn(`Failed writing GitHub installations file for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
604
+ }
605
+ }
606
+ function isHttpsRepositoryUrl(value) {
607
+ try {
608
+ const parsed = new URL(value);
609
+ return parsed.protocol === "https:";
610
+ }
611
+ catch {
612
+ return false;
613
+ }
614
+ }
615
+ function normalizeThreadGitSkillDirectoryPath(value) {
616
+ const trimmed = value.trim();
617
+ if (!trimmed) {
618
+ return null;
619
+ }
620
+ if (trimmed.startsWith("/")) {
621
+ return null;
622
+ }
623
+ if (trimmed.includes("\\")) {
624
+ return null;
625
+ }
626
+ const segments = trimmed.split("/").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
627
+ if (segments.length === 0) {
628
+ return null;
629
+ }
630
+ if (segments.some((segment) => segment === "." || segment === "..")) {
631
+ return null;
632
+ }
633
+ return segments.join("/");
634
+ }
635
+ function createThreadGitSkillLinkName(rawDirectoryPath) {
636
+ const fallback = "skill";
637
+ const segments = rawDirectoryPath.split("/").filter((segment) => segment.length > 0);
638
+ const lastPathSegment = segments.length > 0 ? segments[segments.length - 1] : fallback;
639
+ const sanitized = lastPathSegment
640
+ .replace(/[^a-zA-Z0-9._-]+/g, "-")
641
+ .replace(/^-+/, "")
642
+ .replace(/-+$/, "")
643
+ .replace(/^\.+/, "");
644
+ return sanitized.length > 0 ? sanitized : fallback;
645
+ }
646
+ function createThreadGitSkillCheckoutDirectoryName(repositoryUrl, commitReference, index) {
647
+ const digest = (0, node_crypto_1.createHash)("sha256")
648
+ .update(`${repositoryUrl}\n${commitReference}`)
649
+ .digest("hex")
650
+ .slice(0, 12);
651
+ const repoPathPart = repositoryUrl
652
+ .replace(/^https?:\/\//i, "")
653
+ .replace(/[^a-zA-Z0-9]+/g, "-")
654
+ .replace(/^-+/, "")
655
+ .replace(/-+$/, "")
656
+ .toLowerCase()
657
+ .slice(0, 48) || "repo";
658
+ return `${String(index + 1).padStart(2, "0")}-${repoPathPart}-${digest}`;
659
+ }
660
+ function normalizeThreadGitSkillPackagesForThreadConfig(rawPackages, logger) {
661
+ if (!Array.isArray(rawPackages) || rawPackages.length === 0) {
662
+ return [];
663
+ }
664
+ const normalizedPackages = [];
665
+ const linkNameAllocations = new Map();
666
+ for (const [packageIndex, rawPackage] of rawPackages.entries()) {
667
+ const repositoryUrl = normalizeNonEmptyString(rawPackage.repositoryUrl);
668
+ const commitReference = normalizeNonEmptyString(rawPackage.commitReference);
669
+ if (!repositoryUrl || !isHttpsRepositoryUrl(repositoryUrl)) {
670
+ logger.warn(`Skipping thread git skill package at index ${packageIndex}: repositoryUrl must be an https URL.`);
671
+ continue;
672
+ }
673
+ if (!commitReference) {
674
+ logger.warn(`Skipping thread git skill package at index ${packageIndex}: commitReference is required.`);
675
+ continue;
676
+ }
677
+ const rawSkills = Array.isArray(rawPackage.skills) ? rawPackage.skills : [];
678
+ const skills = [];
679
+ for (const rawSkill of rawSkills) {
680
+ const normalizedDirectoryPath = normalizeThreadGitSkillDirectoryPath(rawSkill.directoryPath ?? "");
681
+ if (!normalizedDirectoryPath) {
682
+ logger.warn(`Skipping thread git skill '${rawSkill.directoryPath ?? ""}' in package '${repositoryUrl}': invalid relative directory path.`);
683
+ continue;
684
+ }
685
+ const baseLinkName = createThreadGitSkillLinkName(normalizedDirectoryPath);
686
+ const allocation = linkNameAllocations.get(baseLinkName) ?? 0;
687
+ linkNameAllocations.set(baseLinkName, allocation + 1);
688
+ const linkName = allocation === 0 ? baseLinkName : `${baseLinkName}-${allocation + 1}`;
689
+ skills.push({
690
+ directoryPath: normalizedDirectoryPath,
691
+ linkName,
692
+ });
693
+ }
694
+ if (skills.length === 0) {
695
+ logger.warn(`Skipping thread git skill package '${repositoryUrl}@${commitReference}': no valid skill directory paths were provided.`);
696
+ continue;
697
+ }
698
+ normalizedPackages.push({
699
+ repositoryUrl,
700
+ commitReference,
701
+ checkoutDirectoryName: createThreadGitSkillCheckoutDirectoryName(repositoryUrl, commitReference, normalizedPackages.length),
702
+ skills,
703
+ });
704
+ }
705
+ return normalizedPackages;
706
+ }
707
+ function normalizeThreadMcpHeaderEntries(rawEntries, context, logger) {
708
+ if (!Array.isArray(rawEntries) || rawEntries.length === 0) {
709
+ return [];
710
+ }
711
+ const seenKeys = new Set();
712
+ const normalizedEntries = [];
713
+ for (const rawEntry of rawEntries) {
714
+ const key = normalizeNonEmptyString(rawEntry.key);
715
+ if (!key) {
716
+ logger.warn(`Skipping ${context} entry with empty key.`);
717
+ continue;
718
+ }
719
+ const dedupeKey = key.toLowerCase();
720
+ if (seenKeys.has(dedupeKey)) {
721
+ logger.warn(`Skipping duplicate ${context} key '${key}'.`);
722
+ continue;
723
+ }
724
+ seenKeys.add(dedupeKey);
725
+ normalizedEntries.push({
726
+ key,
727
+ value: typeof rawEntry.value === "string" ? rawEntry.value : "",
728
+ });
729
+ }
730
+ return normalizedEntries;
731
+ }
732
+ function normalizeThreadMcpServersForThreadConfig(rawServers, logger) {
733
+ if (!Array.isArray(rawServers) || rawServers.length === 0) {
734
+ return [];
735
+ }
736
+ const nameAllocations = new Map();
737
+ const normalizedServers = [];
738
+ for (const [serverIndex, rawServer] of rawServers.entries()) {
739
+ const rawName = normalizeNonEmptyString(rawServer.name);
740
+ if (!rawName) {
741
+ logger.warn(`Skipping thread MCP server at index ${serverIndex}: name is required.`);
742
+ continue;
743
+ }
744
+ const normalizedNameKey = rawName.toLowerCase();
745
+ const allocation = nameAllocations.get(normalizedNameKey) ?? 0;
746
+ nameAllocations.set(normalizedNameKey, allocation + 1);
747
+ const resolvedName = allocation === 0 ? rawName : `${rawName}-${allocation + 1}`;
748
+ if (resolvedName !== rawName) {
749
+ logger.warn(`Renaming duplicate thread MCP server '${rawName}' to '${resolvedName}'.`);
750
+ }
751
+ if (rawServer.transportConfig.case === "stdio") {
752
+ const command = normalizeNonEmptyString(rawServer.transportConfig.value.command);
753
+ if (!command) {
754
+ logger.warn(`Skipping thread MCP stdio server '${resolvedName}': command is required.`);
755
+ continue;
756
+ }
757
+ const args = Array.isArray(rawServer.transportConfig.value.args)
758
+ ? rawServer.transportConfig.value.args.filter((arg) => typeof arg === "string")
759
+ : [];
760
+ const envVars = normalizeThreadMcpHeaderEntries(rawServer.transportConfig.value.envVars, `thread MCP stdio env var for '${resolvedName}'`, logger);
761
+ normalizedServers.push({
762
+ name: resolvedName,
763
+ transport: "stdio",
764
+ command,
765
+ args,
766
+ envVars,
767
+ authType: "none",
768
+ headers: [],
769
+ });
770
+ continue;
771
+ }
772
+ if (rawServer.transportConfig.case !== "streamableHttp") {
773
+ logger.warn(`Skipping thread MCP server '${resolvedName}': transport is missing.`);
774
+ continue;
775
+ }
776
+ const url = normalizeNonEmptyString(rawServer.transportConfig.value.url);
777
+ if (!url) {
778
+ logger.warn(`Skipping thread MCP streamable_http server '${resolvedName}': url is required.`);
779
+ continue;
780
+ }
781
+ const authType = rawServer.transportConfig.value.authType === THREAD_MCP_AUTH_TYPE_BEARER_TOKEN
782
+ ? "bearer_token"
783
+ : "none";
784
+ const bearerToken = authType === "bearer_token"
785
+ ? normalizeNonEmptyString(rawServer.transportConfig.value.bearerToken)
786
+ : null;
787
+ if (authType === "bearer_token" && !bearerToken) {
788
+ logger.warn(`Skipping thread MCP streamable_http server '${resolvedName}': bearer token is required.`);
789
+ continue;
790
+ }
791
+ const headers = normalizeThreadMcpHeaderEntries(rawServer.transportConfig.value.headers, `thread MCP streamable_http header for '${resolvedName}'`, logger);
792
+ normalizedServers.push({
793
+ name: resolvedName,
794
+ transport: "streamable_http",
795
+ args: [],
796
+ envVars: [],
797
+ url,
798
+ authType,
799
+ bearerToken,
800
+ headers,
801
+ });
802
+ }
803
+ return normalizedServers;
804
+ }
805
+ function resolveThreadMcpConfigPath(workspaceDirectory) {
806
+ return (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY, THREAD_MCP_CONFIG_FILENAME);
807
+ }
808
+ function writeWorkspaceThreadMcpConfig(workspaceDirectory, mcpServers, logger) {
809
+ const configPath = resolveThreadMcpConfigPath(workspaceDirectory);
810
+ const configDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
811
+ const temporaryPath = `${configPath}.tmp`;
812
+ try {
813
+ (0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
814
+ if (mcpServers.length === 0) {
815
+ (0, node_fs_1.rmSync)(configPath, { force: true });
816
+ (0, node_fs_1.rmSync)(temporaryPath, { force: true });
817
+ return;
818
+ }
819
+ (0, node_fs_1.writeFileSync)(temporaryPath, `${JSON.stringify({ servers: mcpServers }, null, 2)}\n`, "utf8");
820
+ (0, node_fs_1.renameSync)(temporaryPath, configPath);
821
+ }
822
+ catch (error) {
823
+ logger.warn(`Failed writing thread MCP config for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
824
+ }
825
+ }
826
+ function parseThreadMcpConfig(content) {
827
+ if (!isRecord(content) || !Array.isArray(content.servers)) {
828
+ return null;
829
+ }
830
+ const parsedServers = [];
831
+ for (const rawServer of content.servers) {
832
+ if (!isRecord(rawServer)) {
833
+ return null;
834
+ }
835
+ const name = normalizeNonEmptyString(rawServer.name);
836
+ const transport = rawServer.transport;
837
+ const authType = rawServer.authType;
838
+ if (!name ||
839
+ (transport !== "stdio" && transport !== "streamable_http") ||
840
+ (authType !== "none" && authType !== "bearer_token")) {
841
+ return null;
842
+ }
843
+ const args = Array.isArray(rawServer.args) && rawServer.args.every((arg) => typeof arg === "string")
844
+ ? rawServer.args
845
+ : [];
846
+ const envVars = Array.isArray(rawServer.envVars)
847
+ ? rawServer.envVars
848
+ .filter((entry) => isRecord(entry))
849
+ .map((entry) => ({
850
+ key: normalizeNonEmptyString(entry.key) ?? "",
851
+ value: typeof entry.value === "string" ? entry.value : "",
852
+ }))
853
+ .filter((entry) => entry.key.length > 0)
854
+ : [];
855
+ const headers = Array.isArray(rawServer.headers)
856
+ ? rawServer.headers
857
+ .filter((entry) => isRecord(entry))
858
+ .map((entry) => ({
859
+ key: normalizeNonEmptyString(entry.key) ?? "",
860
+ value: typeof entry.value === "string" ? entry.value : "",
861
+ }))
862
+ .filter((entry) => entry.key.length > 0)
863
+ : [];
864
+ if (transport === "stdio") {
865
+ const command = normalizeNonEmptyString(rawServer.command);
866
+ if (!command) {
867
+ return null;
868
+ }
869
+ parsedServers.push({
870
+ name,
871
+ transport,
872
+ command,
873
+ args,
874
+ envVars,
875
+ authType,
876
+ headers: [],
877
+ });
878
+ continue;
879
+ }
880
+ const url = normalizeNonEmptyString(rawServer.url);
881
+ const bearerToken = authType === "bearer_token"
882
+ ? normalizeNonEmptyString(rawServer.bearerToken)
883
+ : null;
884
+ if (!url) {
885
+ return null;
886
+ }
887
+ if (authType === "bearer_token" && !bearerToken) {
888
+ return null;
889
+ }
890
+ parsedServers.push({
891
+ name,
892
+ transport,
893
+ args: [],
894
+ envVars: [],
895
+ url,
896
+ authType,
897
+ bearerToken,
898
+ headers,
899
+ });
900
+ }
901
+ return parsedServers;
902
+ }
903
+ function readWorkspaceThreadMcpConfig(workspaceDirectory, logger) {
904
+ const configPath = resolveThreadMcpConfigPath(workspaceDirectory);
905
+ try {
906
+ const rawContent = (0, node_fs_1.readFileSync)(configPath, "utf8");
907
+ const parsedContent = JSON.parse(rawContent);
908
+ const parsedConfig = parseThreadMcpConfig(parsedContent);
909
+ if (!parsedConfig) {
910
+ logger.warn(`Thread MCP config has invalid shape at '${configPath}'.`);
911
+ return [];
912
+ }
913
+ return parsedConfig;
914
+ }
915
+ catch (error) {
916
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
917
+ return [];
918
+ }
919
+ logger.warn(`Failed reading thread MCP config at '${configPath}': ${toErrorMessage(error)}`);
920
+ return [];
921
+ }
922
+ }
923
+ function resolveThreadAgentCliConfigPath(workspaceDirectory) {
924
+ return (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY, THREAD_AGENT_CLI_CONFIG_FILENAME);
925
+ }
926
+ function parseThreadAgentCliConfig(content) {
927
+ if (!isRecord(content)) {
928
+ return null;
929
+ }
930
+ const agentApiUrl = normalizeNonEmptyString(content.agent_api_url);
931
+ const token = normalizeNonEmptyString(content.token);
932
+ if (!agentApiUrl || !token) {
933
+ return null;
934
+ }
935
+ return {
936
+ agent_api_url: agentApiUrl,
937
+ token,
938
+ };
939
+ }
940
+ function writeWorkspaceThreadAgentCliConfig(workspaceDirectory, cliSecret, agentApiUrl, logger) {
941
+ const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
942
+ const configDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
943
+ const temporaryPath = `${configPath}.tmp`;
944
+ try {
945
+ (0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
946
+ if (cliSecret.length === 0) {
947
+ (0, node_fs_1.rmSync)(configPath, { force: true });
948
+ (0, node_fs_1.rmSync)(temporaryPath, { force: true });
949
+ return;
950
+ }
951
+ const payload = {
952
+ agent_api_url: normalizeThreadAgentApiUrlForRuntime(agentApiUrl),
953
+ token: cliSecret,
954
+ };
955
+ (0, node_fs_1.writeFileSync)(temporaryPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
956
+ (0, node_fs_1.renameSync)(temporaryPath, configPath);
957
+ }
958
+ catch (error) {
959
+ logger.warn(`Failed writing thread agent CLI config for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
960
+ }
961
+ }
962
+ function readWorkspaceThreadAgentCliConfig(workspaceDirectory, logger) {
963
+ const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
964
+ try {
965
+ const rawContent = (0, node_fs_1.readFileSync)(configPath, "utf8");
966
+ const parsedContent = JSON.parse(rawContent);
967
+ const parsedConfig = parseThreadAgentCliConfig(parsedContent);
968
+ if (!parsedConfig) {
969
+ logger.warn(`Thread agent CLI config has invalid shape at '${configPath}'.`);
970
+ return null;
971
+ }
972
+ return parsedConfig;
973
+ }
974
+ catch (error) {
975
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
976
+ return null;
977
+ }
978
+ logger.warn(`Failed reading thread agent CLI config at '${configPath}': ${toErrorMessage(error)}`);
979
+ return null;
980
+ }
981
+ }
982
+ function escapeTomlString(value) {
983
+ return JSON.stringify(value);
984
+ }
985
+ function formatTomlKey(value) {
986
+ return /^[A-Za-z0-9_-]+$/.test(value) ? value : escapeTomlString(value);
987
+ }
988
+ function buildThreadMcpBearerTokenEnvVarName(serverName, serverIndex) {
989
+ const normalized = serverName
990
+ .toUpperCase()
991
+ .replace(/[^A-Z0-9]+/g, "_")
992
+ .replace(/^_+/, "")
993
+ .replace(/_+$/, "");
994
+ const suffix = normalized.length > 0 ? normalized : `SERVER_${serverIndex + 1}`;
995
+ return `${THREAD_MCP_BEARER_TOKEN_ENV_PREFIX}${suffix}`;
996
+ }
997
+ function buildThreadCodexMcpSetup(mcpServers) {
998
+ const lines = [
999
+ "# Generated by CompanyHelm. Thread-scoped MCP server configuration for Codex.",
1000
+ ];
1001
+ const appServerEnv = {};
1002
+ for (const [serverIndex, server] of mcpServers.entries()) {
1003
+ const serverTableName = escapeTomlString(server.name);
1004
+ lines.push("", `[mcp_servers.${serverTableName}]`);
1005
+ lines.push(`startup_timeout_sec = ${THREAD_MCP_STARTUP_TIMEOUT_SECONDS}`);
1006
+ if (server.transport === "stdio") {
1007
+ lines.push(`command = ${escapeTomlString(server.command ?? "")}`);
1008
+ if (server.args.length > 0) {
1009
+ lines.push(`args = [${server.args.map((arg) => escapeTomlString(arg)).join(", ")}]`);
1010
+ }
1011
+ if (server.envVars.length > 0) {
1012
+ lines.push("", `[mcp_servers.${serverTableName}.env]`);
1013
+ for (const envVar of server.envVars) {
1014
+ lines.push(`${formatTomlKey(envVar.key)} = ${escapeTomlString(envVar.value)}`);
1015
+ }
1016
+ }
1017
+ continue;
1018
+ }
1019
+ lines.push(`url = ${escapeTomlString(server.url ?? "")}`);
1020
+ if (server.authType === "bearer_token" && server.bearerToken) {
1021
+ const envVarName = buildThreadMcpBearerTokenEnvVarName(server.name, serverIndex);
1022
+ lines.push(`bearer_token_env_var = ${escapeTomlString(envVarName)}`);
1023
+ appServerEnv[envVarName] = server.bearerToken;
1024
+ }
1025
+ if (server.headers.length > 0) {
1026
+ const renderedHeaders = server.headers
1027
+ .map((header) => `${formatTomlKey(header.key)} = ${escapeTomlString(header.value)}`)
1028
+ .join(", ");
1029
+ lines.push(`http_headers = { ${renderedHeaders} }`);
1030
+ }
1031
+ }
1032
+ return {
1033
+ configToml: `${lines.join("\n").trimEnd()}\n`,
1034
+ appServerEnv,
1035
+ };
1036
+ }
1037
+ function resolveThreadGitSkillsConfigPath(workspaceDirectory) {
1038
+ return (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY, THREAD_GIT_SKILLS_CONFIG_FILENAME);
1039
+ }
1040
+ function writeWorkspaceThreadGitSkillsConfig(workspaceDirectory, gitSkillPackages, logger) {
1041
+ const configPath = resolveThreadGitSkillsConfigPath(workspaceDirectory);
1042
+ const configDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
1043
+ const temporaryPath = `${configPath}.tmp`;
1044
+ try {
1045
+ (0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
1046
+ if (gitSkillPackages.length === 0) {
1047
+ (0, node_fs_1.rmSync)(configPath, { force: true });
1048
+ (0, node_fs_1.rmSync)(temporaryPath, { force: true });
1049
+ return;
1050
+ }
1051
+ (0, node_fs_1.writeFileSync)(temporaryPath, `${JSON.stringify({ packages: gitSkillPackages }, null, 2)}\n`, "utf8");
1052
+ (0, node_fs_1.renameSync)(temporaryPath, configPath);
1053
+ }
1054
+ catch (error) {
1055
+ logger.warn(`Failed writing thread git skills config for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
1056
+ }
1057
+ }
1058
+ function parseThreadGitSkillsConfig(content) {
1059
+ if (!isRecord(content) || !Array.isArray(content.packages)) {
1060
+ return null;
1061
+ }
1062
+ const parsedPackages = [];
1063
+ for (const rawPackage of content.packages) {
1064
+ if (!isRecord(rawPackage)) {
1065
+ return null;
1066
+ }
1067
+ const repositoryUrl = normalizeNonEmptyString(rawPackage.repositoryUrl);
1068
+ const commitReference = normalizeNonEmptyString(rawPackage.commitReference);
1069
+ const checkoutDirectoryName = normalizeNonEmptyString(rawPackage.checkoutDirectoryName);
1070
+ const rawSkills = rawPackage.skills;
1071
+ if (!repositoryUrl ||
1072
+ !isHttpsRepositoryUrl(repositoryUrl) ||
1073
+ !commitReference ||
1074
+ !checkoutDirectoryName ||
1075
+ checkoutDirectoryName.includes("/") ||
1076
+ checkoutDirectoryName.includes("\\") ||
1077
+ !Array.isArray(rawSkills)) {
1078
+ return null;
1079
+ }
1080
+ const parsedSkills = [];
1081
+ for (const rawSkill of rawSkills) {
1082
+ if (!isRecord(rawSkill)) {
1083
+ return null;
1084
+ }
1085
+ const directoryPath = normalizeThreadGitSkillDirectoryPath(normalizeNonEmptyString(rawSkill.directoryPath) ?? "");
1086
+ const linkName = normalizeNonEmptyString(rawSkill.linkName);
1087
+ if (!directoryPath ||
1088
+ !linkName ||
1089
+ linkName.includes("/") ||
1090
+ linkName.includes("\\") ||
1091
+ linkName.trim().length === 0 ||
1092
+ linkName.trim() === "." ||
1093
+ linkName.trim() === "..") {
1094
+ return null;
1095
+ }
1096
+ parsedSkills.push({ directoryPath, linkName });
1097
+ }
1098
+ if (parsedSkills.length === 0) {
1099
+ continue;
1100
+ }
1101
+ parsedPackages.push({
1102
+ repositoryUrl,
1103
+ commitReference,
1104
+ checkoutDirectoryName,
1105
+ skills: parsedSkills,
1106
+ });
1107
+ }
1108
+ return parsedPackages;
1109
+ }
1110
+ function readWorkspaceThreadGitSkillsConfig(workspaceDirectory, logger) {
1111
+ const configPath = resolveThreadGitSkillsConfigPath(workspaceDirectory);
1112
+ try {
1113
+ const rawContent = (0, node_fs_1.readFileSync)(configPath, "utf8");
1114
+ const parsedContent = JSON.parse(rawContent);
1115
+ const parsedPackages = parseThreadGitSkillsConfig(parsedContent);
1116
+ if (!parsedPackages) {
1117
+ logger.warn(`Thread git skills config has invalid shape at '${configPath}'.`);
1118
+ return [];
1119
+ }
1120
+ return parsedPackages;
1121
+ }
1122
+ catch (error) {
1123
+ if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
1124
+ return [];
1125
+ }
1126
+ logger.warn(`Failed reading thread git skills config at '${configPath}': ${toErrorMessage(error)}`);
1127
+ return [];
1128
+ }
1129
+ }
1130
+ async function ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger) {
1131
+ const packages = readWorkspaceThreadGitSkillsConfig(threadState.workspace, logger);
1132
+ if (packages.length === 0) {
1133
+ return;
1134
+ }
1135
+ await containerService.ensureRuntimeContainerThreadGitSkills(threadState.runtimeContainer, {
1136
+ uid: threadState.uid,
1137
+ gid: threadState.gid,
1138
+ agentUser: cfg.agent_user,
1139
+ agentHomeDirectory: threadState.homeDirectory,
1140
+ }, {
1141
+ cloneRootDirectory: cfg.thread_git_skills_directory,
1142
+ packages,
1143
+ });
1144
+ }
1145
+ function buildThreadRuntimeUser(cfg, threadState) {
1146
+ return {
1147
+ uid: threadState.uid,
1148
+ gid: threadState.gid,
1149
+ agentUser: cfg.agent_user,
1150
+ agentHomeDirectory: threadState.homeDirectory,
1151
+ };
1152
+ }
1153
+ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, logger) {
1154
+ if (!threadState.isCurrentTurnRunning) {
1155
+ return threadState;
1156
+ }
1157
+ if (!threadState.sdkThreadId || !threadState.currentSdkTurnId) {
1158
+ await updateThreadTurnState(cfg, threadState.id, {
1159
+ isCurrentTurnRunning: false,
1160
+ });
1161
+ logger.warn(`Cleared stale running state for thread '${threadState.id}' before user message handling because the tracked SDK thread/turn identifiers were incomplete.`);
1162
+ return {
1163
+ ...threadState,
1164
+ isCurrentTurnRunning: false,
1165
+ };
1166
+ }
1167
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
1168
+ if (!(await containerService.isContainerRunning(threadState.runtimeContainer))) {
1169
+ await stopThreadAppServerSession(threadState.id);
1170
+ await updateThreadTurnState(cfg, threadState.id, {
1171
+ isCurrentTurnRunning: false,
1172
+ });
1173
+ logger.info(`Cleared stale running state for thread '${threadState.id}' before user message handling because runtime container '${threadState.runtimeContainer}' is not running.`);
1174
+ return {
1175
+ ...threadState,
1176
+ isCurrentTurnRunning: false,
1177
+ };
1178
+ }
1179
+ const threadMcpSetup = buildThreadCodexMcpSetup(readWorkspaceThreadMcpConfig(threadState.workspace, logger));
1180
+ const threadAgentCliConfig = readWorkspaceThreadAgentCliConfig(threadState.workspace, logger);
1181
+ const appServerSession = await getOrCreateThreadAppServerSession(threadState.id, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
1182
+ const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
1183
+ await (0, thread_runtime_js_1.ensureThreadRuntimeReady)({
1184
+ dindContainer: threadState.dindContainer,
1185
+ runtimeContainer: threadState.runtimeContainer,
1186
+ containerService,
1187
+ gitUserName: cfg.git_user_name,
1188
+ gitUserEmail: cfg.git_user_email,
1189
+ user: runtimeUser,
1190
+ });
1191
+ await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
1192
+ if (threadAgentCliConfig) {
1193
+ await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
1194
+ }
1195
+ if (!appServerSession.started) {
1196
+ await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
1197
+ }
1198
+ await ensureThreadAppServerSessionStarted(appServerSession);
1199
+ const resumeResult = await appServerSession.appServer.resumeThread({
1200
+ threadId: threadState.sdkThreadId,
1201
+ approvalPolicy: YOLO_APPROVAL_POLICY,
1202
+ sandbox: YOLO_SANDBOX_MODE,
1203
+ persistExtendedHistory: true,
1204
+ });
1205
+ appServerSession.sdkThreadId = resumeResult.thread.id;
1206
+ appServerSession.rolloutPath = resumeResult.thread.path;
1207
+ rememberThreadRolloutPath(threadState.id, resumeResult.thread.path);
1208
+ const trackedTurn = resumeResult.thread.turns.find((turn) => turn.id === threadState.currentSdkTurnId);
1209
+ if (trackedTurn?.status === "inProgress") {
1210
+ return {
1211
+ ...threadState,
1212
+ sdkThreadId: resumeResult.thread.id,
1213
+ };
1214
+ }
1215
+ await updateThreadTurnState(cfg, threadState.id, {
1216
+ sdkThreadId: resumeResult.thread.id,
1217
+ isCurrentTurnRunning: false,
1218
+ });
1219
+ logger.info(`Cleared stale running state for thread '${threadState.id}' before user message handling because SDK turn '${threadState.currentSdkTurnId}' is no longer in progress.`);
1220
+ return {
1221
+ ...threadState,
1222
+ sdkThreadId: resumeResult.thread.id,
1223
+ isCurrentTurnRunning: false,
1224
+ };
1225
+ }
1226
+ async function listTrackedThreadWorkspaces(cfg, logger) {
1227
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1228
+ try {
1229
+ const rows = await db.select({ workspace: schema_js_1.threads.workspace }).from(schema_js_1.threads);
1230
+ return [...new Set(rows.map((row) => row.workspace.trim()).filter((workspace) => workspace.length > 0))];
1231
+ }
1232
+ catch (error) {
1233
+ logger.warn(`Failed to list tracked thread workspaces for GitHub installation sync: ${toErrorMessage(error)}`);
1234
+ return [];
1235
+ }
1236
+ finally {
1237
+ client.close();
1238
+ }
1239
+ }
1240
+ function resolveGithubInstallationsSyncDelayMs(installations) {
1241
+ let syncDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
1242
+ const now = Date.now();
1243
+ for (const installation of installations) {
1244
+ const expirationUnixTimeMs = Number(installation.accessTokenExpiresUnixTimeMs);
1245
+ if (!Number.isFinite(expirationUnixTimeMs) || expirationUnixTimeMs <= 0) {
1246
+ continue;
1247
+ }
1248
+ const refreshInMs = expirationUnixTimeMs - now - GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS;
1249
+ const boundedRefreshDelayMs = Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, refreshInMs));
1250
+ syncDelayMs = Math.min(syncDelayMs, boundedRefreshDelayMs);
1251
+ }
1252
+ return Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, syncDelayMs));
1253
+ }
1254
+ async function syncGithubInstallationsForWorkspaces(apiClient, options, workspaceDirectories, logger) {
1255
+ const uniqueWorkspaces = [
1256
+ ...new Set(workspaceDirectories.map((workspace) => workspace.trim()).filter((workspace) => workspace.length > 0)),
1257
+ ];
1258
+ if (uniqueWorkspaces.length === 0) {
1259
+ return [];
1260
+ }
1261
+ const installations = await loadRuntimeGithubInstallations(apiClient, options, logger);
1262
+ const payload = buildWorkspaceGithubInstallationsPayload(installations);
1263
+ for (const workspaceDirectory of uniqueWorkspaces) {
1264
+ writeWorkspaceGithubInstallationsPayload(workspaceDirectory, payload, logger);
1265
+ }
1266
+ logger.debug(`Synced ${installations.length} GitHub installation token(s) to ${uniqueWorkspaces.length} workspace(s).`);
1267
+ return installations;
1268
+ }
1269
+ async function waitForAbort(signal, delayMs) {
1270
+ if (signal.aborted) {
1271
+ return;
1272
+ }
1273
+ await new Promise((resolve) => {
1274
+ const timer = setTimeout(() => {
1275
+ signal.removeEventListener("abort", handleAbort);
1276
+ resolve();
1277
+ }, delayMs);
1278
+ function handleAbort() {
1279
+ clearTimeout(timer);
1280
+ signal.removeEventListener("abort", handleAbort);
1281
+ resolve();
1282
+ }
1283
+ signal.addEventListener("abort", handleAbort);
1284
+ });
1285
+ }
1286
+ async function runGithubInstallationsSyncLoop(cfg, apiClient, options, logger, signal) {
1287
+ while (!signal.aborted) {
1288
+ let nextDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
1289
+ try {
1290
+ const workspaces = await listTrackedThreadWorkspaces(cfg, logger);
1291
+ const installations = await syncGithubInstallationsForWorkspaces(apiClient, options, workspaces, logger);
1292
+ nextDelayMs = resolveGithubInstallationsSyncDelayMs(installations);
1293
+ }
1294
+ catch (error) {
1295
+ logger.warn(`GitHub installation sync loop iteration failed: ${toErrorMessage(error)}`);
1296
+ nextDelayMs = GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS;
1297
+ }
1298
+ await waitForAbort(signal, nextDelayMs);
1299
+ }
1300
+ }
1301
+ function normalizeReasoningEffort(value) {
1302
+ if (!value) {
1303
+ return null;
1304
+ }
1305
+ const normalized = value.trim().toLowerCase();
1306
+ if (!SUPPORTED_REASONING_EFFORTS.has(normalized)) {
1307
+ return null;
1308
+ }
1309
+ return normalized;
1310
+ }
1311
+ function normalizeAdditionalModelInstructions(value) {
1312
+ if (typeof value !== "string") {
1313
+ return null;
1314
+ }
1315
+ const trimmed = value.trim();
1316
+ return trimmed.length > 0 ? trimmed : null;
1317
+ }
1318
+ function buildThreadDeveloperInstructions(additionalModelInstructions) {
1319
+ return normalizeAdditionalModelInstructions(additionalModelInstructions);
1320
+ }
1321
+ function buildUserTextInput(text) {
1322
+ return [
1323
+ {
1324
+ type: "text",
1325
+ text,
1326
+ text_elements: [],
1327
+ },
1328
+ ];
1329
+ }
1330
+ function truncateSummary(text, maxLength = 240) {
1331
+ const normalized = text.trim();
1332
+ if (normalized.length <= maxLength) {
1333
+ return normalized;
1334
+ }
1335
+ return `${normalized.slice(0, Math.max(0, maxLength - 3))}...`;
1336
+ }
1337
+ function summarizeUserInput(input) {
1338
+ switch (input.type) {
1339
+ case "text":
1340
+ return input.text.trim();
1341
+ case "image":
1342
+ return `[image] ${input.url}`;
1343
+ case "localImage":
1344
+ return `[local image] ${input.path}`;
1345
+ case "skill":
1346
+ return `[skill] ${input.name} (${input.path})`;
1347
+ case "mention":
1348
+ return `[mention] ${input.name} (${input.path})`;
1349
+ default:
1350
+ return "";
1351
+ }
1352
+ }
1353
+ function summarizeWebSearchItem(item) {
1354
+ if (!item.action) {
1355
+ return `Web search: ${truncateSummary(item.query, 180)}`;
1356
+ }
1357
+ switch (item.action.type) {
1358
+ case "search": {
1359
+ const query = item.action.query?.trim()
1360
+ || item.action.queries?.map((entry) => entry.trim()).filter((entry) => entry.length > 0).join(", ")
1361
+ || item.query;
1362
+ return `Web search: ${truncateSummary(query, 180)}`;
1363
+ }
1364
+ case "openPage":
1365
+ return item.action.url ? `Opened web page: ${item.action.url}` : "Opened web page";
1366
+ case "findInPage": {
1367
+ const target = item.action.url ? ` in ${item.action.url}` : "";
1368
+ const pattern = item.action.pattern?.trim();
1369
+ if (!pattern) {
1370
+ return `Find in page${target}`;
1371
+ }
1372
+ return `Find in page${target}: ${truncateSummary(pattern, 140)}`;
1373
+ }
1374
+ case "other":
1375
+ default:
1376
+ return `Web search action: ${truncateSummary(item.query, 180)}`;
1377
+ }
1378
+ }
1379
+ function mapThreadItemType(item) {
1380
+ switch (item.type) {
1381
+ case "userMessage":
1382
+ return protos_1.ItemType.USER_MESSAGE;
1383
+ case "agentMessage":
1384
+ return protos_1.ItemType.AGENT_MESSAGE;
1385
+ case "plan":
1386
+ return protos_1.ItemType.PLAN;
1387
+ case "reasoning":
1388
+ return protos_1.ItemType.REASONING;
1389
+ case "commandExecution":
1390
+ return protos_1.ItemType.COMMAND_EXECUTION;
1391
+ case "fileChange":
1392
+ return protos_1.ItemType.FILE_CHANGE;
1393
+ case "mcpToolCall":
1394
+ return protos_1.ItemType.MCP_TOOL_CALL;
1395
+ case "collabAgentToolCall":
1396
+ return protos_1.ItemType.COLLAB_AGENT_TOOL_CALL;
1397
+ case "webSearch":
1398
+ return protos_1.ItemType.WEB_SEARCH;
1399
+ case "imageView":
1400
+ return protos_1.ItemType.IMAGE_VIEW;
1401
+ case "enteredReviewMode":
1402
+ return protos_1.ItemType.ENTERED_REVIEW_MODE;
1403
+ case "exitedReviewMode":
1404
+ return protos_1.ItemType.EXITED_REVIEW_MODE;
1405
+ case "contextCompaction":
1406
+ return protos_1.ItemType.CONTEXT_COMPACTION;
1407
+ default:
1408
+ return protos_1.ItemType.ITEM_TYPE_UNKNOWN;
1409
+ }
1410
+ }
1411
+ function summarizeThreadItemText(item) {
1412
+ switch (item.type) {
1413
+ case "userMessage": {
1414
+ const summarizedInputs = item.content
1415
+ .map((input) => summarizeUserInput(input))
1416
+ .map((value) => value.trim())
1417
+ .filter((value) => value.length > 0);
1418
+ if (summarizedInputs.length === 0) {
1419
+ return "User message";
1420
+ }
1421
+ return truncateSummary(summarizedInputs.join("\n"), 800);
1422
+ }
1423
+ case "agentMessage":
1424
+ return item.text.trim() || "Agent message";
1425
+ case "plan":
1426
+ return item.text.trim() || "Plan update";
1427
+ case "reasoning": {
1428
+ const summary = item.summary.join("\n").trim();
1429
+ if (summary) {
1430
+ return truncateSummary(summary, 800);
1431
+ }
1432
+ const reasoningContent = item.content.join("\n").trim();
1433
+ return reasoningContent ? truncateSummary(reasoningContent, 800) : "Reasoning update";
1434
+ }
1435
+ case "commandExecution":
1436
+ return item.command.trim() || "Command execution";
1437
+ case "fileChange": {
1438
+ const changedPaths = item.changes
1439
+ .map((change) => String(change.path || "").trim())
1440
+ .filter((path) => path.length > 0);
1441
+ if (changedPaths.length === 0) {
1442
+ return `File change (${item.status})`;
1443
+ }
1444
+ const preview = changedPaths.slice(0, 3).join(", ");
1445
+ const suffix = changedPaths.length > 3 ? ", ..." : "";
1446
+ const noun = changedPaths.length === 1 ? "file" : "files";
1447
+ return `File change (${item.status}): ${changedPaths.length} ${noun} (${preview}${suffix})`;
1448
+ }
1449
+ case "mcpToolCall": {
1450
+ const base = `MCP ${item.server}/${item.tool} (${item.status})`;
1451
+ if (item.error?.message) {
1452
+ return `${base}: ${truncateSummary(item.error.message, 140)}`;
1453
+ }
1454
+ if (item.status === "completed") {
1455
+ return `${base}: completed`;
1456
+ }
1457
+ return base;
1458
+ }
1459
+ case "collabAgentToolCall": {
1460
+ const receiverCount = item.receiverThreadIds.length;
1461
+ const receiverLabel = receiverCount === 1 ? "1 receiver" : `${receiverCount} receivers`;
1462
+ const prompt = item.prompt?.trim();
1463
+ if (prompt) {
1464
+ return `Collab ${item.tool} (${item.status}, ${receiverLabel}): ${truncateSummary(prompt, 140)}`;
1465
+ }
1466
+ return `Collab ${item.tool} (${item.status}, ${receiverLabel})`;
1467
+ }
1468
+ case "webSearch":
1469
+ return summarizeWebSearchItem(item);
1470
+ case "imageView":
1471
+ return item.path.trim() ? `Viewed image: ${item.path}` : "Viewed image";
1472
+ case "enteredReviewMode":
1473
+ return item.review.trim() ? `Entered review mode: ${truncateSummary(item.review, 180)}` : "Entered review mode";
1474
+ case "exitedReviewMode":
1475
+ return item.review.trim() ? `Exited review mode: ${truncateSummary(item.review, 180)}` : "Exited review mode";
1476
+ case "contextCompaction":
1477
+ return "Context compaction";
1478
+ default:
1479
+ return undefined;
1480
+ }
1481
+ }
1482
+ function buildCommandExecutionItem(item) {
1483
+ if (item.type !== "commandExecution") {
1484
+ return undefined;
1485
+ }
1486
+ return {
1487
+ command: item.command,
1488
+ cwd: item.cwd,
1489
+ processId: item.processId ?? "unknown",
1490
+ output: item.aggregatedOutput ?? undefined,
1491
+ };
1492
+ }
1493
+ function removeWorkspaceDirectory(workspacePath) {
1494
+ (0, node_fs_1.rmSync)(workspacePath, { recursive: true, force: true });
1495
+ }
1496
+ async function sendRequestError(commandChannel, errorMessage, requestId) {
1497
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1498
+ requestId,
1499
+ payload: {
1500
+ case: "requestError",
1501
+ value: {
1502
+ errorMessage,
1503
+ },
1504
+ },
1505
+ });
1506
+ await commandChannel.send(message);
1507
+ }
1508
+ async function sendHeartbeatResponse(commandChannel, requestId) {
1509
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1510
+ requestId,
1511
+ payload: {
1512
+ case: "heartbeatResponse",
1513
+ value: {},
1514
+ },
1515
+ });
1516
+ await commandChannel.send(message);
1517
+ }
1518
+ async function sendThreadUpdate(commandChannel, threadId, status, requestId) {
1519
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1520
+ requestId,
1521
+ payload: {
1522
+ case: "threadUpdate",
1523
+ value: {
1524
+ threadId,
1525
+ status,
1526
+ },
1527
+ },
1528
+ });
1529
+ await commandChannel.send(message);
1530
+ }
1531
+ async function sendThreadNameUpdate(commandChannel, threadId, threadName) {
1532
+ const normalizedThreadName = typeof threadName === "string"
1533
+ ? threadName.trim() || undefined
1534
+ : undefined;
1535
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1536
+ payload: {
1537
+ case: "threadNameUpdate",
1538
+ value: {
1539
+ threadId,
1540
+ threadName: normalizedThreadName,
1541
+ },
1542
+ },
1543
+ });
1544
+ await commandChannel.send(message);
1545
+ }
1546
+ async function sendTurnExecutionUpdate(commandChannel, threadId, sdkTurnId, status, requestId) {
1547
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1548
+ requestId,
1549
+ payload: {
1550
+ case: "turnUpdate",
1551
+ value: {
1552
+ threadId,
1553
+ sdkTurnId,
1554
+ status,
1555
+ },
1556
+ },
1557
+ });
1558
+ await commandChannel.send(message);
1559
+ }
1560
+ async function sendItemExecutionUpdate(commandChannel, threadId, sdkTurnId, sdkItemId, status, item, requestId) {
1561
+ const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
1562
+ requestId,
1563
+ payload: {
1564
+ case: "itemUpdate",
1565
+ value: {
1566
+ sdkItemId,
1567
+ status,
1568
+ itemType: mapThreadItemType(item),
1569
+ text: summarizeThreadItemText(item),
1570
+ commandExecutionItem: buildCommandExecutionItem(item),
1571
+ threadId,
1572
+ sdkTurnId,
1573
+ },
1574
+ },
1575
+ });
1576
+ await commandChannel.send(message);
1577
+ }
1578
+ async function buildRegisterRunnerRequest(cfg) {
1579
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1580
+ try {
1581
+ const configuredSdks = await db.select().from(schema_js_1.agentSdks).orderBy(schema_js_1.agentSdks.name).all();
1582
+ if (configuredSdks.length === 0) {
1583
+ throw new Error("No SDKs configured. Run startup before connecting to CompanyHelm API.");
1584
+ }
1585
+ const models = await db.select().from(schema_js_1.llmModels).orderBy(schema_js_1.llmModels.sdkName, schema_js_1.llmModels.name).all();
1586
+ const modelsBySdk = new Map();
1587
+ for (const model of models) {
1588
+ const sdkModels = modelsBySdk.get(model.sdkName) ?? [];
1589
+ sdkModels.push({
1590
+ name: model.name,
1591
+ reasoning: normalizeReasoningLevels(model.reasoningLevels),
1592
+ });
1593
+ modelsBySdk.set(model.sdkName, sdkModels);
1594
+ }
1595
+ return (0, protobuf_1.create)(protos_1.RegisterRunnerRequestSchema, {
1596
+ agentSdks: configuredSdks.map((sdk) => ({
1597
+ name: sdk.name,
1598
+ models: modelsBySdk.get(sdk.name) ?? [],
1599
+ })),
1600
+ });
1601
+ }
1602
+ finally {
1603
+ client.close();
1604
+ }
1605
+ }
1606
+ async function hasConfiguredSdks(cfg) {
1607
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1608
+ try {
1609
+ const configuredSdks = await db.select().from(schema_js_1.agentSdks).all();
1610
+ return configuredSdks.length > 0;
1611
+ }
1612
+ finally {
1613
+ client.close();
1614
+ }
1615
+ }
1616
+ async function countSdkModels(cfg, sdkName) {
1617
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1618
+ try {
1619
+ const models = await db.select({ name: schema_js_1.llmModels.name }).from(schema_js_1.llmModels).where((0, drizzle_orm_1.eq)(schema_js_1.llmModels.sdkName, sdkName)).all();
1620
+ return models.length;
1621
+ }
1622
+ finally {
1623
+ client.close();
1624
+ }
1625
+ }
1626
+ async function refreshCodexModelsForRegistration(cfg, logger) {
1627
+ try {
1628
+ const results = await (0, refresh_models_js_1.refreshSdkModels)({ sdk: "codex", logger });
1629
+ const modelCount = results[0]?.modelCount ?? 0;
1630
+ logger.info(`Refreshed Codex models from container app-server (${modelCount} models).`);
1631
+ }
1632
+ catch (error) {
1633
+ const cachedModelCount = await countSdkModels(cfg, "codex");
1634
+ const failureMessage = (0, refresh_models_js_1.formatSdkModelRefreshFailure)("codex", error);
1635
+ if (cachedModelCount === 0) {
1636
+ throw new Error(`${failureMessage} Runner startup aborted because no cached Codex models are available; refusing to register zero models.`);
1637
+ }
1638
+ logger.warn(`${failureMessage} Using ${cachedModelCount} cached Codex model(s) from local state instead of registering zero models.`);
1639
+ }
1640
+ }
1641
+ async function resolveThreadAuthMode(cfg) {
1642
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1643
+ try {
1644
+ const codexSdk = await db.select().from(schema_js_1.agentSdks).where((0, drizzle_orm_1.eq)(schema_js_1.agentSdks.name, "codex")).get();
1645
+ if (!codexSdk) {
1646
+ throw new Error("Codex SDK is not configured.");
1647
+ }
1648
+ if (codexSdk.authentication !== "host" && codexSdk.authentication !== "dedicated") {
1649
+ throw new Error(`Unsupported Codex authentication mode '${codexSdk.authentication}' for thread creation.`);
1650
+ }
1651
+ return codexSdk.authentication;
1652
+ }
1653
+ finally {
1654
+ client.close();
1655
+ }
1656
+ }
1657
+ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId, apiClient, apiCallOptions, logger) {
1658
+ const threadId = (request.threadId ?? "").trim();
1659
+ const modelName = (request.model ?? "").trim();
1660
+ const requestedReasoningLevel = (request.reasoningLevel ?? "").trim();
1661
+ if (!threadId) {
1662
+ logger.warn("Rejecting createThreadRequest: threadId is required.");
1663
+ await sendRequestError(commandChannel, "Thread id is required.", requestId);
1664
+ return;
1665
+ }
1666
+ if (!modelName) {
1667
+ logger.warn("Rejecting createThreadRequest: model is required.");
1668
+ await sendRequestError(commandChannel, "Model is required.", requestId);
1669
+ return;
1670
+ }
1671
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1672
+ const threadDirectory = (0, thread_lifecycle_js_1.resolveThreadDirectory)(cfg.config_directory, cfg.workspaces_directory, threadId);
1673
+ const containerNames = (0, thread_lifecycle_js_1.buildThreadContainerNames)(threadId);
1674
+ const hostInfo = (0, host_js_1.getHostInfo)(cfg.codex.codex_auth_path);
1675
+ const normalizedAdditionalModelInstructions = normalizeAdditionalModelInstructions(request.additionalModelInstructions);
1676
+ const threadGitSkillPackages = normalizeThreadGitSkillPackagesForThreadConfig(request.gitSkillPackages, logger);
1677
+ const threadMcpServers = normalizeThreadMcpServersForThreadConfig(request.mcpServers, logger);
1678
+ const cliSecret = String(request.cliSecret ?? "").trim();
1679
+ logger.debug(`Received createThreadRequest for thread '${threadId}' (model '${modelName}', reasoning '${requestedReasoningLevel}', additional instructions length '${normalizedAdditionalModelInstructions?.length ?? 0}', git skill packages '${threadGitSkillPackages.length}', MCP servers '${threadMcpServers.length}').`);
1680
+ let authMode;
1681
+ try {
1682
+ authMode = await resolveThreadAuthMode(cfg);
1683
+ const modelConfig = await db
1684
+ .select({
1685
+ name: schema_js_1.llmModels.name,
1686
+ reasoningLevels: schema_js_1.llmModels.reasoningLevels,
1687
+ })
1688
+ .from(schema_js_1.llmModels)
1689
+ .where((0, drizzle_orm_1.eq)(schema_js_1.llmModels.name, modelName))
1690
+ .get();
1691
+ const configuredModelSample = await db
1692
+ .select({ name: schema_js_1.llmModels.name })
1693
+ .from(schema_js_1.llmModels)
1694
+ .limit(1)
1695
+ .all();
1696
+ if (configuredModelSample.length > 0) {
1697
+ if (!modelConfig) {
1698
+ throw new Error(`Model '${modelName}' is not configured.`);
1699
+ }
1700
+ if (requestedReasoningLevel.length > 0) {
1701
+ const supportedReasoningLevels = normalizeReasoningLevels(modelConfig.reasoningLevels);
1702
+ if (supportedReasoningLevels.length > 0 && !supportedReasoningLevels.includes(requestedReasoningLevel)) {
1703
+ throw new Error(`Reasoning level '${requestedReasoningLevel}' is not configured for model '${modelName}'.`);
1704
+ }
1705
+ }
1706
+ }
1707
+ await db.insert(schema_js_1.threads).values({
1708
+ id: threadId,
1709
+ sdkThreadId: null,
1710
+ cliSecret: cliSecret.length > 0 ? cliSecret : null,
1711
+ model: modelName,
1712
+ reasoningLevel: requestedReasoningLevel,
1713
+ additionalModelInstructions: normalizedAdditionalModelInstructions,
1714
+ status: "pending",
1715
+ currentSdkTurnId: null,
1716
+ isCurrentTurnRunning: false,
1717
+ workspace: threadDirectory,
1718
+ runtimeContainer: containerNames.runtime,
1719
+ dindContainer: cfg.use_host_docker_runtime ? null : containerNames.dind,
1720
+ homeDirectory: cfg.agent_home_directory,
1721
+ uid: hostInfo.uid,
1722
+ gid: hostInfo.gid,
1723
+ });
1724
+ logger.debug(`Thread '${threadId}' inserted with status 'pending'.`);
1725
+ }
1726
+ catch (error) {
1727
+ logger.warn(`Failed to initialize thread '${threadId}': ${toErrorMessage(error)}`);
1728
+ await sendRequestError(commandChannel, `Failed to initialize thread '${threadId}': ${toErrorMessage(error)}`, requestId);
1729
+ return;
1730
+ }
1731
+ finally {
1732
+ client.close();
1733
+ }
1734
+ (0, node_fs_1.mkdirSync)(threadDirectory, { recursive: true });
1735
+ (0, workspace_agents_js_1.ensureWorkspaceAgentsMd)(threadDirectory, cfg.agent_home_directory);
1736
+ writeWorkspaceThreadGitSkillsConfig(threadDirectory, threadGitSkillPackages, logger);
1737
+ writeWorkspaceThreadMcpConfig(threadDirectory, threadMcpServers, logger);
1738
+ writeWorkspaceThreadAgentCliConfig(threadDirectory, cliSecret, cfg.agent_api_url, logger);
1739
+ await syncGithubInstallationsForWorkspaces(apiClient, apiCallOptions, [threadDirectory], logger);
1740
+ logger.debug(`Thread '${threadId}' workspace initialized at '${threadDirectory}'.`);
1741
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
1742
+ const mounts = (0, thread_lifecycle_js_1.buildSharedThreadMounts)({
1743
+ threadDirectory,
1744
+ homeVolumeName: containerNames.home,
1745
+ tmpVolumeName: containerNames.tmp,
1746
+ codexAuthMode: authMode,
1747
+ codexAuthPath: cfg.codex.codex_auth_path,
1748
+ codexAuthFilePath: cfg.codex.codex_auth_file_path,
1749
+ configDirectory: cfg.config_directory,
1750
+ containerHomeDirectory: cfg.agent_home_directory,
1751
+ });
1752
+ try {
1753
+ await containerService.createThreadContainers({
1754
+ dindImage: cfg.dind_image,
1755
+ runtimeImage: cfg.runtime_image,
1756
+ names: containerNames,
1757
+ user: {
1758
+ uid: hostInfo.uid,
1759
+ gid: hostInfo.gid,
1760
+ agentUser: cfg.agent_user,
1761
+ agentHomeDirectory: cfg.agent_home_directory,
1762
+ },
1763
+ mounts,
1764
+ useHostDockerRuntime: cfg.use_host_docker_runtime,
1765
+ hostDockerPath: cfg.host_docker_path,
1766
+ imageStatusReporter: (message) => {
1767
+ logger.info(`[thread ${threadId}] ${message}`);
1768
+ },
1769
+ });
1770
+ if (cfg.use_host_docker_runtime) {
1771
+ logger.debug(`Thread '${threadId}' runtime container created (${containerNames.runtime}) in host docker mode.`);
1772
+ }
1773
+ else {
1774
+ logger.debug(`Thread '${threadId}' containers created (${containerNames.runtime}, ${containerNames.dind}).`);
1775
+ }
1776
+ }
1777
+ catch (error) {
1778
+ logger.warn(`Failed to create containers for thread '${threadId}': ${toErrorMessage(error)}`);
1779
+ await sendRequestError(commandChannel, `Failed to create containers for thread '${threadId}': ${toErrorMessage(error)}`, requestId);
1780
+ return;
1781
+ }
1782
+ let readyRequestId;
1783
+ const { db: updateDb, client: updateClient } = await (0, db_js_1.initDb)(cfg.state_db_path);
1784
+ try {
1785
+ const threadState = await (0, thread_turn_state_js_1.loadThreadMessageExecutionState)(cfg.state_db_path, threadId);
1786
+ if (!threadState) {
1787
+ throw new Error(`Thread '${threadId}' disappeared before SDK bootstrap.`);
1788
+ }
1789
+ const threadMcpSetup = buildThreadCodexMcpSetup(readWorkspaceThreadMcpConfig(threadState.workspace, logger));
1790
+ const threadAgentCliConfig = readWorkspaceThreadAgentCliConfig(threadState.workspace, logger);
1791
+ const appServerSession = await getOrCreateThreadAppServerSession(threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
1792
+ const runtimeUser = {
1793
+ uid: threadState.uid,
1794
+ gid: threadState.gid,
1795
+ agentUser: cfg.agent_user,
1796
+ agentHomeDirectory: threadState.homeDirectory,
1797
+ };
1798
+ await (0, thread_runtime_js_1.ensureThreadRuntimeReady)({
1799
+ dindContainer: threadState.dindContainer,
1800
+ runtimeContainer: threadState.runtimeContainer,
1801
+ containerService,
1802
+ gitUserName: cfg.git_user_name,
1803
+ gitUserEmail: cfg.git_user_email,
1804
+ user: runtimeUser,
1805
+ });
1806
+ await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
1807
+ if (threadAgentCliConfig) {
1808
+ await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
1809
+ }
1810
+ if (!appServerSession.started) {
1811
+ await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
1812
+ }
1813
+ await ensureThreadAppServerSessionStarted(appServerSession);
1814
+ const developerInstructions = buildThreadDeveloperInstructions(threadState.additionalModelInstructions);
1815
+ logger.debug(`Starting app-server thread '${threadId}' with developer instructions: ${JSON.stringify(developerInstructions)}.`);
1816
+ const threadStartResponse = await appServerSession.appServer.startThreadWithResponse({
1817
+ model: threadState.model,
1818
+ modelProvider: null,
1819
+ cwd: "/workspace",
1820
+ approvalPolicy: YOLO_APPROVAL_POLICY,
1821
+ sandbox: YOLO_SANDBOX_MODE,
1822
+ config: null,
1823
+ baseInstructions: null,
1824
+ developerInstructions,
1825
+ personality: null,
1826
+ ephemeral: null,
1827
+ experimentalRawEvents: false,
1828
+ persistExtendedHistory: true,
1829
+ }, requestId);
1830
+ if (requestId && threadStartResponse.id !== requestId) {
1831
+ throw new Error(`App-server thread/start response id '${String(threadStartResponse.id)}' did not match runner request id '${requestId}'.`);
1832
+ }
1833
+ if (!threadStartResponse.result.thread?.id) {
1834
+ throw new Error(`App-server thread/start did not return an SDK thread id for thread '${threadId}'.`);
1835
+ }
1836
+ readyRequestId = typeof threadStartResponse.id === "string" && threadStartResponse.id.length > 0
1837
+ ? threadStartResponse.id
1838
+ : undefined;
1839
+ appServerSession.sdkThreadId = threadStartResponse.result.thread.id;
1840
+ appServerSession.rolloutPath = threadStartResponse.result.thread.path;
1841
+ rememberThreadRolloutPath(threadId, threadStartResponse.result.thread.path);
1842
+ await updateDb
1843
+ .update(schema_js_1.threads)
1844
+ .set({
1845
+ status: "ready",
1846
+ sdkThreadId: threadStartResponse.result.thread.id,
1847
+ })
1848
+ .where((0, drizzle_orm_1.eq)(schema_js_1.threads.id, threadId));
1849
+ }
1850
+ catch (error) {
1851
+ logger.warn(`Failed to mark thread '${threadId}' as ready: ${toErrorMessage(error)}`);
1852
+ await containerService.forceRemoveContainer(containerNames.runtime);
1853
+ if (!cfg.use_host_docker_runtime) {
1854
+ await containerService.forceRemoveContainer(containerNames.dind);
1855
+ }
1856
+ await containerService.forceRemoveVolume(containerNames.home);
1857
+ await containerService.forceRemoveVolume(containerNames.tmp);
1858
+ await sendRequestError(commandChannel, `Failed to mark thread '${threadId}' as ready: ${toErrorMessage(error)}`, requestId);
1859
+ return;
1860
+ }
1861
+ finally {
1862
+ updateClient.close();
1863
+ }
1864
+ logger.info(`Thread '${threadId}' created and ready.`);
1865
+ await sendThreadUpdate(commandChannel, threadId, protos_1.ThreadStatus.READY, readyRequestId);
1866
+ }
1867
+ async function deleteThreadWithCleanup(cfg, request) {
1868
+ const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
1869
+ let existingThread;
1870
+ try {
1871
+ existingThread = await db
1872
+ .select({
1873
+ id: schema_js_1.threads.id,
1874
+ runtimeContainer: schema_js_1.threads.runtimeContainer,
1875
+ dindContainer: schema_js_1.threads.dindContainer,
1876
+ workspace: schema_js_1.threads.workspace,
1877
+ })
1878
+ .from(schema_js_1.threads)
1879
+ .where((0, drizzle_orm_1.eq)(schema_js_1.threads.id, request.threadId))
1880
+ .get();
1881
+ }
1882
+ catch (error) {
1883
+ return {
1884
+ kind: "error",
1885
+ message: `Failed to load thread '${request.threadId}': ${toErrorMessage(error)}`,
1886
+ };
1887
+ }
1888
+ finally {
1889
+ client.close();
1890
+ }
1891
+ if (!existingThread) {
1892
+ return { kind: "not_found" };
1893
+ }
1894
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
1895
+ try {
1896
+ const containerNames = (0, thread_lifecycle_js_1.buildThreadContainerNames)(existingThread.id);
1897
+ await stopThreadAppServerSession(request.threadId);
1898
+ threadRolloutPaths.delete(request.threadId);
1899
+ await containerService.forceRemoveContainer(existingThread.runtimeContainer);
1900
+ if (existingThread.dindContainer && existingThread.dindContainer.trim().length > 0) {
1901
+ await containerService.forceRemoveContainer(existingThread.dindContainer);
1902
+ }
1903
+ await containerService.forceRemoveVolume(containerNames.home);
1904
+ await containerService.forceRemoveVolume(containerNames.tmp);
1905
+ removeWorkspaceDirectory(existingThread.workspace);
1906
+ }
1907
+ catch (error) {
1908
+ return {
1909
+ kind: "error",
1910
+ message: `Failed to delete resources for thread '${request.threadId}': ${toErrorMessage(error)}`,
1911
+ };
1912
+ }
1913
+ const { db: deleteDb, client: deleteClient } = await (0, db_js_1.initDb)(cfg.state_db_path);
1914
+ try {
1915
+ await deleteDb
1916
+ .delete(schema_js_1.threads)
1917
+ .where((0, drizzle_orm_1.eq)(schema_js_1.threads.id, request.threadId));
1918
+ }
1919
+ catch (error) {
1920
+ return {
1921
+ kind: "error",
1922
+ message: `Failed to delete thread '${request.threadId}': ${toErrorMessage(error)}`,
1923
+ };
1924
+ }
1925
+ finally {
1926
+ deleteClient.close();
1927
+ }
1928
+ return { kind: "deleted" };
1929
+ }
1930
+ async function handleDeleteThreadRequest(cfg, commandChannel, request, logger) {
1931
+ const deleteResult = await deleteThreadWithCleanup(cfg, request);
1932
+ if (deleteResult.kind === "not_found") {
1933
+ logger.warn(`Delete requested for missing thread '${request.threadId}'. Treating as deleted.`);
1934
+ await sendThreadUpdate(commandChannel, request.threadId, protos_1.ThreadStatus.DELETED);
1935
+ return;
1936
+ }
1937
+ if (deleteResult.kind === "error") {
1938
+ await sendRequestError(commandChannel, deleteResult.message);
1939
+ return;
1940
+ }
1941
+ await sendThreadUpdate(commandChannel, request.threadId, protos_1.ThreadStatus.DELETED);
1942
+ }
1943
+ async function reportNoRunningInterruptAsReady(cfg, commandChannel, request, threadState, logger, logMessage) {
1944
+ try {
1945
+ await updateThreadTurnState(cfg, request.threadId, {
1946
+ isCurrentTurnRunning: false,
1947
+ });
1948
+ }
1949
+ catch (error) {
1950
+ logger.warn(`Failed to persist non-running interrupt state for thread '${request.threadId}': ${toErrorMessage(error)}`);
1951
+ }
1952
+ if (threadState.currentSdkTurnId) {
1953
+ await sendTurnExecutionUpdate(commandChannel, request.threadId, threadState.currentSdkTurnId, protos_1.TurnStatus.COMPLETED);
1954
+ }
1955
+ await sendThreadUpdate(commandChannel, request.threadId, protos_1.ThreadStatus.READY);
1956
+ logger.info(logMessage);
1957
+ }
1958
+ async function handleInterruptTurnRequest(cfg, commandChannel, request, logger) {
1959
+ let threadState;
1960
+ try {
1961
+ threadState = await (0, thread_turn_state_js_1.loadThreadMessageExecutionState)(cfg.state_db_path, request.threadId);
1962
+ }
1963
+ catch (error) {
1964
+ await sendRequestError(commandChannel, `Failed to load thread '${request.threadId}': ${toErrorMessage(error)}`);
1965
+ return;
1966
+ }
1967
+ if (!threadState) {
1968
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' does not exist.`);
1969
+ return;
1970
+ }
1971
+ if (!threadState.isCurrentTurnRunning) {
1972
+ await reportNoRunningInterruptAsReady(cfg, commandChannel, request, threadState, logger, `Interrupt requested for thread '${request.threadId}' with no running turn; reported ready state.`);
1973
+ return;
1974
+ }
1975
+ if (!threadState.currentSdkTurnId) {
1976
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' is running but current SDK turn id is missing.`);
1977
+ return;
1978
+ }
1979
+ if (!threadState.sdkThreadId) {
1980
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' is running but SDK thread id is missing.`);
1981
+ return;
1982
+ }
1983
+ const appServerSession = await getOrCreateThreadAppServerSession(request.threadId, threadState.runtimeContainer, {}, cfg.codex.app_server_client_name, logger);
1984
+ try {
1985
+ await ensureThreadAppServerSessionStarted(appServerSession);
1986
+ }
1987
+ catch (error) {
1988
+ const message = toErrorMessage(error);
1989
+ logger.warn(`Failed to start app-server session for interrupt: ${message}`);
1990
+ await sendRequestError(commandChannel, `Failed to connect to app-server for thread '${request.threadId}': ${message}`);
1991
+ return;
1992
+ }
1993
+ const interruptParams = {
1994
+ threadId: threadState.sdkThreadId,
1995
+ turnId: threadState.currentSdkTurnId,
1996
+ };
1997
+ try {
1998
+ await appServerSession.appServer.interruptTurn(interruptParams);
1999
+ }
2000
+ catch (error) {
2001
+ if (isNoRunningTurnInterruptError(error)) {
2002
+ await reportNoRunningInterruptAsReady(cfg, commandChannel, request, threadState, logger, `Interrupt requested for thread '${request.threadId}' but turn '${threadState.currentSdkTurnId}' was already stopped; reported ready state.`);
2003
+ return;
2004
+ }
2005
+ const message = toErrorMessage(error);
2006
+ logger.warn(`Failed to interrupt turn '${threadState.currentSdkTurnId}': ${message}`);
2007
+ await sendRequestError(commandChannel, `Failed to interrupt turn '${threadState.currentSdkTurnId}' for thread '${request.threadId}': ${message}`);
2008
+ return;
2009
+ }
2010
+ logger.info(`Requested interrupt of turn '${threadState.currentSdkTurnId}' for thread '${request.threadId}'.`);
2011
+ }
2012
+ async function updateThreadTurnState(cfg, threadId, update) {
2013
+ await (0, thread_turn_state_js_1.updateThreadTurnState)(cfg.state_db_path, threadId, update);
2014
+ }
2015
+ async function waitForThreadTurnCompletion(stateDbPath, appServer, commandChannel, threadId, sdkThreadId, sdkTurnId, logger, requestId) {
2016
+ let receivedThreadNameUpdate = false;
2017
+ try {
2018
+ const terminalStatus = await appServer.waitForTurnCompletion(sdkThreadId, sdkTurnId, async (notification) => {
2019
+ const threadNameUpdate = extractThreadNameUpdateFromNotification(notification);
2020
+ if (threadNameUpdate && threadNameUpdate.sdkThreadId === sdkThreadId) {
2021
+ receivedThreadNameUpdate = true;
2022
+ await sendThreadNameUpdate(commandChannel, threadId, threadNameUpdate.threadName);
2023
+ }
2024
+ if (notification.method === "item/started" &&
2025
+ notification.params.threadId === sdkThreadId &&
2026
+ notification.params.turnId === sdkTurnId) {
2027
+ const itemRequestId = notification.params.item.type === "userMessage"
2028
+ ? (await (0, thread_user_message_request_store_js_1.assignPendingUserMessageRequestIdForItem)(stateDbPath, threadId, sdkTurnId, notification.params.item.id) ?? requestId)
2029
+ : requestId;
2030
+ await sendItemExecutionUpdate(commandChannel, threadId, sdkTurnId, notification.params.item.id, protos_1.ItemStatus.RUNNING, notification.params.item, itemRequestId);
2031
+ }
2032
+ if (notification.method === "item/completed" &&
2033
+ notification.params.threadId === sdkThreadId &&
2034
+ notification.params.turnId === sdkTurnId) {
2035
+ const itemRequestId = notification.params.item.type === "userMessage"
2036
+ ? (await (0, thread_user_message_request_store_js_1.consumePendingUserMessageRequestIdForItem)(stateDbPath, threadId, sdkTurnId, notification.params.item.id) ?? requestId)
2037
+ : requestId;
2038
+ await sendItemExecutionUpdate(commandChannel, threadId, sdkTurnId, notification.params.item.id, protos_1.ItemStatus.COMPLETED, notification.params.item, itemRequestId);
2039
+ }
2040
+ }, TURN_COMPLETION_TIMEOUT_MS);
2041
+ if (!receivedThreadNameUpdate) {
2042
+ try {
2043
+ const threadReadResponse = await appServer.readThread({
2044
+ threadId: sdkThreadId,
2045
+ includeTurns: false,
2046
+ });
2047
+ const fallbackThreadName = normalizeNonEmptyString(threadReadResponse.thread.preview);
2048
+ if (fallbackThreadName) {
2049
+ await sendThreadNameUpdate(commandChannel, threadId, fallbackThreadName);
2050
+ }
2051
+ }
2052
+ catch (error) {
2053
+ logger.debug(`Failed to read SDK thread '${sdkThreadId}' for fallback thread title inference: ${toErrorMessage(error)}`);
2054
+ }
2055
+ }
2056
+ return terminalStatus;
2057
+ }
2058
+ finally {
2059
+ await (0, thread_user_message_request_store_js_1.clearPendingUserMessageRequestIdsForTurn)(stateDbPath, threadId, sdkTurnId);
2060
+ }
2061
+ }
2062
+ async function executeCreateUserMessageRequest(cfg, commandChannel, request, requestId, threadState, startedFromIdle, trackTurnCompletion, logger) {
2063
+ const containerService = new thread_lifecycle_js_1.ThreadContainerService();
2064
+ const threadMcpSetup = buildThreadCodexMcpSetup(readWorkspaceThreadMcpConfig(threadState.workspace, logger));
2065
+ const threadAgentCliConfig = readWorkspaceThreadAgentCliConfig(threadState.workspace, logger);
2066
+ const appServerSession = await getOrCreateThreadAppServerSession(request.threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
2067
+ const appServer = appServerSession.appServer;
2068
+ const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
2069
+ let sdkThreadId = threadState.sdkThreadId;
2070
+ let sdkTurnId = threadState.currentSdkTurnId;
2071
+ let turnAccepted = false;
2072
+ let keepRuntimeWarm = false;
2073
+ let shouldTrackTurnCompletion = trackTurnCompletion;
2074
+ let enqueuedRequestTurnId = null;
2075
+ let turnCompletionWaitStarted = false;
2076
+ try {
2077
+ await (0, thread_runtime_js_1.ensureThreadRuntimeReady)({
2078
+ dindContainer: threadState.dindContainer,
2079
+ runtimeContainer: threadState.runtimeContainer,
2080
+ containerService,
2081
+ gitUserName: cfg.git_user_name,
2082
+ gitUserEmail: cfg.git_user_email,
2083
+ user: runtimeUser,
2084
+ });
2085
+ await ensureThreadGitSkillsInRuntime(cfg, threadState, containerService, logger);
2086
+ if (threadAgentCliConfig) {
2087
+ await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
2088
+ }
2089
+ if (!appServerSession.started) {
2090
+ await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
2091
+ }
2092
+ await ensureThreadAppServerSessionStarted(appServerSession);
2093
+ if (sdkThreadId) {
2094
+ if (appServerSession.sdkThreadId !== sdkThreadId) {
2095
+ const resumeParams = {
2096
+ threadId: sdkThreadId,
2097
+ approvalPolicy: YOLO_APPROVAL_POLICY,
2098
+ sandbox: YOLO_SANDBOX_MODE,
2099
+ persistExtendedHistory: true,
2100
+ };
2101
+ const resumeResult = await appServer.resumeThread(resumeParams);
2102
+ appServerSession.sdkThreadId = resumeResult.thread.id;
2103
+ appServerSession.rolloutPath = resumeResult.thread.path;
2104
+ rememberThreadRolloutPath(request.threadId, resumeResult.thread.path);
2105
+ }
2106
+ }
2107
+ else if (appServerSession.sdkThreadId) {
2108
+ sdkThreadId = appServerSession.sdkThreadId;
2109
+ await updateThreadTurnState(cfg, request.threadId, { sdkThreadId });
2110
+ }
2111
+ else {
2112
+ const developerInstructions = buildThreadDeveloperInstructions(threadState.additionalModelInstructions);
2113
+ const threadStartParams = {
2114
+ model: request.model ?? threadState.model,
2115
+ modelProvider: null,
2116
+ cwd: "/workspace",
2117
+ approvalPolicy: YOLO_APPROVAL_POLICY,
2118
+ sandbox: YOLO_SANDBOX_MODE,
2119
+ config: null,
2120
+ baseInstructions: null,
2121
+ developerInstructions,
2122
+ personality: null,
2123
+ ephemeral: null,
2124
+ experimentalRawEvents: false,
2125
+ persistExtendedHistory: true,
2126
+ };
2127
+ logger.debug(`Starting app-server thread '${request.threadId}' with developer instructions: ${JSON.stringify(developerInstructions)}.`);
2128
+ const threadStartResult = await appServer.startThread(threadStartParams);
2129
+ sdkThreadId = threadStartResult.thread.id;
2130
+ appServerSession.sdkThreadId = sdkThreadId;
2131
+ appServerSession.rolloutPath = threadStartResult.thread.path;
2132
+ rememberThreadRolloutPath(request.threadId, threadStartResult.thread.path);
2133
+ await updateThreadTurnState(cfg, request.threadId, { sdkThreadId });
2134
+ }
2135
+ if (!sdkThreadId) {
2136
+ throw new Error(`Failed to resolve SDK thread id for thread '${request.threadId}'.`);
2137
+ }
2138
+ const resolvedSdkThreadId = sdkThreadId;
2139
+ const input = buildUserTextInput(request.text);
2140
+ const startNewTurn = async () => {
2141
+ const turnStartParams = {
2142
+ threadId: resolvedSdkThreadId,
2143
+ input,
2144
+ model: request.model ?? null,
2145
+ effort: normalizeReasoningEffort(request.modelReasoningLevel ?? threadState.reasoningLevel),
2146
+ summary: null,
2147
+ personality: null,
2148
+ cwd: null,
2149
+ approvalPolicy: YOLO_APPROVAL_POLICY,
2150
+ sandboxPolicy: YOLO_SANDBOX_POLICY,
2151
+ outputSchema: null,
2152
+ collaborationMode: null,
2153
+ };
2154
+ const turnStartResult = await appServer.startTurn(turnStartParams);
2155
+ return turnStartResult.turn.id;
2156
+ };
2157
+ if (shouldUseTurnSteer(request.allowSteer, startedFromIdle)) {
2158
+ if (!threadState.currentSdkTurnId) {
2159
+ throw new Error(`Thread '${request.threadId}' is marked running but has no current SDK turn id.`);
2160
+ }
2161
+ const activeSdkTurnId = threadState.currentSdkTurnId;
2162
+ const steerParams = {
2163
+ threadId: resolvedSdkThreadId,
2164
+ input,
2165
+ expectedTurnId: activeSdkTurnId,
2166
+ };
2167
+ try {
2168
+ const turnSteerResult = await appServer.steerTurn(steerParams);
2169
+ if (turnSteerResult.turnId && turnSteerResult.turnId !== activeSdkTurnId) {
2170
+ logger.debug(`turn/steer returned turn '${turnSteerResult.turnId}' for thread '${request.threadId}', preserving active turn '${activeSdkTurnId}' as the canonical turn id.`);
2171
+ }
2172
+ sdkTurnId = activeSdkTurnId;
2173
+ }
2174
+ catch (error) {
2175
+ if (!isNoActiveTurnSteerError(error)) {
2176
+ throw error;
2177
+ }
2178
+ logger.warn(`No active turn to steer for thread '${request.threadId}'. Starting a new turn for queued steer request.`);
2179
+ shouldTrackTurnCompletion = true;
2180
+ sdkTurnId = await startNewTurn();
2181
+ }
2182
+ }
2183
+ else {
2184
+ sdkTurnId = await startNewTurn();
2185
+ }
2186
+ if (!sdkTurnId) {
2187
+ throw new Error(`Failed to create SDK turn for thread '${request.threadId}'.`);
2188
+ }
2189
+ turnAccepted = true;
2190
+ await (0, thread_user_message_request_store_js_1.enqueuePendingUserMessageRequestIdForTurn)(cfg.state_db_path, request.threadId, sdkTurnId, requestId);
2191
+ enqueuedRequestTurnId = requestId ? sdkTurnId : null;
2192
+ await updateThreadTurnState(cfg, request.threadId, {
2193
+ sdkThreadId,
2194
+ currentSdkTurnId: sdkTurnId,
2195
+ isCurrentTurnRunning: true,
2196
+ });
2197
+ await sendTurnExecutionUpdate(commandChannel, request.threadId, sdkTurnId, protos_1.TurnStatus.RUNNING, requestId);
2198
+ if (!shouldTrackTurnCompletion) {
2199
+ keepRuntimeWarm = true;
2200
+ return;
2201
+ }
2202
+ turnCompletionWaitStarted = true;
2203
+ const terminalStatus = await waitForThreadTurnCompletion(cfg.state_db_path, appServer, commandChannel, request.threadId, sdkThreadId, sdkTurnId, logger, requestId);
2204
+ await updateThreadTurnState(cfg, request.threadId, {
2205
+ currentSdkTurnId: sdkTurnId,
2206
+ isCurrentTurnRunning: false,
2207
+ });
2208
+ await sendTurnExecutionUpdate(commandChannel, request.threadId, sdkTurnId, protos_1.TurnStatus.COMPLETED, requestId);
2209
+ if (terminalStatus === "failed") {
2210
+ await sendRequestError(commandChannel, `Turn '${sdkTurnId}' finished with status '${terminalStatus}' for thread '${request.threadId}'.`, requestId);
2211
+ }
2212
+ else if (terminalStatus === "interrupted") {
2213
+ logger.info(`Turn '${sdkTurnId}' for thread '${request.threadId}' was interrupted.`);
2214
+ keepRuntimeWarm = true;
2215
+ }
2216
+ else {
2217
+ // Keep app-server + containers warm for fast follow-up user messages on the same thread.
2218
+ keepRuntimeWarm = true;
2219
+ }
2220
+ }
2221
+ catch (error) {
2222
+ if (enqueuedRequestTurnId && requestId) {
2223
+ await (0, thread_user_message_request_store_js_1.removePendingUserMessageRequestIdForTurn)(cfg.state_db_path, request.threadId, enqueuedRequestTurnId, requestId);
2224
+ }
2225
+ if (turnCompletionWaitStarted && !isTurnCompletionTimeoutError(error)) {
2226
+ await updateThreadTurnState(cfg, request.threadId, {
2227
+ isCurrentTurnRunning: false,
2228
+ }).catch(() => undefined);
2229
+ }
2230
+ else if (startedFromIdle && !turnAccepted) {
2231
+ await updateThreadTurnState(cfg, request.threadId, {
2232
+ isCurrentTurnRunning: false,
2233
+ }).catch(() => undefined);
2234
+ }
2235
+ logger.warn(`Failed to create user message turn for thread '${request.threadId}': ${toErrorMessage(error)}`);
2236
+ await sendRequestError(commandChannel, toErrorMessage(error), requestId);
2237
+ }
2238
+ finally {
2239
+ if (!keepRuntimeWarm) {
2240
+ await stopThreadAppServerSession(request.threadId);
2241
+ await containerService.stopContainer(threadState.runtimeContainer).catch((error) => {
2242
+ logger.warn(`Failed to stop runtime container '${threadState.runtimeContainer}': ${toErrorMessage(error)}`);
2243
+ });
2244
+ if (threadState.dindContainer && threadState.dindContainer.trim().length > 0) {
2245
+ await containerService.stopContainer(threadState.dindContainer).catch((error) => {
2246
+ logger.warn(`Failed to stop DinD container '${threadState.dindContainer}': ${toErrorMessage(error)}`);
2247
+ });
2248
+ }
2249
+ }
2250
+ }
2251
+ }
2252
+ async function handleCreateUserMessageRequest(cfg, commandChannel, request, requestId, logger) {
2253
+ let threadState;
2254
+ try {
2255
+ threadState = await (0, thread_turn_state_js_1.loadThreadMessageExecutionState)(cfg.state_db_path, request.threadId);
2256
+ if (!threadState) {
2257
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' does not exist.`, requestId);
2258
+ return;
2259
+ }
2260
+ if (threadState.isCurrentTurnRunning && !request.allowSteer) {
2261
+ threadState = await reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, logger);
2262
+ }
2263
+ if (!request.allowSteer && threadState.isCurrentTurnRunning) {
2264
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' already has a running turn and allowSteer=false.`, requestId);
2265
+ return;
2266
+ }
2267
+ if (threadState.isCurrentTurnRunning && request.allowSteer && !threadState.currentSdkTurnId) {
2268
+ await sendRequestError(commandChannel, `Thread '${request.threadId}' is in an inconsistent state: running turn id is missing.`, requestId);
2269
+ return;
2270
+ }
2271
+ }
2272
+ catch (error) {
2273
+ await sendRequestError(commandChannel, `Failed to load thread '${request.threadId}': ${toErrorMessage(error)}`, requestId);
2274
+ return;
2275
+ }
2276
+ if (!threadState) {
2277
+ return;
2278
+ }
2279
+ const startedFromIdle = !threadState.isCurrentTurnRunning;
2280
+ if (startedFromIdle) {
2281
+ try {
2282
+ await updateThreadTurnState(cfg, request.threadId, {
2283
+ isCurrentTurnRunning: true,
2284
+ });
2285
+ threadState.isCurrentTurnRunning = true;
2286
+ }
2287
+ catch (error) {
2288
+ await sendRequestError(commandChannel, `Failed to reserve thread '${request.threadId}' for execution: ${toErrorMessage(error)}`, requestId);
2289
+ return;
2290
+ }
2291
+ }
2292
+ const trackTurnCompletion = startedFromIdle;
2293
+ void executeCreateUserMessageRequest(cfg, commandChannel, request, requestId, threadState, startedFromIdle, trackTurnCompletion, logger);
2294
+ }
2295
+ async function runCommandLoop(cfg, commandChannel, commandMessageSink, apiClient, apiCallOptions, logger) {
2296
+ for await (const serverMessage of commandChannel) {
2297
+ const requestId = extractServerMessageRequestId(serverMessage);
2298
+ switch (serverMessage.request.case) {
2299
+ case "createThreadRequest":
2300
+ await handleCreateThreadRequest(cfg, commandMessageSink, serverMessage.request.value, requestId, apiClient, apiCallOptions, logger);
2301
+ break;
2302
+ case "deleteThreadRequest":
2303
+ await handleDeleteThreadRequest(cfg, commandMessageSink, serverMessage.request.value, logger);
2304
+ break;
2305
+ case "createUserMessageRequest":
2306
+ void handleCreateUserMessageRequest(cfg, commandMessageSink, serverMessage.request.value, requestId, logger).catch((error) => {
2307
+ logger.warn(`Unhandled createUserMessageRequest error: ${toErrorMessage(error)}`);
2308
+ });
2309
+ break;
2310
+ case "interruptTurnRequest":
2311
+ await handleInterruptTurnRequest(cfg, commandMessageSink, serverMessage.request.value, logger);
2312
+ break;
2313
+ case "heartbeatRequest":
2314
+ await sendHeartbeatResponse(commandMessageSink, requestId);
2315
+ break;
2316
+ default:
2317
+ break;
2318
+ }
2319
+ }
2320
+ }
2321
+ function buildGrpcAuthCallOptions(secret) {
2322
+ if (!secret || secret.trim().length === 0) {
2323
+ return undefined;
2324
+ }
2325
+ const metadata = new grpc.Metadata();
2326
+ metadata.set("authorization", `Bearer ${secret}`);
2327
+ return { metadata };
2328
+ }
2329
+ function isInternalDaemonChildProcess() {
2330
+ return process.env[daemon_js_1.DAEMON_CHILD_ENV] === "1";
2331
+ }
2332
+ function abortErrorFromSignal(signal) {
2333
+ return signal.reason instanceof Error ? signal.reason : new RootCommandInterruptedError();
2334
+ }
2335
+ function throwIfAborted(signal) {
2336
+ if (signal.aborted) {
2337
+ throw abortErrorFromSignal(signal);
2338
+ }
2339
+ }
2340
+ function raceWithAbort(promise, signal) {
2341
+ if (signal.aborted) {
2342
+ return Promise.reject(abortErrorFromSignal(signal));
2343
+ }
2344
+ return Promise.race([
2345
+ promise,
2346
+ new Promise((_, reject) => {
2347
+ const onAbort = () => {
2348
+ signal.removeEventListener("abort", onAbort);
2349
+ reject(abortErrorFromSignal(signal));
2350
+ };
2351
+ signal.addEventListener("abort", onAbort, { once: true });
2352
+ }),
2353
+ ]);
2354
+ }
2355
+ function delayWithAbort(delayMs, signal) {
2356
+ if (signal.aborted) {
2357
+ return Promise.reject(abortErrorFromSignal(signal));
2358
+ }
2359
+ return new Promise((resolve, reject) => {
2360
+ const timeout = setTimeout(() => {
2361
+ signal.removeEventListener("abort", onAbort);
2362
+ resolve();
2363
+ }, delayMs);
2364
+ const onAbort = () => {
2365
+ clearTimeout(timeout);
2366
+ signal.removeEventListener("abort", onAbort);
2367
+ reject(abortErrorFromSignal(signal));
2368
+ };
2369
+ signal.addEventListener("abort", onAbort, { once: true });
2370
+ });
2371
+ }
2372
+ function installRootInterruptHandlers(logger, onInterrupt) {
2373
+ const controller = new AbortController();
2374
+ const requestInterrupt = (reason) => {
2375
+ if (controller.signal.aborted) {
2376
+ return;
2377
+ }
2378
+ (0, terminal_js_1.restoreInteractiveTerminalState)();
2379
+ process.exitCode = 130;
2380
+ logger.info(`${reason} received. Shutting down root command.`);
2381
+ try {
2382
+ onInterrupt();
2383
+ }
2384
+ catch {
2385
+ // Best-effort shutdown hook.
2386
+ }
2387
+ controller.abort(new RootCommandInterruptedError(reason));
2388
+ };
2389
+ const handleSigint = () => {
2390
+ requestInterrupt("SIGINT");
2391
+ };
2392
+ const handleSigterm = () => {
2393
+ requestInterrupt("SIGTERM");
2394
+ };
2395
+ const handleStdinData = (chunk) => {
2396
+ const input = typeof chunk === "string" ? chunk : chunk.toString("utf8");
2397
+ if ((0, terminal_js_1.containsCtrlCInterruptInput)(input)) {
2398
+ requestInterrupt("Ctrl-C");
2399
+ }
2400
+ };
2401
+ process.on("SIGINT", handleSigint);
2402
+ process.on("SIGTERM", handleSigterm);
2403
+ if (process.stdin.isTTY) {
2404
+ process.stdin.on("data", handleStdinData);
2405
+ process.stdin.resume();
2406
+ }
2407
+ return {
2408
+ signal: controller.signal,
2409
+ dispose: () => {
2410
+ process.off("SIGINT", handleSigint);
2411
+ process.off("SIGTERM", handleSigterm);
2412
+ if (process.stdin.isTTY) {
2413
+ process.stdin.off("data", handleStdinData);
2414
+ }
2415
+ },
2416
+ };
2417
+ }
2418
+ function resolveEffectiveDaemonLogPath(cfg) {
2419
+ const envPath = process.env[daemon_js_1.DAEMON_LOG_PATH_ENV];
2420
+ if (envPath && envPath.trim().length > 0) {
2421
+ return (0, path_js_1.expandHome)(envPath);
2422
+ }
2423
+ return (0, daemon_js_1.resolveDaemonLogPath)(cfg.state_db_path);
2424
+ }
2425
+ async function runDetachedDaemonProcess(options) {
2426
+ const cfg = buildRootConfig(options);
2427
+ const logPath = options.logPath && options.logPath.trim().length > 0
2428
+ ? (0, path_js_1.expandHome)(options.logPath)
2429
+ : (0, daemon_js_1.resolveDaemonLogPath)(cfg.state_db_path);
2430
+ (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(logPath), { recursive: true });
2431
+ const logFd = (0, node_fs_1.openSync)(logPath, "a");
2432
+ try {
2433
+ await new Promise((resolve, reject) => {
2434
+ const child = (0, node_child_process_1.spawn)(process.execPath, process.argv.slice(1), {
2435
+ cwd: process.cwd(),
2436
+ detached: true,
2437
+ env: {
2438
+ ...process.env,
2439
+ [daemon_js_1.DAEMON_CHILD_ENV]: "1",
2440
+ [daemon_js_1.DAEMON_LOG_PATH_ENV]: logPath,
2441
+ },
2442
+ stdio: ["ignore", logFd, logFd, "ipc"],
2443
+ windowsHide: true,
2444
+ });
2445
+ let settled = false;
2446
+ const timeout = setTimeout(() => {
2447
+ if (settled) {
2448
+ return;
2449
+ }
2450
+ settled = true;
2451
+ child.kill();
2452
+ reject(new Error(`Timed out waiting for daemon startup confirmation. See ${logPath}.`));
2453
+ }, DAEMON_STARTUP_TIMEOUT_MS);
2454
+ const finish = (callback) => {
2455
+ if (settled) {
2456
+ return;
2457
+ }
2458
+ settled = true;
2459
+ clearTimeout(timeout);
2460
+ callback();
2461
+ };
2462
+ child.once("error", (error) => {
2463
+ finish(() => reject(error));
2464
+ });
2465
+ child.once("exit", (code, signal) => {
2466
+ finish(() => {
2467
+ reject(new Error(`Daemon exited before startup completed (code=${code ?? "null"}, signal=${signal ?? "null"}). See ${logPath}.`));
2468
+ });
2469
+ });
2470
+ child.on("message", (message) => {
2471
+ if (!message || typeof message !== "object" || !("type" in message)) {
2472
+ return;
2473
+ }
2474
+ const type = message.type;
2475
+ if (type === "daemon-ready") {
2476
+ finish(() => {
2477
+ if (child.connected) {
2478
+ child.disconnect();
2479
+ }
2480
+ child.unref();
2481
+ console.log(`CompanyHelm daemon started (pid ${child.pid}). Logs: ${logPath}`);
2482
+ resolve();
2483
+ });
2484
+ return;
2485
+ }
2486
+ if (type === "daemon-error") {
2487
+ const messageValue = message.message;
2488
+ const daemonErrorMessage = typeof messageValue === "string" ? messageValue : `Daemon startup failed. See ${logPath}.`;
2489
+ finish(() => reject(new Error(daemonErrorMessage)));
2490
+ }
2491
+ });
2492
+ });
2493
+ }
2494
+ finally {
2495
+ (0, node_fs_1.closeSync)(logFd);
2496
+ }
2497
+ }
2498
+ function sendDaemonParentMessage(message) {
2499
+ if (typeof process.send === "function") {
2500
+ process.send(message);
2501
+ }
2502
+ }
2503
+ async function runRootCommand(options, runtimeOptions) {
2504
+ const logger = (0, logger_js_1.createLogger)(options.logLevel ?? "INFO", { daemonMode: options.daemon ?? false });
2505
+ const cfg = buildRootConfig(options);
2506
+ const configuredSdks = await hasConfiguredSdks(cfg);
2507
+ if (!configuredSdks && options.daemon) {
2508
+ throw new Error("No SDKs configured. Daemon mode requires at least one configured SDK.");
2509
+ }
2510
+ if (!configuredSdks) {
2511
+ await (0, startup_js_1.startup)(cfg);
2512
+ (0, terminal_js_1.restoreInteractiveTerminalState)();
2513
+ }
2514
+ await refreshCodexModelsForRegistration(cfg, logger);
2515
+ const registerRequest = await buildRegisterRunnerRequest(cfg);
2516
+ const apiCallOptions = buildGrpcAuthCallOptions(options.secret);
2517
+ if (options.daemon) {
2518
+ await (0, daemon_state_js_1.claimCurrentDaemonState)(cfg.state_db_path, process.pid, resolveEffectiveDaemonLogPath(cfg));
2519
+ runtimeOptions?.onDaemonReady?.();
2520
+ }
2521
+ const commandMessageSink = new buffered_client_message_sender_js_1.BufferedClientMessageSender({
2522
+ maxBufferedEvents: cfg.client_message_buffer_limit,
2523
+ logger,
2524
+ });
2525
+ await reconcileTrackedRunningThreadsOnStartup(cfg, logger);
2526
+ let reconnectAttempt = 0;
2527
+ let activeApiClient = null;
2528
+ let activeCommandChannel = null;
2529
+ const interruptState = installRootInterruptHandlers(logger, () => {
2530
+ activeCommandChannel?.cancel();
2531
+ activeApiClient?.close();
2532
+ });
2533
+ try {
2534
+ while (true) {
2535
+ throwIfAborted(interruptState.signal);
2536
+ const apiClient = new companyhelm_api_client_js_1.CompanyhelmApiClient({ apiUrl: cfg.companyhelm_api_url, logger });
2537
+ activeApiClient = apiClient;
2538
+ let commandChannel = null;
2539
+ let githubInstallationsSyncAbortController = null;
2540
+ let githubInstallationsSyncTask = null;
2541
+ try {
2542
+ reconnectAttempt += 1;
2543
+ commandChannel = await apiClient.connect(registerRequest, apiCallOptions);
2544
+ activeCommandChannel = commandChannel;
2545
+ await raceWithAbort(commandChannel.waitForOpen(COMMAND_CHANNEL_OPEN_TIMEOUT_MS), interruptState.signal);
2546
+ commandMessageSink.bind(commandChannel);
2547
+ const bufferedMessages = commandMessageSink.getBufferedMessageCount();
2548
+ if (bufferedMessages > 0) {
2549
+ logger.info(`Connected to CompanyHelm API at ${cfg.companyhelm_api_url}; flushing ${bufferedMessages} buffered message(s).`);
2550
+ }
2551
+ else {
2552
+ logger.info(`Connected to CompanyHelm API at ${cfg.companyhelm_api_url}`);
2553
+ }
2554
+ reconnectAttempt = 0;
2555
+ githubInstallationsSyncAbortController = new AbortController();
2556
+ githubInstallationsSyncTask = runGithubInstallationsSyncLoop(cfg, apiClient, apiCallOptions, logger, githubInstallationsSyncAbortController.signal).catch((error) => {
2557
+ if (!githubInstallationsSyncAbortController?.signal.aborted) {
2558
+ logger.warn(`GitHub installation sync loop exited unexpectedly: ${toErrorMessage(error)}`);
2559
+ }
2560
+ });
2561
+ await raceWithAbort(runCommandLoop(cfg, commandChannel, commandMessageSink, apiClient, apiCallOptions, logger), interruptState.signal);
2562
+ logger.warn("CompanyHelm API command channel closed. Reconnecting...");
2563
+ }
2564
+ catch (error) {
2565
+ if (error instanceof RootCommandInterruptedError) {
2566
+ return;
2567
+ }
2568
+ const failureMessage = formatApiConnectionFailureMessage(error, cfg.companyhelm_api_url, options.secret);
2569
+ const diagnostics = formatApiConnectionFailureDiagnostics(error);
2570
+ if (diagnostics) {
2571
+ logger.debug(`CompanyHelm API failure diagnostics: ${diagnostics}`);
2572
+ }
2573
+ if (!isRetryableApiConnectionError(error)) {
2574
+ throw new Error(failureMessage);
2575
+ }
2576
+ logger.warn(`CompanyHelm API connection attempt ${reconnectAttempt} failed: ${failureMessage}. ` +
2577
+ "Retrying...");
2578
+ }
2579
+ finally {
2580
+ if (githubInstallationsSyncAbortController) {
2581
+ githubInstallationsSyncAbortController.abort();
2582
+ }
2583
+ void githubInstallationsSyncTask;
2584
+ if (commandChannel) {
2585
+ commandChannel.cancel();
2586
+ commandMessageSink.unbind(commandChannel);
2587
+ }
2588
+ else {
2589
+ commandMessageSink.unbind();
2590
+ }
2591
+ if (activeCommandChannel === commandChannel) {
2592
+ activeCommandChannel = null;
2593
+ }
2594
+ if (activeApiClient === apiClient) {
2595
+ activeApiClient = null;
2596
+ }
2597
+ apiClient.close();
2598
+ }
2599
+ await delayWithAbort(COMMAND_CHANNEL_CONNECT_RETRY_DELAY_MS, interruptState.signal);
2600
+ }
2601
+ }
2602
+ finally {
2603
+ interruptState.dispose();
2604
+ const droppedMessages = commandMessageSink.getDroppedMessageCount();
2605
+ if (droppedMessages > 0) {
2606
+ logger.warn(`Dropped ${droppedMessages} outbound client message(s) while command channel was disconnected.`);
2607
+ }
2608
+ if (options.daemon) {
2609
+ await (0, daemon_state_js_1.clearCurrentDaemonState)(cfg.state_db_path, process.pid).catch((error) => {
2610
+ logger.warn(`Failed to clear daemon state: ${toErrorMessage(error)}`);
2611
+ });
2612
+ }
2613
+ await stopAllThreadAppServerSessions();
2614
+ await stopAllThreadContainers(cfg, logger);
2615
+ }
2616
+ }
2617
+ function buildRootConfig(options) {
2618
+ return config_js_1.config.parse({
2619
+ config_directory: options.configPath,
2620
+ state_db_path: options.stateDbPath,
2621
+ companyhelm_api_url: options.serverUrl,
2622
+ agent_api_url: options.agentApiUrl,
2623
+ use_host_docker_runtime: options.useHostDockerRuntime,
2624
+ host_docker_path: options.hostDockerPath,
2625
+ thread_git_skills_directory: options.threadGitSkillsDirectory,
2626
+ });
2627
+ }