ryco-cli 0.1.6 → 0.1.8

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 (159) hide show
  1. package/dist/{AzureDevOpsSourceControlProvider-CC2_COxP.mjs → AzureDevOpsSourceControlProvider-B5vb4DOU.mjs} +2 -2
  2. package/dist/{AzureDevOpsSourceControlProvider-6holYS_D.cjs → AzureDevOpsSourceControlProvider-CYZmJqSM.cjs} +2 -2
  3. package/dist/{BitbucketSourceControlProvider-CYR_BD1d.mjs → BitbucketSourceControlProvider-CcAb4IgH.mjs} +2 -2
  4. package/dist/{BitbucketSourceControlProvider-a0IsNFyG.cjs → BitbucketSourceControlProvider-D_7i4YQz.cjs} +2 -2
  5. package/dist/{ClaudeDriver-D3nsxwH9.mjs → ClaudeDriver-CoPBpdrp.mjs} +198 -8
  6. package/dist/{ClaudeDriver-C6tQNEWO.cjs → ClaudeDriver-ScqmbPKW.cjs} +197 -7
  7. package/dist/{CodexDriver-ms_YgFD7.cjs → CodexDriver-CtCCc3CC.cjs} +36 -8
  8. package/dist/{CodexDriver-BILrJG8o.mjs → CodexDriver-DH7vqku4.mjs} +37 -9
  9. package/dist/{CodexTextGeneration-BIKHl7E3.mjs → CodexTextGeneration-CJV1jPda.mjs} +3 -3
  10. package/dist/{CodexTextGeneration-CHEgoANm.cjs → CodexTextGeneration-Db2hn8Qz.cjs} +2 -2
  11. package/dist/{CopilotDriver-CmD3Ffyl.mjs → CopilotDriver-CFCF68Te.mjs} +7 -6
  12. package/dist/{CopilotDriver-B0--ZwIK.cjs → CopilotDriver-DLN2KEoc.cjs} +6 -5
  13. package/dist/{CursorDriver-DTVPXZ-R.cjs → CursorDriver-BlvQbOEZ.cjs} +201 -15
  14. package/dist/{CursorDriver-CbeaIbdb.mjs → CursorDriver-Dej81rwD.mjs} +202 -16
  15. package/dist/{ForgejoSourceControlProvider-DX6d9gvz.mjs → ForgejoSourceControlProvider-3cFD7SqY.mjs} +2 -2
  16. package/dist/{ForgejoSourceControlProvider-DwAog76b.cjs → ForgejoSourceControlProvider-DYFUGvBk.cjs} +2 -2
  17. package/dist/{GitHubSourceControlProvider-9lJFzjsu.cjs → GitHubSourceControlProvider-B1Tp0TbY.cjs} +27 -4
  18. package/dist/{GitHubSourceControlProvider-C-LGwho3.mjs → GitHubSourceControlProvider-Kf4rrBTF.mjs} +27 -4
  19. package/dist/{GitLabSourceControlProvider-CvKA74lg.cjs → GitLabSourceControlProvider-D3vvKrB4.cjs} +2 -2
  20. package/dist/{GitLabSourceControlProvider-CkeT2aEx.mjs → GitLabSourceControlProvider-Ygppeftk.mjs} +2 -2
  21. package/dist/{NodeSqliteClient-B5aDHbNc.cjs → NodeSqliteClient-D1O-MR8w.cjs} +4 -1
  22. package/dist/{NodeSqliteClient-A1TEgwj_.mjs → NodeSqliteClient-DdTjpAko.mjs} +4 -1
  23. package/dist/{OpenCodeDriver-ClWtCk0j.mjs → OpenCodeDriver-BGa9Tkot.mjs} +326 -39
  24. package/dist/{OpenCodeDriver-CXUsnl5o.cjs → OpenCodeDriver-mbR9mF54.cjs} +325 -38
  25. package/dist/{SourceControlProviderDiscoveryCatalog-_rg0mc1o.cjs → SourceControlProviderDiscoveryCatalog-CTb100Uk.cjs} +1 -64
  26. package/dist/{SourceControlProviderDiscoveryCatalog-yMDer_Yh.mjs → SourceControlProviderDiscoveryCatalog-Zk11tJoL.mjs} +3 -48
  27. package/dist/bin.cjs +1481 -704
  28. package/dist/bin.mjs +1435 -659
  29. package/dist/client/assets/{AnimatedHeight-DgItDSis.js → AnimatedHeight-CPQ2q38I.js} +1 -1
  30. package/dist/client/assets/AppearanceSettings-B0FYiIxL.js +1 -0
  31. package/dist/client/assets/{BranchToolbar.logic-C_xHEt9C.js → BranchToolbar.logic-C1yjFjxJ.js} +1 -1
  32. package/dist/client/assets/ChatRightPanel-CJDSKMU2.js +188 -0
  33. package/dist/client/assets/ChatThreadRouteView-BaKwKGbC.js +1 -0
  34. package/dist/client/assets/CommandPaletteDialog-BHG_MdZk.js +1 -0
  35. package/dist/client/assets/CompositeRoot-Bs0hfelE.js +1 -0
  36. package/dist/client/assets/ConnectionsSettings-DvZNgO60.js +1 -0
  37. package/dist/client/assets/{DiagnosticsSettings-B340S_mq.js → DiagnosticsSettings-0v_hh1Qh.js} +1 -1
  38. package/dist/client/assets/{DialogHandle-BJY7cHCV.js → DialogHandle-CwEAVRF_.js} +1 -1
  39. package/dist/client/assets/{DialogRoot-Ej1FVk-_.js → DialogRoot-CYuaaDI3.js} +1 -1
  40. package/dist/client/assets/DialogTitle-Cak6zT3u.js +1 -0
  41. package/dist/client/assets/DialogTrigger-BD0KYYT-.js +1 -0
  42. package/dist/client/assets/{DialogViewport-DvjAl76K.js → DialogViewport-9sGf4JUp.js} +1 -1
  43. package/dist/client/assets/{DraftChatThreadRouteView-pOkyeKPS.js → DraftChatThreadRouteView-nvKLHlii.js} +1 -1
  44. package/dist/client/assets/{JetBrainsIcons-DTTF47I0.js → JetBrainsIcons-CflvH7gO.js} +1 -1
  45. package/dist/client/assets/KeybindingsSettings-DhLKhgYn.js +7 -0
  46. package/dist/client/assets/McpServersSettings-COs_dng1.js +6 -0
  47. package/dist/client/assets/{NoActiveThreadState-DVHwrkCp.js → NoActiveThreadState-cxMCW-cQ.js} +1 -1
  48. package/dist/client/assets/OpinionatedPluginsSettings-ag_OgOGj.js +1 -0
  49. package/dist/client/assets/{ProjectFavicon-TDP7HRfn.js → ProjectFavicon-D97Hy4xo.js} +1 -1
  50. package/dist/client/assets/{ProviderUpdateLaunchNotification.logic-6TpFsmiI.js → ProviderUpdateLaunchNotification.logic-RMYvRSEk.js} +1 -1
  51. package/dist/client/assets/ProvidersSettingsPanel-DcdVMRVC.js +1 -0
  52. package/dist/client/assets/{RedactedSensitiveText-BfoVjPsE.js → RedactedSensitiveText-CQY4pFAq.js} +1 -1
  53. package/dist/client/assets/RootAppShell-BRjETnaT.js +12 -0
  54. package/dist/client/assets/SettingsDialog-DRi5jsmq.js +2 -0
  55. package/dist/client/assets/SourceControlSettings-CdUulS4O.js +1 -0
  56. package/dist/client/assets/StatisticsPanel-NLU3ecLt.js +64 -0
  57. package/dist/client/assets/{ThreadStatusIndicators-B0leIJGD.js → ThreadStatusIndicators-CebYbeXP.js} +1 -1
  58. package/dist/client/assets/{ThreadTerminalDrawer-COq2Fk17.js → ThreadTerminalDrawer-DlI0gGer.js} +17 -17
  59. package/dist/client/assets/ThreadWorkspacePanel-CEIVrCZ0.js +166 -0
  60. package/dist/client/assets/{alert-dialog-lMfKpG2t.js → alert-dialog-wh07u3JU.js} +1 -1
  61. package/dist/client/assets/chart-column-Cg34FxwD.js +1 -0
  62. package/dist/client/assets/{chatThreadActions-PPyTj_x0.js → chatThreadActions-UitETN_n.js} +1 -1
  63. package/dist/client/assets/chevron-up-C3DS3evc.js +1 -0
  64. package/dist/client/assets/{clamp-JiD4IZ0Z.js → clamp-DPbdAzEu.js} +1 -1
  65. package/dist/client/assets/{clipboard-copy-CYBLSG2q.js → clipboard-copy-ClVNnHW5.js} +1 -1
  66. package/dist/client/assets/clock-Cr66Pt4W.js +1 -0
  67. package/dist/client/assets/{commandPaletteStore-uVfCD7zE.js → commandPaletteStore-DzUBFRor.js} +1 -1
  68. package/dist/client/assets/composerDraftStore-BfLPHwHL.js +21 -0
  69. package/dist/client/assets/{desktopUpdateAtoms-X6uM5SoD.js → desktopUpdateAtoms-BiqPT2_z.js} +1 -1
  70. package/dist/client/assets/{download-DLBlpp4h.js → download-DJ1juHK4.js} +1 -1
  71. package/dist/client/assets/{draft-input-dLlVMl5P.js → draft-input-B1txZFC7.js} +1 -1
  72. package/dist/client/assets/editorPreferences-Bbmh6Bm1.js +1 -0
  73. package/dist/client/assets/{empty-DlLY-Hhj.js → empty-CUEYr1p8.js} +1 -1
  74. package/dist/client/assets/{group-zDBPF7rR.js → group-BouZ2n9F.js} +1 -1
  75. package/dist/client/assets/{index-Dq8sT2lF.js → index-DiHgOLH_.js} +2 -2
  76. package/dist/client/assets/{input-B9-RWT56.js → input-Do_m9U-4.js} +1 -1
  77. package/dist/client/assets/kbd-DJilTfYF.js +1 -0
  78. package/dist/client/assets/{keybindings-Dx4Nc1vY.js → keybindings-CRkz0yvp.js} +1 -1
  79. package/dist/client/assets/{keyboard-DDTWQkr0.js → keyboard-D3WgbAUQ.js} +1 -1
  80. package/dist/client/assets/{keyedQuery-Ds4OGhX2.js → keyedQuery-PQcOnB2O.js} +0 -0
  81. package/dist/client/assets/{main-DxfpmoKJ.js → main-C-491vdD.js} +2 -2
  82. package/dist/client/assets/main-fW5L_SdM.css +2 -0
  83. package/dist/client/assets/{palette-BQcz-ZYV.js → palette-BRoYo327.js} +1 -1
  84. package/dist/client/assets/{play-QloDGe4R.js → play-BZbHk_vw.js} +1 -1
  85. package/dist/client/assets/plus-D0S72hZS.js +1 -0
  86. package/dist/client/assets/projectAtoms-KYCIIuVn.js +1 -0
  87. package/dist/client/assets/providerDriverMeta-BX4CLldz.js +1 -0
  88. package/dist/client/assets/providerIconUtils-DTRc3qno.js +47 -0
  89. package/dist/client/assets/radio-group-De3hRQoY.js +1 -0
  90. package/dist/client/assets/{refresh-cw-DkBMGllm.js → refresh-cw-D_1NT-T2.js} +1 -1
  91. package/dist/client/assets/{resolveAriaLabelledBy-DDfIbj05.js → resolveAriaLabelledBy-RblUm3b1.js} +1 -1
  92. package/dist/client/assets/rightPanelRouteSearch-DLllyqRr.js +1 -0
  93. package/dist/client/assets/{scale-BActunFu.js → scale-s57Ro96s.js} +1 -1
  94. package/dist/client/assets/{scroll-area-D-YOUG02.js → scroll-area-B0_k8-iT.js} +1 -1
  95. package/dist/client/assets/{separator-TInKeIDo.js → separator-AyXRgdnJ.js} +1 -1
  96. package/dist/client/assets/{server-BtEG3NAw.js → server-Z-m1OqE5.js} +1 -1
  97. package/dist/client/assets/{session-logic-DNf1aXq_.js → session-logic-B87Htlw4.js} +2 -2
  98. package/dist/client/assets/settingsDialogStore-CxUfUnd9.js +1 -0
  99. package/dist/client/assets/{settingsLayout-D22Mm0u3.js → settingsLayout-BtKS3Z8f.js} +1 -1
  100. package/dist/client/assets/sidebar-B0WxOej2.js +1 -0
  101. package/dist/client/assets/sidebarStatusText-C_EsHj20.js +1 -0
  102. package/dist/client/assets/{skeleton-DWaac_qp.js → skeleton-COt2Az5i.js} +1 -1
  103. package/dist/client/assets/{storeSelectors-Cx1IYPA4.js → storeSelectors-BuB4Kzj6.js} +1 -1
  104. package/dist/client/assets/threadSelectionStore-BS6X6HHt.js +1 -0
  105. package/dist/client/assets/toast-CaV8TQi_.js +1 -0
  106. package/dist/client/assets/toggle-DE4ApNIU.js +1 -0
  107. package/dist/client/assets/{toggle-group-CnLjaHSL.js → toggle-group-BuzAkAEk.js} +1 -1
  108. package/dist/client/assets/{tokenModePresentation-BSYdIvBm.js → tokenModePresentation-BCL2aeat.js} +1 -1
  109. package/dist/client/assets/{trash-2-BI5xcbsm.js → trash-2-ZZ1feCT6.js} +1 -1
  110. package/dist/client/assets/{triangle-alert-DJef_Uvh.js → triangle-alert-CL3zqnKL.js} +1 -1
  111. package/dist/client/assets/{undo-2-DeZ2cMGS.js → undo-2-Cu9H3R3q.js} +1 -1
  112. package/dist/client/assets/{useHandleNewThread-XB82O6SA.js → useHandleNewThread-Gke-ZFBr.js} +1 -1
  113. package/dist/client/assets/useLabelableId-DfuxWqrk.js +1 -0
  114. package/dist/client/assets/{useNavigate-B2tgshhW.js → useNavigate-DQEHh6ky.js} +1 -1
  115. package/dist/client/assets/useTheme-HcKmBfpF.js +1 -0
  116. package/dist/client/assets/{vscode-icons-DM9qFoEr.js → vscode-icons-DGDiHkqG.js} +1 -1
  117. package/dist/client/assets/{wrench-je4QOyG-.js → wrench-C_OATMLq.js} +1 -1
  118. package/dist/client/assets/x-BG9zZGeD.js +1 -0
  119. package/dist/client/index.html +1 -1
  120. package/dist/{collectUint8StreamText-TCG6xW_P.cjs → collectUint8StreamText-B-GW3u_2.cjs} +367 -15
  121. package/dist/{collectUint8StreamText-C2z5Y8tF.mjs → collectUint8StreamText-COE2_gie.mjs} +345 -17
  122. package/dist/{makeManagedServerProvider-BXO8QvzC.mjs → makeManagedServerProvider-D1_iEWGi.mjs} +1 -1
  123. package/dist/{makeManagedServerProvider-DPFw9gV_.cjs → makeManagedServerProvider-DTsQ87HL.cjs} +1 -1
  124. package/package.json +4 -2
  125. package/dist/client/assets/AppearanceSettings-BlsMFBvJ.js +0 -1
  126. package/dist/client/assets/ChatRightPanel-CThudBb1.js +0 -188
  127. package/dist/client/assets/ChatThreadRouteView-CV6ETYG5.js +0 -1
  128. package/dist/client/assets/CommandPaletteDialog-C_H7R8S5.js +0 -1
  129. package/dist/client/assets/CompositeRoot-DjnzUIwC.js +0 -1
  130. package/dist/client/assets/ConnectionsSettings-hArR4Oe3.js +0 -1
  131. package/dist/client/assets/DialogTitle-H1yIMmA9.js +0 -1
  132. package/dist/client/assets/DialogTrigger-BHMq_K2G.js +0 -1
  133. package/dist/client/assets/KeybindingsSettings-CW3RRi-7.js +0 -7
  134. package/dist/client/assets/McpServersSettings-dQ3BzOWY.js +0 -6
  135. package/dist/client/assets/OpinionatedPluginsSettings-Cjj23Fs4.js +0 -1
  136. package/dist/client/assets/ProvidersSettingsPanel-D15EeWAq.js +0 -1
  137. package/dist/client/assets/RootAppShell-CWBPSA2w.js +0 -12
  138. package/dist/client/assets/SettingsDialog-oJCE5foT.js +0 -2
  139. package/dist/client/assets/SourceControlSettings-CPnlMxNm.js +0 -1
  140. package/dist/client/assets/ThreadWorkspacePanel-qF9y6QT9.js +0 -166
  141. package/dist/client/assets/composerDraftStore-D12lUSfh.js +0 -21
  142. package/dist/client/assets/editorPreferences-BLeEBSD_.js +0 -1
  143. package/dist/client/assets/folder-open-DMjz0lpx.js +0 -1
  144. package/dist/client/assets/kbd-aeSsjiPI.js +0 -1
  145. package/dist/client/assets/main-C57RhHZ_.css +0 -2
  146. package/dist/client/assets/plus-4Oy56d-3.js +0 -1
  147. package/dist/client/assets/projectAtoms-DUEBb7Zz.js +0 -1
  148. package/dist/client/assets/providerDriverMeta-Dwi6Zqgd.js +0 -1
  149. package/dist/client/assets/providerIconUtils-uhWb4bdg.js +0 -47
  150. package/dist/client/assets/radio-group-DHyQlLeX.js +0 -1
  151. package/dist/client/assets/rightPanelRouteSearch-DfuWVvXN.js +0 -1
  152. package/dist/client/assets/settingsDialogStore-BINi7pdD.js +0 -1
  153. package/dist/client/assets/sidebar-DHwNQmWp.js +0 -1
  154. package/dist/client/assets/threadSelectionStore-C0ig4405.js +0 -1
  155. package/dist/client/assets/toast-DE7a4wkW.js +0 -1
  156. package/dist/client/assets/toggle-BIW4AyPY.js +0 -1
  157. package/dist/client/assets/useLabelableId-DBAgv9qQ.js +0 -1
  158. package/dist/client/assets/useTheme-Dv42Z8uy.js +0 -1
  159. package/dist/client/assets/x-fI9snvD3.js +0 -1
package/dist/bin.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { $ as ProviderStopSessionInput, $n as ApprovalRequestId, $t as ThreadMetaUpdatedPayload$1, A as OpinionatedPluginError, At as OrchestrationThread, B as ServerSettingsError, Bt as ProjectionPendingApprovalDecision, C as McpOauthLoginResult, Cn as SourceControlProviderError, Ct as OrchestrationMessageRole, D as McpServerName, Dt as OrchestrationSession, E as McpServerConfig, En as VcsDriverKind, Et as OrchestrationReplayEventsError, F as CopilotSettings, Fn as MODEL_SLUG_ALIASES_BY_PROVIDER, Ft as ProjectCreatedPayload$1, G as ResolvedKeybindingRule, Gn as EDITORS, Gt as RuntimeMode, H as KeybindingsConfig, Hn as TerminalHistoryError, I as CursorSettings, In as ProviderDriverKind, It as ProjectDeletedPayload$1, J as ProviderInterruptTurnInput, Jn as AuthClientMetadataDeviceType, Jt as ThreadAttachedToWorktreePayload$1, K as THREAD_JUMP_KEYBINDING_COMMANDS, Kn as OpenError, Kt as ThreadActivityAppendedPayload$1, L as DEFAULT_SERVER_SETTINGS, Ln as ProviderInstanceId, Lt as ProjectMetaUpdatedPayload$1, M as ServerProviderUpdateError, Mt as OrchestrationWorktreeShell, N as ClaudeSettings, Nn as DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER, O as McpSettingsError, On as VcsProcessExitError, Ot as OrchestrationSessionStatus, P as CodexSettings, Pn as DEFAULT_MODEL, Pt as ProjectAvatarSetPayload$1, Q as ProviderSessionStartInput, Qn as AuthRpcError, Qt as ThreadMessageSentPayload, R as OpenCodeSettings, Rn as defaultInstanceIdForDriver, Rt as ProjectMetadataDir, S as McpListServersResult, St as OrchestrationMessage, T as McpServer, Tt as OrchestrationReadModel, U as KeybindingsConfigError, Un as TerminalNotRunningError, Ut as ProviderInteractionMode, V as KeybindingRule, Vn as TerminalCwdError, Vt as ProjectionPendingApprovalStatus, W as MODEL_PICKER_JUMP_KEYBINDING_COMMANDS, Wn as TerminalSessionLookupError, Wt as ProviderSessionRuntimeStatus, X as ProviderRespondToUserInputInput, Xn as AuthRevokeClientSessionInput, Xt as ThreadDeletedPayload$1, Y as ProviderRespondToRequestInput, Yn as AuthCreatePairingCredentialInput, Yt as ThreadCreatedPayload$1, Z as ProviderSendTurnInput, Zn as AuthRevokePairingLinkInput, Zt as ThreadInteractionModeSetPayload$1, _ as GitManagerError, _n as WorktreeId, _r as TrimmedNonEmptyString, _t as OrchestrationEventType, an as ThreadTokenModeSetPayload$1, ar as EnvironmentId, at as DEFAULT_PROVIDER_INTERACTION_MODE, b as McpInventoryResourceTemplate, bn as WorkItemProviderError, bt as OrchestrationGetTurnDiffError, c as ProjectSearchEntriesError, cn as WorktreeArchivedPayload$1, cr as MessageId, ct as OrchestrationActorKind, d as AtlassianCapability, dn as WorktreeManualPositionSetPayload$1, dr as ProjectId, dt as OrchestrationCheckpointStatus, en as ThreadProposedPlanUpsertedPayload$1, er as AtlassianConnectionId, et as AgentTokenMode, f as AtlassianConnectionError, fn as WorktreeMetaUpdatedPayload$1, ft as OrchestrationCheckpointSummary, g as GitCommandError, gn as Worktree, gr as ThreadId, gt as OrchestrationEventMetadata, h as AtlassianProduct, hn as StatusBucket, ht as OrchestrationEvent, i as FilesystemBrowseError, in as ThreadStatusBucketOverriddenPayload$1, ir as CommandId, j as ServerProvider, jn as VcsUnsupportedOperationError, jt as OrchestrationThreadActivityTone, k as McpWorkspaceId, kt as OrchestrationShellSnapshot, l as ProjectStageFileReferenceError, ln as WorktreeCreatedPayload$1, lr as NonNegativeInt, lt as OrchestrationAggregateKind, m as AtlassianConnectionStatus, mn as WorktreeSourceControlStateUpdatedPayload$1, mt as OrchestrationDispatchCommandError, n as WS_METHODS, nn as ThreadRuntimeModeSetPayload$1, nr as AuthSessionId, nt as ClientOrchestrationCommand, o as ProjectListEntriesError, on as ThreadTurnDiffCompletedPayload$1, or as EventId, ot as ModelSelection, p as AtlassianConnectionKind, pn as WorktreeRestoredPayload$1, pt as OrchestrationCommandReceiptStatus, q as isToolLifecycleItemType, qn as AuthBootstrapInput, qt as ThreadArchivedPayload$1, r as WsRpcGroup, rn as ThreadSessionSetPayload$1, rr as CheckpointRef, rt as DEFAULT_AGENT_TOKEN_MODE, s as ProjectReadFileError, sn as ThreadUnarchivedPayload$1, sr as IsoDateTime, st as ORCHESTRATION_WS_METHODS, t as collectUint8StreamText, tn as ThreadRevertedPayload$1, tr as AtlassianResourceId, tt as ChatAttachment, u as ProjectWriteFileError, un as WorktreeDeletedPayload$1, ur as PositiveInt, ut as OrchestrationCheckpointFile, v as TextGenerationError, vn as WORK_ITEM_DETAIL_BODY_MAX_BYTES, vr as TurnId, vt as OrchestrationGetFullThreadDiffError, w as McpProviderSupport, wn as SourceControlRepositoryError, wt as OrchestrationProposedPlanId, x as McpInventoryTool, xn as WorkItemProviderKind, xt as OrchestrationGetTurnDiffResult, y as McpInventoryResource, yn as WORK_ITEM_DETAIL_COMMENT_BODY_MAX_BYTES, yt as OrchestrationGetSnapshotError, z as ServerSettings, zn as isProviderDriverKind, zt as ProjectScript } from "./collectUint8StreamText-C2z5Y8tF.mjs";
4
- import { _ as formatSchemaError, a as makeForgejoDiscovery, c as sourceBranch, d as firstNonEmptyLine, f as probeSourceControlProvider, g as decodeJsonResult, h as layer$19, i as makeBitbucketDiscovery, m as VcsProcess, n as githubDiscovery, o as SourceControlProvider, p as refineUnknownRemoteProvider, r as gitlabDiscovery, s as parseSourceControlOwnerRef, t as azureDevOpsDiscovery, u as detailFromCause, v as fromLenientJson } from "./SourceControlProviderDiscoveryCatalog-yMDer_Yh.mjs";
3
+ import { $ as ProviderRespondToUserInputInput, $n as AuthRevokeClientSessionInput, $t as ThreadDeletedPayload$1, A as McpServerName, At as OrchestrationSession, B as DEFAULT_SERVER_SETTINGS, Bn as ProviderInstanceId, Bt as ProjectMetaUpdatedPayload$1, C as McpInventoryResourceTemplate, Cn as WorkItemProviderError, Ct as OrchestrationGetTurnDiffError, D as McpProviderSupport, Dn as SourceControlRepositoryError, Dt as OrchestrationProposedPlanId, E as McpOauthLoginResult, En as SourceControlProviderError, Et as OrchestrationMessageRole, F as ServerProviderUpdateError, Ft as OrchestrationWorktreeShell, G as KeybindingsConfig, Gn as TerminalHistoryError, H as ServerSettings, Hn as isProviderDriverKind, Ht as ProjectScript, I as ClaudeSettings, In as DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER, J as ResolvedKeybindingRule, Jn as EDITORS, Jt as RuntimeMode, K as KeybindingsConfigError, Kn as TerminalNotRunningError, Kt as ProviderInteractionMode, L as CodexSettings, Ln as DEFAULT_MODEL, Lt as ProjectAvatarSetPayload$1, M as McpWorkspaceId, Mt as OrchestrationShellSnapshot, N as OpinionatedPluginError, Nt as OrchestrationThread, O as McpServer, Ot as OrchestrationReadModel, P as ServerProvider, Pn as VcsUnsupportedOperationError, Pt as OrchestrationThreadActivityTone, Q as ProviderRespondToRequestInput, Qn as AuthCreatePairingCredentialInput, Qt as ThreadCreatedPayload$1, R as CopilotSettings, Rn as MODEL_SLUG_ALIASES_BY_PROVIDER, Rt as ProjectCreatedPayload$1, S as McpInventoryResource, Sn as WORK_ITEM_DETAIL_COMMENT_BODY_MAX_BYTES, Sr as TurnId, St as OrchestrationGetSnapshotError, T as McpListServersResult, Tt as OrchestrationMessage, U as ServerSettingsError, Ut as ProjectionPendingApprovalDecision, V as OpenCodeSettings, Vn as defaultInstanceIdForDriver, Vt as ProjectMetadataDir, W as KeybindingRule, Wn as TerminalCwdError, Wt as ProjectionPendingApprovalStatus, X as isToolLifecycleItemType, Xn as AuthBootstrapInput, Xt as ThreadArchivedPayload$1, Y as THREAD_JUMP_KEYBINDING_COMMANDS, Yn as OpenError, Yt as ThreadActivityAppendedPayload$1, Z as ProviderInterruptTurnInput, Zn as AuthClientMetadataDeviceType, Zt as ThreadAttachedToWorktreePayload$1, _ as AtlassianConnectionStatus, _n as WorktreeSourceControlStateUpdatedPayload$1, _t as OrchestrationDispatchCommandError, a as WS_METHODS, an as ThreadRuntimeModeSetPayload$1, ar as AuthSessionId, at as ClientOrchestrationCommand, b as GitManagerError, bn as WorktreeId, br as ThreadId, bt as OrchestrationEventType, cn as ThreadTokenModeSetPayload$1, cr as EnvironmentId, ct as DEFAULT_PROVIDER_INTERACTION_MODE, d as ProjectSearchEntriesError, dn as WorktreeArchivedPayload$1, dr as MessageId, dt as OrchestrationActorKind, en as ThreadInteractionModeSetPayload$1, er as AuthRevokePairingLinkInput, et as ProviderSendTurnInput, f as ProjectStageFileReferenceError, fn as WorktreeCreatedPayload$1, fr as NonNegativeInt, ft as OrchestrationAggregateKind, g as AtlassianConnectionKind, gn as WorktreeRestoredPayload$1, gt as OrchestrationCommandReceiptStatus, h as AtlassianConnectionError, hn as WorktreeMetaUpdatedPayload$1, ht as OrchestrationCheckpointSummary, i as fromLenientJson, in as ThreadRevertedPayload$1, ir as AtlassianResourceId, it as ChatAttachment, j as McpSettingsError, jn as VcsProcessExitError, jt as OrchestrationSessionStatus, k as McpServerConfig, kn as VcsDriverKind, kt as OrchestrationReplayEventsError, l as ProjectListEntriesError, ln as ThreadTurnDiffCompletedPayload$1, lr as EventId, lt as ModelSelection, m as AtlassianCapability, mn as WorktreeManualPositionSetPayload$1, mr as ProjectId, mt as OrchestrationCheckpointStatus, n as decodeJsonResult, nn as ThreadMetaUpdatedPayload$1, nr as ApprovalRequestId, nt as ProviderStopSessionInput, o as WsRpcGroup, on as ThreadSessionSetPayload$1, or as CheckpointRef, ot as DEFAULT_AGENT_TOKEN_MODE, p as ProjectWriteFileError, pn as WorktreeDeletedPayload$1, pr as PositiveInt, pt as OrchestrationCheckpointFile, q as MODEL_PICKER_JUMP_KEYBINDING_COMMANDS, qn as TerminalSessionLookupError, qt as ProviderSessionRuntimeStatus, r as formatSchemaError, rn as ThreadProposedPlanUpsertedPayload$1, rr as AtlassianConnectionId, rt as AgentTokenMode, s as FilesystemBrowseError, sn as ThreadStatusBucketOverriddenPayload$1, sr as CommandId, t as collectUint8StreamText, tn as ThreadMessageSentPayload, tr as AuthRpcError, tt as ProviderSessionStartInput, u as ProjectReadFileError, un as ThreadUnarchivedPayload$1, ur as IsoDateTime, ut as ORCHESTRATION_WS_METHODS, v as AtlassianProduct, vn as StatusBucket, vt as OrchestrationEvent, w as McpInventoryTool, wn as WorkItemProviderKind, wt as OrchestrationGetTurnDiffResult, x as TextGenerationError, xn as WORK_ITEM_DETAIL_BODY_MAX_BYTES, xr as TrimmedNonEmptyString, xt as OrchestrationGetFullThreadDiffError, y as GitCommandError, yn as Worktree, yt as OrchestrationEventMetadata, z as CursorSettings, zn as ProviderDriverKind, zt as ProjectDeletedPayload$1 } from "./collectUint8StreamText-COE2_gie.mjs";
5
4
  import { A as terminalRestartsTotal, B as mergeGitStatusParts, C as runtimeQueueDequeuesTotal, D as startupCommandGateQueueDepth, E as startupCommandGateEnqueuesTotal, F as WORKTREE_BRANCH_PREFIX, G as compactTraceAttributes, H as resolveAutoFeatureBranchName, I as buildTemporaryWorktreeBranchName, J as RuntimeMode$1, K as outcomeFromExit, L as dedupeRemoteBranchesWithLocalMatches, M as withMetrics, N as LocalDiagnosticsMetrics, O as startupCommandGateQueueHighWater, P as LocalDiagnosticsMetricsLive, Q as resolveStaticDir, R as detectSourceControlProviderFromGitRemoteUrl, S as runtimeQueueDepth, T as runtimeQueueHighWater, U as sanitizeBranchFragment, V as normalizeGitRemoteUrl, W as sanitizeFeatureBranchName, X as deriveServerPaths, Y as ServerConfig, Z as ensureServerDirectories, _ as providerTurnDuration, a as orchestrationCommandAckDuration, b as rpcRequestDuration, c as orchestrationEventsProcessedTotal, d as providerRuntimeEventsTotal, f as providerSessionsTotal, g as providerStartupQueueWaitDuration, h as providerStartupQueueHighWater, i as metricAttributes, j as terminalSessionsTotal, k as startupCommandGateQueueWaitDuration, l as providerEventLogRecordsDroppedTotal, m as providerStartupQueueDepth, n as gitCommandsTotal, o as orchestrationCommandDuration, p as providerStartupAdmissionTotal, q as DEFAULT_PORT, r as increment, s as orchestrationCommandsTotal, t as gitCommandDuration, u as providerMetricAttributes, v as providerTurnMetricAttributes, w as runtimeQueueEnqueuesTotal, x as rpcRequestsTotal, y as providerTurnsTotal, z as isTemporaryWorktreeBranch } from "./Metrics-6Xj17Se3.mjs";
5
+ import { a as makeForgejoDiscovery, c as sourceBranch, d as firstNonEmptyLine, f as probeSourceControlProvider, h as layer$19, i as makeBitbucketDiscovery, m as VcsProcess, n as githubDiscovery, o as SourceControlProvider, p as refineUnknownRemoteProvider, r as gitlabDiscovery, s as parseSourceControlOwnerRef, t as azureDevOpsDiscovery, u as detailFromCause } from "./SourceControlProviderDiscoveryCatalog-Zk11tJoL.mjs";
6
6
  import { n as detectSourceControlProviderFromRemoteUrl, r as getChangeRequestTerminologyForKind } from "./sourceControl-iWG-m4wE.mjs";
7
7
  import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
8
8
  import * as NodeServices from "@effect/platform-node/NodeServices";
@@ -31,10 +31,10 @@ import sharp from "sharp";
31
31
  import { RpcSerialization, RpcServer } from "effect/unstable/rpc";
32
32
  import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
33
33
  import * as Semaphore$1 from "effect/Semaphore";
34
+ import * as Random$1 from "effect/Random";
34
35
  import "effect/PlatformError";
35
36
  import * as SqlClient from "effect/unstable/sql/SqlClient";
36
37
  import * as Migrator from "effect/unstable/sql/Migrator";
37
- import * as Random$1 from "effect/Random";
38
38
  import fsPromises, { open, realpath } from "node:fs/promises";
39
39
  import * as Context$1 from "effect/Context";
40
40
  import "effect/Scope";
@@ -46,8 +46,8 @@ import * as Ref$1 from "effect/Ref";
46
46
  import * as Stream$1 from "effect/Stream";
47
47
  import * as SchemaIssue$1 from "effect/SchemaIssue";
48
48
  import * as Sink$1 from "effect/Sink";
49
- import { clamp } from "effect/Number";
50
49
  import * as SqlSchema from "effect/unstable/sql/SqlSchema";
50
+ import { clamp } from "effect/Number";
51
51
  import { pathToFileURL } from "node:url";
52
52
  import { parsePatchFiles } from "@pierre/diffs";
53
53
  import { monitorEventLoopDelay } from "node:perf_hooks";
@@ -1629,6 +1629,388 @@ const makeProviderMaintenanceCommandCoordinator = Effect.fn("makeProviderMainten
1629
1629
  return { withCommandLock };
1630
1630
  });
1631
1631
  //#endregion
1632
+ //#region src/atomicWrite.ts
1633
+ const writeFileStringAtomically = (input) => Effect.scoped(Effect.gen(function* () {
1634
+ const fs = yield* FileSystem.FileSystem;
1635
+ const path = yield* Path.Path;
1636
+ const tempFileId = yield* Random$1.nextUUIDv4;
1637
+ const targetDirectory = path.dirname(input.filePath);
1638
+ yield* fs.makeDirectory(targetDirectory, { recursive: true });
1639
+ const tempDirectory = yield* fs.makeTempDirectoryScoped({
1640
+ directory: targetDirectory,
1641
+ prefix: `${path.basename(input.filePath)}.`
1642
+ });
1643
+ const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`);
1644
+ yield* fs.writeFileString(tempPath, input.contents);
1645
+ yield* fs.rename(tempPath, input.filePath);
1646
+ }));
1647
+ //#endregion
1648
+ //#region src/auth/Services/ServerSecretStore.ts
1649
+ var SecretStoreError = class extends Data.TaggedError("SecretStoreError") {};
1650
+ var ServerSecretStore = class extends Context.Service()("ryco/auth/Services/ServerSecretStore") {};
1651
+ //#endregion
1652
+ //#region src/auth/Layers/ServerSecretStore.ts
1653
+ const makeServerSecretStore = Effect.gen(function* () {
1654
+ const fileSystem = yield* FileSystem.FileSystem;
1655
+ const path = yield* Path.Path;
1656
+ const serverConfig = yield* ServerConfig;
1657
+ yield* fileSystem.makeDirectory(serverConfig.secretsDir, { recursive: true });
1658
+ yield* fileSystem.chmod(serverConfig.secretsDir, 448).pipe(Effect.mapError((cause) => new SecretStoreError({
1659
+ message: `Failed to secure secrets directory ${serverConfig.secretsDir}.`,
1660
+ cause
1661
+ })));
1662
+ const resolveSecretPath = (name) => path.join(serverConfig.secretsDir, `${name}.bin`);
1663
+ const ensureSecretDirectory = (secretPath) => {
1664
+ const directory = path.dirname(secretPath);
1665
+ return fileSystem.makeDirectory(directory, { recursive: true }).pipe(Effect.flatMap(() => fileSystem.chmod(directory, 448)));
1666
+ };
1667
+ const isPlatformError = (u) => Predicate.isTagged(u, "PlatformError");
1668
+ const get = (name) => fileSystem.readFile(resolveSecretPath(name)).pipe(Effect.map((bytes) => Uint8Array.from(bytes)), Effect.catch((cause) => cause.reason._tag === "NotFound" ? Effect.succeed(null) : Effect.fail(new SecretStoreError({
1669
+ message: `Failed to read secret ${name}.`,
1670
+ cause
1671
+ }))));
1672
+ const set = (name, value) => {
1673
+ const secretPath = resolveSecretPath(name);
1674
+ const tempPath = `${secretPath}.${Crypto.randomUUID()}.tmp`;
1675
+ return Effect.gen(function* () {
1676
+ yield* ensureSecretDirectory(secretPath);
1677
+ yield* fileSystem.writeFile(tempPath, value);
1678
+ yield* fileSystem.chmod(tempPath, 384);
1679
+ yield* fileSystem.rename(tempPath, secretPath);
1680
+ yield* fileSystem.chmod(secretPath, 384);
1681
+ }).pipe(Effect.catch((cause) => fileSystem.remove(tempPath).pipe(Effect.ignore, Effect.flatMap(() => Effect.fail(new SecretStoreError({
1682
+ message: `Failed to persist secret ${name}.`,
1683
+ cause
1684
+ }))))));
1685
+ };
1686
+ const create = (name, value) => {
1687
+ const secretPath = resolveSecretPath(name);
1688
+ return Effect.scoped(Effect.gen(function* () {
1689
+ yield* ensureSecretDirectory(secretPath);
1690
+ const file = yield* fileSystem.open(secretPath, {
1691
+ flag: "wx",
1692
+ mode: 384
1693
+ });
1694
+ yield* file.writeAll(value);
1695
+ yield* file.sync;
1696
+ yield* fileSystem.chmod(secretPath, 384);
1697
+ })).pipe(Effect.mapError((cause) => new SecretStoreError({
1698
+ message: `Failed to persist secret ${name}.`,
1699
+ cause
1700
+ })));
1701
+ };
1702
+ const getOrCreateRandom = (name, bytes) => get(name).pipe(Effect.flatMap((existing) => {
1703
+ if (existing) return Effect.succeed(existing);
1704
+ const generated = Crypto.randomBytes(bytes);
1705
+ return create(name, generated).pipe(Effect.as(Uint8Array.from(generated)), Effect.catchTag("SecretStoreError", (error) => isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists" ? get(name).pipe(Effect.flatMap((created) => created !== null ? Effect.succeed(created) : Effect.fail(new SecretStoreError({ message: `Failed to read secret ${name} after concurrent creation.` })))) : Effect.fail(error)));
1706
+ }));
1707
+ const remove = (name) => fileSystem.remove(resolveSecretPath(name)).pipe(Effect.catch((cause) => cause.reason._tag === "NotFound" ? Effect.void : Effect.fail(new SecretStoreError({
1708
+ message: `Failed to remove secret ${name}.`,
1709
+ cause
1710
+ }))));
1711
+ return {
1712
+ get,
1713
+ set,
1714
+ getOrCreateRandom,
1715
+ remove
1716
+ };
1717
+ });
1718
+ const ServerSecretStoreLive = Layer.effect(ServerSecretStore, makeServerSecretStore);
1719
+ //#endregion
1720
+ //#region src/serverSettings.ts
1721
+ /**
1722
+ * ServerSettings - Server-authoritative settings service.
1723
+ *
1724
+ * Owns persistence, validation, and change notification of settings that affect
1725
+ * server-side behavior (binary paths, streaming mode, env mode, custom models,
1726
+ * text generation model selection).
1727
+ *
1728
+ * Follows the same pattern as `keybindings.ts`: JSON file + Cache + PubSub +
1729
+ * Semaphore + FileSystem.watch for concurrency and external edit detection.
1730
+ *
1731
+ * @module ServerSettings
1732
+ */
1733
+ const textEncoder$2 = new TextEncoder();
1734
+ const textDecoder$2 = new TextDecoder();
1735
+ function providerEnvironmentSecretName(input) {
1736
+ return `provider-env-${Buffer.from(input.instanceId, "utf8").toString("base64url")}-${Buffer.from(input.name, "utf8").toString("base64url")}`;
1737
+ }
1738
+ function redactProviderEnvironmentVariable(variable) {
1739
+ if (!variable.sensitive) {
1740
+ const { valueRedacted: _omit, ...rest } = variable;
1741
+ return rest;
1742
+ }
1743
+ return {
1744
+ ...variable,
1745
+ value: "",
1746
+ ...variable.value.length > 0 || variable.valueRedacted ? { valueRedacted: true } : {}
1747
+ };
1748
+ }
1749
+ function redactServerSettingsForClient(settings) {
1750
+ const providerInstances = Object.fromEntries(Object.entries(settings.providerInstances).map(([instanceId, instance]) => [instanceId, instance.environment ? {
1751
+ ...instance,
1752
+ environment: instance.environment.map(redactProviderEnvironmentVariable)
1753
+ } : instance]));
1754
+ return {
1755
+ ...settings,
1756
+ providerInstances
1757
+ };
1758
+ }
1759
+ var ServerSettingsService = class ServerSettingsService extends Context.Service()("ryco/serverSettings/ServerSettingsService") {
1760
+ static layerTest = (overrides = {}) => Layer.effect(ServerSettingsService, Effect.gen(function* () {
1761
+ const currentSettingsRef = yield* Ref.make(deepMerge(DEFAULT_SERVER_SETTINGS, overrides));
1762
+ return {
1763
+ start: Effect.void,
1764
+ ready: Effect.void,
1765
+ getSettings: Ref.get(currentSettingsRef),
1766
+ updateSettings: (patch) => Ref.get(currentSettingsRef).pipe(Effect.flatMap((currentSettings) => Schema.decodeEffect(ServerSettings)(applyServerSettingsPatch(currentSettings, patch)).pipe(Effect.mapError((cause) => new ServerSettingsError({
1767
+ settingsPath: "<memory>",
1768
+ detail: `failed to normalize server settings: ${SchemaIssue.makeFormatterDefault()(cause.issue)}`,
1769
+ cause
1770
+ })))), Effect.tap((nextSettings) => Ref.set(currentSettingsRef, nextSettings))),
1771
+ streamChanges: Stream.empty
1772
+ };
1773
+ }));
1774
+ };
1775
+ const ServerSettingsJson = fromLenientJson(ServerSettings);
1776
+ const getLegacyProviderSettings = (settings, provider) => settings.providers[provider];
1777
+ /**
1778
+ * Ensure the `textGenerationModelSelection` points to an enabled provider.
1779
+ * If the selected provider is disabled, fall back to the first enabled
1780
+ * provider with its default model. This is applied at read-time so the
1781
+ * persisted preference is preserved for when a provider is re-enabled.
1782
+ */
1783
+ function resolveTextGenerationProvider(settings) {
1784
+ const selection = settings.textGenerationModelSelection;
1785
+ const instanceConfig = settings.providerInstances[selection.instanceId];
1786
+ if (instanceConfig !== void 0) return instanceConfig.enabled ?? true ? settings : fallbackTextGenerationProvider(settings);
1787
+ if (isProviderDriverKind(selection.instanceId) && getLegacyProviderSettings(settings, selection.instanceId)?.enabled) return settings;
1788
+ return fallbackTextGenerationProvider(settings);
1789
+ }
1790
+ function fallbackTextGenerationProvider(settings) {
1791
+ const fallbackEntry = Object.entries(settings.providers).find(([, provider]) => provider.enabled);
1792
+ const fallback = fallbackEntry ? ProviderDriverKind.make(fallbackEntry[0]) : void 0;
1793
+ if (!fallback) return settings;
1794
+ return {
1795
+ ...settings,
1796
+ textGenerationModelSelection: {
1797
+ instanceId: ProviderInstanceId.make(fallback),
1798
+ model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[fallback] ?? "gpt-5.4-mini"
1799
+ }
1800
+ };
1801
+ }
1802
+ const ATOMIC_SETTINGS_KEYS = new Set(["textGenerationModelSelection"]);
1803
+ function stripDefaultServerSettings(current, defaults) {
1804
+ if (Array.isArray(current) || Array.isArray(defaults)) return Equal.equals(current, defaults) ? void 0 : current;
1805
+ if (current !== null && defaults !== null && typeof current === "object" && typeof defaults === "object") {
1806
+ const currentRecord = current;
1807
+ const defaultsRecord = defaults;
1808
+ const next = {};
1809
+ for (const key of Object.keys(currentRecord)) if (ATOMIC_SETTINGS_KEYS.has(key)) {
1810
+ if (!Equal.equals(currentRecord[key], defaultsRecord[key])) next[key] = currentRecord[key];
1811
+ } else {
1812
+ const stripped = stripDefaultServerSettings(currentRecord[key], defaultsRecord[key]);
1813
+ if (stripped !== void 0) next[key] = stripped;
1814
+ }
1815
+ return Object.keys(next).length > 0 ? next : void 0;
1816
+ }
1817
+ return Object.is(current, defaults) ? void 0 : current;
1818
+ }
1819
+ const makeServerSettings = Effect.gen(function* () {
1820
+ const { settingsPath } = yield* ServerConfig;
1821
+ const fs = yield* FileSystem.FileSystem;
1822
+ const pathService = yield* Path.Path;
1823
+ const secretStore = yield* ServerSecretStore;
1824
+ const writeSemaphore = yield* Semaphore$1.make(1);
1825
+ const cacheKey = "settings";
1826
+ const changesPubSub = yield* PubSub.unbounded();
1827
+ const startedRef = yield* Ref.make(false);
1828
+ const startedDeferred = yield* Deferred.make();
1829
+ const watcherScope = yield* Scope.make("sequential");
1830
+ yield* Effect.addFinalizer(() => Scope.close(watcherScope, Exit.void));
1831
+ const emitChange = (settings) => PubSub.publish(changesPubSub, settings).pipe(Effect.asVoid);
1832
+ const readConfigExists = fs.exists(settingsPath).pipe(Effect.mapError((cause) => new ServerSettingsError({
1833
+ settingsPath,
1834
+ detail: "failed to check settings file existence",
1835
+ cause
1836
+ })));
1837
+ const readRawConfig = fs.readFileString(settingsPath).pipe(Effect.mapError((cause) => new ServerSettingsError({
1838
+ settingsPath,
1839
+ detail: "failed to read settings file",
1840
+ cause
1841
+ })));
1842
+ const loadSettingsFromDisk = Effect.gen(function* () {
1843
+ if (!(yield* readConfigExists)) return DEFAULT_SERVER_SETTINGS;
1844
+ const raw = yield* readRawConfig;
1845
+ const decoded = Schema.decodeUnknownExit(ServerSettingsJson)(raw);
1846
+ if (decoded._tag === "Failure") {
1847
+ yield* Effect.logWarning("failed to parse settings.json, using defaults", {
1848
+ path: settingsPath,
1849
+ issues: Cause.pretty(decoded.cause)
1850
+ });
1851
+ return DEFAULT_SERVER_SETTINGS;
1852
+ }
1853
+ return decoded.value;
1854
+ });
1855
+ const settingsCache = yield* Cache.make({
1856
+ capacity: 1,
1857
+ lookup: () => loadSettingsFromDisk
1858
+ });
1859
+ const getSettingsFromCache = Cache.get(settingsCache, cacheKey);
1860
+ const toSettingsError = (detail, cause) => new ServerSettingsError({
1861
+ settingsPath,
1862
+ detail,
1863
+ cause
1864
+ });
1865
+ const materializeProviderEnvironmentSecrets = (settings) => Effect.gen(function* () {
1866
+ const providerInstances = { ...settings.providerInstances };
1867
+ for (const [instanceId, instance] of Object.entries(settings.providerInstances)) {
1868
+ if (!instance.environment) continue;
1869
+ const environment = [];
1870
+ for (const variable of instance.environment) {
1871
+ if (!variable.sensitive || !variable.valueRedacted) {
1872
+ environment.push(variable);
1873
+ continue;
1874
+ }
1875
+ const secret = yield* secretStore.get(providerEnvironmentSecretName({
1876
+ instanceId,
1877
+ name: variable.name
1878
+ })).pipe(Effect.mapError((cause) => toSettingsError(`failed to read sensitive environment variable ${variable.name}`, cause)));
1879
+ environment.push({
1880
+ ...variable,
1881
+ value: secret ? textDecoder$2.decode(secret) : ""
1882
+ });
1883
+ }
1884
+ providerInstances[instanceId] = {
1885
+ ...instance,
1886
+ environment
1887
+ };
1888
+ }
1889
+ return {
1890
+ ...settings,
1891
+ providerInstances
1892
+ };
1893
+ });
1894
+ const persistProviderEnvironmentSecrets = (current, next) => Effect.gen(function* () {
1895
+ const providerInstances = { ...next.providerInstances };
1896
+ const nextSecretKeys = /* @__PURE__ */ new Set();
1897
+ for (const [instanceId, instance] of Object.entries(next.providerInstances)) {
1898
+ if (!instance.environment) continue;
1899
+ const environment = [];
1900
+ for (const variable of instance.environment) {
1901
+ const secretName = providerEnvironmentSecretName({
1902
+ instanceId,
1903
+ name: variable.name
1904
+ });
1905
+ if (!variable.sensitive) {
1906
+ yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
1907
+ environment.push(redactProviderEnvironmentVariable(variable));
1908
+ continue;
1909
+ }
1910
+ nextSecretKeys.add(secretName);
1911
+ if (!variable.valueRedacted) {
1912
+ if (variable.value.length > 0) {
1913
+ yield* secretStore.set(secretName, textEncoder$2.encode(variable.value)).pipe(Effect.mapError((cause) => toSettingsError(`failed to persist environment secret ${variable.name}`, cause)));
1914
+ environment.push({
1915
+ ...variable,
1916
+ value: "",
1917
+ valueRedacted: true
1918
+ });
1919
+ } else {
1920
+ yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
1921
+ const { valueRedacted: _omit, ...rest } = variable;
1922
+ environment.push(rest);
1923
+ }
1924
+ continue;
1925
+ }
1926
+ environment.push(redactProviderEnvironmentVariable(variable));
1927
+ }
1928
+ providerInstances[instanceId] = {
1929
+ ...instance,
1930
+ environment
1931
+ };
1932
+ }
1933
+ for (const [instanceId, instance] of Object.entries(current.providerInstances)) for (const variable of instance.environment ?? []) {
1934
+ if (!variable.sensitive) continue;
1935
+ const secretName = providerEnvironmentSecretName({
1936
+ instanceId,
1937
+ name: variable.name
1938
+ });
1939
+ if (nextSecretKeys.has(secretName)) continue;
1940
+ yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove stale environment secret ${variable.name}`, cause)));
1941
+ }
1942
+ return {
1943
+ ...next,
1944
+ providerInstances
1945
+ };
1946
+ });
1947
+ const writeSettingsAtomically = (settings) => {
1948
+ const sparseSettings = stripDefaultServerSettings(settings, DEFAULT_SERVER_SETTINGS) ?? {};
1949
+ return writeFileStringAtomically({
1950
+ filePath: settingsPath,
1951
+ contents: `${JSON.stringify(sparseSettings, null, 2)}\n`
1952
+ }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, pathService), Effect.mapError((cause) => new ServerSettingsError({
1953
+ settingsPath,
1954
+ detail: "failed to write settings file",
1955
+ cause
1956
+ })));
1957
+ };
1958
+ const revalidateAndEmit = writeSemaphore.withPermits(1)(Effect.gen(function* () {
1959
+ yield* Cache.invalidate(settingsCache, cacheKey);
1960
+ yield* emitChange(yield* getSettingsFromCache);
1961
+ }));
1962
+ const startWatcher = Effect.gen(function* () {
1963
+ const settingsDir = pathService.dirname(settingsPath);
1964
+ const settingsFile = pathService.basename(settingsPath);
1965
+ const settingsPathResolved = pathService.resolve(settingsPath);
1966
+ yield* fs.makeDirectory(settingsDir, { recursive: true }).pipe(Effect.mapError((cause) => new ServerSettingsError({
1967
+ settingsPath,
1968
+ detail: "failed to prepare settings directory",
1969
+ cause
1970
+ })));
1971
+ const revalidateAndEmitSafely = revalidateAndEmit.pipe(Effect.ignoreCause({ log: true }));
1972
+ const debouncedSettingsEvents = fs.watch(settingsDir).pipe(Stream.filter((event) => {
1973
+ return event.path === settingsFile || event.path === settingsPath || pathService.resolve(settingsDir, event.path) === settingsPathResolved;
1974
+ }), Stream.debounce(Duration.millis(100)));
1975
+ yield* Stream.runForEach(debouncedSettingsEvents, () => revalidateAndEmitSafely).pipe(Effect.ignoreCause({ log: true }), Effect.forkIn(watcherScope), Effect.asVoid);
1976
+ });
1977
+ return {
1978
+ start: Effect.gen(function* () {
1979
+ if (!(yield* Ref.modify(startedRef, (started) => [!started, true]))) return yield* Deferred.await(startedDeferred);
1980
+ const startup = Effect.gen(function* () {
1981
+ yield* startWatcher;
1982
+ yield* Cache.invalidate(settingsCache, cacheKey);
1983
+ yield* getSettingsFromCache;
1984
+ });
1985
+ const startupExit = yield* Effect.exit(startup);
1986
+ if (startupExit._tag === "Failure") {
1987
+ yield* Deferred.failCause(startedDeferred, startupExit.cause).pipe(Effect.orDie);
1988
+ return yield* Effect.failCause(startupExit.cause);
1989
+ }
1990
+ yield* Deferred.succeed(startedDeferred, void 0).pipe(Effect.orDie);
1991
+ }),
1992
+ ready: Deferred.await(startedDeferred),
1993
+ getSettings: getSettingsFromCache.pipe(Effect.flatMap(materializeProviderEnvironmentSecrets), Effect.map(resolveTextGenerationProvider)),
1994
+ updateSettings: (patch) => writeSemaphore.withPermits(1)(Effect.gen(function* () {
1995
+ const current = yield* getSettingsFromCache;
1996
+ const nextPersisted = yield* persistProviderEnvironmentSecrets(current, applyServerSettingsPatch(current, patch));
1997
+ const next = yield* Schema.decodeEffect(ServerSettings)(nextPersisted).pipe(Effect.mapError((cause) => new ServerSettingsError({
1998
+ settingsPath: "<memory>",
1999
+ detail: `failed to normalize server settings: ${SchemaIssue.makeFormatterDefault()(cause.issue)}`,
2000
+ cause
2001
+ })));
2002
+ yield* writeSettingsAtomically(next);
2003
+ yield* Cache.set(settingsCache, cacheKey, next);
2004
+ yield* emitChange(next);
2005
+ return resolveTextGenerationProvider(yield* materializeProviderEnvironmentSecrets(next));
2006
+ })),
2007
+ get streamChanges() {
2008
+ return Stream.fromPubSub(changesPubSub).pipe(Stream.mapEffect((settings) => materializeProviderEnvironmentSecrets(settings).pipe(Effect.catch((error) => Effect.logWarning("failed to materialize provider environment secrets", { detail: error.detail }).pipe(Effect.as(settings))))), Stream.map(resolveTextGenerationProvider));
2009
+ }
2010
+ };
2011
+ });
2012
+ const ServerSettingsLive = Layer.effect(ServerSettingsService, makeServerSettings).pipe(Layer.provide(ServerSecretStoreLive));
2013
+ //#endregion
1632
2014
  //#region src/provider/cliVersion.ts
1633
2015
  const CLI_VERSION_NUMBER_SEGMENT = /^\d+$/;
1634
2016
  function normalizeCliVersion(version) {
@@ -1929,7 +2311,8 @@ const resolveLatestProviderVersion = Effect.fn("resolveLatestProviderVersion")(f
1929
2311
  });
1930
2312
  const enrichProviderSnapshotWithVersionAdvisory = Effect.fn("enrichProviderSnapshotWithVersionAdvisory")(function* (snapshot, maintenanceCapabilities) {
1931
2313
  const capabilities = maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(snapshot.driver);
1932
- if (!snapshot.enabled || !snapshot.installed || !snapshot.version) return {
2314
+ const { enableProviderUpdateChecks } = yield* (yield* ServerSettingsService).getSettings.pipe(Effect.orElseSucceed(() => DEFAULT_SERVER_SETTINGS));
2315
+ if (!enableProviderUpdateChecks || !snapshot.enabled || !snapshot.installed || !snapshot.version) return {
1933
2316
  ...snapshot,
1934
2317
  versionAdvisory: createProviderVersionAdvisory({
1935
2318
  driver: snapshot.driver,
@@ -2024,6 +2407,7 @@ const make$21 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
2024
2407
  const providerRegistry = yield* ProviderRegistry;
2025
2408
  const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
2026
2409
  const httpClient = yield* HttpClient.HttpClient;
2410
+ const serverSettings = yield* ServerSettingsService;
2027
2411
  const runMaintenanceCommand = (command, args) => runProviderMaintenanceCommandWithSpawner({
2028
2412
  spawner,
2029
2413
  command,
@@ -2042,7 +2426,7 @@ const make$21 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
2042
2426
  providers,
2043
2427
  verifiedProviders: []
2044
2428
  });
2045
- return Effect.forEach(refreshedProviders, (refreshedProvider) => enrichProviderSnapshotWithVersionAdvisory(refreshedProvider, maintenanceCapabilities).pipe(Effect.provideService(HttpClient.HttpClient, httpClient)), { concurrency: "unbounded" }).pipe(Effect.map((verifiedProviders) => ({
2429
+ return Effect.forEach(refreshedProviders, (refreshedProvider) => enrichProviderSnapshotWithVersionAdvisory(refreshedProvider, maintenanceCapabilities).pipe(Effect.provideService(HttpClient.HttpClient, httpClient), Effect.provideService(ServerSettingsService, serverSettings)), { concurrency: "unbounded" }).pipe(Effect.map((verifiedProviders) => ({
2046
2430
  providers,
2047
2431
  verifiedProviders
2048
2432
  })), Effect.catchCause((cause) => Effect.logWarning("Provider post-update version verification failed", {
@@ -2132,78 +2516,6 @@ const make$21 = Effect.fn("ProviderMaintenanceRunner.make")(function* () {
2132
2516
  });
2133
2517
  const layer$18 = Layer.effect(ProviderMaintenanceRunner, make$21());
2134
2518
  //#endregion
2135
- //#region src/auth/Services/ServerSecretStore.ts
2136
- var SecretStoreError = class extends Data.TaggedError("SecretStoreError") {};
2137
- var ServerSecretStore = class extends Context.Service()("ryco/auth/Services/ServerSecretStore") {};
2138
- //#endregion
2139
- //#region src/auth/Layers/ServerSecretStore.ts
2140
- const makeServerSecretStore = Effect.gen(function* () {
2141
- const fileSystem = yield* FileSystem.FileSystem;
2142
- const path = yield* Path.Path;
2143
- const serverConfig = yield* ServerConfig;
2144
- yield* fileSystem.makeDirectory(serverConfig.secretsDir, { recursive: true });
2145
- yield* fileSystem.chmod(serverConfig.secretsDir, 448).pipe(Effect.mapError((cause) => new SecretStoreError({
2146
- message: `Failed to secure secrets directory ${serverConfig.secretsDir}.`,
2147
- cause
2148
- })));
2149
- const resolveSecretPath = (name) => path.join(serverConfig.secretsDir, `${name}.bin`);
2150
- const ensureSecretDirectory = (secretPath) => {
2151
- const directory = path.dirname(secretPath);
2152
- return fileSystem.makeDirectory(directory, { recursive: true }).pipe(Effect.flatMap(() => fileSystem.chmod(directory, 448)));
2153
- };
2154
- const isPlatformError = (u) => Predicate.isTagged(u, "PlatformError");
2155
- const get = (name) => fileSystem.readFile(resolveSecretPath(name)).pipe(Effect.map((bytes) => Uint8Array.from(bytes)), Effect.catch((cause) => cause.reason._tag === "NotFound" ? Effect.succeed(null) : Effect.fail(new SecretStoreError({
2156
- message: `Failed to read secret ${name}.`,
2157
- cause
2158
- }))));
2159
- const set = (name, value) => {
2160
- const secretPath = resolveSecretPath(name);
2161
- const tempPath = `${secretPath}.${Crypto.randomUUID()}.tmp`;
2162
- return Effect.gen(function* () {
2163
- yield* ensureSecretDirectory(secretPath);
2164
- yield* fileSystem.writeFile(tempPath, value);
2165
- yield* fileSystem.chmod(tempPath, 384);
2166
- yield* fileSystem.rename(tempPath, secretPath);
2167
- yield* fileSystem.chmod(secretPath, 384);
2168
- }).pipe(Effect.catch((cause) => fileSystem.remove(tempPath).pipe(Effect.ignore, Effect.flatMap(() => Effect.fail(new SecretStoreError({
2169
- message: `Failed to persist secret ${name}.`,
2170
- cause
2171
- }))))));
2172
- };
2173
- const create = (name, value) => {
2174
- const secretPath = resolveSecretPath(name);
2175
- return Effect.scoped(Effect.gen(function* () {
2176
- yield* ensureSecretDirectory(secretPath);
2177
- const file = yield* fileSystem.open(secretPath, {
2178
- flag: "wx",
2179
- mode: 384
2180
- });
2181
- yield* file.writeAll(value);
2182
- yield* file.sync;
2183
- yield* fileSystem.chmod(secretPath, 384);
2184
- })).pipe(Effect.mapError((cause) => new SecretStoreError({
2185
- message: `Failed to persist secret ${name}.`,
2186
- cause
2187
- })));
2188
- };
2189
- const getOrCreateRandom = (name, bytes) => get(name).pipe(Effect.flatMap((existing) => {
2190
- if (existing) return Effect.succeed(existing);
2191
- const generated = Crypto.randomBytes(bytes);
2192
- return create(name, generated).pipe(Effect.as(Uint8Array.from(generated)), Effect.catchTag("SecretStoreError", (error) => isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists" ? get(name).pipe(Effect.flatMap((created) => created !== null ? Effect.succeed(created) : Effect.fail(new SecretStoreError({ message: `Failed to read secret ${name} after concurrent creation.` })))) : Effect.fail(error)));
2193
- }));
2194
- const remove = (name) => fileSystem.remove(resolveSecretPath(name)).pipe(Effect.catch((cause) => cause.reason._tag === "NotFound" ? Effect.void : Effect.fail(new SecretStoreError({
2195
- message: `Failed to remove secret ${name}.`,
2196
- cause
2197
- }))));
2198
- return {
2199
- get,
2200
- set,
2201
- getOrCreateRandom,
2202
- remove
2203
- };
2204
- });
2205
- const ServerSecretStoreLive = Layer.effect(ServerSecretStore, makeServerSecretStore);
2206
- //#endregion
2207
2519
  //#region src/persistence/Errors.ts
2208
2520
  var PersistenceSqlError = class extends Schema.TaggedErrorClass()("PersistenceSqlError", {
2209
2521
  operation: Schema.String,
@@ -3897,6 +4209,35 @@ var _040_ProjectionThreadsProjectUpdatedAtIndex_default = Effect$1.gen(function*
3897
4209
  `;
3898
4210
  });
3899
4211
  //#endregion
4212
+ //#region src/persistence/Migrations/041_ProjectionThreadsSubagentNesting.ts
4213
+ var _041_ProjectionThreadsSubagentNesting_default = Effect$1.gen(function* () {
4214
+ const sql = yield* SqlClient.SqlClient;
4215
+ const columns = yield* sql`
4216
+ PRAGMA table_info(projection_threads)
4217
+ `;
4218
+ const columnNames = new Set(columns.map((column) => column.name));
4219
+ if (!columnNames.has("thread_kind")) yield* sql`
4220
+ ALTER TABLE projection_threads
4221
+ ADD COLUMN thread_kind TEXT NOT NULL DEFAULT 'normal'
4222
+ `;
4223
+ if (!columnNames.has("visibility")) yield* sql`
4224
+ ALTER TABLE projection_threads
4225
+ ADD COLUMN visibility TEXT NOT NULL DEFAULT 'normal'
4226
+ `;
4227
+ if (!columnNames.has("parent_thread_id")) yield* sql`
4228
+ ALTER TABLE projection_threads
4229
+ ADD COLUMN parent_thread_id TEXT
4230
+ `;
4231
+ if (!columnNames.has("parent_subagent_id")) yield* sql`
4232
+ ALTER TABLE projection_threads
4233
+ ADD COLUMN parent_subagent_id TEXT
4234
+ `;
4235
+ yield* sql`
4236
+ CREATE INDEX IF NOT EXISTS idx_projection_threads_parent_thread
4237
+ ON projection_threads(parent_thread_id, created_at, thread_id)
4238
+ `;
4239
+ });
4240
+ //#endregion
3900
4241
  //#region src/persistence/Migrations.ts
3901
4242
  /**
3902
4243
  * MigrationsLive - Migration runner with inline loader
@@ -4117,6 +4458,11 @@ const migrationEntries = [
4117
4458
  40,
4118
4459
  "ProjectionThreadsProjectUpdatedAtIndex",
4119
4460
  _040_ProjectionThreadsProjectUpdatedAtIndex_default
4461
+ ],
4462
+ [
4463
+ 41,
4464
+ "ProjectionThreadsSubagentNesting",
4465
+ _041_ProjectionThreadsSubagentNesting_default
4120
4466
  ]
4121
4467
  ];
4122
4468
  const makeMigrationLoader = (throughId) => Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -4186,6 +4532,45 @@ const repairProjectionTokenModeColumns = Effect$1.fn("repairProjectionTokenModeC
4186
4532
  yield* Effect$1.log("Repaired projection_thread_sessions.token_mode column");
4187
4533
  }
4188
4534
  });
4535
+ const repairProjectionThreadSubagentNestingColumns = Effect$1.fn("repairProjectionThreadSubagentNestingColumns")(function* () {
4536
+ const sql = yield* SqlClient.SqlClient;
4537
+ if ((yield* sql`
4538
+ SELECT name FROM sqlite_master
4539
+ WHERE type = 'table' AND name = 'projection_threads'
4540
+ `).length === 0) return;
4541
+ const columns = yield* sql`
4542
+ PRAGMA table_info(projection_threads)
4543
+ `;
4544
+ const columnNames = new Set(columns.map((column) => column.name));
4545
+ if (!columnNames.has("thread_kind")) {
4546
+ yield* sql`
4547
+ ALTER TABLE projection_threads
4548
+ ADD COLUMN thread_kind TEXT NOT NULL DEFAULT 'normal'
4549
+ `;
4550
+ yield* Effect$1.log("Repaired projection_threads.thread_kind column");
4551
+ }
4552
+ if (!columnNames.has("visibility")) {
4553
+ yield* sql`
4554
+ ALTER TABLE projection_threads
4555
+ ADD COLUMN visibility TEXT NOT NULL DEFAULT 'normal'
4556
+ `;
4557
+ yield* Effect$1.log("Repaired projection_threads.visibility column");
4558
+ }
4559
+ if (!columnNames.has("parent_thread_id")) {
4560
+ yield* sql`
4561
+ ALTER TABLE projection_threads
4562
+ ADD COLUMN parent_thread_id TEXT
4563
+ `;
4564
+ yield* Effect$1.log("Repaired projection_threads.parent_thread_id column");
4565
+ }
4566
+ if (!columnNames.has("parent_subagent_id")) {
4567
+ yield* sql`
4568
+ ALTER TABLE projection_threads
4569
+ ADD COLUMN parent_subagent_id TEXT
4570
+ `;
4571
+ yield* Effect$1.log("Repaired projection_threads.parent_subagent_id column");
4572
+ }
4573
+ });
4189
4574
  /**
4190
4575
  * Run all pending migrations.
4191
4576
  *
@@ -4202,6 +4587,7 @@ const runMigrations = Effect$1.fn("runMigrations")(function* ({ toMigrationInclu
4202
4587
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 31) yield* repairProjectionWorktreeTitleColumn();
4203
4588
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 34) yield* repairProjectionProjectAvatarColumns();
4204
4589
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 35) yield* repairProjectionTokenModeColumns();
4590
+ if (toMigrationInclusive === void 0 || toMigrationInclusive >= 41) yield* repairProjectionThreadSubagentNestingColumns();
4205
4591
  yield* Effect$1.log("Migrations ran successfully").pipe(Effect$1.annotateLogs({ migrations: executedMigrations.map(([id, name]) => `${id}_${name}`) }));
4206
4592
  return executedMigrations;
4207
4593
  });
@@ -4210,7 +4596,7 @@ Layer$1.effectDiscard(runMigrations());
4210
4596
  //#region src/persistence/Layers/Sqlite.ts
4211
4597
  const defaultSqliteClientLoaders = {
4212
4598
  bun: () => import("@effect/sql-sqlite-bun/SqliteClient"),
4213
- node: () => import("./NodeSqliteClient-A1TEgwj_.mjs")
4599
+ node: () => import("./NodeSqliteClient-DdTjpAko.mjs")
4214
4600
  };
4215
4601
  const makeRuntimeSqliteLayer = Effect.fn("makeRuntimeSqliteLayer")(function* (config) {
4216
4602
  const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
@@ -4995,7 +5381,7 @@ Schema.Struct({
4995
5381
  var ProjectAtlassianLinkRepository = class extends Context.Service()("ryco/persistence/Services/ProjectAtlassianLinks/ProjectAtlassianLinkRepository") {};
4996
5382
  //#endregion
4997
5383
  //#region src/atlassian/AtlassianConnectionService.ts
4998
- const textEncoder$2 = new TextEncoder();
5384
+ const textEncoder$1 = new TextEncoder();
4999
5385
  var AtlassianConnectionService = class extends Context.Service()("ryco/atlassian/AtlassianConnectionService") {};
5000
5386
  function atlassianError(operation, detail, cause) {
5001
5387
  return new AtlassianConnectionError({
@@ -5123,7 +5509,7 @@ const make$19 = Effect.fn("makeAtlassianConnectionService")(function* () {
5123
5509
  createdAt,
5124
5510
  updatedAt: createdAt
5125
5511
  };
5126
- return secretStore.set(manualBitbucketTokenSecretName(connectionId), textEncoder$2.encode(input.token)).pipe(Effect.flatMap(() => connections.upsert(record)), cleanupSecretOnFailure(manualBitbucketTokenSecretName(connectionId)), Effect.as(toSummary(record)), Effect.mapError(mapError$1("atlassian.saveManualBitbucketToken", "Failed to save the Bitbucket token.")));
5512
+ return secretStore.set(manualBitbucketTokenSecretName(connectionId), textEncoder$1.encode(input.token)).pipe(Effect.flatMap(() => connections.upsert(record)), cleanupSecretOnFailure(manualBitbucketTokenSecretName(connectionId)), Effect.as(toSummary(record)), Effect.mapError(mapError$1("atlassian.saveManualBitbucketToken", "Failed to save the Bitbucket token.")));
5127
5513
  },
5128
5514
  saveManualJiraToken: (input) => {
5129
5515
  const createdAt = nowIso$2();
@@ -5159,7 +5545,7 @@ const make$19 = Effect.fn("makeAtlassianConnectionService")(function* () {
5159
5545
  avatarUrl: null,
5160
5546
  updatedAt: createdAt
5161
5547
  };
5162
- return secretStore.set(manualJiraTokenSecretName(connectionId), textEncoder$2.encode(input.token)).pipe(Effect.flatMap(() => connections.upsert(record)), Effect.flatMap(() => resources.upsertForConnection({
5548
+ return secretStore.set(manualJiraTokenSecretName(connectionId), textEncoder$1.encode(input.token)).pipe(Effect.flatMap(() => connections.upsert(record)), Effect.flatMap(() => resources.upsertForConnection({
5163
5549
  connectionId,
5164
5550
  resources: [resource]
5165
5551
  })), cleanupSecretOnFailure(manualJiraTokenSecretName(connectionId)), Effect.as(toSummary(record)), Effect.mapError(mapError$1("atlassian.saveManualJiraToken", "Failed to save the Jira API token.")));
@@ -5275,6 +5661,11 @@ const NON_REPOSITORY_STATUS_DETAILS = Object.freeze({
5275
5661
  behindCount: 0,
5276
5662
  aheadOfDefaultCount: 0
5277
5663
  });
5664
+ const emptyDiffTotals = () => ({
5665
+ files: [],
5666
+ insertions: 0,
5667
+ deletions: 0
5668
+ });
5278
5669
  var StatusRemoteRefreshCacheKey = class extends Data.Class {};
5279
5670
  function parseBranchAb(value) {
5280
5671
  const match = value.match(/^\+(\d+)\s+-(\d+)$/);
@@ -5921,6 +6312,31 @@ const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* (option
5921
6312
  const parsed = Number.parseInt(result.stdout.trim(), 10);
5922
6313
  return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
5923
6314
  });
6315
+ const readCommittedAgainstBase = Effect.fn("readCommittedAgainstBase")(function* (cwd, refName) {
6316
+ const baseRef = yield* resolveBaseBranchForNoUpstream(cwd, refName).pipe(Effect.catch(() => Effect.succeed(null)));
6317
+ if (!baseRef) return emptyDiffTotals();
6318
+ const result = yield* executeGit("GitVcsDriver.readCommittedAgainstBase.diff", cwd, [
6319
+ "diff",
6320
+ "--numstat",
6321
+ `${baseRef}...HEAD`
6322
+ ], { allowNonZeroExit: true });
6323
+ if (result.exitCode !== 0) return emptyDiffTotals();
6324
+ let insertions = 0;
6325
+ let deletions = 0;
6326
+ return {
6327
+ files: parseNumstatEntries(result.stdout).map((entry) => {
6328
+ insertions += entry.insertions;
6329
+ deletions += entry.deletions;
6330
+ return {
6331
+ path: entry.path,
6332
+ insertions: entry.insertions,
6333
+ deletions: entry.deletions
6334
+ };
6335
+ }).toSorted((a, b) => a.path.localeCompare(b.path)),
6336
+ insertions,
6337
+ deletions
6338
+ };
6339
+ });
5924
6340
  const readBranchRecency = Effect.fn("readBranchRecency")(function* (cwd) {
5925
6341
  const branchRecency = yield* executeGit("GitVcsDriver.readBranchRecency", cwd, [
5926
6342
  "for-each-ref",
@@ -6039,6 +6455,7 @@ const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* (option
6039
6455
  });
6040
6456
  }
6041
6457
  files.sort((a, b) => a.path.localeCompare(b.path));
6458
+ const committed = refName && !isDefaultBranch ? yield* readCommittedAgainstBase(cwd, refName).pipe(Effect.catch(() => Effect.succeed(emptyDiffTotals()))) : void 0;
6042
6459
  return {
6043
6460
  isRepo: true,
6044
6461
  hasOriginRemote: hasPrimaryRemote,
@@ -6051,6 +6468,7 @@ const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* (option
6051
6468
  insertions,
6052
6469
  deletions
6053
6470
  },
6471
+ ...committed ? { committed } : {},
6054
6472
  hasUpstream: upstreamRef !== null,
6055
6473
  aheadCount,
6056
6474
  behindCount,
@@ -6071,6 +6489,7 @@ const makeGitVcsDriverCore = Effect.fn("makeGitVcsDriverCore")(function* (option
6071
6489
  refName: details.branch,
6072
6490
  hasWorkingTreeChanges: details.hasWorkingTreeChanges,
6073
6491
  workingTree: details.workingTree,
6492
+ ...details.committed ? { committed: details.committed } : {},
6074
6493
  hasUpstream: details.hasUpstream,
6075
6494
  aheadCount: details.aheadCount,
6076
6495
  behindCount: details.behindCount,
@@ -6963,7 +7382,7 @@ const layer$13 = Layer.effect(VcsDriverRegistry, make$16()).pipe(Layer.provide(l
6963
7382
  //#endregion
6964
7383
  //#region src/sourceControl/BitbucketApi.ts
6965
7384
  const DEFAULT_API_BASE_URL = "https://api.bitbucket.org/2.0";
6966
- const textDecoder$2 = new TextDecoder();
7385
+ const textDecoder$1 = new TextDecoder();
6967
7386
  const BitbucketApiEnvConfig = Config.all({
6968
7387
  baseUrl: Config.string("RYCO_BITBUCKET_API_BASE_URL").pipe(Config.withDefault(DEFAULT_API_BASE_URL)),
6969
7388
  accessToken: Config.string("RYCO_BITBUCKET_ACCESS_TOKEN").pipe(Config.option),
@@ -7308,7 +7727,7 @@ const make$15 = Effect.fn("makeBitbucketApi")(function* () {
7308
7727
  cause
7309
7728
  })));
7310
7729
  if (!tokenBytes || tokenBytes.length === 0) continue;
7311
- const token = textDecoder$2.decode(tokenBytes).trim();
7730
+ const token = textDecoder$1.decode(tokenBytes).trim();
7312
7731
  if (!token) continue;
7313
7732
  return {
7314
7733
  source: "stored",
@@ -9268,6 +9687,7 @@ function workflowRunsEndpoint(input) {
9268
9687
  const params = new URLSearchParams();
9269
9688
  params.set("per_page", String(workflowRunLimit(input.limit)));
9270
9689
  if (input.headSha?.trim()) params.set("head_sha", input.headSha.trim());
9690
+ else if (input.branch?.trim()) params.set("branch", input.branch.trim());
9271
9691
  return `repos/{owner}/{repo}/actions/runs?${params.toString()}`;
9272
9692
  }
9273
9693
  function isWorkflowRerunStateError(error) {
@@ -10532,26 +10952,26 @@ const make$11 = Effect.fn("makeSourceControlProviderRegistry")(function* () {
10532
10952
  const azureDevOpsCli = yield* AzureDevOpsCli;
10533
10953
  const fileSystem = yield* FileSystem.FileSystem;
10534
10954
  const github = yield* makeLazyProvider("github", Effect.tryPromise({
10535
- try: () => import("./GitHubSourceControlProvider-C-LGwho3.mjs"),
10955
+ try: () => import("./GitHubSourceControlProvider-Kf4rrBTF.mjs"),
10536
10956
  catch: (cause) => providerLoadError("github", cause)
10537
10957
  }).pipe(Effect.flatMap((module) => module.make()), Effect.provideService(GitHubCli, githubCli), Effect.provideService(FileSystem.FileSystem, fileSystem)));
10538
10958
  const gitlab = yield* makeLazyProvider("gitlab", Effect.tryPromise({
10539
- try: () => import("./GitLabSourceControlProvider-CkeT2aEx.mjs"),
10959
+ try: () => import("./GitLabSourceControlProvider-Ygppeftk.mjs"),
10540
10960
  catch: (cause) => providerLoadError("gitlab", cause)
10541
10961
  }).pipe(Effect.flatMap((module) => module.make()), Effect.provideService(GitLabCli, gitlabCli)));
10542
10962
  const bitbucket = yield* makeLazyProvider("bitbucket", Effect.tryPromise({
10543
- try: () => import("./BitbucketSourceControlProvider-CYR_BD1d.mjs"),
10963
+ try: () => import("./BitbucketSourceControlProvider-CcAb4IgH.mjs"),
10544
10964
  catch: (cause) => providerLoadError("bitbucket", cause)
10545
10965
  }).pipe(Effect.flatMap((module) => module.make()), Effect.provideService(BitbucketApi, bitbucketApi)), {
10546
10966
  searchRepositories: true,
10547
10967
  cloneAuthentication: true
10548
10968
  });
10549
10969
  const forgejo = yield* makeLazyProvider("forgejo", Effect.tryPromise({
10550
- try: () => import("./ForgejoSourceControlProvider-DX6d9gvz.mjs"),
10970
+ try: () => import("./ForgejoSourceControlProvider-3cFD7SqY.mjs"),
10551
10971
  catch: (cause) => providerLoadError("forgejo", cause)
10552
10972
  }).pipe(Effect.flatMap((module) => module.make()), Effect.provideService(ForgejoApi, forgejoApi)));
10553
10973
  const azureDevOps = yield* makeLazyProvider("azure-devops", Effect.tryPromise({
10554
- try: () => import("./AzureDevOpsSourceControlProvider-CC2_COxP.mjs"),
10974
+ try: () => import("./AzureDevOpsSourceControlProvider-B5vb4DOU.mjs"),
10555
10975
  catch: (cause) => providerLoadError("azure-devops", cause)
10556
10976
  }).pipe(Effect.flatMap((module) => module.make()), Effect.provideService(AzureDevOpsCli, azureDevOpsCli)));
10557
10977
  const bitbucketDiscovery = makeBitbucketDiscovery(bitbucketApi);
@@ -10674,22 +11094,6 @@ var CheckpointDiffQuery = class extends Context.Service()("ryco/checkpointing/Se
10674
11094
  //#region src/diagnostics/Services/Diagnostics.ts
10675
11095
  var Diagnostics = class extends Context.Service()("ryco/diagnostics/Services/Diagnostics") {};
10676
11096
  //#endregion
10677
- //#region src/atomicWrite.ts
10678
- const writeFileStringAtomically = (input) => Effect.scoped(Effect.gen(function* () {
10679
- const fs = yield* FileSystem.FileSystem;
10680
- const path = yield* Path.Path;
10681
- const tempFileId = yield* Random$1.nextUUIDv4;
10682
- const targetDirectory = path.dirname(input.filePath);
10683
- yield* fs.makeDirectory(targetDirectory, { recursive: true });
10684
- const tempDirectory = yield* fs.makeTempDirectoryScoped({
10685
- directory: targetDirectory,
10686
- prefix: `${path.basename(input.filePath)}.`
10687
- });
10688
- const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`);
10689
- yield* fs.writeFileString(tempPath, input.contents);
10690
- yield* fs.rename(tempPath, input.filePath);
10691
- }));
10692
- //#endregion
10693
11097
  //#region ../../packages/shared/src/keybindings.ts
10694
11098
  const DEFAULT_KEYBINDINGS = [
10695
11099
  {
@@ -10760,6 +11164,11 @@ const DEFAULT_KEYBINDINGS = [
10760
11164
  key: "mod+o",
10761
11165
  command: "editor.openFavorite"
10762
11166
  },
11167
+ {
11168
+ key: "mod+f",
11169
+ command: "thread.find",
11170
+ when: "!terminalFocus"
11171
+ },
10763
11172
  {
10764
11173
  key: "mod+shift+[",
10765
11174
  command: "thread.previous"
@@ -11581,14 +11990,7 @@ const ClientRequest__PluginsMigration = Schema$1.Struct({
11581
11990
  marketplaceName: Schema$1.String,
11582
11991
  pluginNames: Schema$1.Array(Schema$1.String)
11583
11992
  });
11584
- const ClientRequest__ReasoningEffort = Schema$1.Literals([
11585
- "none",
11586
- "minimal",
11587
- "low",
11588
- "medium",
11589
- "high",
11590
- "xhigh"
11591
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
11993
+ const ClientRequest__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
11592
11994
  const ClientRequest__ReasoningItemContent = Schema$1.Union([Schema$1.Struct({
11593
11995
  text: Schema$1.String,
11594
11996
  type: Schema$1.Literal("reasoning_text").annotate({ title: "ReasoningTextReasoningItemContentType" })
@@ -12164,14 +12566,7 @@ const ServerNotification__RateLimitWindow = Schema$1.Struct({
12164
12566
  windowDurationMins: Schema$1.optionalKey(Schema$1.Union([Schema$1.Number.annotate({ format: "int64" }).check(Schema$1.isInt()), Schema$1.Null]))
12165
12567
  });
12166
12568
  const ServerNotification__RealtimeConversationVersion = Schema$1.Literals(["v1", "v2"]);
12167
- const ServerNotification__ReasoningEffort = Schema$1.Literals([
12168
- "none",
12169
- "minimal",
12170
- "low",
12171
- "medium",
12172
- "high",
12173
- "xhigh"
12174
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
12569
+ const ServerNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12175
12570
  const ServerNotification__ReasoningSummaryPartAddedNotification = Schema$1.Struct({
12176
12571
  itemId: Schema$1.String,
12177
12572
  summaryIndex: Schema$1.Number.annotate({ format: "int64" }).check(Schema$1.isInt()),
@@ -12550,14 +12945,7 @@ const V2ConfigReadResponse__AskForApproval = Schema$1.Union([Schema$1.Literals([
12550
12945
  skill_approval: Schema$1.optionalKey(Schema$1.Boolean.annotate({ default: false }))
12551
12946
  }) }).annotate({ title: "GranularAskForApproval" })], { mode: "oneOf" });
12552
12947
  const V2ConfigReadResponse__ForcedLoginMethod = Schema$1.Literals(["chatgpt", "api"]);
12553
- const V2ConfigReadResponse__ReasoningEffort = Schema$1.Literals([
12554
- "none",
12555
- "minimal",
12556
- "low",
12557
- "medium",
12558
- "high",
12559
- "xhigh"
12560
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
12948
+ const V2ConfigReadResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12561
12949
  const V2ConfigReadResponse__ReasoningSummary = Schema$1.Union([Schema$1.Literals([
12562
12950
  "auto",
12563
12951
  "concise",
@@ -12875,14 +13263,7 @@ const V2ItemCompletedNotification__PatchChangeKind = Schema$1.Union([
12875
13263
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
12876
13264
  }).annotate({ title: "UpdatePatchChangeKind" })
12877
13265
  ], { mode: "oneOf" });
12878
- const V2ItemCompletedNotification__ReasoningEffort = Schema$1.Literals([
12879
- "none",
12880
- "minimal",
12881
- "low",
12882
- "medium",
12883
- "high",
12884
- "xhigh"
12885
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13266
+ const V2ItemCompletedNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12886
13267
  const V2ItemCompletedNotification__TextElement = Schema$1.Struct({
12887
13268
  byteRange: Schema$1.Struct({
12888
13269
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -13070,14 +13451,7 @@ const V2ItemStartedNotification__PatchChangeKind = Schema$1.Union([
13070
13451
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13071
13452
  }).annotate({ title: "UpdatePatchChangeKind" })
13072
13453
  ], { mode: "oneOf" });
13073
- const V2ItemStartedNotification__ReasoningEffort = Schema$1.Literals([
13074
- "none",
13075
- "minimal",
13076
- "low",
13077
- "medium",
13078
- "high",
13079
- "xhigh"
13080
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13454
+ const V2ItemStartedNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13081
13455
  const V2ItemStartedNotification__TextElement = Schema$1.Struct({
13082
13456
  byteRange: Schema$1.Struct({
13083
13457
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -13165,14 +13539,7 @@ const V2ModelListResponse__ModelUpgradeInfo = Schema$1.Struct({
13165
13539
  modelLink: Schema$1.optionalKey(Schema$1.Union([Schema$1.String, Schema$1.Null])),
13166
13540
  upgradeCopy: Schema$1.optionalKey(Schema$1.Union([Schema$1.String, Schema$1.Null]))
13167
13541
  });
13168
- const V2ModelListResponse__ReasoningEffort = Schema$1.Literals([
13169
- "none",
13170
- "minimal",
13171
- "low",
13172
- "medium",
13173
- "high",
13174
- "xhigh"
13175
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13542
+ const V2ModelListResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13176
13543
  const V2ModelReroutedNotification__ModelRerouteReason = Schema$1.Literal("highRiskCyberActivity");
13177
13544
  const V2PluginInstallParams__AbsolutePathBuf = Schema$1.String.annotate({ description: "A path that is guaranteed to be absolute and normalized (though it is not guaranteed to be canonicalized or exist on the filesystem).\n\nIMPORTANT: When deserializing an `AbsolutePathBuf`, a base path must be set using [AbsolutePathBufGuard::new]. If no base path is set, the deserialization will fail unless the path being deserialized is already absolute." });
13178
13545
  const V2PluginInstallResponse__AppSummary = Schema$1.Struct({
@@ -13355,14 +13722,7 @@ const V2ReviewStartResponse__PatchChangeKind = Schema$1.Union([
13355
13722
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13356
13723
  }).annotate({ title: "UpdatePatchChangeKind" })
13357
13724
  ], { mode: "oneOf" });
13358
- const V2ReviewStartResponse__ReasoningEffort = Schema$1.Literals([
13359
- "none",
13360
- "minimal",
13361
- "low",
13362
- "medium",
13363
- "high",
13364
- "xhigh"
13365
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13725
+ const V2ReviewStartResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13366
13726
  const V2ReviewStartResponse__TextElement = Schema$1.Struct({
13367
13727
  byteRange: Schema$1.Struct({
13368
13728
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -13526,14 +13886,7 @@ const V2ThreadForkResponse__PatchChangeKind = Schema$1.Union([
13526
13886
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13527
13887
  }).annotate({ title: "UpdatePatchChangeKind" })
13528
13888
  ], { mode: "oneOf" });
13529
- const V2ThreadForkResponse__ReasoningEffort = Schema$1.Literals([
13530
- "none",
13531
- "minimal",
13532
- "low",
13533
- "medium",
13534
- "high",
13535
- "xhigh"
13536
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13889
+ const V2ThreadForkResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13537
13890
  const V2ThreadForkResponse__ServiceTier = Schema$1.Literals([
13538
13891
  "fast",
13539
13892
  "flex",
@@ -13656,14 +14009,7 @@ const V2ThreadListResponse__PatchChangeKind = Schema$1.Union([
13656
14009
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13657
14010
  }).annotate({ title: "UpdatePatchChangeKind" })
13658
14011
  ], { mode: "oneOf" });
13659
- const V2ThreadListResponse__ReasoningEffort = Schema$1.Literals([
13660
- "none",
13661
- "minimal",
13662
- "low",
13663
- "medium",
13664
- "high",
13665
- "xhigh"
13666
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14012
+ const V2ThreadListResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13667
14013
  const V2ThreadListResponse__TextElement = Schema$1.Struct({
13668
14014
  byteRange: Schema$1.Struct({
13669
14015
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -13772,14 +14118,7 @@ const V2ThreadMetadataUpdateResponse__PatchChangeKind = Schema$1.Union([
13772
14118
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13773
14119
  }).annotate({ title: "UpdatePatchChangeKind" })
13774
14120
  ], { mode: "oneOf" });
13775
- const V2ThreadMetadataUpdateResponse__ReasoningEffort = Schema$1.Literals([
13776
- "none",
13777
- "minimal",
13778
- "low",
13779
- "medium",
13780
- "high",
13781
- "xhigh"
13782
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14121
+ const V2ThreadMetadataUpdateResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13783
14122
  const V2ThreadMetadataUpdateResponse__TextElement = Schema$1.Struct({
13784
14123
  byteRange: Schema$1.Struct({
13785
14124
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -13883,14 +14222,7 @@ const V2ThreadReadResponse__PatchChangeKind = Schema$1.Union([
13883
14222
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13884
14223
  }).annotate({ title: "UpdatePatchChangeKind" })
13885
14224
  ], { mode: "oneOf" });
13886
- const V2ThreadReadResponse__ReasoningEffort = Schema$1.Literals([
13887
- "none",
13888
- "minimal",
13889
- "low",
13890
- "medium",
13891
- "high",
13892
- "xhigh"
13893
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14225
+ const V2ThreadReadResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13894
14226
  const V2ThreadReadResponse__TextElement = Schema$1.Struct({
13895
14227
  byteRange: Schema$1.Struct({
13896
14228
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14096,14 +14428,7 @@ const V2ThreadResumeResponse__PatchChangeKind = Schema$1.Union([
14096
14428
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14097
14429
  }).annotate({ title: "UpdatePatchChangeKind" })
14098
14430
  ], { mode: "oneOf" });
14099
- const V2ThreadResumeResponse__ReasoningEffort = Schema$1.Literals([
14100
- "none",
14101
- "minimal",
14102
- "low",
14103
- "medium",
14104
- "high",
14105
- "xhigh"
14106
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14431
+ const V2ThreadResumeResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14107
14432
  const V2ThreadResumeResponse__ServiceTier = Schema$1.Literals([
14108
14433
  "fast",
14109
14434
  "flex",
@@ -14212,14 +14537,7 @@ const V2ThreadRollbackResponse__PatchChangeKind = Schema$1.Union([
14212
14537
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14213
14538
  }).annotate({ title: "UpdatePatchChangeKind" })
14214
14539
  ], { mode: "oneOf" });
14215
- const V2ThreadRollbackResponse__ReasoningEffort = Schema$1.Literals([
14216
- "none",
14217
- "minimal",
14218
- "low",
14219
- "medium",
14220
- "high",
14221
- "xhigh"
14222
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14540
+ const V2ThreadRollbackResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14223
14541
  const V2ThreadRollbackResponse__TextElement = Schema$1.Struct({
14224
14542
  byteRange: Schema$1.Struct({
14225
14543
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14323,14 +14641,7 @@ const V2ThreadStartedNotification__PatchChangeKind = Schema$1.Union([
14323
14641
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14324
14642
  }).annotate({ title: "UpdatePatchChangeKind" })
14325
14643
  ], { mode: "oneOf" });
14326
- const V2ThreadStartedNotification__ReasoningEffort = Schema$1.Literals([
14327
- "none",
14328
- "minimal",
14329
- "low",
14330
- "medium",
14331
- "high",
14332
- "xhigh"
14333
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14644
+ const V2ThreadStartedNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14334
14645
  const V2ThreadStartedNotification__TextElement = Schema$1.Struct({
14335
14646
  byteRange: Schema$1.Struct({
14336
14647
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14475,14 +14786,7 @@ const V2ThreadStartResponse__PatchChangeKind = Schema$1.Union([
14475
14786
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14476
14787
  }).annotate({ title: "UpdatePatchChangeKind" })
14477
14788
  ], { mode: "oneOf" });
14478
- const V2ThreadStartResponse__ReasoningEffort = Schema$1.Literals([
14479
- "none",
14480
- "minimal",
14481
- "low",
14482
- "medium",
14483
- "high",
14484
- "xhigh"
14485
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14789
+ const V2ThreadStartResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14486
14790
  const V2ThreadStartResponse__ServiceTier = Schema$1.Literals([
14487
14791
  "fast",
14488
14792
  "flex",
@@ -14594,14 +14898,7 @@ const V2ThreadTurnsListResponse__PatchChangeKind = Schema$1.Union([
14594
14898
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14595
14899
  }).annotate({ title: "UpdatePatchChangeKind" })
14596
14900
  ], { mode: "oneOf" });
14597
- const V2ThreadTurnsListResponse__ReasoningEffort = Schema$1.Literals([
14598
- "none",
14599
- "minimal",
14600
- "low",
14601
- "medium",
14602
- "high",
14603
- "xhigh"
14604
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14901
+ const V2ThreadTurnsListResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14605
14902
  const V2ThreadTurnsListResponse__TextElement = Schema$1.Struct({
14606
14903
  byteRange: Schema$1.Struct({
14607
14904
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14703,14 +15000,7 @@ const V2ThreadUnarchiveResponse__PatchChangeKind = Schema$1.Union([
14703
15000
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14704
15001
  }).annotate({ title: "UpdatePatchChangeKind" })
14705
15002
  ], { mode: "oneOf" });
14706
- const V2ThreadUnarchiveResponse__ReasoningEffort = Schema$1.Literals([
14707
- "none",
14708
- "minimal",
14709
- "low",
14710
- "medium",
14711
- "high",
14712
- "xhigh"
14713
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15003
+ const V2ThreadUnarchiveResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14714
15004
  const V2ThreadUnarchiveResponse__TextElement = Schema$1.Struct({
14715
15005
  byteRange: Schema$1.Struct({
14716
15006
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14813,14 +15103,7 @@ const V2TurnCompletedNotification__PatchChangeKind = Schema$1.Union([
14813
15103
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14814
15104
  }).annotate({ title: "UpdatePatchChangeKind" })
14815
15105
  ], { mode: "oneOf" });
14816
- const V2TurnCompletedNotification__ReasoningEffort = Schema$1.Literals([
14817
- "none",
14818
- "minimal",
14819
- "low",
14820
- "medium",
14821
- "high",
14822
- "xhigh"
14823
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15106
+ const V2TurnCompletedNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14824
15107
  const V2TurnCompletedNotification__TextElement = Schema$1.Struct({
14825
15108
  byteRange: Schema$1.Struct({
14826
15109
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14921,14 +15204,7 @@ const V2TurnStartedNotification__PatchChangeKind = Schema$1.Union([
14921
15204
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14922
15205
  }).annotate({ title: "UpdatePatchChangeKind" })
14923
15206
  ], { mode: "oneOf" });
14924
- const V2TurnStartedNotification__ReasoningEffort = Schema$1.Literals([
14925
- "none",
14926
- "minimal",
14927
- "low",
14928
- "medium",
14929
- "high",
14930
- "xhigh"
14931
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15207
+ const V2TurnStartedNotification__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14932
15208
  const V2TurnStartedNotification__TextElement = Schema$1.Struct({
14933
15209
  byteRange: Schema$1.Struct({
14934
15210
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -14979,14 +15255,7 @@ const V2TurnStartParams__Personality = Schema$1.Literals([
14979
15255
  "friendly",
14980
15256
  "pragmatic"
14981
15257
  ]);
14982
- const V2TurnStartParams__ReasoningEffort = Schema$1.Literals([
14983
- "none",
14984
- "minimal",
14985
- "low",
14986
- "medium",
14987
- "high",
14988
- "xhigh"
14989
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15258
+ const V2TurnStartParams__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14990
15259
  const V2TurnStartParams__ReasoningSummary = Schema$1.Union([Schema$1.Literals([
14991
15260
  "auto",
14992
15261
  "concise",
@@ -15069,14 +15338,7 @@ const V2TurnStartResponse__PatchChangeKind = Schema$1.Union([
15069
15338
  type: Schema$1.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
15070
15339
  }).annotate({ title: "UpdatePatchChangeKind" })
15071
15340
  ], { mode: "oneOf" });
15072
- const V2TurnStartResponse__ReasoningEffort = Schema$1.Literals([
15073
- "none",
15074
- "minimal",
15075
- "low",
15076
- "medium",
15077
- "high",
15078
- "xhigh"
15079
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15341
+ const V2TurnStartResponse__ReasoningEffort = Schema$1.String.check(Schema$1.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
15080
15342
  const V2TurnStartResponse__TextElement = Schema$1.Struct({
15081
15343
  byteRange: Schema$1.Struct({
15082
15344
  end: Schema$1.Number.annotate({ format: "uint" }).check(Schema$1.isInt()).check(Schema$1.isGreaterThanOrEqualTo(0)),
@@ -26075,300 +26337,6 @@ async function runProcess(command, args, options = {}) {
26075
26337
  });
26076
26338
  }
26077
26339
  //#endregion
26078
- //#region src/serverSettings.ts
26079
- /**
26080
- * ServerSettings - Server-authoritative settings service.
26081
- *
26082
- * Owns persistence, validation, and change notification of settings that affect
26083
- * server-side behavior (binary paths, streaming mode, env mode, custom models,
26084
- * text generation model selection).
26085
- *
26086
- * Follows the same pattern as `keybindings.ts`: JSON file + Cache + PubSub +
26087
- * Semaphore + FileSystem.watch for concurrency and external edit detection.
26088
- *
26089
- * @module ServerSettings
26090
- */
26091
- const textEncoder$1 = new TextEncoder();
26092
- const textDecoder$1 = new TextDecoder();
26093
- function providerEnvironmentSecretName(input) {
26094
- return `provider-env-${Buffer.from(input.instanceId, "utf8").toString("base64url")}-${Buffer.from(input.name, "utf8").toString("base64url")}`;
26095
- }
26096
- function redactProviderEnvironmentVariable(variable) {
26097
- if (!variable.sensitive) {
26098
- const { valueRedacted: _omit, ...rest } = variable;
26099
- return rest;
26100
- }
26101
- return {
26102
- ...variable,
26103
- value: "",
26104
- ...variable.value.length > 0 || variable.valueRedacted ? { valueRedacted: true } : {}
26105
- };
26106
- }
26107
- function redactServerSettingsForClient(settings) {
26108
- const providerInstances = Object.fromEntries(Object.entries(settings.providerInstances).map(([instanceId, instance]) => [instanceId, instance.environment ? {
26109
- ...instance,
26110
- environment: instance.environment.map(redactProviderEnvironmentVariable)
26111
- } : instance]));
26112
- return {
26113
- ...settings,
26114
- providerInstances
26115
- };
26116
- }
26117
- var ServerSettingsService = class ServerSettingsService extends Context.Service()("ryco/serverSettings/ServerSettingsService") {
26118
- static layerTest = (overrides = {}) => Layer.effect(ServerSettingsService, Effect.gen(function* () {
26119
- const currentSettingsRef = yield* Ref.make(deepMerge(DEFAULT_SERVER_SETTINGS, overrides));
26120
- return {
26121
- start: Effect.void,
26122
- ready: Effect.void,
26123
- getSettings: Ref.get(currentSettingsRef),
26124
- updateSettings: (patch) => Ref.get(currentSettingsRef).pipe(Effect.flatMap((currentSettings) => Schema.decodeEffect(ServerSettings)(applyServerSettingsPatch(currentSettings, patch)).pipe(Effect.mapError((cause) => new ServerSettingsError({
26125
- settingsPath: "<memory>",
26126
- detail: `failed to normalize server settings: ${SchemaIssue.makeFormatterDefault()(cause.issue)}`,
26127
- cause
26128
- })))), Effect.tap((nextSettings) => Ref.set(currentSettingsRef, nextSettings))),
26129
- streamChanges: Stream.empty
26130
- };
26131
- }));
26132
- };
26133
- const ServerSettingsJson = fromLenientJson(ServerSettings);
26134
- const getLegacyProviderSettings = (settings, provider) => settings.providers[provider];
26135
- /**
26136
- * Ensure the `textGenerationModelSelection` points to an enabled provider.
26137
- * If the selected provider is disabled, fall back to the first enabled
26138
- * provider with its default model. This is applied at read-time so the
26139
- * persisted preference is preserved for when a provider is re-enabled.
26140
- */
26141
- function resolveTextGenerationProvider(settings) {
26142
- const selection = settings.textGenerationModelSelection;
26143
- const instanceConfig = settings.providerInstances[selection.instanceId];
26144
- if (instanceConfig !== void 0) return instanceConfig.enabled ?? true ? settings : fallbackTextGenerationProvider(settings);
26145
- if (isProviderDriverKind(selection.instanceId) && getLegacyProviderSettings(settings, selection.instanceId)?.enabled) return settings;
26146
- return fallbackTextGenerationProvider(settings);
26147
- }
26148
- function fallbackTextGenerationProvider(settings) {
26149
- const fallbackEntry = Object.entries(settings.providers).find(([, provider]) => provider.enabled);
26150
- const fallback = fallbackEntry ? ProviderDriverKind.make(fallbackEntry[0]) : void 0;
26151
- if (!fallback) return settings;
26152
- return {
26153
- ...settings,
26154
- textGenerationModelSelection: {
26155
- instanceId: ProviderInstanceId.make(fallback),
26156
- model: DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[fallback] ?? "gpt-5.4-mini"
26157
- }
26158
- };
26159
- }
26160
- const ATOMIC_SETTINGS_KEYS = new Set(["textGenerationModelSelection"]);
26161
- function stripDefaultServerSettings(current, defaults) {
26162
- if (Array.isArray(current) || Array.isArray(defaults)) return Equal.equals(current, defaults) ? void 0 : current;
26163
- if (current !== null && defaults !== null && typeof current === "object" && typeof defaults === "object") {
26164
- const currentRecord = current;
26165
- const defaultsRecord = defaults;
26166
- const next = {};
26167
- for (const key of Object.keys(currentRecord)) if (ATOMIC_SETTINGS_KEYS.has(key)) {
26168
- if (!Equal.equals(currentRecord[key], defaultsRecord[key])) next[key] = currentRecord[key];
26169
- } else {
26170
- const stripped = stripDefaultServerSettings(currentRecord[key], defaultsRecord[key]);
26171
- if (stripped !== void 0) next[key] = stripped;
26172
- }
26173
- return Object.keys(next).length > 0 ? next : void 0;
26174
- }
26175
- return Object.is(current, defaults) ? void 0 : current;
26176
- }
26177
- const makeServerSettings = Effect.gen(function* () {
26178
- const { settingsPath } = yield* ServerConfig;
26179
- const fs = yield* FileSystem.FileSystem;
26180
- const pathService = yield* Path.Path;
26181
- const secretStore = yield* ServerSecretStore;
26182
- const writeSemaphore = yield* Semaphore$1.make(1);
26183
- const cacheKey = "settings";
26184
- const changesPubSub = yield* PubSub.unbounded();
26185
- const startedRef = yield* Ref.make(false);
26186
- const startedDeferred = yield* Deferred.make();
26187
- const watcherScope = yield* Scope.make("sequential");
26188
- yield* Effect.addFinalizer(() => Scope.close(watcherScope, Exit.void));
26189
- const emitChange = (settings) => PubSub.publish(changesPubSub, settings).pipe(Effect.asVoid);
26190
- const readConfigExists = fs.exists(settingsPath).pipe(Effect.mapError((cause) => new ServerSettingsError({
26191
- settingsPath,
26192
- detail: "failed to check settings file existence",
26193
- cause
26194
- })));
26195
- const readRawConfig = fs.readFileString(settingsPath).pipe(Effect.mapError((cause) => new ServerSettingsError({
26196
- settingsPath,
26197
- detail: "failed to read settings file",
26198
- cause
26199
- })));
26200
- const loadSettingsFromDisk = Effect.gen(function* () {
26201
- if (!(yield* readConfigExists)) return DEFAULT_SERVER_SETTINGS;
26202
- const raw = yield* readRawConfig;
26203
- const decoded = Schema.decodeUnknownExit(ServerSettingsJson)(raw);
26204
- if (decoded._tag === "Failure") {
26205
- yield* Effect.logWarning("failed to parse settings.json, using defaults", {
26206
- path: settingsPath,
26207
- issues: Cause.pretty(decoded.cause)
26208
- });
26209
- return DEFAULT_SERVER_SETTINGS;
26210
- }
26211
- return decoded.value;
26212
- });
26213
- const settingsCache = yield* Cache.make({
26214
- capacity: 1,
26215
- lookup: () => loadSettingsFromDisk
26216
- });
26217
- const getSettingsFromCache = Cache.get(settingsCache, cacheKey);
26218
- const toSettingsError = (detail, cause) => new ServerSettingsError({
26219
- settingsPath,
26220
- detail,
26221
- cause
26222
- });
26223
- const materializeProviderEnvironmentSecrets = (settings) => Effect.gen(function* () {
26224
- const providerInstances = { ...settings.providerInstances };
26225
- for (const [instanceId, instance] of Object.entries(settings.providerInstances)) {
26226
- if (!instance.environment) continue;
26227
- const environment = [];
26228
- for (const variable of instance.environment) {
26229
- if (!variable.sensitive || !variable.valueRedacted) {
26230
- environment.push(variable);
26231
- continue;
26232
- }
26233
- const secret = yield* secretStore.get(providerEnvironmentSecretName({
26234
- instanceId,
26235
- name: variable.name
26236
- })).pipe(Effect.mapError((cause) => toSettingsError(`failed to read sensitive environment variable ${variable.name}`, cause)));
26237
- environment.push({
26238
- ...variable,
26239
- value: secret ? textDecoder$1.decode(secret) : ""
26240
- });
26241
- }
26242
- providerInstances[instanceId] = {
26243
- ...instance,
26244
- environment
26245
- };
26246
- }
26247
- return {
26248
- ...settings,
26249
- providerInstances
26250
- };
26251
- });
26252
- const persistProviderEnvironmentSecrets = (current, next) => Effect.gen(function* () {
26253
- const providerInstances = { ...next.providerInstances };
26254
- const nextSecretKeys = /* @__PURE__ */ new Set();
26255
- for (const [instanceId, instance] of Object.entries(next.providerInstances)) {
26256
- if (!instance.environment) continue;
26257
- const environment = [];
26258
- for (const variable of instance.environment) {
26259
- const secretName = providerEnvironmentSecretName({
26260
- instanceId,
26261
- name: variable.name
26262
- });
26263
- if (!variable.sensitive) {
26264
- yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
26265
- environment.push(redactProviderEnvironmentVariable(variable));
26266
- continue;
26267
- }
26268
- nextSecretKeys.add(secretName);
26269
- if (!variable.valueRedacted) {
26270
- if (variable.value.length > 0) {
26271
- yield* secretStore.set(secretName, textEncoder$1.encode(variable.value)).pipe(Effect.mapError((cause) => toSettingsError(`failed to persist environment secret ${variable.name}`, cause)));
26272
- environment.push({
26273
- ...variable,
26274
- value: "",
26275
- valueRedacted: true
26276
- });
26277
- } else {
26278
- yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
26279
- const { valueRedacted: _omit, ...rest } = variable;
26280
- environment.push(rest);
26281
- }
26282
- continue;
26283
- }
26284
- environment.push(redactProviderEnvironmentVariable(variable));
26285
- }
26286
- providerInstances[instanceId] = {
26287
- ...instance,
26288
- environment
26289
- };
26290
- }
26291
- for (const [instanceId, instance] of Object.entries(current.providerInstances)) for (const variable of instance.environment ?? []) {
26292
- if (!variable.sensitive) continue;
26293
- const secretName = providerEnvironmentSecretName({
26294
- instanceId,
26295
- name: variable.name
26296
- });
26297
- if (nextSecretKeys.has(secretName)) continue;
26298
- yield* secretStore.remove(secretName).pipe(Effect.mapError((cause) => toSettingsError(`failed to remove stale environment secret ${variable.name}`, cause)));
26299
- }
26300
- return {
26301
- ...next,
26302
- providerInstances
26303
- };
26304
- });
26305
- const writeSettingsAtomically = (settings) => {
26306
- const sparseSettings = stripDefaultServerSettings(settings, DEFAULT_SERVER_SETTINGS) ?? {};
26307
- return writeFileStringAtomically({
26308
- filePath: settingsPath,
26309
- contents: `${JSON.stringify(sparseSettings, null, 2)}\n`
26310
- }).pipe(Effect.provideService(FileSystem.FileSystem, fs), Effect.provideService(Path.Path, pathService), Effect.mapError((cause) => new ServerSettingsError({
26311
- settingsPath,
26312
- detail: "failed to write settings file",
26313
- cause
26314
- })));
26315
- };
26316
- const revalidateAndEmit = writeSemaphore.withPermits(1)(Effect.gen(function* () {
26317
- yield* Cache.invalidate(settingsCache, cacheKey);
26318
- yield* emitChange(yield* getSettingsFromCache);
26319
- }));
26320
- const startWatcher = Effect.gen(function* () {
26321
- const settingsDir = pathService.dirname(settingsPath);
26322
- const settingsFile = pathService.basename(settingsPath);
26323
- const settingsPathResolved = pathService.resolve(settingsPath);
26324
- yield* fs.makeDirectory(settingsDir, { recursive: true }).pipe(Effect.mapError((cause) => new ServerSettingsError({
26325
- settingsPath,
26326
- detail: "failed to prepare settings directory",
26327
- cause
26328
- })));
26329
- const revalidateAndEmitSafely = revalidateAndEmit.pipe(Effect.ignoreCause({ log: true }));
26330
- const debouncedSettingsEvents = fs.watch(settingsDir).pipe(Stream.filter((event) => {
26331
- return event.path === settingsFile || event.path === settingsPath || pathService.resolve(settingsDir, event.path) === settingsPathResolved;
26332
- }), Stream.debounce(Duration.millis(100)));
26333
- yield* Stream.runForEach(debouncedSettingsEvents, () => revalidateAndEmitSafely).pipe(Effect.ignoreCause({ log: true }), Effect.forkIn(watcherScope), Effect.asVoid);
26334
- });
26335
- return {
26336
- start: Effect.gen(function* () {
26337
- if (!(yield* Ref.modify(startedRef, (started) => [!started, true]))) return yield* Deferred.await(startedDeferred);
26338
- const startup = Effect.gen(function* () {
26339
- yield* startWatcher;
26340
- yield* Cache.invalidate(settingsCache, cacheKey);
26341
- yield* getSettingsFromCache;
26342
- });
26343
- const startupExit = yield* Effect.exit(startup);
26344
- if (startupExit._tag === "Failure") {
26345
- yield* Deferred.failCause(startedDeferred, startupExit.cause).pipe(Effect.orDie);
26346
- return yield* Effect.failCause(startupExit.cause);
26347
- }
26348
- yield* Deferred.succeed(startedDeferred, void 0).pipe(Effect.orDie);
26349
- }),
26350
- ready: Deferred.await(startedDeferred),
26351
- getSettings: getSettingsFromCache.pipe(Effect.flatMap(materializeProviderEnvironmentSecrets), Effect.map(resolveTextGenerationProvider)),
26352
- updateSettings: (patch) => writeSemaphore.withPermits(1)(Effect.gen(function* () {
26353
- const current = yield* getSettingsFromCache;
26354
- const nextPersisted = yield* persistProviderEnvironmentSecrets(current, applyServerSettingsPatch(current, patch));
26355
- const next = yield* Schema.decodeEffect(ServerSettings)(nextPersisted).pipe(Effect.mapError((cause) => new ServerSettingsError({
26356
- settingsPath: "<memory>",
26357
- detail: `failed to normalize server settings: ${SchemaIssue.makeFormatterDefault()(cause.issue)}`,
26358
- cause
26359
- })));
26360
- yield* writeSettingsAtomically(next);
26361
- yield* Cache.set(settingsCache, cacheKey, next);
26362
- yield* emitChange(next);
26363
- return resolveTextGenerationProvider(yield* materializeProviderEnvironmentSecrets(next));
26364
- })),
26365
- get streamChanges() {
26366
- return Stream.fromPubSub(changesPubSub).pipe(Stream.mapEffect((settings) => materializeProviderEnvironmentSecrets(settings).pipe(Effect.catch((error) => Effect.logWarning("failed to materialize provider environment secrets", { detail: error.detail }).pipe(Effect.as(settings))))), Stream.map(resolveTextGenerationProvider));
26367
- }
26368
- };
26369
- });
26370
- const ServerSettingsLive = Layer.effect(ServerSettingsService, makeServerSettings).pipe(Layer.provide(ServerSecretStoreLive));
26371
- //#endregion
26372
26340
  //#region src/pathExpansion.ts
26373
26341
  /**
26374
26342
  * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the
@@ -26689,7 +26657,7 @@ const BUILT_IN_DRIVERS = [
26689
26657
  configSchema: CodexSettings,
26690
26658
  defaultConfig: () => Schema.decodeSync(CodexSettings)({}),
26691
26659
  create: (input) => Effect.tryPromise({
26692
- try: () => import("./CodexDriver-BILrJG8o.mjs"),
26660
+ try: () => import("./CodexDriver-DH7vqku4.mjs"),
26693
26661
  catch: (cause) => driverImportError(codexDriverKind, input, cause)
26694
26662
  }).pipe(Effect.flatMap(({ CodexDriver }) => CodexDriver.create(input)))
26695
26663
  },
@@ -26702,7 +26670,7 @@ const BUILT_IN_DRIVERS = [
26702
26670
  configSchema: ClaudeSettings,
26703
26671
  defaultConfig: () => Schema.decodeSync(ClaudeSettings)({}),
26704
26672
  create: (input) => Effect.tryPromise({
26705
- try: () => import("./ClaudeDriver-D3nsxwH9.mjs"),
26673
+ try: () => import("./ClaudeDriver-CoPBpdrp.mjs"),
26706
26674
  catch: (cause) => driverImportError(claudeDriverKind, input, cause)
26707
26675
  }).pipe(Effect.flatMap(({ ClaudeDriver }) => ClaudeDriver.create(input)))
26708
26676
  },
@@ -26715,7 +26683,7 @@ const BUILT_IN_DRIVERS = [
26715
26683
  configSchema: CopilotSettings,
26716
26684
  defaultConfig: () => Schema.decodeSync(CopilotSettings)({}),
26717
26685
  create: (input) => Effect.tryPromise({
26718
- try: () => import("./CopilotDriver-CmD3Ffyl.mjs"),
26686
+ try: () => import("./CopilotDriver-CFCF68Te.mjs"),
26719
26687
  catch: (cause) => driverImportError(copilotDriverKind, input, cause)
26720
26688
  }).pipe(Effect.flatMap(({ CopilotDriver }) => CopilotDriver.create(input)))
26721
26689
  },
@@ -26728,7 +26696,7 @@ const BUILT_IN_DRIVERS = [
26728
26696
  configSchema: CursorSettings,
26729
26697
  defaultConfig: () => Schema.decodeSync(CursorSettings)({}),
26730
26698
  create: (input) => Effect.tryPromise({
26731
- try: () => import("./CursorDriver-CbeaIbdb.mjs"),
26699
+ try: () => import("./CursorDriver-Dej81rwD.mjs"),
26732
26700
  catch: (cause) => driverImportError(cursorDriverKind, input, cause)
26733
26701
  }).pipe(Effect.flatMap(({ CursorDriver }) => CursorDriver.create(input)))
26734
26702
  },
@@ -26741,7 +26709,7 @@ const BUILT_IN_DRIVERS = [
26741
26709
  configSchema: OpenCodeSettings,
26742
26710
  defaultConfig: () => Schema.decodeSync(OpenCodeSettings)({}),
26743
26711
  create: (input) => Effect.tryPromise({
26744
- try: () => import("./OpenCodeDriver-ClWtCk0j.mjs"),
26712
+ try: () => import("./OpenCodeDriver-BGa9Tkot.mjs"),
26745
26713
  catch: (cause) => driverImportError(openCodeDriverKind, input, cause)
26746
26714
  }).pipe(Effect.flatMap(({ OpenCodeDriver }) => OpenCodeDriver.create(input)))
26747
26715
  }
@@ -26841,6 +26809,7 @@ function buildServerProvider(input) {
26841
26809
  displayName: input.presentation.displayName,
26842
26810
  ...input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {},
26843
26811
  ...typeof input.presentation.showInteractionModeToggle === "boolean" ? { showInteractionModeToggle: input.presentation.showInteractionModeToggle } : {},
26812
+ ...typeof input.presentation.supportsAskMode === "boolean" ? { supportsAskMode: input.presentation.supportsAskMode } : {},
26844
26813
  enabled: input.enabled,
26845
26814
  installed: input.probe.installed,
26846
26815
  version: input.probe.version,
@@ -27454,13 +27423,14 @@ const scopedSafeTeardown = (label) => (effect) => Effect.gen(function* () {
27454
27423
  });
27455
27424
  //#endregion
27456
27425
  //#region package.json
27457
- var version = "0.1.6";
27426
+ var version = "0.1.8";
27458
27427
  //#endregion
27459
27428
  //#region src/provider/Layers/CodexProvider.ts
27460
27429
  const PROVIDER_PROBE_TIMEOUT_MS = 8e3;
27461
27430
  const CODEX_PRESENTATION = {
27462
27431
  displayName: "Codex",
27463
- showInteractionModeToggle: true
27432
+ showInteractionModeToggle: true,
27433
+ supportsAskMode: true
27464
27434
  };
27465
27435
  const REASONING_EFFORT_LABELS = {
27466
27436
  none: "None",
@@ -27468,8 +27438,15 @@ const REASONING_EFFORT_LABELS = {
27468
27438
  low: "Low",
27469
27439
  medium: "Medium",
27470
27440
  high: "High",
27471
- xhigh: "Extra High"
27441
+ xhigh: "Extra High",
27442
+ max: "Max",
27443
+ ultra: "Ultra"
27472
27444
  };
27445
+ function formatReasoningEffortLabel(reasoningEffort) {
27446
+ const known = REASONING_EFFORT_LABELS[reasoningEffort];
27447
+ if (known) return known;
27448
+ return reasoningEffort.replace(/[_-]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
27449
+ }
27473
27450
  function codexAccountAuthLabel(account) {
27474
27451
  if (!account) return void 0;
27475
27452
  if (account.type === "apiKey") return "OpenAI API Key";
@@ -27498,11 +27475,11 @@ function codexAccountEmail(account) {
27498
27475
  function mapCodexModelCapabilities(model) {
27499
27476
  const reasoningOptions = model.supportedReasoningEfforts.map(({ reasoningEffort }) => reasoningEffort === model.defaultReasoningEffort ? {
27500
27477
  id: reasoningEffort,
27501
- label: REASONING_EFFORT_LABELS[reasoningEffort],
27478
+ label: formatReasoningEffortLabel(reasoningEffort),
27502
27479
  isDefault: true
27503
27480
  } : {
27504
27481
  id: reasoningEffort,
27505
- label: REASONING_EFFORT_LABELS[reasoningEffort]
27482
+ label: formatReasoningEffortLabel(reasoningEffort)
27506
27483
  });
27507
27484
  const defaultReasoning = reasoningOptions.find((option) => option.isDefault)?.id;
27508
27485
  const supportsFastMode = (model.additionalSpeedTiers ?? []).includes("fast");
@@ -28491,6 +28468,425 @@ var OrchestrationEngineService = class extends Context.Service()("ryco/orchestra
28491
28468
  */
28492
28469
  var ProjectionSnapshotQuery = class extends Context.Service()("ryco/orchestration/Services/ProjectionSnapshotQuery") {};
28493
28470
  //#endregion
28471
+ //#region src/statistics/StatisticsQuery.ts
28472
+ /**
28473
+ * StatisticsQuery - usage analytics over the orchestration projection tables.
28474
+ *
28475
+ * Aggregates the existing read models (threads, sessions, token-usage
28476
+ * activities, turns/checkpoints, worktrees) into a {@link StatisticsSnapshot}
28477
+ * on demand. No dedicated statistics tables exist; everything is derived from
28478
+ * data already persisted by the projection pipeline.
28479
+ *
28480
+ * Token figures prefer per-turn attribution: for each `(thread, turn)` with
28481
+ * exact `last*` breakdowns we read the final `context-window.updated` activity
28482
+ * and bucket its provider-reported turn total by day/project/provider/model.
28483
+ * Threads without exact deltas (older data / providers that only report
28484
+ * cumulative context usage) fall back to attributing the latest cumulative
28485
+ * total to the thread's primary model and last-activity day.
28486
+ *
28487
+ * @module StatisticsQuery
28488
+ */
28489
+ /**
28490
+ * StatisticsQuery - service tag.
28491
+ */
28492
+ var StatisticsQuery = class extends Context.Service()("ryco/statistics/StatisticsQuery") {};
28493
+ function toPersistenceSqlOrDecodeError$7(sqlOperation, decodeOperation) {
28494
+ return (cause) => Schema.isSchemaError(cause) ? toPersistenceDecodeError(decodeOperation)(cause) : toPersistenceSqlError(sqlOperation)(cause);
28495
+ }
28496
+ const ThreadRow = Schema.Struct({
28497
+ threadId: Schema.String,
28498
+ projectId: ProjectId,
28499
+ model: Schema.NullOr(Schema.String),
28500
+ providerInstanceId: Schema.NullOr(Schema.String),
28501
+ createdAt: Schema.String,
28502
+ providerName: Schema.NullOr(Schema.String)
28503
+ });
28504
+ const ProjectRow = Schema.Struct({
28505
+ projectId: ProjectId,
28506
+ title: Schema.String,
28507
+ deletedAt: Schema.NullOr(Schema.String)
28508
+ });
28509
+ const TokenActivityRow = Schema.Struct({
28510
+ threadId: Schema.String,
28511
+ turnId: Schema.NullOr(Schema.String),
28512
+ sequence: Schema.NullOr(Schema.Number),
28513
+ createdAt: Schema.String,
28514
+ payloadJson: Schema.String
28515
+ });
28516
+ const ToolUseRow = Schema.Struct({
28517
+ threadId: Schema.String,
28518
+ date: Schema.String,
28519
+ count: Schema.Number
28520
+ });
28521
+ const TurnRow = Schema.Struct({
28522
+ threadId: Schema.String,
28523
+ startedAt: Schema.NullOr(Schema.String),
28524
+ completedAt: Schema.NullOr(Schema.String),
28525
+ checkpointTurnCount: Schema.NullOr(Schema.Number),
28526
+ filesJson: Schema.String
28527
+ });
28528
+ const WorktreeRow = Schema.Struct({
28529
+ createdAt: Schema.String,
28530
+ archivedAt: Schema.NullOr(Schema.String),
28531
+ prNumber: Schema.NullOr(Schema.Number)
28532
+ });
28533
+ function num(value) {
28534
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
28535
+ }
28536
+ function dayOf(iso) {
28537
+ return iso.length >= 10 ? iso.slice(0, 10) : null;
28538
+ }
28539
+ function bestPerTurnTotalTokenCount(payload) {
28540
+ const reportedTotal = num(payload?.lastUsedTokens);
28541
+ if (reportedTotal > 0) return reportedTotal;
28542
+ return num(payload?.lastInputTokens) + num(payload?.lastOutputTokens);
28543
+ }
28544
+ function bestCumulativeTotalTokenCount(payload) {
28545
+ const reportedTotal = num(payload?.usedTokens);
28546
+ if (reportedTotal > 0) return reportedTotal;
28547
+ return num(payload?.inputTokens) + num(payload?.outputTokens);
28548
+ }
28549
+ function hasExactPerTurnBreakdown(payload) {
28550
+ return num(payload?.lastInputTokens) + num(payload?.lastOutputTokens) + num(payload?.lastCachedInputTokens) + num(payload?.lastReasoningOutputTokens) > 0;
28551
+ }
28552
+ function addPerTurnUsage(bucket, payload) {
28553
+ bucket.inputTokens += num(payload?.lastInputTokens);
28554
+ bucket.outputTokens += num(payload?.lastOutputTokens);
28555
+ bucket.cachedInputTokens += num(payload?.lastCachedInputTokens);
28556
+ bucket.reasoningTokens += num(payload?.lastReasoningOutputTokens);
28557
+ bucket.totalTokens += bestPerTurnTotalTokenCount(payload);
28558
+ }
28559
+ function addCumulativeUsage(bucket, payload) {
28560
+ bucket.inputTokens += num(payload?.inputTokens);
28561
+ bucket.outputTokens += num(payload?.outputTokens);
28562
+ bucket.cachedInputTokens += num(payload?.cachedInputTokens);
28563
+ bucket.reasoningTokens += num(payload?.reasoningOutputTokens);
28564
+ bucket.totalTokens += bestCumulativeTotalTokenCount(payload);
28565
+ }
28566
+ const makeStatisticsQuery = Effect.gen(function* () {
28567
+ const sql = yield* SqlClient.SqlClient;
28568
+ const listThreadRows = SqlSchema.findAll({
28569
+ Request: Schema.Void,
28570
+ Result: ThreadRow,
28571
+ execute: () => sql`
28572
+ SELECT
28573
+ t.thread_id AS "threadId",
28574
+ t.project_id AS "projectId",
28575
+ json_extract(t.model_selection_json, '$.model') AS "model",
28576
+ COALESCE(
28577
+ json_extract(t.model_selection_json, '$.instanceId'),
28578
+ json_extract(t.model_selection_json, '$.provider')
28579
+ ) AS "providerInstanceId",
28580
+ t.created_at AS "createdAt",
28581
+ COALESCE(
28582
+ s.provider_name,
28583
+ json_extract(t.model_selection_json, '$.instanceId'),
28584
+ json_extract(t.model_selection_json, '$.provider')
28585
+ ) AS "providerName"
28586
+ FROM projection_threads t
28587
+ LEFT JOIN projection_thread_sessions s ON s.thread_id = t.thread_id
28588
+ `
28589
+ });
28590
+ const listProjectRows = SqlSchema.findAll({
28591
+ Request: Schema.Void,
28592
+ Result: ProjectRow,
28593
+ execute: () => sql`
28594
+ SELECT
28595
+ project_id AS "projectId",
28596
+ title,
28597
+ deleted_at AS "deletedAt"
28598
+ FROM projection_projects
28599
+ `
28600
+ });
28601
+ const listTokenActivityRows = SqlSchema.findAll({
28602
+ Request: Schema.Void,
28603
+ Result: TokenActivityRow,
28604
+ execute: () => sql`
28605
+ SELECT
28606
+ thread_id AS "threadId",
28607
+ turn_id AS "turnId",
28608
+ sequence,
28609
+ created_at AS "createdAt",
28610
+ payload_json AS "payloadJson"
28611
+ FROM projection_thread_activities
28612
+ WHERE kind = 'context-window.updated'
28613
+ ORDER BY thread_id ASC, turn_id ASC, sequence ASC, created_at ASC, activity_id ASC
28614
+ `
28615
+ });
28616
+ const listToolUseRows = SqlSchema.findAll({
28617
+ Request: Schema.Void,
28618
+ Result: ToolUseRow,
28619
+ execute: () => sql`
28620
+ SELECT
28621
+ thread_id AS "threadId",
28622
+ substr(created_at, 1, 10) AS "date",
28623
+ COUNT(*) AS "count"
28624
+ FROM projection_thread_activities
28625
+ WHERE kind = 'tool.completed'
28626
+ GROUP BY thread_id, substr(created_at, 1, 10)
28627
+ `
28628
+ });
28629
+ const listTurnRows = SqlSchema.findAll({
28630
+ Request: Schema.Void,
28631
+ Result: TurnRow,
28632
+ execute: () => sql`
28633
+ SELECT
28634
+ thread_id AS "threadId",
28635
+ started_at AS "startedAt",
28636
+ completed_at AS "completedAt",
28637
+ checkpoint_turn_count AS "checkpointTurnCount",
28638
+ checkpoint_files_json AS "filesJson"
28639
+ FROM projection_turns
28640
+ `
28641
+ });
28642
+ const listWorktreeRows = SqlSchema.findAll({
28643
+ Request: Schema.Void,
28644
+ Result: WorktreeRow,
28645
+ execute: () => sql`
28646
+ SELECT
28647
+ created_at AS "createdAt",
28648
+ archived_at AS "archivedAt",
28649
+ pr_number AS "prNumber"
28650
+ FROM projection_worktrees
28651
+ `
28652
+ });
28653
+ const getStatistics = () => Effect.gen(function* () {
28654
+ const [threadRows, projectRows, tokenRows, toolRows, turnRows, worktreeRows] = yield* Effect.all([
28655
+ listThreadRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listThreads:query", "StatisticsQuery.getStatistics:listThreads:decodeRows"))),
28656
+ listProjectRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listProjects:query", "StatisticsQuery.getStatistics:listProjects:decodeRows"))),
28657
+ listTokenActivityRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTokenActivities:query", "StatisticsQuery.getStatistics:listTokenActivities:decodeRows"))),
28658
+ listToolUseRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listToolUses:query", "StatisticsQuery.getStatistics:listToolUses:decodeRows"))),
28659
+ listTurnRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTurns:query", "StatisticsQuery.getStatistics:listTurns:decodeRows"))),
28660
+ listWorktreeRows(void 0).pipe(Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listWorktrees:query", "StatisticsQuery.getStatistics:listWorktrees:decodeRows")))
28661
+ ], { concurrency: "unbounded" });
28662
+ const threadMeta = /* @__PURE__ */ new Map();
28663
+ const modelRefs = /* @__PURE__ */ new Map();
28664
+ for (const row of threadRows) {
28665
+ const provider = row.providerName?.trim() || row.providerInstanceId?.trim() || void 0;
28666
+ const model = (row.model ?? "").trim() || "unknown";
28667
+ threadMeta.set(row.threadId, {
28668
+ projectId: row.projectId,
28669
+ model,
28670
+ provider,
28671
+ createdAt: row.createdAt
28672
+ });
28673
+ const modelKey = `${provider ?? ""}\u0000${model}`;
28674
+ modelRefs.set(modelKey, provider ? {
28675
+ model,
28676
+ provider
28677
+ } : { model });
28678
+ }
28679
+ const buckets = /* @__PURE__ */ new Map();
28680
+ const getBucket = (date, projectId, model, provider) => {
28681
+ const key = `${date}\u0000${projectId}\u0000${provider ?? ""}\u0000${model}`;
28682
+ let bucket = buckets.get(key);
28683
+ if (!bucket) {
28684
+ bucket = {
28685
+ date,
28686
+ projectId,
28687
+ model,
28688
+ provider,
28689
+ inputTokens: 0,
28690
+ outputTokens: 0,
28691
+ cachedInputTokens: 0,
28692
+ reasoningTokens: 0,
28693
+ totalTokens: 0,
28694
+ turns: 0,
28695
+ activeMs: 0,
28696
+ toolUses: 0,
28697
+ filesChanged: 0,
28698
+ additions: 0,
28699
+ deletions: 0,
28700
+ commits: 0,
28701
+ pushes: 0,
28702
+ threadsCreated: 0
28703
+ };
28704
+ buckets.set(key, bucket);
28705
+ }
28706
+ return bucket;
28707
+ };
28708
+ let earliest;
28709
+ const considerIso = (iso) => {
28710
+ if (iso && (earliest === void 0 || iso < earliest)) earliest = iso;
28711
+ };
28712
+ const lastPerTurn = /* @__PURE__ */ new Map();
28713
+ const isLater = (a, b) => {
28714
+ if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt;
28715
+ return (a.sequence ?? -1) >= (b.sequence ?? -1);
28716
+ };
28717
+ for (const row of tokenRows) {
28718
+ considerIso(row.createdAt);
28719
+ const turnKey = `${row.threadId}\u0000${row.turnId ?? ""}`;
28720
+ const prevTurn = lastPerTurn.get(turnKey);
28721
+ if (!prevTurn || isLater(row, prevTurn)) lastPerTurn.set(turnKey, row);
28722
+ }
28723
+ const firstExactPerThread = /* @__PURE__ */ new Map();
28724
+ for (const row of lastPerTurn.values()) if (hasExactPerTurnBreakdown(safeParse(row.payloadJson))) {
28725
+ const prev = firstExactPerThread.get(row.threadId);
28726
+ if (!prev || isLater(prev, row)) firstExactPerThread.set(row.threadId, row);
28727
+ }
28728
+ const cumulativeFallbackByThread = /* @__PURE__ */ new Map();
28729
+ for (const row of lastPerTurn.values()) {
28730
+ if (hasExactPerTurnBreakdown(safeParse(row.payloadJson))) continue;
28731
+ const firstExact = firstExactPerThread.get(row.threadId);
28732
+ if (firstExact && !isLater(firstExact, row)) continue;
28733
+ const prev = cumulativeFallbackByThread.get(row.threadId);
28734
+ if (!prev || isLater(row, prev)) cumulativeFallbackByThread.set(row.threadId, row);
28735
+ }
28736
+ let usedExactPerTurn = false;
28737
+ let usedCumulativeFallback = false;
28738
+ for (const row of lastPerTurn.values()) {
28739
+ const payload = safeParse(row.payloadJson);
28740
+ if (!hasExactPerTurnBreakdown(payload)) continue;
28741
+ const meta = threadMeta.get(row.threadId);
28742
+ const date = dayOf(row.createdAt);
28743
+ if (!meta || !date) continue;
28744
+ addPerTurnUsage(getBucket(date, meta.projectId, meta.model, meta.provider), payload);
28745
+ usedExactPerTurn = true;
28746
+ }
28747
+ for (const row of cumulativeFallbackByThread.values()) {
28748
+ const meta = threadMeta.get(row.threadId);
28749
+ const date = dayOf(row.createdAt);
28750
+ if (!meta || !date) continue;
28751
+ const payload = safeParse(row.payloadJson);
28752
+ if (bestCumulativeTotalTokenCount(payload) <= 0) continue;
28753
+ addCumulativeUsage(getBucket(date, meta.projectId, meta.model, meta.provider), payload);
28754
+ usedCumulativeFallback = true;
28755
+ }
28756
+ const attribution = usedExactPerTurn && usedCumulativeFallback ? "mixed" : usedCumulativeFallback ? "thread-cumulative" : "per-turn-delta";
28757
+ for (const row of turnRows) {
28758
+ const meta = threadMeta.get(row.threadId);
28759
+ if (!meta) continue;
28760
+ considerIso(row.startedAt);
28761
+ considerIso(row.completedAt);
28762
+ const anchor = row.completedAt ?? row.startedAt;
28763
+ const date = anchor ? dayOf(anchor) : null;
28764
+ if (!date) continue;
28765
+ const bucket = getBucket(date, meta.projectId, meta.model, meta.provider);
28766
+ if (row.completedAt) {
28767
+ bucket.turns += 1;
28768
+ if (row.startedAt) {
28769
+ const delta = Date.parse(row.completedAt) - Date.parse(row.startedAt);
28770
+ if (Number.isFinite(delta) && delta > 0) bucket.activeMs += delta;
28771
+ }
28772
+ }
28773
+ const files = safeParseArray(row.filesJson);
28774
+ if (files.length > 0) {
28775
+ bucket.filesChanged += files.length;
28776
+ for (const file of files) {
28777
+ bucket.additions += num(file?.additions);
28778
+ bucket.deletions += num(file?.deletions);
28779
+ }
28780
+ }
28781
+ }
28782
+ for (const row of toolRows) {
28783
+ const meta = threadMeta.get(row.threadId);
28784
+ if (!meta) continue;
28785
+ const bucket = getBucket(row.date, meta.projectId, meta.model, meta.provider);
28786
+ bucket.toolUses += num(row.count);
28787
+ }
28788
+ for (const meta of threadMeta.values()) {
28789
+ considerIso(meta.createdAt);
28790
+ const date = dayOf(meta.createdAt);
28791
+ if (!date) continue;
28792
+ getBucket(date, meta.projectId, meta.model, meta.provider).threadsCreated += 1;
28793
+ }
28794
+ let wtCreated = 0;
28795
+ let wtArchived = 0;
28796
+ let wtOpenPrs = 0;
28797
+ for (const row of worktreeRows) {
28798
+ considerIso(row.createdAt);
28799
+ wtCreated += 1;
28800
+ if (row.archivedAt) wtArchived += 1;
28801
+ else if (row.prNumber !== null) wtOpenPrs += 1;
28802
+ }
28803
+ const dailyBuckets = [];
28804
+ const totals = {
28805
+ inputTokens: 0,
28806
+ outputTokens: 0,
28807
+ cachedInputTokens: 0,
28808
+ reasoningTokens: 0,
28809
+ totalTokens: 0,
28810
+ turns: 0,
28811
+ activeMs: 0,
28812
+ toolUses: 0,
28813
+ filesChanged: 0,
28814
+ additions: 0,
28815
+ deletions: 0,
28816
+ commits: 0,
28817
+ pushes: 0,
28818
+ threads: threadMeta.size,
28819
+ projects: 0
28820
+ };
28821
+ for (const bucket of buckets.values()) {
28822
+ const { provider, ...rest } = bucket;
28823
+ dailyBuckets.push(provider ? {
28824
+ ...rest,
28825
+ provider
28826
+ } : rest);
28827
+ totals.inputTokens += bucket.inputTokens;
28828
+ totals.outputTokens += bucket.outputTokens;
28829
+ totals.cachedInputTokens += bucket.cachedInputTokens;
28830
+ totals.reasoningTokens += bucket.reasoningTokens;
28831
+ totals.turns += bucket.turns;
28832
+ totals.activeMs += bucket.activeMs;
28833
+ totals.toolUses += bucket.toolUses;
28834
+ totals.filesChanged += bucket.filesChanged;
28835
+ totals.additions += bucket.additions;
28836
+ totals.deletions += bucket.deletions;
28837
+ totals.commits += bucket.commits;
28838
+ totals.pushes += bucket.pushes;
28839
+ totals.totalTokens += bucket.totalTokens;
28840
+ }
28841
+ dailyBuckets.sort((a, b) => a.date === b.date ? a.projectId === b.projectId ? (a.provider ?? "").localeCompare(b.provider ?? "") || a.model.localeCompare(b.model) : a.projectId.localeCompare(b.projectId) : a.date.localeCompare(b.date));
28842
+ const projectTitle = /* @__PURE__ */ new Map();
28843
+ for (const row of projectRows) projectTitle.set(row.projectId, row.title);
28844
+ const projectIdsInData = /* @__PURE__ */ new Set();
28845
+ for (const meta of threadMeta.values()) projectIdsInData.add(meta.projectId);
28846
+ for (const bucket of buckets.values()) projectIdsInData.add(bucket.projectId);
28847
+ totals.projects = projectIdsInData.size;
28848
+ const projects = [...projectIdsInData].map((id) => ({
28849
+ id,
28850
+ title: projectTitle.get(id)?.trim() || id
28851
+ }));
28852
+ projects.sort((a, b) => a.title.localeCompare(b.title) || a.id.localeCompare(b.id));
28853
+ const models = [...modelRefs.values()].toSorted((a, b) => (a.provider ?? "").localeCompare(b.provider ?? "") || a.model.localeCompare(b.model));
28854
+ return {
28855
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
28856
+ ...earliest ? { earliestActivityAt: earliest } : {},
28857
+ projects,
28858
+ models,
28859
+ dailyBuckets,
28860
+ worktrees: {
28861
+ created: wtCreated,
28862
+ archived: wtArchived,
28863
+ active: wtCreated - wtArchived,
28864
+ openPrs: wtOpenPrs
28865
+ },
28866
+ totals,
28867
+ tokenAttribution: attribution
28868
+ };
28869
+ });
28870
+ return { getStatistics };
28871
+ });
28872
+ function safeParse(json) {
28873
+ try {
28874
+ const parsed = JSON.parse(json);
28875
+ return parsed && typeof parsed === "object" ? parsed : null;
28876
+ } catch {
28877
+ return null;
28878
+ }
28879
+ }
28880
+ function safeParseArray(json) {
28881
+ try {
28882
+ const parsed = JSON.parse(json);
28883
+ return Array.isArray(parsed) ? parsed : [];
28884
+ } catch {
28885
+ return [];
28886
+ }
28887
+ }
28888
+ const StatisticsQueryLive = Layer.effect(StatisticsQuery, makeStatisticsQuery);
28889
+ //#endregion
28494
28890
  //#region src/serverLifecycleEvents.ts
28495
28891
  var ServerLifecycleEvents = class extends Context.Service()("ryco/serverLifecycleEvents") {};
28496
28892
  const ServerLifecycleEventsLive = Layer.effect(ServerLifecycleEvents, Effect.gen(function* () {
@@ -30380,7 +30776,8 @@ const makeGitManager = Effect.fn("makeGitManager")(function* () {
30380
30776
  isDefaultRef: details.isDefaultBranch,
30381
30777
  refName: details.branch,
30382
30778
  hasWorkingTreeChanges: details.hasWorkingTreeChanges,
30383
- workingTree: details.workingTree
30779
+ workingTree: details.workingTree,
30780
+ ...details.committed ? { committed: details.committed } : {}
30384
30781
  };
30385
30782
  });
30386
30783
  const localStatusResultCache = yield* Cache.makeWith(readLocalStatus, {
@@ -32514,6 +32911,8 @@ const layer = Layer.effect(JiraWorkItemService, make$5());
32514
32911
  //#region src/remote/Services/AdvertisedEndpointRegistry.ts
32515
32912
  var AdvertisedEndpointRegistry = class extends Context.Service()("ryco/remote/Services/AdvertisedEndpointRegistry") {};
32516
32913
  const ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT = 1e3;
32914
+ const ORCHESTRATION_LEGACY_REPLAY_MAX_EVENTS = ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT;
32915
+ const ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS = 1e3;
32517
32916
  //#endregion
32518
32917
  //#region src/ws/context/gitErrors.ts
32519
32918
  function gitErrorText(error) {
@@ -32547,6 +32946,10 @@ const failGitWorkflow = (operation, detail, cause) => Effect.fail(toGitManagerEr
32547
32946
  const wsOrchestrationReplayDepth = Metric.gauge("t3_ws_orchestration_replay_depth", { description: "Current persisted orchestration event replay depth for a replayable WebSocket subscription." });
32548
32947
  const wsOrchestrationLiveBufferDepth = Metric.gauge("t3_ws_orchestration_live_buffer_depth", { description: "Current live orchestration event buffer depth for a replayable WebSocket subscription." });
32549
32948
  const wsOrchestrationLiveBufferHighWater = Metric.gauge("t3_ws_orchestration_live_buffer_high_water", { description: "Highest observed live orchestration event buffer depth for a replayable WebSocket subscription." });
32949
+ const wsOrchestrationLiveBufferOverflowsTotal = Metric.counter("t3_ws_orchestration_live_buffer_overflows_total", {
32950
+ description: "Total live orchestration event buffer overflows that forced replayable WebSocket subscription resync.",
32951
+ incremental: true
32952
+ });
32550
32953
  const wsOrchestrationReplayLag = Metric.gauge("t3_ws_orchestration_replay_lag", { description: "Sequence lag between the latest live event observed and the latest replay/live event drained by a replayable WebSocket subscription." });
32551
32954
  const normalizeSequence = (sequence) => Number.isFinite(sequence) ? Math.max(0, Math.floor(sequence)) : 0;
32552
32955
  const updateGauge$1 = (metric, attributes, value) => Metric.update(Metric.withAttributes(metric, attributes), value);
@@ -32584,6 +32987,18 @@ const makeWsReplayMetrics = (input) => Effect.gen(function* () {
32584
32987
  yield* recordLiveBuffer(yield* Ref.updateAndGet(liveBufferDepth, (current) => Math.max(0, current - 1)), yield* Ref.get(liveBufferHighWater));
32585
32988
  yield* markDrained(sequence);
32586
32989
  }),
32990
+ recordLiveOverflow: (sequence, capacity) => Effect.gen(function* () {
32991
+ const normalizedSequence = normalizeSequence(sequence);
32992
+ const normalizedCapacity = Math.max(0, Math.floor(capacity));
32993
+ yield* Ref.update(latestLiveSequence, (current) => Math.max(current, normalizedSequence));
32994
+ yield* Ref.set(liveBufferDepth, normalizedCapacity);
32995
+ const highWater = yield* Ref.updateAndGet(liveBufferHighWater, (current) => Math.max(current, normalizedCapacity));
32996
+ yield* Effect.all([
32997
+ recordLiveBuffer(normalizedCapacity, highWater),
32998
+ updateGauge$1(wsOrchestrationLiveBufferOverflowsTotal, attributes, 1),
32999
+ recordLag
33000
+ ], { discard: true });
33001
+ }),
32587
33002
  reset: Effect.all([
32588
33003
  updateGauge$1(wsOrchestrationReplayDepth, attributes, 0),
32589
33004
  updateGauge$1(wsOrchestrationLiveBufferDepth, attributes, 0),
@@ -32634,6 +33049,29 @@ const buildWorkItemBranchNameMessage = (input) => {
32634
33049
  };
32635
33050
  //#endregion
32636
33051
  //#region src/ws/context/orchestrationStreams.ts
33052
+ const makeLiveQueueOverflowError = (input) => new OrchestrationGetSnapshotError({
33053
+ message: `Orchestration ${input.stream} live event queue overflowed; reconnect to resynchronize`,
33054
+ cause: /* @__PURE__ */ new Error(`orchestration ${input.stream} live queue overflow at sequence ${input.sequence} with capacity ${input.capacity}`)
33055
+ });
33056
+ const offerOrchestrationLiveEventOrFail = (input) => Effect.gen(function* () {
33057
+ const capacity = input.capacity ?? 1e3;
33058
+ if (Queue.offerUnsafe(input.liveQueue, input.event)) {
33059
+ yield* input.replayMetrics.recordLiveEnqueued(input.event.sequence);
33060
+ return;
33061
+ }
33062
+ if (!(yield* Ref.modify(input.overflowedRef, (overflowed) => [!overflowed, true]))) return;
33063
+ yield* input.replayMetrics.recordLiveOverflow(input.event.sequence, capacity);
33064
+ yield* Effect.logWarning("orchestration live stream queue overflow; forcing resync", {
33065
+ stream: input.stream,
33066
+ sequence: input.event.sequence,
33067
+ capacity
33068
+ });
33069
+ yield* Queue.fail(input.liveQueue, makeLiveQueueOverflowError({
33070
+ stream: input.stream,
33071
+ sequence: input.event.sequence,
33072
+ capacity
33073
+ }));
33074
+ });
32637
33075
  const makeOrchestrationStreamHelpers = (deps) => {
32638
33076
  const { orchestrationEngine, projectionSnapshotQuery, repositoryIdentityResolver } = deps;
32639
33077
  const enrichProjectEvent = (event) => {
@@ -32753,8 +33191,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32753
33191
  const loadedSnapshot = yield* snapshot;
32754
33192
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32755
33193
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32756
- const liveQueue = yield* Queue.unbounded();
33194
+ const liveQueue = yield* Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32757
33195
  const lastSequenceRef = yield* Ref.make(snapshotSequence);
33196
+ const overflowedRef = yield* Ref.make(false);
32758
33197
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32759
33198
  const replayMetrics = yield* makeWsReplayMetrics({
32760
33199
  stream: "shell",
@@ -32763,8 +33202,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32763
33202
  });
32764
33203
  yield* Stream.fromSubscription(liveSubscription).pipe(Stream.runForEach((event) => Effect.gen(function* () {
32765
33204
  yield* replayBoundary.recordLiveSequence(event.sequence);
32766
- yield* Queue.offer(liveQueue, event);
32767
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33205
+ yield* offerOrchestrationLiveEventOrFail({
33206
+ stream: "shell",
33207
+ event,
33208
+ liveQueue,
33209
+ overflowedRef,
33210
+ replayMetrics
33211
+ });
32768
33212
  })), Effect.ensuring(Queue.shutdown(liveQueue)), Effect.ignoreCause({ log: true }), Effect.forkScoped);
32769
33213
  const replayStream = makeBoundedReplayStream({
32770
33214
  snapshotSequence,
@@ -32782,8 +33226,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32782
33226
  const loadedSnapshot = yield* snapshot;
32783
33227
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32784
33228
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32785
- const liveQueue = yield* Queue.unbounded();
33229
+ const liveQueue = yield* Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32786
33230
  const lastSequenceRef = yield* Ref.make(snapshotSequence);
33231
+ const overflowedRef = yield* Ref.make(false);
32787
33232
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32788
33233
  const replayMetrics = yield* makeWsReplayMetrics({
32789
33234
  stream: "thread",
@@ -32796,8 +33241,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32796
33241
  })));
32797
33242
  yield* Stream.fromSubscription(liveSubscription).pipe(Stream.runForEach((event) => Effect.gen(function* () {
32798
33243
  yield* replayBoundary.recordLiveSequence(event.sequence);
32799
- yield* Queue.offer(liveQueue, event);
32800
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33244
+ yield* offerOrchestrationLiveEventOrFail({
33245
+ stream: "thread",
33246
+ event,
33247
+ liveQueue,
33248
+ overflowedRef,
33249
+ replayMetrics
33250
+ });
32801
33251
  })), Effect.ensuring(Queue.shutdown(liveQueue)), Effect.ignoreCause({ log: true }), Effect.forkScoped);
32802
33252
  const replayStream = makeBoundedReplayStream({
32803
33253
  snapshotSequence,
@@ -32829,6 +33279,13 @@ const makeWorktreeOperations = (deps) => {
32829
33279
  onNone: () => failGitWorkflow(operation, `Worktree ${worktreeId} not found.`),
32830
33280
  onSome: Effect.succeed
32831
33281
  })));
33282
+ const isProjectRootPath = (candidate, projectRoot) => {
33283
+ const normalize = (value) => {
33284
+ const resolved = path.resolve(value).replace(/[\\/]+$/g, "");
33285
+ return process.platform === "win32" || process.platform === "darwin" ? resolved.toLowerCase() : resolved;
33286
+ };
33287
+ return normalize(candidate) === normalize(projectRoot);
33288
+ };
32832
33289
  const dispatchWorktreeCommand = (command, operation) => dispatchNormalizedCommand(command).pipe(Effect.mapError((cause) => toGitManagerError(operation, "Failed to dispatch orchestration command.", cause)), Effect.asVoid);
32833
33290
  const launchSetupScriptForWorktreeInBackground = (input) => Effect.gen(function* () {
32834
33291
  const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -33052,20 +33509,27 @@ const makeWorktreeOperations = (deps) => {
33052
33509
  break;
33053
33510
  }
33054
33511
  }
33055
- const worktreePath = preparedWorktreePath ?? (yield* gitWorkflow.createWorktree({
33056
- cwd: project.workspaceRoot,
33057
- refName,
33058
- ...newRefName !== void 0 ? { newRefName } : {},
33059
- path: resolveWorktreeCheckoutPath({
33512
+ let worktreePath;
33513
+ if (preparedWorktreePath !== null) {
33514
+ if (isProjectRootPath(preparedWorktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot create a worktree at the project root.");
33515
+ worktreePath = preparedWorktreePath;
33516
+ } else {
33517
+ const targetPath = resolveWorktreeCheckoutPath({
33060
33518
  location: input.worktreeLocation,
33061
33519
  appWorktreesRoot: config.worktreesDir,
33062
33520
  projectId: input.projectId,
33063
33521
  workspaceRoot: project.workspaceRoot,
33064
33522
  projectMetadataDir: project.projectMetadataDir,
33065
33523
  branchName: branch
33066
- })
33067
- })).worktree.path;
33068
- if (preparedWorktreePath === null) {
33524
+ });
33525
+ if (isProjectRootPath(targetPath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot create a worktree at the project root.");
33526
+ worktreePath = (yield* gitWorkflow.createWorktree({
33527
+ cwd: project.workspaceRoot,
33528
+ refName,
33529
+ ...newRefName !== void 0 ? { newRefName } : {},
33530
+ path: targetPath
33531
+ })).worktree.path;
33532
+ if (isProjectRootPath(worktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Refusing to register a worktree that resolved to the project root.");
33069
33533
  ownedWorktreePath = worktreePath;
33070
33534
  if (newRefName !== void 0) ownedBranchName = newRefName;
33071
33535
  }
@@ -33206,6 +33670,7 @@ const makeWorktreeOperations = (deps) => {
33206
33670
  const worktree = yield* loadWorktreeForGitWorkflow(operation, input.worktreeId);
33207
33671
  if (worktree.origin === "main") return yield* failGitWorkflow(operation, "Cannot delete the main worktree.");
33208
33672
  const project = yield* loadProjectForGitWorkflow(operation, worktree.projectId);
33673
+ if (worktree.worktreePath !== null && isProjectRootPath(worktree.worktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot delete a worktree that points at the project root.");
33209
33674
  if (input.force) {
33210
33675
  if (worktree.worktreePath !== null) {
33211
33676
  if (existsSync(worktree.worktreePath)) return yield* failGitWorkflow(operation, "Cannot force delete: the worktree path still exists on disk. Use a regular delete instead.");
@@ -33338,6 +33803,7 @@ function toDiagnosticsProviderProcess(provider) {
33338
33803
  const makeWsRpcContext = (session) => Effect.gen(function* () {
33339
33804
  const currentSessionId = session.sessionId;
33340
33805
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
33806
+ const statisticsQuery = yield* StatisticsQuery;
33341
33807
  const orchestrationEngine = yield* OrchestrationEngineService;
33342
33808
  const checkpointDiffQuery = yield* CheckpointDiffQuery;
33343
33809
  const keybindings = yield* Keybindings;
@@ -33661,6 +34127,7 @@ const makeWsRpcContext = (session) => Effect.gen(function* () {
33661
34127
  return {
33662
34128
  currentSessionId,
33663
34129
  projectionSnapshotQuery,
34130
+ statisticsQuery,
33664
34131
  orchestrationEngine,
33665
34132
  checkpointDiffQuery,
33666
34133
  keybindings,
@@ -33938,9 +34405,9 @@ function recordServerPerfPayload(label, value, record) {
33938
34405
  //#endregion
33939
34406
  //#region src/ws/orchestrationRpc.ts
33940
34407
  const makeOrchestrationHandlers = (ctx) => {
33941
- const { withAccess, ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
34408
+ const { ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
33942
34409
  return defineWsHandlers({
33943
- [ORCHESTRATION_WS_METHODS.dispatchCommand]: (command) => observeRpcEffect(ORCHESTRATION_WS_METHODS.dispatchCommand, withAccess("authenticated", ORCHESTRATION_WS_METHODS.dispatchCommand, Effect.gen(function* () {
34410
+ [ORCHESTRATION_WS_METHODS.dispatchCommand]: (command) => observeRpcEffect(ORCHESTRATION_WS_METHODS.dispatchCommand, ownerEffect(ORCHESTRATION_WS_METHODS.dispatchCommand, Effect.gen(function* () {
33944
34411
  const normalizedCommand = yield* normalizeDispatchCommand(command);
33945
34412
  const shouldStopSessionAfterArchive = normalizedCommand.type === "thread.archive" ? yield* projectionSnapshotQuery.getThreadShellById(normalizedCommand.threadId).pipe(Effect.map(Option.match({
33946
34413
  onNone: () => false,
@@ -33977,10 +34444,20 @@ const makeOrchestrationHandlers = (ctx) => {
33977
34444
  message: "Failed to load full thread diff",
33978
34445
  cause
33979
34446
  }))), { "rpc.aggregate": "orchestration" }),
34447
+ [ORCHESTRATION_WS_METHODS.searchThreadMessages]: (input) => observeRpcEffect(ORCHESTRATION_WS_METHODS.searchThreadMessages, projectionSnapshotQuery.searchThreadMessages({
34448
+ ...input,
34449
+ limit: clamp(input.limit, {
34450
+ maximum: 50,
34451
+ minimum: 1
34452
+ })
34453
+ }).pipe(Effect.mapError((cause) => new OrchestrationGetSnapshotError({
34454
+ message: "Failed to search thread messages",
34455
+ cause
34456
+ }))), { "rpc.aggregate": "orchestration" }),
33980
34457
  [ORCHESTRATION_WS_METHODS.replayEvents]: (input) => observeRpcEffect(ORCHESTRATION_WS_METHODS.replayEvents, Stream.runCollect(orchestrationEngine.readEvents(clamp(input.fromSequenceExclusive, {
33981
34458
  maximum: Number.MAX_SAFE_INTEGER,
33982
34459
  minimum: 0
33983
- }))).pipe(Effect.map((events) => Array.from(events)), Effect.flatMap(enrichOrchestrationEvents), Effect.mapError((cause) => new OrchestrationReplayEventsError({
34460
+ }), ORCHESTRATION_LEGACY_REPLAY_MAX_EVENTS)).pipe(Effect.map((events) => Array.from(events)), Effect.flatMap(enrichOrchestrationEvents), Effect.mapError((cause) => new OrchestrationReplayEventsError({
33984
34461
  message: "Failed to replay orchestration events",
33985
34462
  cause
33986
34463
  }))), { "rpc.aggregate": "orchestration" }),
@@ -34036,7 +34513,22 @@ const makeOrchestrationHandlers = (ctx) => {
34036
34513
  threadId: input.threadId,
34037
34514
  position: input.position,
34038
34515
  changedAt: (/* @__PURE__ */ new Date()).toISOString()
34039
- }, "threads.setManualPosition").pipe(Effect.as({}))), { "rpc.aggregate": "orchestration" })
34516
+ }, "threads.setManualPosition").pipe(Effect.as({}))), { "rpc.aggregate": "orchestration" }),
34517
+ [WS_METHODS.searchThreadMessages]: (input) => observeRpcEffect(WS_METHODS.searchThreadMessages, ownerEffect(WS_METHODS.searchThreadMessages, Effect.gen(function* () {
34518
+ const query = input.query.trim();
34519
+ if (query.length === 0) return { results: [] };
34520
+ return { results: yield* projectionSnapshotQuery.searchThreadMessages({
34521
+ query,
34522
+ ...input.projectId ? { projectId: input.projectId } : {},
34523
+ limit: clamp(input.limit ?? 20, {
34524
+ maximum: 50,
34525
+ minimum: 1
34526
+ })
34527
+ }) };
34528
+ }).pipe(Effect.mapError(() => new AuthRpcError({
34529
+ message: "Failed to search thread messages.",
34530
+ status: 403
34531
+ })))), { "rpc.aggregate": "orchestration" })
34040
34532
  });
34041
34533
  };
34042
34534
  //#endregion
@@ -34111,10 +34603,25 @@ const makeTerminalHandlers = (ctx) => {
34111
34603
  [WS_METHODS.terminalClear]: (input) => observeRpcEffect(WS_METHODS.terminalClear, ownerEffect(WS_METHODS.terminalClear, terminalManager.clear(input)), { "rpc.aggregate": "terminal" }),
34112
34604
  [WS_METHODS.terminalRestart]: (input) => observeRpcEffect(WS_METHODS.terminalRestart, ownerEffect(WS_METHODS.terminalRestart, terminalManager.restart(input)), { "rpc.aggregate": "terminal" }),
34113
34605
  [WS_METHODS.terminalClose]: (input) => observeRpcEffect(WS_METHODS.terminalClose, ownerEffect(WS_METHODS.terminalClose, terminalManager.close(input)), { "rpc.aggregate": "terminal" }),
34114
- [WS_METHODS.subscribeTerminalEvents]: (_input) => observeRpcStream(WS_METHODS.subscribeTerminalEvents, ownerStream(WS_METHODS.subscribeTerminalEvents, Stream.callback((queue) => Effect.acquireRelease(terminalManager.subscribe((event) => Effect.gen(function* () {
34115
- recordServerPerfPayload("server.ws.terminal.events", event);
34116
- yield* Queue.offer(queue, event);
34117
- })), (unsubscribe) => Effect.sync(unsubscribe)))), { "rpc.aggregate": "terminal" })
34606
+ [WS_METHODS.subscribeTerminalEvents]: (_input) => observeRpcStream(WS_METHODS.subscribeTerminalEvents, ownerStream(WS_METHODS.subscribeTerminalEvents, Stream.callback((queue) => Effect.acquireRelease(Effect.gen(function* () {
34607
+ const unsubscribe = yield* terminalManager.subscribe((event) => Effect.gen(function* () {
34608
+ recordServerPerfPayload("server.ws.terminal.events", event);
34609
+ yield* Queue.offer(queue, event);
34610
+ }));
34611
+ const snapshots = yield* terminalManager.listSessions;
34612
+ yield* Effect.forEach(snapshots.filter((snapshot) => snapshot.status === "running"), (snapshot) => {
34613
+ const event = {
34614
+ type: "started",
34615
+ threadId: snapshot.threadId,
34616
+ terminalId: snapshot.terminalId,
34617
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
34618
+ snapshot
34619
+ };
34620
+ recordServerPerfPayload("server.ws.terminal.events", event);
34621
+ return Queue.offer(queue, event);
34622
+ }, { discard: true });
34623
+ return unsubscribe;
34624
+ }), (unsubscribe) => Effect.sync(unsubscribe)))), { "rpc.aggregate": "terminal" })
34118
34625
  });
34119
34626
  };
34120
34627
  //#endregion
@@ -34209,7 +34716,7 @@ const makeSourceControlHandlers = (ctx) => {
34209
34716
  cwd,
34210
34717
  reason: "sourceControl.searchIssues"
34211
34718
  })))), { "rpc.aggregate": "source-control" }),
34212
- [WS_METHODS.sourceControlListChangeRequests]: ({ cwd, state, limit, query }) => observeRpcEffect(WS_METHODS.sourceControlListChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(Effect.flatMap((provider) => {
34719
+ [WS_METHODS.sourceControlListChangeRequests]: ({ cwd, state, limit, query }) => observeRpcEffect(WS_METHODS.sourceControlListChangeRequests, ownerEffect(WS_METHODS.sourceControlListChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(Effect.flatMap((provider) => {
34213
34720
  const trimmedQuery = query?.trim() ?? "";
34214
34721
  if (trimmedQuery.length > 0) return provider.searchChangeRequests({
34215
34722
  cwd,
@@ -34225,7 +34732,7 @@ const makeSourceControlHandlers = (ctx) => {
34225
34732
  }), Effect.tap(() => refreshLinkedWorktreeSourceControlStates({
34226
34733
  cwd,
34227
34734
  reason: "sourceControl.listChangeRequests"
34228
- }))), { "rpc.aggregate": "source-control" }),
34735
+ })))), { "rpc.aggregate": "source-control" }),
34229
34736
  [WS_METHODS.sourceControlSearchChangeRequests]: ({ cwd, query, limit }) => observeRpcEffect(WS_METHODS.sourceControlSearchChangeRequests, ownerEffect(WS_METHODS.sourceControlSearchChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(Effect.flatMap((provider) => provider.searchChangeRequests({
34230
34737
  cwd,
34231
34738
  query,
@@ -34303,7 +34810,7 @@ const makeSourceControlHandlers = (ctx) => {
34303
34810
  })), { "rpc.aggregate": "source-control" }),
34304
34811
  [WS_METHODS.sourceControlListIssueLabels]: ({ cwd }) => observeRpcEffect(WS_METHODS.sourceControlListIssueLabels, ownerEffect(WS_METHODS.sourceControlListIssueLabels, sourceControlRegistry.resolve({ cwd }).pipe(Effect.flatMap((provider) => provider.listLabels({ cwd })))), { "rpc.aggregate": "source-control" }),
34305
34812
  [WS_METHODS.sourceControlListIssueAssignees]: ({ cwd }) => observeRpcEffect(WS_METHODS.sourceControlListIssueAssignees, ownerEffect(WS_METHODS.sourceControlListIssueAssignees, sourceControlRegistry.resolve({ cwd }).pipe(Effect.flatMap((provider) => provider.listAssignees({ cwd })))), { "rpc.aggregate": "source-control" }),
34306
- [WS_METHODS.sourceControlListWorkflowRuns]: ({ cwd, pullRequestNumber, commitSha, limit }) => observeRpcEffect(WS_METHODS.sourceControlListWorkflowRuns, ownerEffect(WS_METHODS.sourceControlListWorkflowRuns, callSourceControlWorkflowMethod({
34813
+ [WS_METHODS.sourceControlListWorkflowRuns]: ({ cwd, pullRequestNumber, commitSha, branch, limit }) => observeRpcEffect(WS_METHODS.sourceControlListWorkflowRuns, ownerEffect(WS_METHODS.sourceControlListWorkflowRuns, callSourceControlWorkflowMethod({
34307
34814
  cwd,
34308
34815
  operation: "listWorkflowRuns",
34309
34816
  invoke: (provider) => {
@@ -34312,6 +34819,7 @@ const makeSourceControlHandlers = (ctx) => {
34312
34819
  cwd,
34313
34820
  ...pullRequestNumber !== void 0 ? { pullRequestNumber } : {},
34314
34821
  ...commitSha !== void 0 ? { commitSha } : {},
34822
+ ...branch !== void 0 ? { branch } : {},
34315
34823
  ...limit !== void 0 ? { limit } : {}
34316
34824
  });
34317
34825
  }
@@ -34975,12 +35483,19 @@ const makeProviderHandlers = (ctx) => {
34975
35483
  });
34976
35484
  };
34977
35485
  //#endregion
35486
+ //#region src/ws/statisticsRpc.ts
35487
+ const makeStatisticsHandlers = (ctx) => {
35488
+ const { statisticsQuery, ownerEffect } = ctx;
35489
+ return defineWsHandlers({ [WS_METHODS.serverGetStatistics]: (_input) => observeRpcEffect(WS_METHODS.serverGetStatistics, ownerEffect(WS_METHODS.serverGetStatistics, statisticsQuery.getStatistics().pipe(Effect.orDie)), { "rpc.aggregate": "server" }) });
35490
+ };
35491
+ //#endregion
34978
35492
  //#region src/ws/index.ts
34979
35493
  const makeWsRpcLayer = (session) => WsRpcGroup.toLayer(Effect.gen(function* () {
34980
35494
  const ctx = yield* makeWsRpcContext(session);
34981
35495
  return WsRpcGroup.of({
34982
35496
  ...makeOrchestrationHandlers(ctx),
34983
35497
  ...makeProviderHandlers(ctx),
35498
+ ...makeStatisticsHandlers(ctx),
34984
35499
  ...makeSourceControlHandlers(ctx),
34985
35500
  ...makeProjectHandlers(ctx),
34986
35501
  ...makeGitHandlers(ctx),
@@ -35071,7 +35586,7 @@ const TelemetryEnvConfig = Config.all({
35071
35586
  maxBufferedEvents: Config.number("RYCO_TELEMETRY_MAX_BUFFERED_EVENTS").pipe(Config.withDefault(1e3))
35072
35587
  });
35073
35588
  const makeAnalyticsService = Effect.gen(function* () {
35074
- const telemetryConfig = yield* TelemetryEnvConfig.asEffect();
35589
+ const telemetryConfig = yield* TelemetryEnvConfig;
35075
35590
  const httpClient = yield* HttpClient.HttpClient;
35076
35591
  const serverConfig = yield* ServerConfig;
35077
35592
  const identifier = yield* getTelemetryIdentifier;
@@ -37398,6 +37913,8 @@ const DEFAULT_PERSIST_DEBOUNCE_MS = 40;
37398
37913
  const DEFAULT_SUBPROCESS_POLL_INTERVAL_MS = 1e3;
37399
37914
  const DEFAULT_PROCESS_KILL_GRACE_MS = 1e3;
37400
37915
  const DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS = 128;
37916
+ const DEFAULT_MAX_PENDING_PROCESS_EVENTS = 2048;
37917
+ const DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES = 2 * 1024 * 1024;
37401
37918
  const DEFAULT_OPEN_COLS = 120;
37402
37919
  const DEFAULT_OPEN_ROWS = 30;
37403
37920
  const TERMINAL_ENV_BLOCKLIST = new Set([
@@ -37454,13 +37971,86 @@ function cleanupProcessHandles(session) {
37454
37971
  session.unsubscribeExit?.();
37455
37972
  session.unsubscribeExit = null;
37456
37973
  }
37457
- function enqueueProcessEvent(session, expectedPid, event) {
37974
+ function clearPendingProcessEvents(session) {
37975
+ session.pendingProcessEvents = [];
37976
+ session.pendingProcessEventIndex = 0;
37977
+ session.pendingProcessOutputBytes = 0;
37978
+ session.processEventDrainRunning = false;
37979
+ }
37980
+ function enqueueProcessEvent(session, expectedPid, event, limits) {
37458
37981
  if (!session.process || session.status !== "running" || session.pid !== expectedPid) return false;
37982
+ if (event.type === "output" && session.pendingProcessEvents.some((pendingEvent) => pendingEvent.type === "exit")) return false;
37983
+ compactPendingProcessEvents(session);
37459
37984
  session.pendingProcessEvents.push(event);
37985
+ if (event.type === "output") session.pendingProcessOutputBytes += event.byteLength;
37986
+ enforcePendingProcessEventLimits(session, limits);
37460
37987
  if (session.processEventDrainRunning) return false;
37461
37988
  session.processEventDrainRunning = true;
37462
37989
  return true;
37463
37990
  }
37991
+ function compactPendingProcessEvents(session) {
37992
+ if (session.pendingProcessEventIndex <= 0) return;
37993
+ session.pendingProcessEvents = session.pendingProcessEvents.slice(session.pendingProcessEventIndex);
37994
+ session.pendingProcessEventIndex = 0;
37995
+ }
37996
+ function coalesceOldestPendingOutputPair(session) {
37997
+ for (let index = 0; index < session.pendingProcessEvents.length - 1; index += 1) {
37998
+ const current = session.pendingProcessEvents[index];
37999
+ const next = session.pendingProcessEvents[index + 1];
38000
+ if (current?.type !== "output" || next?.type !== "output") continue;
38001
+ session.pendingProcessEvents.splice(index, 2, {
38002
+ type: "output",
38003
+ data: `${current.data}${next.data}`,
38004
+ byteLength: current.byteLength + next.byteLength
38005
+ });
38006
+ return true;
38007
+ }
38008
+ return false;
38009
+ }
38010
+ function trimOutputDataToApproxBytes(data, maxBytes) {
38011
+ if (maxBytes <= 0 || data.length === 0) return {
38012
+ data: "",
38013
+ byteLength: 0
38014
+ };
38015
+ let nextData = data;
38016
+ let byteLength = approximateTextBytes(nextData);
38017
+ while (byteLength > maxBytes && nextData.length > 0) {
38018
+ const excessBytes = byteLength - maxBytes;
38019
+ const sliceStart = Math.min(nextData.length, Math.max(1, excessBytes));
38020
+ nextData = nextData.slice(sliceStart);
38021
+ byteLength = approximateTextBytes(nextData);
38022
+ }
38023
+ return {
38024
+ data: nextData,
38025
+ byteLength
38026
+ };
38027
+ }
38028
+ function trimPendingOutputBytesToLimit(session, maxPendingProcessOutputBytes) {
38029
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38030
+ for (let index = 0; index < session.pendingProcessEvents.length; index += 1) {
38031
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38032
+ const event = session.pendingProcessEvents[index];
38033
+ if (event?.type !== "output") continue;
38034
+ const targetEventBytes = Math.max(0, event.byteLength - (session.pendingProcessOutputBytes - maxPendingProcessOutputBytes));
38035
+ if (targetEventBytes <= 0) {
38036
+ session.pendingProcessEvents.splice(index, 1);
38037
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - event.byteLength);
38038
+ index -= 1;
38039
+ continue;
38040
+ }
38041
+ const trimmed = trimOutputDataToApproxBytes(event.data, targetEventBytes);
38042
+ session.pendingProcessEvents[index] = {
38043
+ type: "output",
38044
+ data: trimmed.data,
38045
+ byteLength: trimmed.byteLength
38046
+ };
38047
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - (event.byteLength - trimmed.byteLength));
38048
+ }
38049
+ }
38050
+ function enforcePendingProcessEventLimits(session, limits) {
38051
+ while (session.pendingProcessEvents.length > limits.maxPendingProcessEvents && coalesceOldestPendingOutputPair(session));
38052
+ trimPendingOutputBytesToLimit(session, limits.maxPendingProcessOutputBytes);
38053
+ }
37464
38054
  function defaultShellResolver(platform = process.platform, env = process.env) {
37465
38055
  if (platform === "win32") return "pwsh.exe";
37466
38056
  return env.SHELL ?? "bash";
@@ -37827,6 +38417,10 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
37827
38417
  const subprocessPollIntervalMs = options.subprocessPollIntervalMs ?? DEFAULT_SUBPROCESS_POLL_INTERVAL_MS;
37828
38418
  const processKillGraceMs = options.processKillGraceMs ?? DEFAULT_PROCESS_KILL_GRACE_MS;
37829
38419
  const maxRetainedInactiveSessions = options.maxRetainedInactiveSessions ?? DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS;
38420
+ const pendingProcessEventLimits = {
38421
+ maxPendingProcessEvents: Math.max(1, options.maxPendingProcessEvents ?? DEFAULT_MAX_PENDING_PROCESS_EVENTS),
38422
+ maxPendingProcessOutputBytes: Math.max(1, options.maxPendingProcessOutputBytes ?? DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES)
38423
+ };
37830
38424
  yield* fileSystem.makeDirectory(logsDir, { recursive: true }).pipe(Effect.orDie);
37831
38425
  const managerStateRef = yield* SynchronizedRef.make({
37832
38426
  sessions: /* @__PURE__ */ new Map(),
@@ -38063,16 +38657,12 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38063
38657
  while (true) {
38064
38658
  const action = yield* Effect.sync(() => {
38065
38659
  if (session.pid !== expectedPid || !session.process || session.status !== "running") {
38066
- session.pendingProcessEvents = [];
38067
- session.pendingProcessEventIndex = 0;
38068
- session.processEventDrainRunning = false;
38660
+ clearPendingProcessEvents(session);
38069
38661
  return { type: "idle" };
38070
38662
  }
38071
38663
  const nextEvent = session.pendingProcessEvents[session.pendingProcessEventIndex];
38072
38664
  if (!nextEvent) {
38073
- session.pendingProcessEvents = [];
38074
- session.pendingProcessEventIndex = 0;
38075
- session.processEventDrainRunning = false;
38665
+ clearPendingProcessEvents(session);
38076
38666
  return { type: "idle" };
38077
38667
  }
38078
38668
  session.pendingProcessEventIndex += 1;
@@ -38081,6 +38671,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38081
38671
  session.pendingProcessEventIndex = 0;
38082
38672
  }
38083
38673
  if (nextEvent.type === "output") {
38674
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - nextEvent.byteLength);
38084
38675
  const sanitized = sanitizeTerminalHistoryChunk(session.pendingHistoryControlSequence, nextEvent.data);
38085
38676
  session.pendingHistoryControlSequence = sanitized.pendingControlSequence;
38086
38677
  if (sanitized.visibleText.length > 0) session.history = capHistory(`${session.history}${sanitized.visibleText}`, historyLineLimit);
@@ -38100,9 +38691,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38100
38691
  session.hasRunningSubprocess = false;
38101
38692
  session.status = "exited";
38102
38693
  session.pendingHistoryControlSequence = "";
38103
- session.pendingProcessEvents = [];
38104
- session.pendingProcessEventIndex = 0;
38105
- session.processEventDrainRunning = false;
38694
+ clearPendingProcessEvents(session);
38106
38695
  session.exitCode = Number.isInteger(nextEvent.event.exitCode) ? nextEvent.event.exitCode : null;
38107
38696
  session.exitSignal = Number.isInteger(nextEvent.event.signal) ? nextEvent.event.signal : null;
38108
38697
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -38150,9 +38739,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38150
38739
  session.hasRunningSubprocess = false;
38151
38740
  session.status = "exited";
38152
38741
  session.pendingHistoryControlSequence = "";
38153
- session.pendingProcessEvents = [];
38154
- session.pendingProcessEventIndex = 0;
38155
- session.processEventDrainRunning = false;
38742
+ clearPendingProcessEvents(session);
38156
38743
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38157
38744
  return [void 0, state];
38158
38745
  });
@@ -38204,9 +38791,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38204
38791
  session.exitCode = null;
38205
38792
  session.exitSignal = null;
38206
38793
  session.hasRunningSubprocess = false;
38207
- session.pendingProcessEvents = [];
38208
- session.pendingProcessEventIndex = 0;
38209
- session.processEventDrainRunning = false;
38794
+ clearPendingProcessEvents(session);
38210
38795
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38211
38796
  return [void 0, state];
38212
38797
  });
@@ -38220,15 +38805,16 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38220
38805
  const unsubscribeData = ptyProcess.onData((data) => {
38221
38806
  if (!enqueueProcessEvent(session, processPid, {
38222
38807
  type: "output",
38223
- data
38224
- })) return;
38808
+ data,
38809
+ byteLength: approximateTextBytes(data)
38810
+ }, pendingProcessEventLimits)) return;
38225
38811
  runFork(drainProcessEvents(session, processPid));
38226
38812
  });
38227
38813
  const unsubscribeExit = ptyProcess.onExit((event) => {
38228
38814
  if (!enqueueProcessEvent(session, processPid, {
38229
38815
  type: "exit",
38230
38816
  event
38231
- })) return;
38817
+ }, pendingProcessEventLimits)) return;
38232
38818
  runFork(drainProcessEvents(session, processPid));
38233
38819
  });
38234
38820
  yield* modifyManagerState((state) => {
@@ -38260,9 +38846,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38260
38846
  session.unsubscribeData = null;
38261
38847
  session.unsubscribeExit = null;
38262
38848
  session.hasRunningSubprocess = false;
38263
- session.pendingProcessEvents = [];
38264
- session.pendingProcessEventIndex = 0;
38265
- session.processEventDrainRunning = false;
38849
+ clearPendingProcessEvents(session);
38266
38850
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38267
38851
  return [void 0, state];
38268
38852
  });
@@ -38372,6 +38956,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38372
38956
  pendingHistoryControlSequence: "",
38373
38957
  pendingProcessEvents: [],
38374
38958
  pendingProcessEventIndex: 0,
38959
+ pendingProcessOutputBytes: 0,
38375
38960
  processEventDrainRunning: false,
38376
38961
  exitCode: null,
38377
38962
  exitSignal: null,
@@ -38418,18 +39003,14 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38418
39003
  liveSession.runtimeEnv = nextRuntimeEnv;
38419
39004
  liveSession.history = "";
38420
39005
  liveSession.pendingHistoryControlSequence = "";
38421
- liveSession.pendingProcessEvents = [];
38422
- liveSession.pendingProcessEventIndex = 0;
38423
- liveSession.processEventDrainRunning = false;
39006
+ clearPendingProcessEvents(liveSession);
38424
39007
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38425
39008
  } else if (liveSession.status === "exited" || liveSession.status === "error") {
38426
39009
  liveSession.runtimeEnv = nextRuntimeEnv;
38427
39010
  liveSession.worktreePath = input.worktreePath ?? null;
38428
39011
  liveSession.history = "";
38429
39012
  liveSession.pendingHistoryControlSequence = "";
38430
- liveSession.pendingProcessEvents = [];
38431
- liveSession.pendingProcessEventIndex = 0;
38432
- liveSession.processEventDrainRunning = false;
39013
+ clearPendingProcessEvents(liveSession);
38433
39014
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38434
39015
  }
38435
39016
  if (!liveSession.process) {
@@ -38483,9 +39064,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38483
39064
  const session = yield* requireSession(input.threadId, terminalId);
38484
39065
  session.history = "";
38485
39066
  session.pendingHistoryControlSequence = "";
38486
- session.pendingProcessEvents = [];
38487
- session.pendingProcessEventIndex = 0;
38488
- session.processEventDrainRunning = false;
39067
+ clearPendingProcessEvents(session);
38489
39068
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38490
39069
  yield* persistHistory(input.threadId, terminalId, session.history);
38491
39070
  yield* publishEvent({
@@ -38516,6 +39095,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38516
39095
  pendingHistoryControlSequence: "",
38517
39096
  pendingProcessEvents: [],
38518
39097
  pendingProcessEventIndex: 0,
39098
+ pendingProcessOutputBytes: 0,
38519
39099
  processEventDrainRunning: false,
38520
39100
  exitCode: null,
38521
39101
  exitSignal: null,
@@ -38549,9 +39129,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38549
39129
  const rows = input.rows ?? session.rows;
38550
39130
  session.history = "";
38551
39131
  session.pendingHistoryControlSequence = "";
38552
- session.pendingProcessEvents = [];
38553
- session.pendingProcessEventIndex = 0;
38554
- session.processEventDrainRunning = false;
39132
+ clearPendingProcessEvents(session);
38555
39133
  yield* persistHistory(input.threadId, terminalId, session.history);
38556
39134
  yield* startSession(session, {
38557
39135
  threadId: input.threadId,
@@ -38573,6 +39151,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38573
39151
  yield* Effect.forEach(threadSessions, (session) => closeSession(input.threadId, session.terminalId, false), { discard: true });
38574
39152
  if (input.deleteHistory) yield* deleteAllHistoryForThread(input.threadId);
38575
39153
  }));
39154
+ const listSessions = SynchronizedRef.get(managerStateRef).pipe(Effect.map((state) => [...state.sessions.values()].map(snapshot).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))));
38576
39155
  return {
38577
39156
  open,
38578
39157
  write,
@@ -38581,6 +39160,7 @@ const makeTerminalManagerWithOptions = Effect.fn("makeTerminalManagerWithOptions
38581
39160
  restart,
38582
39161
  close,
38583
39162
  listDiagnostics: SynchronizedRef.get(managerStateRef).pipe(Effect.map((state) => [...state.sessions.values()].map(diagnosticsSnapshot).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt)))),
39163
+ listSessions,
38584
39164
  subscribe: (listener) => Effect.sync(() => {
38585
39165
  terminalEventListeners.add(listener);
38586
39166
  return () => {
@@ -39076,7 +39656,7 @@ function isGitRepository(cwd) {
39076
39656
  //#endregion
39077
39657
  //#region src/orchestration/Layers/ProviderRuntimeIngestion.ts
39078
39658
  const providerTurnKey = (threadId, turnId) => `${threadId}:${turnId}`;
39079
- const providerCommandId = (event, tag) => CommandId.make(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`);
39659
+ const providerCommandId = (event, tag, discriminator) => CommandId.make(discriminator === void 0 ? `provider:${event.eventId}:${tag}` : `provider:${event.eventId}:${tag}:${discriminator}`);
39080
39660
  const liveAssistantDeltaBufferKey = (threadId, messageId) => `${threadId}:${messageId}`;
39081
39661
  const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 1e4;
39082
39662
  const TURN_MESSAGE_IDS_BY_TURN_TTL = Duration.minutes(120);
@@ -39150,6 +39730,7 @@ function rawPayloadThreadId(event) {
39150
39730
  return asTrimmedString(asRecord(event.raw?.payload)?.threadId);
39151
39731
  }
39152
39732
  function collabReceiverThreadIdsFromRuntimeEvent(event) {
39733
+ if (event.type === "subagent.started" || event.type === "subagent.updated" || event.type === "subagent.completed") return event.payload.subagent.providerThreadId ? [event.payload.subagent.providerThreadId] : [];
39153
39734
  if (event.type !== "item.started" && event.type !== "item.updated" && event.type !== "item.completed") return [];
39154
39735
  if (event.payload.itemType !== "collab_agent_tool_call") return [];
39155
39736
  const item = asRecord(asRecord(event.payload.data)?.item);
@@ -39207,6 +39788,9 @@ function normalizeRuntimeTurnState(value) {
39207
39788
  default: return "completed";
39208
39789
  }
39209
39790
  }
39791
+ function isTerminalProjectionTurnState(state) {
39792
+ return state === "completed" || state === "error" || state === "interrupted";
39793
+ }
39210
39794
  function orchestrationSessionStatusFromRuntimeState(state) {
39211
39795
  switch (state) {
39212
39796
  case "starting": return "starting";
@@ -39394,6 +39978,53 @@ function runtimeEventToActivities(event) {
39394
39978
  turnId: toTurnId$1(event.turnId) ?? null,
39395
39979
  ...maybeSequence
39396
39980
  }];
39981
+ case "subagent.started": return [{
39982
+ id: event.eventId,
39983
+ createdAt: event.createdAt,
39984
+ tone: "tool",
39985
+ kind: "subagent.started",
39986
+ summary: `${event.payload.subagent.label ?? "Subagent"} started`,
39987
+ payload: {
39988
+ itemType: "collab_agent_tool_call",
39989
+ status: "running",
39990
+ subagent: event.payload.subagent
39991
+ },
39992
+ turnId: toTurnId$1(event.turnId) ?? null,
39993
+ ...maybeSequence
39994
+ }];
39995
+ case "subagent.updated": return [{
39996
+ id: event.eventId,
39997
+ createdAt: event.createdAt,
39998
+ tone: "tool",
39999
+ kind: "subagent.updated",
40000
+ summary: event.payload.summary ?? `${event.payload.subagent.label ?? "Subagent"} updated`,
40001
+ payload: {
40002
+ itemType: "collab_agent_tool_call",
40003
+ ...event.payload.status ? { status: event.payload.status } : {},
40004
+ subagent: event.payload.subagent,
40005
+ ...event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {},
40006
+ ...event.payload.summary ? { summary: truncateDetail(event.payload.summary) } : {},
40007
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40008
+ },
40009
+ turnId: toTurnId$1(event.turnId) ?? null,
40010
+ ...maybeSequence
40011
+ }];
40012
+ case "subagent.completed": return [{
40013
+ id: event.eventId,
40014
+ createdAt: event.createdAt,
40015
+ tone: event.payload.status === "failed" ? "error" : "tool",
40016
+ kind: "subagent.completed",
40017
+ summary: event.payload.status === "failed" ? `${event.payload.subagent.label ?? "Subagent"} failed` : event.payload.status === "stopped" ? `${event.payload.subagent.label ?? "Subagent"} stopped` : `${event.payload.subagent.label ?? "Subagent"} completed`,
40018
+ payload: {
40019
+ itemType: "collab_agent_tool_call",
40020
+ status: event.payload.status,
40021
+ subagent: event.payload.subagent,
40022
+ ...event.payload.summary ? { detail: truncateDetail(event.payload.summary) } : {},
40023
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40024
+ },
40025
+ turnId: toTurnId$1(event.turnId) ?? null,
40026
+ ...maybeSequence
40027
+ }];
39397
40028
  case "thread.state.changed":
39398
40029
  if (event.payload.state !== "compacted") return [];
39399
40030
  return [{
@@ -39586,7 +40217,7 @@ const make$3 = Effect.gen(function* () {
39586
40217
  }
39587
40218
  yield* orchestrationEngine.dispatch({
39588
40219
  type: "thread.message.assistant.delta",
39589
- commandId: providerCommandId(buffer.event, input.commandTag),
40220
+ commandId: providerCommandId(buffer.event, input.commandTag, String(buffer.messageId)),
39590
40221
  threadId: buffer.threadId,
39591
40222
  messageId: buffer.messageId,
39592
40223
  delta: buffer.text,
@@ -39665,9 +40296,10 @@ const make$3 = Effect.gen(function* () {
39665
40296
  };
39666
40297
  const upsertSubagentMessageActivity = (input) => Effect.gen(function* () {
39667
40298
  const sessionSequence = input.event.sessionSequence;
40299
+ const commandKey = `${input.providerThreadId}:${input.providerItemId}`;
39668
40300
  yield* orchestrationEngine.dispatch({
39669
40301
  type: "thread.activity.append",
39670
- commandId: providerCommandId(input.event, "subagent-message-upsert"),
40302
+ commandId: providerCommandId(input.event, "subagent-message-upsert", commandKey),
39671
40303
  threadId: input.threadId,
39672
40304
  activity: {
39673
40305
  id: subagentMessageActivityId({
@@ -39685,6 +40317,7 @@ const make$3 = Effect.gen(function* () {
39685
40317
  streaming: input.streaming,
39686
40318
  providerThreadId: input.providerThreadId,
39687
40319
  providerItemId: input.providerItemId,
40320
+ ...input.subagentId ? { subagentId: input.subagentId } : {},
39688
40321
  ...input.event.providerRefs ? { providerRefs: input.event.providerRefs } : {}
39689
40322
  },
39690
40323
  turnId: toTurnId$1(input.event.turnId) ?? null,
@@ -39724,6 +40357,16 @@ const make$3 = Effect.gen(function* () {
39724
40357
  streaming: false
39725
40358
  });
39726
40359
  });
40360
+ const bufferedSubagentProviderItemIds = (input) => {
40361
+ const prefix = subagentMessageBufferKey({
40362
+ threadId: input.threadId,
40363
+ providerThreadId: input.providerThreadId,
40364
+ providerItemId: ""
40365
+ });
40366
+ const providerItemIds = /* @__PURE__ */ new Set();
40367
+ for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) providerItemIds.add(key.slice(prefix.length));
40368
+ return [...providerItemIds];
40369
+ };
39727
40370
  const clearSubagentMessageBuffersForThread = (threadId) => Effect.sync(() => {
39728
40371
  const prefix = `${threadId}:`;
39729
40372
  for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) subagentMessageTextByKey.delete(key);
@@ -39757,7 +40400,7 @@ const make$3 = Effect.gen(function* () {
39757
40400
  if (!hasRenderableAssistantText(bufferedText)) return false;
39758
40401
  yield* orchestrationEngine.dispatch({
39759
40402
  type: "thread.message.assistant.delta",
39760
- commandId: providerCommandId(input.event, input.commandTag),
40403
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39761
40404
  threadId: input.threadId,
39762
40405
  messageId: input.messageId,
39763
40406
  delta: bufferedText,
@@ -39785,7 +40428,7 @@ const make$3 = Effect.gen(function* () {
39785
40428
  const hasRenderableText = hasRenderableAssistantText(text);
39786
40429
  if (hasRenderableText) yield* orchestrationEngine.dispatch({
39787
40430
  type: "thread.message.assistant.delta",
39788
- commandId: providerCommandId(input.event, input.finalDeltaCommandTag),
40431
+ commandId: providerCommandId(input.event, input.finalDeltaCommandTag, String(input.messageId)),
39789
40432
  threadId: input.threadId,
39790
40433
  messageId: input.messageId,
39791
40434
  delta: text,
@@ -39794,7 +40437,7 @@ const make$3 = Effect.gen(function* () {
39794
40437
  });
39795
40438
  if (input.hasProjectedMessage || hasRenderableText) yield* orchestrationEngine.dispatch({
39796
40439
  type: "thread.message.assistant.complete",
39797
- commandId: providerCommandId(input.event, input.commandTag),
40440
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39798
40441
  threadId: input.threadId,
39799
40442
  messageId: input.messageId,
39800
40443
  ...input.turnId ? { turnId: input.turnId } : {},
@@ -39828,7 +40471,7 @@ const make$3 = Effect.gen(function* () {
39828
40471
  const existingPlan = findProposedPlanById(input.threadProposedPlans, input.planId);
39829
40472
  yield* orchestrationEngine.dispatch({
39830
40473
  type: "thread.proposed-plan.upsert",
39831
- commandId: providerCommandId(input.event, "proposed-plan-upsert"),
40474
+ commandId: providerCommandId(input.event, "proposed-plan-upsert", input.planId),
39832
40475
  threadId: input.threadId,
39833
40476
  proposedPlan: {
39834
40477
  id: input.planId,
@@ -39895,13 +40538,13 @@ const make$3 = Effect.gen(function* () {
39895
40538
  if (!sameId$1(yield* getExpectedProviderTurnIdForThread(threadId), eventTurnId)) return null;
39896
40539
  return yield* getSourceProposedPlanReferenceForPendingTurnStart(threadId);
39897
40540
  });
39898
- const markSourceProposedPlanImplemented = Effect.fn("markSourceProposedPlanImplemented")(function* (sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
40541
+ const markSourceProposedPlanImplemented = Effect.fn("markSourceProposedPlanImplemented")(function* (event, sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
39899
40542
  const sourceThread = yield* resolveThreadDetail(sourceThreadId);
39900
40543
  const sourcePlan = sourceThread?.proposedPlans.find((entry) => entry.id === sourcePlanId);
39901
40544
  if (!sourceThread || !sourcePlan || sourcePlan.implementedAt !== null) return;
39902
40545
  yield* orchestrationEngine.dispatch({
39903
40546
  type: "thread.proposed-plan.upsert",
39904
- commandId: CommandId.make(`provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`),
40547
+ commandId: providerCommandId(event, "source-proposed-plan-implemented", `${sourceThreadId}:${sourcePlanId}:${implementationThreadId}`),
39905
40548
  threadId: sourceThread.id,
39906
40549
  proposedPlan: {
39907
40550
  ...sourcePlan,
@@ -39926,6 +40569,12 @@ const make$3 = Effect.gen(function* () {
39926
40569
  const activeTurnId = thread.session?.activeTurnId ?? null;
39927
40570
  const conflictsWithActiveTurn = activeTurnId !== null && eventTurnId !== void 0 && !sameId$1(activeTurnId, eventTurnId);
39928
40571
  const missingTurnForActiveTurn = activeTurnId !== null && eventTurnId === void 0;
40572
+ const existingEventTurn = event.type === "turn.started" && eventTurnId !== void 0 ? yield* projectionTurnRepository.getByTurnId({
40573
+ threadId: thread.id,
40574
+ turnId: eventTurnId
40575
+ }) : Option.none();
40576
+ const eventTurnAlreadyTerminal = Option.isSome(existingEventTurn) && isTerminalProjectionTurnState(existingEventTurn.value.state);
40577
+ const eventTurnAlreadyProjectedWithoutActiveSession = activeTurnId === null && Option.isSome(existingEventTurn);
39929
40578
  if (!isAssistantTextDeltaEvent(event)) yield* flushLiveAssistantDeltaBuffersSafely({ commandTag: "assistant-delta-flush-before-runtime-event" });
39930
40579
  yield* rememberSubagentProviderThreadIds(thread.id, event);
39931
40580
  const eventProviderThreadId = rawPayloadThreadId(event);
@@ -39936,8 +40585,9 @@ const make$3 = Effect.gen(function* () {
39936
40585
  case "session.exited": return true;
39937
40586
  case "session.started":
39938
40587
  case "thread.started": return true;
39939
- case "turn.started": return !conflictsWithActiveTurn;
40588
+ case "turn.started": return !conflictsWithActiveTurn && !eventTurnAlreadyTerminal && !eventTurnAlreadyProjectedWithoutActiveSession;
39940
40589
  case "turn.completed":
40590
+ case "turn.aborted":
39941
40591
  if (conflictsWithActiveTurn || missingTurnForActiveTurn) return false;
39942
40592
  if (activeTurnId !== null && eventTurnId !== void 0) return sameId$1(activeTurnId, eventTurnId);
39943
40593
  return true;
@@ -39945,21 +40595,22 @@ const make$3 = Effect.gen(function* () {
39945
40595
  }
39946
40596
  })();
39947
40597
  const acceptedTurnStartedSourcePlan = event.type === "turn.started" && shouldApplyThreadLifecycle ? yield* getSourceProposedPlanReferenceForAcceptedTurnStart(thread.id, eventTurnId) : null;
39948
- if (event.type === "session.started" || event.type === "session.state.changed" || event.type === "session.exited" || event.type === "thread.started" || event.type === "turn.started" || event.type === "turn.completed") {
39949
- const nextActiveTurnId = event.type === "turn.started" ? eventTurnId ?? null : event.type === "turn.completed" || event.type === "session.exited" ? null : activeTurnId;
40598
+ if (event.type === "session.started" || event.type === "session.state.changed" || event.type === "session.exited" || event.type === "thread.started" || event.type === "turn.started" || event.type === "turn.completed" || event.type === "turn.aborted") {
40599
+ const nextActiveTurnId = event.type === "turn.started" ? eventTurnId ?? null : event.type === "turn.completed" || event.type === "turn.aborted" || event.type === "session.exited" ? null : activeTurnId;
39950
40600
  const status = (() => {
39951
40601
  switch (event.type) {
39952
40602
  case "session.state.changed": return orchestrationSessionStatusFromRuntimeState(event.payload.state);
39953
40603
  case "turn.started": return "running";
39954
40604
  case "session.exited": return "stopped";
39955
40605
  case "turn.completed": return normalizeRuntimeTurnState(event.payload.state) === "failed" ? "error" : "ready";
40606
+ case "turn.aborted": return "ready";
39956
40607
  case "session.started":
39957
40608
  case "thread.started": return activeTurnId !== null ? "running" : "ready";
39958
40609
  }
39959
40610
  })();
39960
40611
  const lastError = event.type === "session.state.changed" && event.payload.state === "error" ? event.payload.reason ?? thread.session?.lastError ?? "Provider session error" : event.type === "turn.completed" && normalizeRuntimeTurnState(event.payload.state) === "failed" ? event.payload.errorMessage ?? thread.session?.lastError ?? "Turn failed" : status === "ready" ? null : thread.session?.lastError ?? null;
39961
40612
  if (shouldApplyThreadLifecycle) {
39962
- if (event.type === "turn.started" && acceptedTurnStartedSourcePlan !== null) yield* markSourceProposedPlanImplemented(acceptedTurnStartedSourcePlan.sourceThreadId, acceptedTurnStartedSourcePlan.sourcePlanId, thread.id, now).pipe(Effect.catchCause((cause) => Effect.logWarning("provider runtime ingestion failed to mark source proposed plan", {
40613
+ if (event.type === "turn.started" && acceptedTurnStartedSourcePlan !== null) yield* markSourceProposedPlanImplemented(event, acceptedTurnStartedSourcePlan.sourceThreadId, acceptedTurnStartedSourcePlan.sourcePlanId, thread.id, now).pipe(Effect.catchCause((cause) => Effect.logWarning("provider runtime ingestion failed to mark source proposed plan", {
39963
40614
  eventId: event.eventId,
39964
40615
  eventType: event.type,
39965
40616
  cause: Cause.pretty(cause)
@@ -39981,11 +40632,30 @@ const make$3 = Effect.gen(function* () {
39981
40632
  },
39982
40633
  createdAt: now
39983
40634
  });
40635
+ if (event.type === "turn.aborted" && eventTurnId !== void 0) yield* orchestrationEngine.dispatch({
40636
+ type: "thread.turn.interrupt",
40637
+ commandId: providerCommandId(event, "thread-turn-interrupt", String(eventTurnId)),
40638
+ threadId: thread.id,
40639
+ turnId: eventTurnId,
40640
+ createdAt: now
40641
+ });
39984
40642
  }
39985
40643
  }
39986
40644
  const assistantDelta = isAssistantTextDeltaEvent(event) ? event.payload.delta : void 0;
40645
+ const subagentDelta = event.type === "subagent.message.delta" ? event.payload.delta : void 0;
39987
40646
  const proposedPlanDelta = event.type === "turn.proposed.delta" ? event.payload.delta : void 0;
39988
- if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
40647
+ if (event.type === "subagent.message.delta" && subagentDelta && subagentDelta.length > 0) {
40648
+ const subagentPayload = event.payload;
40649
+ yield* appendSubagentMessageDelta({
40650
+ event,
40651
+ threadId: thread.id,
40652
+ subagentId: String(subagentPayload.subagentId),
40653
+ providerThreadId: subagentPayload.providerThreadId ?? String(subagentPayload.subagentId),
40654
+ providerItemId: subagentPayload.providerMessageId ?? String(subagentPayload.subagentId),
40655
+ delta: subagentDelta,
40656
+ createdAt: now
40657
+ });
40658
+ } else if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
39989
40659
  event,
39990
40660
  threadId: thread.id,
39991
40661
  providerThreadId: eventProviderThreadId,
@@ -40005,7 +40675,7 @@ const make$3 = Effect.gen(function* () {
40005
40675
  const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, assistantDelta);
40006
40676
  if (spillChunk.length > 0) yield* orchestrationEngine.dispatch({
40007
40677
  type: "thread.message.assistant.delta",
40008
- commandId: providerCommandId(event, "assistant-delta-buffer-spill"),
40678
+ commandId: providerCommandId(event, "assistant-delta-buffer-spill", String(assistantMessageId)),
40009
40679
  threadId: thread.id,
40010
40680
  messageId: assistantMessageId,
40011
40681
  delta: spillChunk,
@@ -40090,6 +40760,26 @@ const make$3 = Effect.gen(function* () {
40090
40760
  ...subagentMessageCompletion.fallbackText !== void 0 ? { fallbackText: subagentMessageCompletion.fallbackText } : {},
40091
40761
  createdAt: now
40092
40762
  });
40763
+ if (event.type === "subagent.completed") {
40764
+ const subagentId = String(event.payload.subagent.subagentId);
40765
+ const providerThreadId = event.payload.subagent.providerThreadId ?? subagentId;
40766
+ const providerItemIds = bufferedSubagentProviderItemIds({
40767
+ threadId: thread.id,
40768
+ providerThreadId
40769
+ });
40770
+ yield* Effect.forEach(providerItemIds.length > 0 ? providerItemIds : [subagentId], (providerItemId) => completeSubagentMessage({
40771
+ event,
40772
+ threadId: thread.id,
40773
+ subagentId,
40774
+ providerThreadId,
40775
+ providerItemId,
40776
+ ...event.payload.summary !== void 0 ? { fallbackText: event.payload.summary } : {},
40777
+ createdAt: now
40778
+ }), {
40779
+ concurrency: 1,
40780
+ discard: true
40781
+ });
40782
+ }
40093
40783
  if (proposedPlanCompletion) {
40094
40784
  const detailedThread = yield* getLoadedThreadDetail();
40095
40785
  yield* finalizeBufferedProposedPlan({
@@ -40102,7 +40792,7 @@ const make$3 = Effect.gen(function* () {
40102
40792
  updatedAt: now
40103
40793
  });
40104
40794
  }
40105
- if (event.type === "turn.completed") {
40795
+ if (event.type === "turn.completed" || event.type === "turn.aborted") {
40106
40796
  const detailedThread = yield* getLoadedThreadDetail();
40107
40797
  const messages = detailedThread?.messages ?? [];
40108
40798
  const proposedPlans = detailedThread?.proposedPlans ?? [];
@@ -40172,7 +40862,7 @@ const make$3 = Effect.gen(function* () {
40172
40862
  const assistantMessageId = MessageId.make(`assistant:${event.itemId ?? event.turnId ?? event.eventId}`);
40173
40863
  yield* orchestrationEngine.dispatch({
40174
40864
  type: "thread.turn.diff.complete",
40175
- commandId: providerCommandId(event, "thread-turn-diff-complete"),
40865
+ commandId: providerCommandId(event, "thread-turn-diff-complete", String(turnId)),
40176
40866
  threadId: thread.id,
40177
40867
  turnId,
40178
40868
  completedAt: now,
@@ -40188,7 +40878,7 @@ const make$3 = Effect.gen(function* () {
40188
40878
  const activities = runtimeEventToActivities(event);
40189
40879
  yield* Effect.forEach(activities, (activity) => orchestrationEngine.dispatch({
40190
40880
  type: "thread.activity.append",
40191
- commandId: providerCommandId(event, "thread-activity-append"),
40881
+ commandId: providerCommandId(event, "thread-activity-append", String(activity.id)),
40192
40882
  threadId: thread.id,
40193
40883
  activity,
40194
40884
  createdAt: activity.createdAt
@@ -40252,6 +40942,7 @@ function mapProviderSessionStatusToOrchestrationStatus(status) {
40252
40942
  }
40253
40943
  const turnStartKeyForEvent = (event) => event.commandId !== null ? `command:${event.commandId}` : `event:${event.eventId}`;
40254
40944
  const serverCommandId$1 = (tag) => CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
40945
+ const isProviderOriginatedCommandId = (commandId) => commandId !== null && String(commandId).startsWith("provider:");
40255
40946
  const HANDLED_TURN_START_KEY_MAX = 1e4;
40256
40947
  const HANDLED_TURN_START_KEY_TTL = Duration.minutes(30);
40257
40948
  const DEFAULT_RUNTIME_MODE = "full-access";
@@ -40603,6 +41294,17 @@ const make$2 = Effect.gen(function* () {
40603
41294
  if (yield* hasHandledTurnStartRecently(turnStartKeyForEvent(event))) return;
40604
41295
  const thread = yield* resolveThread(event.payload.threadId);
40605
41296
  if (!thread) return;
41297
+ if (thread.session?.status === "running" && thread.session.activeTurnId !== null) {
41298
+ yield* appendProviderFailureActivity({
41299
+ threadId: event.payload.threadId,
41300
+ kind: "provider.turn.start.failed",
41301
+ summary: "Provider turn start rejected",
41302
+ detail: `Thread already has active turn '${thread.session.activeTurnId}'. Queuing overlapping turns is not supported; wait for the active turn to finish before starting another.`,
41303
+ turnId: null,
41304
+ createdAt: event.payload.createdAt
41305
+ });
41306
+ return;
41307
+ }
40606
41308
  const message = thread.messages.find((entry) => entry.id === event.payload.messageId);
40607
41309
  if (!message || message.role !== "user") {
40608
41310
  yield* appendProviderFailureActivity({
@@ -40673,6 +41375,7 @@ const make$2 = Effect.gen(function* () {
40673
41375
  yield* providerService.sendTurn(sendTurnRequest.value).pipe(Effect.catchCause(recoverTurnStartFailure), Effect.forkScoped);
40674
41376
  });
40675
41377
  const processTurnInterruptRequested = Effect.fn("processTurnInterruptRequested")(function* (event) {
41378
+ if (isProviderOriginatedCommandId(event.commandId)) return;
40676
41379
  const thread = yield* resolveThread(event.payload.threadId);
40677
41380
  if (!thread) return;
40678
41381
  if (!(thread.session && thread.session.status !== "stopped")) return yield* appendProviderFailureActivity({
@@ -46969,6 +47672,10 @@ const decideOrchestrationCommand = Effect.fn("decideOrchestrationCommand")(funct
46969
47672
  command,
46970
47673
  threadId: command.threadId
46971
47674
  });
47675
+ if (targetThread.session?.status === "running" && targetThread.session.activeTurnId !== null) return yield* new OrchestrationCommandInvariantError({
47676
+ commandType: command.type,
47677
+ detail: `Thread '${command.threadId}' already has active turn '${targetThread.session.activeTurnId}' and cannot start another turn until it finishes.`
47678
+ });
46972
47679
  const sourceProposedPlan = command.sourceProposedPlan;
46973
47680
  const sourceThread = sourceProposedPlan ? yield* requireThread({
46974
47681
  readModel,
@@ -49816,8 +50523,20 @@ const WorkspaceRootLookupInput = Schema.Struct({ workspaceRoot: Schema.String })
49816
50523
  const ProjectIdLookupInput = Schema.Struct({ projectId: ProjectId });
49817
50524
  const ThreadIdLookupInput = Schema.Struct({ threadId: ThreadId });
49818
50525
  const WorktreeIdLookupInput = Schema.Struct({ worktreeId: WorktreeId });
50526
+ const ThreadMessageSearchQueryInput = Schema.Struct({
50527
+ likePattern: Schema.String,
50528
+ projectId: Schema.NullOr(ProjectId),
50529
+ limit: NonNegativeInt
50530
+ });
49819
50531
  const ProjectionProjectLookupRowSchema = ProjectionProjectDbRowSchema;
49820
50532
  const ProjectionThreadIdLookupRowSchema = Schema.Struct({ threadId: ThreadId });
50533
+ const ProjectionThreadMessageSearchRowSchema = Schema.Struct({
50534
+ threadId: ThreadId,
50535
+ messageId: MessageId,
50536
+ text: Schema.String,
50537
+ createdAt: IsoDateTime,
50538
+ updatedAt: IsoDateTime
50539
+ });
49821
50540
  const ProjectionThreadCheckpointContextThreadRowSchema = Schema.Struct({
49822
50541
  threadId: ThreadId,
49823
50542
  projectId: ProjectId,
@@ -49849,6 +50568,20 @@ function computeSnapshotSequence(stateRows) {
49849
50568
  }
49850
50569
  return Number.isFinite(minSequence) ? minSequence : 0;
49851
50570
  }
50571
+ function escapeSqlLikePattern(value) {
50572
+ return value.replace(/[\\%_]/g, (match) => `\\${match}`);
50573
+ }
50574
+ function buildMessageSearchSnippet(input) {
50575
+ const maxLength = input.maxLength ?? 180;
50576
+ const normalizedQuery = input.query.trim().toLowerCase();
50577
+ const normalizedText = input.text.toLowerCase();
50578
+ const matchIndex = normalizedQuery.length > 0 ? normalizedText.indexOf(normalizedQuery) : -1;
50579
+ const contextLength = Math.floor(maxLength / 2);
50580
+ const start = matchIndex <= contextLength ? 0 : Math.max(0, matchIndex - Math.floor(contextLength / 2));
50581
+ const end = Math.min(input.text.length, start + maxLength);
50582
+ const snippet = input.text.slice(start, end).replace(/\s+/g, " ").trim();
50583
+ return `${start > 0 ? "..." : ""}${snippet}${end < input.text.length ? "..." : ""}`;
50584
+ }
49852
50585
  function mapLatestTurn(row) {
49853
50586
  return {
49854
50587
  turnId: row.turnId,
@@ -50318,6 +51051,30 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
50318
51051
  FROM projection_thread_messages
50319
51052
  WHERE thread_id = ${threadId}
50320
51053
  ORDER BY created_at ASC, message_id ASC
51054
+ `
51055
+ });
51056
+ const searchThreadMessageRows = SqlSchema.findAll({
51057
+ Request: ThreadMessageSearchQueryInput,
51058
+ Result: ProjectionThreadMessageSearchRowSchema,
51059
+ execute: ({ likePattern, projectId, limit }) => sql`
51060
+ SELECT
51061
+ messages.thread_id AS "threadId",
51062
+ messages.message_id AS "messageId",
51063
+ messages.text,
51064
+ messages.created_at AS "createdAt",
51065
+ messages.updated_at AS "updatedAt"
51066
+ FROM projection_thread_messages AS messages
51067
+ INNER JOIN projection_threads AS threads
51068
+ ON threads.thread_id = messages.thread_id
51069
+ INNER JOIN projection_projects AS projects
51070
+ ON projects.project_id = threads.project_id
51071
+ WHERE threads.deleted_at IS NULL
51072
+ AND projects.deleted_at IS NULL
51073
+ AND messages.role IN ('user', 'assistant')
51074
+ AND (${projectId} IS NULL OR threads.project_id = ${projectId})
51075
+ AND lower(messages.text) LIKE ${likePattern} ESCAPE '\\'
51076
+ ORDER BY messages.created_at DESC, messages.message_id DESC
51077
+ LIMIT ${limit}
50321
51078
  `
50322
51079
  });
50323
51080
  const listThreadProposedPlanRowsByThread = SqlSchema.findAll({
@@ -50906,6 +51663,24 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
50906
51663
  };
50907
51664
  return Option.some(yield* decodeThread(thread).pipe(Effect.mapError(toPersistenceDecodeError("ProjectionSnapshotQuery.getThreadDetailById:decodeThread"))));
50908
51665
  });
51666
+ const searchThreadMessages = (input) => {
51667
+ const query = input.query.trim();
51668
+ if (query.length === 0) return Effect.succeed([]);
51669
+ const limit = Math.min(Math.max(1, input.limit), 50);
51670
+ return searchThreadMessageRows({
51671
+ likePattern: `%${escapeSqlLikePattern(query.toLowerCase())}%`,
51672
+ projectId: input.projectId ?? null,
51673
+ limit
51674
+ }).pipe(Effect.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.searchThreadMessages:query", "ProjectionSnapshotQuery.searchThreadMessages:decodeRows")), Effect.map((rows) => rows.map((row) => ({
51675
+ threadId: row.threadId,
51676
+ messageId: row.messageId,
51677
+ snippet: buildMessageSearchSnippet({
51678
+ text: row.text,
51679
+ query
51680
+ }),
51681
+ timestamp: row.createdAt
51682
+ }))));
51683
+ };
50909
51684
  return {
50910
51685
  getCommandReadModel,
50911
51686
  getSnapshot,
@@ -50918,7 +51693,8 @@ const makeProjectionSnapshotQuery = Effect.gen(function* () {
50918
51693
  getThreadCheckpointContext,
50919
51694
  getThreadShellById,
50920
51695
  getWorktreeShellById,
50921
- getThreadDetailById
51696
+ getThreadDetailById,
51697
+ searchThreadMessages
50922
51698
  };
50923
51699
  });
50924
51700
  const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery);
@@ -50926,7 +51702,7 @@ const OrchestrationProjectionSnapshotQueryLive = Layer.effect(ProjectionSnapshot
50926
51702
  //#region src/orchestration/runtimeLayer.ts
50927
51703
  const OrchestrationEventInfrastructureLayerLive = Layer.mergeAll(OrchestrationEventStoreLive, OrchestrationCommandReceiptRepositoryLive);
50928
51704
  const OrchestrationProjectionPipelineLayerLive = OrchestrationProjectionPipelineLive.pipe(Layer.provide(OrchestrationEventStoreLive));
50929
- const OrchestrationInfrastructureLayerLive = Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
51705
+ const OrchestrationInfrastructureLayerLive = Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, StatisticsQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
50930
51706
  const OrchestrationLayerLive = Layer.mergeAll(OrchestrationInfrastructureLayerLive, OrchestrationEngineLive.pipe(Layer.provide(OrchestrationInfrastructureLayerLive)));
50931
51707
  //#endregion
50932
51708
  //#region src/serverRuntimeState.ts
@@ -51702,4 +52478,4 @@ const CliRuntimeLayer = Layer$1.mergeAll(NodeServices.layer, NetService.layer);
51702
52478
  const program = Command.run(cli, { version }).pipe(Effect$1.scoped, Effect$1.provide(CliRuntimeLayer));
51703
52479
  NodeRuntime.runMain(program);
51704
52480
  //#endregion
51705
- export { ServerRequest__CommandExecutionRequestApprovalParams as $, makeProviderMaintenanceCapabilities as $t, nonEmptyTrimmed as A, V2ThreadStatusChangedNotification as At, codexContinuationIdentity as B, GitLabCli as Bt, DEFAULT_TIMEOUT_MS as C, V2ThreadNameUpdatedNotification as Ct, collectStreamAsString as D, V2ThreadRealtimeOutputAudioDeltaNotification as Dt, buildServerProvider as E, V2ThreadRealtimeItemAddedNotification as Et, ProviderAdapterRequestError as F, V2TurnStartParams as Ft, layerChildProcess as G, withoutStatusCheckRollupJsonField as Gt, resolveCodexHomeLayout as H, GitHubCli as Ht, ProviderAdapterSessionClosedError as I, V2TurnStartParams__CollaborationMode as It, CodexAppServerRequestError as J, BitbucketApi as Jt, CodexAppServerProcessExitedError as K, decodeGitHubPullRequestListJson as Kt, ProviderAdapterSessionNotFoundError as L, V2TurnStartResponse as Lt, providerModelsFromSettings as M, V2TurnCompletedNotification as Mt, spawnAndCollect as N, V2TurnDiffUpdatedNotification as Nt, detailFromResult as O, V2ThreadRealtimeStartedNotification as Ot, ProviderAdapterProcessError as P, V2TurnPlanUpdatedNotification as Pt, ServerRequest__ApplyPatchApprovalParams as Q, makePackageManagedProviderMaintenanceResolver as Qt, ProviderAdapterValidationError as R, V2WindowsSandboxSetupCompletedNotification as Rt, makePendingCodexProvider as S, V2ServerRequestResolvedNotification as St, buildSelectOptionDescriptor as T, V2ThreadRealtimeErrorNotification as Tt, expandHomePath$3 as U, formatGitHubJsonFields as Ut, materializeCodexShadowHome as V, GITHUB_PULL_REQUEST_LIST_JSON_FIELDS as Vt, CodexAppServerClient as W, isStatusCheckRollupAccessError as Wt, CodexAppServerTransportError as X, enrichProviderSnapshotWithVersionAdvisory as Xt, CodexAppServerSpawnError as Y, AzureDevOpsCli as Yt, SERVER_NOTIFICATION_METHODS as Z, makeManualOnlyProviderMaintenanceCapabilities as Zt, buildTokenReductionInstructions as _, V2McpToolCallProgressNotification as _t, OpenCodeRuntime as a, resolveCommandPath as an, ToolRequestUserInputResponse as at, buildCodexInitializeParams as b, V2ReasoningSummaryTextDeltaNotification as bt, openCodeQuestionId as c, getModelSelectionBooleanOptionValue as cn, V2AgentMessageDeltaNotification as ct, runOpenCodeSdk as d, getProviderOptionCurrentValue as dn, V2DeprecationNoticeNotification as dt, makeStaticProviderMaintenanceResolver as en, ServerRequest__DynamicToolCallParams as et, toOpenCodeFileParts as f, getProviderOptionDescriptors as fn, V2ErrorNotification as ft, makeEventNdjsonLogger as g, V2McpServerOauthLoginCompletedNotification as gt, ProviderEventLoggers as h, resolvePromptInjectedEffort as hn, V2ItemStartedNotification as ht, PtySpawnError as i, resolveAttachmentPath as in, ToolRequestUserInputParams as it, parseGenericCliVersion as j, V2ThreadTokenUsageUpdatedNotification as jt, isCommandMissingCause as k, V2ThreadStartedNotification as kt, openCodeRuntimeErrorDetail as l, getModelSelectionStringOptionValue as ln, V2CommandExecutionOutputDeltaNotification as lt, toOpenCodeQuestionAnswers as m, normalizeModelSlug as mn, V2ItemCompletedNotification as mt, ignoreProviderBackgroundCause as n, resolveProviderMaintenanceCapabilitiesEffect as nn, ServerRequest__FileChangeRequestApprovalParams as nt, OpenCodeRuntimeError as o, applyClaudePromptEffortPrefix as on, V2AccountRateLimitsUpdatedNotification as ot, toOpenCodePermissionReply as p, getProviderOptionStringSelectionValue as pn, V2FileChangeOutputDeltaNotification as pt, CodexAppServerProtocolParseError as q, ForgejoApi as qt, PtyAdapter as r, compareCliVersions as rn, ServerRequest__ToolRequestUserInputParams as rt, buildOpenCodePermissionRules as s, createModelCapabilities as sn, V2AccountUpdatedNotification as st, makeServerQueueMetrics as t, normalizeCommandPath as tn, ServerRequest__ExecCommandApprovalParams as tt, parseOpenCodeModelSlug as u, getProviderOptionBooleanSelectionValue as un, V2ConfigWarningNotification as ut, checkRtkAvailability as v, V2ModelReroutedNotification as vt, buildBooleanOptionDescriptor as w, V2ThreadRealtimeClosedNotification as wt, checkCodexProviderStatus as x, V2ReasoningTextDeltaNotification as xt, mergeProviderInstanceEnvironment as y, V2PlanDeltaNotification as yt, ProviderDriverError as z, V2WindowsWorldWritableWarningNotification as zt };
52481
+ export { ServerRequest__CommandExecutionRequestApprovalParams as $, makeProviderMaintenanceCapabilities as $t, nonEmptyTrimmed as A, V2ThreadStatusChangedNotification as At, codexContinuationIdentity as B, GitLabCli as Bt, DEFAULT_TIMEOUT_MS as C, V2ThreadNameUpdatedNotification as Ct, collectStreamAsString as D, V2ThreadRealtimeOutputAudioDeltaNotification as Dt, buildServerProvider as E, V2ThreadRealtimeItemAddedNotification as Et, ProviderAdapterRequestError as F, V2TurnStartParams as Ft, layerChildProcess as G, withoutStatusCheckRollupJsonField as Gt, resolveCodexHomeLayout as H, GitHubCli as Ht, ProviderAdapterSessionClosedError as I, V2TurnStartParams__CollaborationMode as It, CodexAppServerRequestError as J, BitbucketApi as Jt, CodexAppServerProcessExitedError as K, decodeGitHubPullRequestListJson as Kt, ProviderAdapterSessionNotFoundError as L, V2TurnStartResponse as Lt, providerModelsFromSettings as M, V2TurnCompletedNotification as Mt, spawnAndCollect as N, V2TurnDiffUpdatedNotification as Nt, detailFromResult as O, V2ThreadRealtimeStartedNotification as Ot, ProviderAdapterProcessError as P, V2TurnPlanUpdatedNotification as Pt, ServerRequest__ApplyPatchApprovalParams as Q, makePackageManagedProviderMaintenanceResolver as Qt, ProviderAdapterValidationError as R, V2WindowsSandboxSetupCompletedNotification as Rt, makePendingCodexProvider as S, V2ServerRequestResolvedNotification as St, buildSelectOptionDescriptor as T, V2ThreadRealtimeErrorNotification as Tt, expandHomePath$3 as U, formatGitHubJsonFields as Ut, materializeCodexShadowHome as V, GITHUB_PULL_REQUEST_LIST_JSON_FIELDS as Vt, CodexAppServerClient as W, isStatusCheckRollupAccessError as Wt, CodexAppServerTransportError as X, enrichProviderSnapshotWithVersionAdvisory as Xt, CodexAppServerSpawnError as Y, AzureDevOpsCli as Yt, SERVER_NOTIFICATION_METHODS as Z, makeManualOnlyProviderMaintenanceCapabilities as Zt, buildTokenReductionInstructions as _, V2McpToolCallProgressNotification as _t, OpenCodeRuntime as a, resolveAttachmentPath as an, ToolRequestUserInputResponse as at, buildCodexInitializeParams as b, V2ReasoningSummaryTextDeltaNotification as bt, openCodeQuestionId as c, createModelCapabilities as cn, V2AgentMessageDeltaNotification as ct, runOpenCodeSdk as d, getProviderOptionBooleanSelectionValue as dn, V2DeprecationNoticeNotification as dt, makeStaticProviderMaintenanceResolver as en, ServerRequest__DynamicToolCallParams as et, toOpenCodeFileParts as f, getProviderOptionCurrentValue as fn, V2ErrorNotification as ft, makeEventNdjsonLogger as g, resolvePromptInjectedEffort as gn, V2McpServerOauthLoginCompletedNotification as gt, ProviderEventLoggers as h, normalizeModelSlug as hn, V2ItemStartedNotification as ht, PtySpawnError as i, ServerSettingsService as in, ToolRequestUserInputParams as it, parseGenericCliVersion as j, V2ThreadTokenUsageUpdatedNotification as jt, isCommandMissingCause as k, V2ThreadStartedNotification as kt, openCodeRuntimeErrorDetail as l, getModelSelectionBooleanOptionValue as ln, V2CommandExecutionOutputDeltaNotification as lt, toOpenCodeQuestionAnswers as m, getProviderOptionStringSelectionValue as mn, V2ItemCompletedNotification as mt, ignoreProviderBackgroundCause as n, resolveProviderMaintenanceCapabilitiesEffect as nn, ServerRequest__FileChangeRequestApprovalParams as nt, OpenCodeRuntimeError as o, resolveCommandPath as on, V2AccountRateLimitsUpdatedNotification as ot, toOpenCodePermissionReply as p, getProviderOptionDescriptors as pn, V2FileChangeOutputDeltaNotification as pt, CodexAppServerProtocolParseError as q, ForgejoApi as qt, PtyAdapter as r, compareCliVersions as rn, ServerRequest__ToolRequestUserInputParams as rt, buildOpenCodePermissionRules as s, applyClaudePromptEffortPrefix as sn, V2AccountUpdatedNotification as st, makeServerQueueMetrics as t, normalizeCommandPath as tn, ServerRequest__ExecCommandApprovalParams as tt, parseOpenCodeModelSlug as u, getModelSelectionStringOptionValue as un, V2ConfigWarningNotification as ut, checkRtkAvailability as v, V2ModelReroutedNotification as vt, buildBooleanOptionDescriptor as w, V2ThreadRealtimeClosedNotification as wt, checkCodexProviderStatus as x, V2ReasoningTextDeltaNotification as xt, mergeProviderInstanceEnvironment as y, V2PlanDeltaNotification as yt, ProviderDriverError as z, V2WindowsWorldWritableWarningNotification as zt };