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.cjs CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
4
  const require_chunk = require("./chunk-jM1PLTSZ.cjs");
5
- const require_collectUint8StreamText = require("./collectUint8StreamText-TCG6xW_P.cjs");
6
- const require_SourceControlProviderDiscoveryCatalog = require("./SourceControlProviderDiscoveryCatalog-_rg0mc1o.cjs");
5
+ const require_collectUint8StreamText = require("./collectUint8StreamText-B-GW3u_2.cjs");
7
6
  const require_Metrics = require("./Metrics-CIW1QH7M.cjs");
7
+ const require_SourceControlProviderDiscoveryCatalog = require("./SourceControlProviderDiscoveryCatalog-CTb100Uk.cjs");
8
8
  const require_sourceControl = require("./sourceControl-x2t62T8h.cjs");
9
9
  let _effect_platform_node_NodeRuntime = require("@effect/platform-node/NodeRuntime");
10
10
  _effect_platform_node_NodeRuntime = require_chunk.__toESM(_effect_platform_node_NodeRuntime, 1);
@@ -44,13 +44,13 @@ let effect_unstable_rpc = require("effect/unstable/rpc");
44
44
  let effect_unstable_process = require("effect/unstable/process");
45
45
  let effect_Semaphore = require("effect/Semaphore");
46
46
  effect_Semaphore = require_chunk.__toESM(effect_Semaphore, 1);
47
+ let effect_Random = require("effect/Random");
48
+ effect_Random = require_chunk.__toESM(effect_Random, 1);
47
49
  require("effect/PlatformError");
48
50
  let effect_unstable_sql_SqlClient = require("effect/unstable/sql/SqlClient");
49
51
  effect_unstable_sql_SqlClient = require_chunk.__toESM(effect_unstable_sql_SqlClient, 1);
50
52
  let effect_unstable_sql_Migrator = require("effect/unstable/sql/Migrator");
51
53
  effect_unstable_sql_Migrator = require_chunk.__toESM(effect_unstable_sql_Migrator, 1);
52
- let effect_Random = require("effect/Random");
53
- effect_Random = require_chunk.__toESM(effect_Random, 1);
54
54
  let node_fs_promises = require("node:fs/promises");
55
55
  node_fs_promises = require_chunk.__toESM(node_fs_promises, 1);
56
56
  let effect_Context = require("effect/Context");
@@ -72,9 +72,9 @@ let effect_SchemaIssue = require("effect/SchemaIssue");
72
72
  effect_SchemaIssue = require_chunk.__toESM(effect_SchemaIssue, 1);
73
73
  let effect_Sink = require("effect/Sink");
74
74
  effect_Sink = require_chunk.__toESM(effect_Sink, 1);
75
- let effect_Number = require("effect/Number");
76
75
  let effect_unstable_sql_SqlSchema = require("effect/unstable/sql/SqlSchema");
77
76
  effect_unstable_sql_SqlSchema = require_chunk.__toESM(effect_unstable_sql_SqlSchema, 1);
77
+ let effect_Number = require("effect/Number");
78
78
  let node_url = require("node:url");
79
79
  let _pierre_diffs = require("@pierre/diffs");
80
80
  let node_perf_hooks = require("node:perf_hooks");
@@ -336,7 +336,7 @@ function applyClaudePromptEffortPrefix(text, effort) {
336
336
  }
337
337
  //#endregion
338
338
  //#region ../../packages/shared/src/serverSettings.ts
339
- const ServerSettingsJson$1 = require_SourceControlProviderDiscoveryCatalog.fromLenientJson(require_collectUint8StreamText.ServerSettings);
339
+ const ServerSettingsJson$1 = require_collectUint8StreamText.fromLenientJson(require_collectUint8StreamText.ServerSettings);
340
340
  function normalizePersistedServerSettingString(value) {
341
341
  const trimmed = value?.trim();
342
342
  return trimmed && trimmed.length > 0 ? trimmed : void 0;
@@ -424,7 +424,7 @@ const readBootstrapEnvelope = effect.Effect.fn("readBootstrapEnvelope")(function
424
424
  })));
425
425
  };
426
426
  const handleLine = (line) => {
427
- const parsed = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(schema)(line);
427
+ const parsed = require_collectUint8StreamText.decodeJsonResult(schema)(line);
428
428
  if (effect.Result.isSuccess(parsed)) resume(effect.Effect.succeedSome(parsed.success));
429
429
  else resume(effect.Effect.fail(new BootstrapError({
430
430
  message: "Failed to decode bootstrap envelope.",
@@ -1657,6 +1657,388 @@ const makeProviderMaintenanceCommandCoordinator = effect.Effect.fn("makeProvider
1657
1657
  return { withCommandLock };
1658
1658
  });
1659
1659
  //#endregion
1660
+ //#region src/atomicWrite.ts
1661
+ const writeFileStringAtomically = (input) => effect.Effect.scoped(effect.Effect.gen(function* () {
1662
+ const fs = yield* effect.FileSystem.FileSystem;
1663
+ const path = yield* effect.Path.Path;
1664
+ const tempFileId = yield* effect_Random.nextUUIDv4;
1665
+ const targetDirectory = path.dirname(input.filePath);
1666
+ yield* fs.makeDirectory(targetDirectory, { recursive: true });
1667
+ const tempDirectory = yield* fs.makeTempDirectoryScoped({
1668
+ directory: targetDirectory,
1669
+ prefix: `${path.basename(input.filePath)}.`
1670
+ });
1671
+ const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`);
1672
+ yield* fs.writeFileString(tempPath, input.contents);
1673
+ yield* fs.rename(tempPath, input.filePath);
1674
+ }));
1675
+ //#endregion
1676
+ //#region src/auth/Services/ServerSecretStore.ts
1677
+ var SecretStoreError = class extends effect.Data.TaggedError("SecretStoreError") {};
1678
+ var ServerSecretStore = class extends effect.Context.Service()("ryco/auth/Services/ServerSecretStore") {};
1679
+ //#endregion
1680
+ //#region src/auth/Layers/ServerSecretStore.ts
1681
+ const makeServerSecretStore = effect.Effect.gen(function* () {
1682
+ const fileSystem = yield* effect.FileSystem.FileSystem;
1683
+ const path = yield* effect.Path.Path;
1684
+ const serverConfig = yield* require_Metrics.ServerConfig;
1685
+ yield* fileSystem.makeDirectory(serverConfig.secretsDir, { recursive: true });
1686
+ yield* fileSystem.chmod(serverConfig.secretsDir, 448).pipe(effect.Effect.mapError((cause) => new SecretStoreError({
1687
+ message: `Failed to secure secrets directory ${serverConfig.secretsDir}.`,
1688
+ cause
1689
+ })));
1690
+ const resolveSecretPath = (name) => path.join(serverConfig.secretsDir, `${name}.bin`);
1691
+ const ensureSecretDirectory = (secretPath) => {
1692
+ const directory = path.dirname(secretPath);
1693
+ return fileSystem.makeDirectory(directory, { recursive: true }).pipe(effect.Effect.flatMap(() => fileSystem.chmod(directory, 448)));
1694
+ };
1695
+ const isPlatformError = (u) => effect.Predicate.isTagged(u, "PlatformError");
1696
+ const get = (name) => fileSystem.readFile(resolveSecretPath(name)).pipe(effect.Effect.map((bytes) => Uint8Array.from(bytes)), effect.Effect.catch((cause) => cause.reason._tag === "NotFound" ? effect.Effect.succeed(null) : effect.Effect.fail(new SecretStoreError({
1697
+ message: `Failed to read secret ${name}.`,
1698
+ cause
1699
+ }))));
1700
+ const set = (name, value) => {
1701
+ const secretPath = resolveSecretPath(name);
1702
+ const tempPath = `${secretPath}.${node_crypto.randomUUID()}.tmp`;
1703
+ return effect.Effect.gen(function* () {
1704
+ yield* ensureSecretDirectory(secretPath);
1705
+ yield* fileSystem.writeFile(tempPath, value);
1706
+ yield* fileSystem.chmod(tempPath, 384);
1707
+ yield* fileSystem.rename(tempPath, secretPath);
1708
+ yield* fileSystem.chmod(secretPath, 384);
1709
+ }).pipe(effect.Effect.catch((cause) => fileSystem.remove(tempPath).pipe(effect.Effect.ignore, effect.Effect.flatMap(() => effect.Effect.fail(new SecretStoreError({
1710
+ message: `Failed to persist secret ${name}.`,
1711
+ cause
1712
+ }))))));
1713
+ };
1714
+ const create = (name, value) => {
1715
+ const secretPath = resolveSecretPath(name);
1716
+ return effect.Effect.scoped(effect.Effect.gen(function* () {
1717
+ yield* ensureSecretDirectory(secretPath);
1718
+ const file = yield* fileSystem.open(secretPath, {
1719
+ flag: "wx",
1720
+ mode: 384
1721
+ });
1722
+ yield* file.writeAll(value);
1723
+ yield* file.sync;
1724
+ yield* fileSystem.chmod(secretPath, 384);
1725
+ })).pipe(effect.Effect.mapError((cause) => new SecretStoreError({
1726
+ message: `Failed to persist secret ${name}.`,
1727
+ cause
1728
+ })));
1729
+ };
1730
+ const getOrCreateRandom = (name, bytes) => get(name).pipe(effect.Effect.flatMap((existing) => {
1731
+ if (existing) return effect.Effect.succeed(existing);
1732
+ const generated = node_crypto.randomBytes(bytes);
1733
+ return create(name, generated).pipe(effect.Effect.as(Uint8Array.from(generated)), effect.Effect.catchTag("SecretStoreError", (error) => isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists" ? get(name).pipe(effect.Effect.flatMap((created) => created !== null ? effect.Effect.succeed(created) : effect.Effect.fail(new SecretStoreError({ message: `Failed to read secret ${name} after concurrent creation.` })))) : effect.Effect.fail(error)));
1734
+ }));
1735
+ const remove = (name) => fileSystem.remove(resolveSecretPath(name)).pipe(effect.Effect.catch((cause) => cause.reason._tag === "NotFound" ? effect.Effect.void : effect.Effect.fail(new SecretStoreError({
1736
+ message: `Failed to remove secret ${name}.`,
1737
+ cause
1738
+ }))));
1739
+ return {
1740
+ get,
1741
+ set,
1742
+ getOrCreateRandom,
1743
+ remove
1744
+ };
1745
+ });
1746
+ const ServerSecretStoreLive = effect.Layer.effect(ServerSecretStore, makeServerSecretStore);
1747
+ //#endregion
1748
+ //#region src/serverSettings.ts
1749
+ /**
1750
+ * ServerSettings - Server-authoritative settings service.
1751
+ *
1752
+ * Owns persistence, validation, and change notification of settings that affect
1753
+ * server-side behavior (binary paths, streaming mode, env mode, custom models,
1754
+ * text generation model selection).
1755
+ *
1756
+ * Follows the same pattern as `keybindings.ts`: JSON file + Cache + PubSub +
1757
+ * Semaphore + FileSystem.watch for concurrency and external edit detection.
1758
+ *
1759
+ * @module ServerSettings
1760
+ */
1761
+ const textEncoder$2 = new TextEncoder();
1762
+ const textDecoder$2 = new TextDecoder();
1763
+ function providerEnvironmentSecretName(input) {
1764
+ return `provider-env-${Buffer.from(input.instanceId, "utf8").toString("base64url")}-${Buffer.from(input.name, "utf8").toString("base64url")}`;
1765
+ }
1766
+ function redactProviderEnvironmentVariable(variable) {
1767
+ if (!variable.sensitive) {
1768
+ const { valueRedacted: _omit, ...rest } = variable;
1769
+ return rest;
1770
+ }
1771
+ return {
1772
+ ...variable,
1773
+ value: "",
1774
+ ...variable.value.length > 0 || variable.valueRedacted ? { valueRedacted: true } : {}
1775
+ };
1776
+ }
1777
+ function redactServerSettingsForClient(settings) {
1778
+ const providerInstances = Object.fromEntries(Object.entries(settings.providerInstances).map(([instanceId, instance]) => [instanceId, instance.environment ? {
1779
+ ...instance,
1780
+ environment: instance.environment.map(redactProviderEnvironmentVariable)
1781
+ } : instance]));
1782
+ return {
1783
+ ...settings,
1784
+ providerInstances
1785
+ };
1786
+ }
1787
+ var ServerSettingsService = class ServerSettingsService extends effect.Context.Service()("ryco/serverSettings/ServerSettingsService") {
1788
+ static layerTest = (overrides = {}) => effect.Layer.effect(ServerSettingsService, effect.Effect.gen(function* () {
1789
+ const currentSettingsRef = yield* effect.Ref.make(deepMerge(require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS, overrides));
1790
+ return {
1791
+ start: effect.Effect.void,
1792
+ ready: effect.Effect.void,
1793
+ getSettings: effect.Ref.get(currentSettingsRef),
1794
+ updateSettings: (patch) => effect.Ref.get(currentSettingsRef).pipe(effect.Effect.flatMap((currentSettings) => effect.Schema.decodeEffect(require_collectUint8StreamText.ServerSettings)(applyServerSettingsPatch(currentSettings, patch)).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
1795
+ settingsPath: "<memory>",
1796
+ detail: `failed to normalize server settings: ${effect.SchemaIssue.makeFormatterDefault()(cause.issue)}`,
1797
+ cause
1798
+ })))), effect.Effect.tap((nextSettings) => effect.Ref.set(currentSettingsRef, nextSettings))),
1799
+ streamChanges: effect.Stream.empty
1800
+ };
1801
+ }));
1802
+ };
1803
+ const ServerSettingsJson = require_collectUint8StreamText.fromLenientJson(require_collectUint8StreamText.ServerSettings);
1804
+ const getLegacyProviderSettings = (settings, provider) => settings.providers[provider];
1805
+ /**
1806
+ * Ensure the `textGenerationModelSelection` points to an enabled provider.
1807
+ * If the selected provider is disabled, fall back to the first enabled
1808
+ * provider with its default model. This is applied at read-time so the
1809
+ * persisted preference is preserved for when a provider is re-enabled.
1810
+ */
1811
+ function resolveTextGenerationProvider(settings) {
1812
+ const selection = settings.textGenerationModelSelection;
1813
+ const instanceConfig = settings.providerInstances[selection.instanceId];
1814
+ if (instanceConfig !== void 0) return instanceConfig.enabled ?? true ? settings : fallbackTextGenerationProvider(settings);
1815
+ if (require_collectUint8StreamText.isProviderDriverKind(selection.instanceId) && getLegacyProviderSettings(settings, selection.instanceId)?.enabled) return settings;
1816
+ return fallbackTextGenerationProvider(settings);
1817
+ }
1818
+ function fallbackTextGenerationProvider(settings) {
1819
+ const fallbackEntry = Object.entries(settings.providers).find(([, provider]) => provider.enabled);
1820
+ const fallback = fallbackEntry ? require_collectUint8StreamText.ProviderDriverKind.make(fallbackEntry[0]) : void 0;
1821
+ if (!fallback) return settings;
1822
+ return {
1823
+ ...settings,
1824
+ textGenerationModelSelection: {
1825
+ instanceId: require_collectUint8StreamText.ProviderInstanceId.make(fallback),
1826
+ model: require_collectUint8StreamText.DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[fallback] ?? "gpt-5.4-mini"
1827
+ }
1828
+ };
1829
+ }
1830
+ const ATOMIC_SETTINGS_KEYS = new Set(["textGenerationModelSelection"]);
1831
+ function stripDefaultServerSettings(current, defaults) {
1832
+ if (Array.isArray(current) || Array.isArray(defaults)) return effect.Equal.equals(current, defaults) ? void 0 : current;
1833
+ if (current !== null && defaults !== null && typeof current === "object" && typeof defaults === "object") {
1834
+ const currentRecord = current;
1835
+ const defaultsRecord = defaults;
1836
+ const next = {};
1837
+ for (const key of Object.keys(currentRecord)) if (ATOMIC_SETTINGS_KEYS.has(key)) {
1838
+ if (!effect.Equal.equals(currentRecord[key], defaultsRecord[key])) next[key] = currentRecord[key];
1839
+ } else {
1840
+ const stripped = stripDefaultServerSettings(currentRecord[key], defaultsRecord[key]);
1841
+ if (stripped !== void 0) next[key] = stripped;
1842
+ }
1843
+ return Object.keys(next).length > 0 ? next : void 0;
1844
+ }
1845
+ return Object.is(current, defaults) ? void 0 : current;
1846
+ }
1847
+ const makeServerSettings = effect.Effect.gen(function* () {
1848
+ const { settingsPath } = yield* require_Metrics.ServerConfig;
1849
+ const fs = yield* effect.FileSystem.FileSystem;
1850
+ const pathService = yield* effect.Path.Path;
1851
+ const secretStore = yield* ServerSecretStore;
1852
+ const writeSemaphore = yield* effect_Semaphore.make(1);
1853
+ const cacheKey = "settings";
1854
+ const changesPubSub = yield* effect.PubSub.unbounded();
1855
+ const startedRef = yield* effect.Ref.make(false);
1856
+ const startedDeferred = yield* effect.Deferred.make();
1857
+ const watcherScope = yield* effect.Scope.make("sequential");
1858
+ yield* effect.Effect.addFinalizer(() => effect.Scope.close(watcherScope, effect.Exit.void));
1859
+ const emitChange = (settings) => effect.PubSub.publish(changesPubSub, settings).pipe(effect.Effect.asVoid);
1860
+ const readConfigExists = fs.exists(settingsPath).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
1861
+ settingsPath,
1862
+ detail: "failed to check settings file existence",
1863
+ cause
1864
+ })));
1865
+ const readRawConfig = fs.readFileString(settingsPath).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
1866
+ settingsPath,
1867
+ detail: "failed to read settings file",
1868
+ cause
1869
+ })));
1870
+ const loadSettingsFromDisk = effect.Effect.gen(function* () {
1871
+ if (!(yield* readConfigExists)) return require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS;
1872
+ const raw = yield* readRawConfig;
1873
+ const decoded = effect.Schema.decodeUnknownExit(ServerSettingsJson)(raw);
1874
+ if (decoded._tag === "Failure") {
1875
+ yield* effect.Effect.logWarning("failed to parse settings.json, using defaults", {
1876
+ path: settingsPath,
1877
+ issues: effect.Cause.pretty(decoded.cause)
1878
+ });
1879
+ return require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS;
1880
+ }
1881
+ return decoded.value;
1882
+ });
1883
+ const settingsCache = yield* effect.Cache.make({
1884
+ capacity: 1,
1885
+ lookup: () => loadSettingsFromDisk
1886
+ });
1887
+ const getSettingsFromCache = effect.Cache.get(settingsCache, cacheKey);
1888
+ const toSettingsError = (detail, cause) => new require_collectUint8StreamText.ServerSettingsError({
1889
+ settingsPath,
1890
+ detail,
1891
+ cause
1892
+ });
1893
+ const materializeProviderEnvironmentSecrets = (settings) => effect.Effect.gen(function* () {
1894
+ const providerInstances = { ...settings.providerInstances };
1895
+ for (const [instanceId, instance] of Object.entries(settings.providerInstances)) {
1896
+ if (!instance.environment) continue;
1897
+ const environment = [];
1898
+ for (const variable of instance.environment) {
1899
+ if (!variable.sensitive || !variable.valueRedacted) {
1900
+ environment.push(variable);
1901
+ continue;
1902
+ }
1903
+ const secret = yield* secretStore.get(providerEnvironmentSecretName({
1904
+ instanceId,
1905
+ name: variable.name
1906
+ })).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to read sensitive environment variable ${variable.name}`, cause)));
1907
+ environment.push({
1908
+ ...variable,
1909
+ value: secret ? textDecoder$2.decode(secret) : ""
1910
+ });
1911
+ }
1912
+ providerInstances[instanceId] = {
1913
+ ...instance,
1914
+ environment
1915
+ };
1916
+ }
1917
+ return {
1918
+ ...settings,
1919
+ providerInstances
1920
+ };
1921
+ });
1922
+ const persistProviderEnvironmentSecrets = (current, next) => effect.Effect.gen(function* () {
1923
+ const providerInstances = { ...next.providerInstances };
1924
+ const nextSecretKeys = /* @__PURE__ */ new Set();
1925
+ for (const [instanceId, instance] of Object.entries(next.providerInstances)) {
1926
+ if (!instance.environment) continue;
1927
+ const environment = [];
1928
+ for (const variable of instance.environment) {
1929
+ const secretName = providerEnvironmentSecretName({
1930
+ instanceId,
1931
+ name: variable.name
1932
+ });
1933
+ if (!variable.sensitive) {
1934
+ yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
1935
+ environment.push(redactProviderEnvironmentVariable(variable));
1936
+ continue;
1937
+ }
1938
+ nextSecretKeys.add(secretName);
1939
+ if (!variable.valueRedacted) {
1940
+ if (variable.value.length > 0) {
1941
+ yield* secretStore.set(secretName, textEncoder$2.encode(variable.value)).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to persist environment secret ${variable.name}`, cause)));
1942
+ environment.push({
1943
+ ...variable,
1944
+ value: "",
1945
+ valueRedacted: true
1946
+ });
1947
+ } else {
1948
+ yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
1949
+ const { valueRedacted: _omit, ...rest } = variable;
1950
+ environment.push(rest);
1951
+ }
1952
+ continue;
1953
+ }
1954
+ environment.push(redactProviderEnvironmentVariable(variable));
1955
+ }
1956
+ providerInstances[instanceId] = {
1957
+ ...instance,
1958
+ environment
1959
+ };
1960
+ }
1961
+ for (const [instanceId, instance] of Object.entries(current.providerInstances)) for (const variable of instance.environment ?? []) {
1962
+ if (!variable.sensitive) continue;
1963
+ const secretName = providerEnvironmentSecretName({
1964
+ instanceId,
1965
+ name: variable.name
1966
+ });
1967
+ if (nextSecretKeys.has(secretName)) continue;
1968
+ yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove stale environment secret ${variable.name}`, cause)));
1969
+ }
1970
+ return {
1971
+ ...next,
1972
+ providerInstances
1973
+ };
1974
+ });
1975
+ const writeSettingsAtomically = (settings) => {
1976
+ const sparseSettings = stripDefaultServerSettings(settings, require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS) ?? {};
1977
+ return writeFileStringAtomically({
1978
+ filePath: settingsPath,
1979
+ contents: `${JSON.stringify(sparseSettings, null, 2)}\n`
1980
+ }).pipe(effect.Effect.provideService(effect.FileSystem.FileSystem, fs), effect.Effect.provideService(effect.Path.Path, pathService), effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
1981
+ settingsPath,
1982
+ detail: "failed to write settings file",
1983
+ cause
1984
+ })));
1985
+ };
1986
+ const revalidateAndEmit = writeSemaphore.withPermits(1)(effect.Effect.gen(function* () {
1987
+ yield* effect.Cache.invalidate(settingsCache, cacheKey);
1988
+ yield* emitChange(yield* getSettingsFromCache);
1989
+ }));
1990
+ const startWatcher = effect.Effect.gen(function* () {
1991
+ const settingsDir = pathService.dirname(settingsPath);
1992
+ const settingsFile = pathService.basename(settingsPath);
1993
+ const settingsPathResolved = pathService.resolve(settingsPath);
1994
+ yield* fs.makeDirectory(settingsDir, { recursive: true }).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
1995
+ settingsPath,
1996
+ detail: "failed to prepare settings directory",
1997
+ cause
1998
+ })));
1999
+ const revalidateAndEmitSafely = revalidateAndEmit.pipe(effect.Effect.ignoreCause({ log: true }));
2000
+ const debouncedSettingsEvents = fs.watch(settingsDir).pipe(effect.Stream.filter((event) => {
2001
+ return event.path === settingsFile || event.path === settingsPath || pathService.resolve(settingsDir, event.path) === settingsPathResolved;
2002
+ }), effect.Stream.debounce(effect.Duration.millis(100)));
2003
+ yield* effect.Stream.runForEach(debouncedSettingsEvents, () => revalidateAndEmitSafely).pipe(effect.Effect.ignoreCause({ log: true }), effect.Effect.forkIn(watcherScope), effect.Effect.asVoid);
2004
+ });
2005
+ return {
2006
+ start: effect.Effect.gen(function* () {
2007
+ if (!(yield* effect.Ref.modify(startedRef, (started) => [!started, true]))) return yield* effect.Deferred.await(startedDeferred);
2008
+ const startup = effect.Effect.gen(function* () {
2009
+ yield* startWatcher;
2010
+ yield* effect.Cache.invalidate(settingsCache, cacheKey);
2011
+ yield* getSettingsFromCache;
2012
+ });
2013
+ const startupExit = yield* effect.Effect.exit(startup);
2014
+ if (startupExit._tag === "Failure") {
2015
+ yield* effect.Deferred.failCause(startedDeferred, startupExit.cause).pipe(effect.Effect.orDie);
2016
+ return yield* effect.Effect.failCause(startupExit.cause);
2017
+ }
2018
+ yield* effect.Deferred.succeed(startedDeferred, void 0).pipe(effect.Effect.orDie);
2019
+ }),
2020
+ ready: effect.Deferred.await(startedDeferred),
2021
+ getSettings: getSettingsFromCache.pipe(effect.Effect.flatMap(materializeProviderEnvironmentSecrets), effect.Effect.map(resolveTextGenerationProvider)),
2022
+ updateSettings: (patch) => writeSemaphore.withPermits(1)(effect.Effect.gen(function* () {
2023
+ const current = yield* getSettingsFromCache;
2024
+ const nextPersisted = yield* persistProviderEnvironmentSecrets(current, applyServerSettingsPatch(current, patch));
2025
+ const next = yield* effect.Schema.decodeEffect(require_collectUint8StreamText.ServerSettings)(nextPersisted).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
2026
+ settingsPath: "<memory>",
2027
+ detail: `failed to normalize server settings: ${effect.SchemaIssue.makeFormatterDefault()(cause.issue)}`,
2028
+ cause
2029
+ })));
2030
+ yield* writeSettingsAtomically(next);
2031
+ yield* effect.Cache.set(settingsCache, cacheKey, next);
2032
+ yield* emitChange(next);
2033
+ return resolveTextGenerationProvider(yield* materializeProviderEnvironmentSecrets(next));
2034
+ })),
2035
+ get streamChanges() {
2036
+ return effect.Stream.fromPubSub(changesPubSub).pipe(effect.Stream.mapEffect((settings) => materializeProviderEnvironmentSecrets(settings).pipe(effect.Effect.catch((error) => effect.Effect.logWarning("failed to materialize provider environment secrets", { detail: error.detail }).pipe(effect.Effect.as(settings))))), effect.Stream.map(resolveTextGenerationProvider));
2037
+ }
2038
+ };
2039
+ });
2040
+ const ServerSettingsLive = effect.Layer.effect(ServerSettingsService, makeServerSettings).pipe(effect.Layer.provide(ServerSecretStoreLive));
2041
+ //#endregion
1660
2042
  //#region src/provider/cliVersion.ts
1661
2043
  const CLI_VERSION_NUMBER_SEGMENT = /^\d+$/;
1662
2044
  function normalizeCliVersion(version) {
@@ -1957,7 +2339,8 @@ const resolveLatestProviderVersion = effect.Effect.fn("resolveLatestProviderVers
1957
2339
  });
1958
2340
  const enrichProviderSnapshotWithVersionAdvisory = effect.Effect.fn("enrichProviderSnapshotWithVersionAdvisory")(function* (snapshot, maintenanceCapabilities) {
1959
2341
  const capabilities = maintenanceCapabilities ?? makeManualProviderMaintenanceCapabilities$1(snapshot.driver);
1960
- if (!snapshot.enabled || !snapshot.installed || !snapshot.version) return {
2342
+ const { enableProviderUpdateChecks } = yield* (yield* ServerSettingsService).getSettings.pipe(effect.Effect.orElseSucceed(() => require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS));
2343
+ if (!enableProviderUpdateChecks || !snapshot.enabled || !snapshot.installed || !snapshot.version) return {
1961
2344
  ...snapshot,
1962
2345
  versionAdvisory: createProviderVersionAdvisory({
1963
2346
  driver: snapshot.driver,
@@ -2052,6 +2435,7 @@ const make$21 = effect.Effect.fn("ProviderMaintenanceRunner.make")(function* ()
2052
2435
  const providerRegistry = yield* ProviderRegistry;
2053
2436
  const spawner = yield* effect_unstable_process.ChildProcessSpawner.ChildProcessSpawner;
2054
2437
  const httpClient = yield* effect_unstable_http.HttpClient.HttpClient;
2438
+ const serverSettings = yield* ServerSettingsService;
2055
2439
  const runMaintenanceCommand = (command, args) => runProviderMaintenanceCommandWithSpawner({
2056
2440
  spawner,
2057
2441
  command,
@@ -2070,7 +2454,7 @@ const make$21 = effect.Effect.fn("ProviderMaintenanceRunner.make")(function* ()
2070
2454
  providers,
2071
2455
  verifiedProviders: []
2072
2456
  });
2073
- return effect.Effect.forEach(refreshedProviders, (refreshedProvider) => enrichProviderSnapshotWithVersionAdvisory(refreshedProvider, maintenanceCapabilities).pipe(effect.Effect.provideService(effect_unstable_http.HttpClient.HttpClient, httpClient)), { concurrency: "unbounded" }).pipe(effect.Effect.map((verifiedProviders) => ({
2457
+ return effect.Effect.forEach(refreshedProviders, (refreshedProvider) => enrichProviderSnapshotWithVersionAdvisory(refreshedProvider, maintenanceCapabilities).pipe(effect.Effect.provideService(effect_unstable_http.HttpClient.HttpClient, httpClient), effect.Effect.provideService(ServerSettingsService, serverSettings)), { concurrency: "unbounded" }).pipe(effect.Effect.map((verifiedProviders) => ({
2074
2458
  providers,
2075
2459
  verifiedProviders
2076
2460
  })), effect.Effect.catchCause((cause) => effect.Effect.logWarning("Provider post-update version verification failed", {
@@ -2160,78 +2544,6 @@ const make$21 = effect.Effect.fn("ProviderMaintenanceRunner.make")(function* ()
2160
2544
  });
2161
2545
  const layer$18 = effect.Layer.effect(ProviderMaintenanceRunner, make$21());
2162
2546
  //#endregion
2163
- //#region src/auth/Services/ServerSecretStore.ts
2164
- var SecretStoreError = class extends effect.Data.TaggedError("SecretStoreError") {};
2165
- var ServerSecretStore = class extends effect.Context.Service()("ryco/auth/Services/ServerSecretStore") {};
2166
- //#endregion
2167
- //#region src/auth/Layers/ServerSecretStore.ts
2168
- const makeServerSecretStore = effect.Effect.gen(function* () {
2169
- const fileSystem = yield* effect.FileSystem.FileSystem;
2170
- const path = yield* effect.Path.Path;
2171
- const serverConfig = yield* require_Metrics.ServerConfig;
2172
- yield* fileSystem.makeDirectory(serverConfig.secretsDir, { recursive: true });
2173
- yield* fileSystem.chmod(serverConfig.secretsDir, 448).pipe(effect.Effect.mapError((cause) => new SecretStoreError({
2174
- message: `Failed to secure secrets directory ${serverConfig.secretsDir}.`,
2175
- cause
2176
- })));
2177
- const resolveSecretPath = (name) => path.join(serverConfig.secretsDir, `${name}.bin`);
2178
- const ensureSecretDirectory = (secretPath) => {
2179
- const directory = path.dirname(secretPath);
2180
- return fileSystem.makeDirectory(directory, { recursive: true }).pipe(effect.Effect.flatMap(() => fileSystem.chmod(directory, 448)));
2181
- };
2182
- const isPlatformError = (u) => effect.Predicate.isTagged(u, "PlatformError");
2183
- const get = (name) => fileSystem.readFile(resolveSecretPath(name)).pipe(effect.Effect.map((bytes) => Uint8Array.from(bytes)), effect.Effect.catch((cause) => cause.reason._tag === "NotFound" ? effect.Effect.succeed(null) : effect.Effect.fail(new SecretStoreError({
2184
- message: `Failed to read secret ${name}.`,
2185
- cause
2186
- }))));
2187
- const set = (name, value) => {
2188
- const secretPath = resolveSecretPath(name);
2189
- const tempPath = `${secretPath}.${node_crypto.randomUUID()}.tmp`;
2190
- return effect.Effect.gen(function* () {
2191
- yield* ensureSecretDirectory(secretPath);
2192
- yield* fileSystem.writeFile(tempPath, value);
2193
- yield* fileSystem.chmod(tempPath, 384);
2194
- yield* fileSystem.rename(tempPath, secretPath);
2195
- yield* fileSystem.chmod(secretPath, 384);
2196
- }).pipe(effect.Effect.catch((cause) => fileSystem.remove(tempPath).pipe(effect.Effect.ignore, effect.Effect.flatMap(() => effect.Effect.fail(new SecretStoreError({
2197
- message: `Failed to persist secret ${name}.`,
2198
- cause
2199
- }))))));
2200
- };
2201
- const create = (name, value) => {
2202
- const secretPath = resolveSecretPath(name);
2203
- return effect.Effect.scoped(effect.Effect.gen(function* () {
2204
- yield* ensureSecretDirectory(secretPath);
2205
- const file = yield* fileSystem.open(secretPath, {
2206
- flag: "wx",
2207
- mode: 384
2208
- });
2209
- yield* file.writeAll(value);
2210
- yield* file.sync;
2211
- yield* fileSystem.chmod(secretPath, 384);
2212
- })).pipe(effect.Effect.mapError((cause) => new SecretStoreError({
2213
- message: `Failed to persist secret ${name}.`,
2214
- cause
2215
- })));
2216
- };
2217
- const getOrCreateRandom = (name, bytes) => get(name).pipe(effect.Effect.flatMap((existing) => {
2218
- if (existing) return effect.Effect.succeed(existing);
2219
- const generated = node_crypto.randomBytes(bytes);
2220
- return create(name, generated).pipe(effect.Effect.as(Uint8Array.from(generated)), effect.Effect.catchTag("SecretStoreError", (error) => isPlatformError(error.cause) && error.cause.reason._tag === "AlreadyExists" ? get(name).pipe(effect.Effect.flatMap((created) => created !== null ? effect.Effect.succeed(created) : effect.Effect.fail(new SecretStoreError({ message: `Failed to read secret ${name} after concurrent creation.` })))) : effect.Effect.fail(error)));
2221
- }));
2222
- const remove = (name) => fileSystem.remove(resolveSecretPath(name)).pipe(effect.Effect.catch((cause) => cause.reason._tag === "NotFound" ? effect.Effect.void : effect.Effect.fail(new SecretStoreError({
2223
- message: `Failed to remove secret ${name}.`,
2224
- cause
2225
- }))));
2226
- return {
2227
- get,
2228
- set,
2229
- getOrCreateRandom,
2230
- remove
2231
- };
2232
- });
2233
- const ServerSecretStoreLive = effect.Layer.effect(ServerSecretStore, makeServerSecretStore);
2234
- //#endregion
2235
2547
  //#region src/persistence/Errors.ts
2236
2548
  var PersistenceSqlError = class extends effect.Schema.TaggedErrorClass()("PersistenceSqlError", {
2237
2549
  operation: effect.Schema.String,
@@ -3925,6 +4237,35 @@ var _040_ProjectionThreadsProjectUpdatedAtIndex_default = effect_Effect.gen(func
3925
4237
  `;
3926
4238
  });
3927
4239
  //#endregion
4240
+ //#region src/persistence/Migrations/041_ProjectionThreadsSubagentNesting.ts
4241
+ var _041_ProjectionThreadsSubagentNesting_default = effect_Effect.gen(function* () {
4242
+ const sql = yield* effect_unstable_sql_SqlClient.SqlClient;
4243
+ const columns = yield* sql`
4244
+ PRAGMA table_info(projection_threads)
4245
+ `;
4246
+ const columnNames = new Set(columns.map((column) => column.name));
4247
+ if (!columnNames.has("thread_kind")) yield* sql`
4248
+ ALTER TABLE projection_threads
4249
+ ADD COLUMN thread_kind TEXT NOT NULL DEFAULT 'normal'
4250
+ `;
4251
+ if (!columnNames.has("visibility")) yield* sql`
4252
+ ALTER TABLE projection_threads
4253
+ ADD COLUMN visibility TEXT NOT NULL DEFAULT 'normal'
4254
+ `;
4255
+ if (!columnNames.has("parent_thread_id")) yield* sql`
4256
+ ALTER TABLE projection_threads
4257
+ ADD COLUMN parent_thread_id TEXT
4258
+ `;
4259
+ if (!columnNames.has("parent_subagent_id")) yield* sql`
4260
+ ALTER TABLE projection_threads
4261
+ ADD COLUMN parent_subagent_id TEXT
4262
+ `;
4263
+ yield* sql`
4264
+ CREATE INDEX IF NOT EXISTS idx_projection_threads_parent_thread
4265
+ ON projection_threads(parent_thread_id, created_at, thread_id)
4266
+ `;
4267
+ });
4268
+ //#endregion
3928
4269
  //#region src/persistence/Migrations.ts
3929
4270
  /**
3930
4271
  * MigrationsLive - Migration runner with inline loader
@@ -4145,6 +4486,11 @@ const migrationEntries = [
4145
4486
  40,
4146
4487
  "ProjectionThreadsProjectUpdatedAtIndex",
4147
4488
  _040_ProjectionThreadsProjectUpdatedAtIndex_default
4489
+ ],
4490
+ [
4491
+ 41,
4492
+ "ProjectionThreadsSubagentNesting",
4493
+ _041_ProjectionThreadsSubagentNesting_default
4148
4494
  ]
4149
4495
  ];
4150
4496
  const makeMigrationLoader = (throughId) => effect_unstable_sql_Migrator.fromRecord(Object.fromEntries(migrationEntries.filter(([id]) => throughId === void 0 || id <= throughId).map(([id, name, migration]) => [`${id}_${name}`, migration])));
@@ -4214,6 +4560,45 @@ const repairProjectionTokenModeColumns = effect_Effect.fn("repairProjectionToken
4214
4560
  yield* effect_Effect.log("Repaired projection_thread_sessions.token_mode column");
4215
4561
  }
4216
4562
  });
4563
+ const repairProjectionThreadSubagentNestingColumns = effect_Effect.fn("repairProjectionThreadSubagentNestingColumns")(function* () {
4564
+ const sql = yield* effect_unstable_sql_SqlClient.SqlClient;
4565
+ if ((yield* sql`
4566
+ SELECT name FROM sqlite_master
4567
+ WHERE type = 'table' AND name = 'projection_threads'
4568
+ `).length === 0) return;
4569
+ const columns = yield* sql`
4570
+ PRAGMA table_info(projection_threads)
4571
+ `;
4572
+ const columnNames = new Set(columns.map((column) => column.name));
4573
+ if (!columnNames.has("thread_kind")) {
4574
+ yield* sql`
4575
+ ALTER TABLE projection_threads
4576
+ ADD COLUMN thread_kind TEXT NOT NULL DEFAULT 'normal'
4577
+ `;
4578
+ yield* effect_Effect.log("Repaired projection_threads.thread_kind column");
4579
+ }
4580
+ if (!columnNames.has("visibility")) {
4581
+ yield* sql`
4582
+ ALTER TABLE projection_threads
4583
+ ADD COLUMN visibility TEXT NOT NULL DEFAULT 'normal'
4584
+ `;
4585
+ yield* effect_Effect.log("Repaired projection_threads.visibility column");
4586
+ }
4587
+ if (!columnNames.has("parent_thread_id")) {
4588
+ yield* sql`
4589
+ ALTER TABLE projection_threads
4590
+ ADD COLUMN parent_thread_id TEXT
4591
+ `;
4592
+ yield* effect_Effect.log("Repaired projection_threads.parent_thread_id column");
4593
+ }
4594
+ if (!columnNames.has("parent_subagent_id")) {
4595
+ yield* sql`
4596
+ ALTER TABLE projection_threads
4597
+ ADD COLUMN parent_subagent_id TEXT
4598
+ `;
4599
+ yield* effect_Effect.log("Repaired projection_threads.parent_subagent_id column");
4600
+ }
4601
+ });
4217
4602
  /**
4218
4603
  * Run all pending migrations.
4219
4604
  *
@@ -4230,6 +4615,7 @@ const runMigrations = effect_Effect.fn("runMigrations")(function* ({ toMigration
4230
4615
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 31) yield* repairProjectionWorktreeTitleColumn();
4231
4616
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 34) yield* repairProjectionProjectAvatarColumns();
4232
4617
  if (toMigrationInclusive === void 0 || toMigrationInclusive >= 35) yield* repairProjectionTokenModeColumns();
4618
+ if (toMigrationInclusive === void 0 || toMigrationInclusive >= 41) yield* repairProjectionThreadSubagentNestingColumns();
4233
4619
  yield* effect_Effect.log("Migrations ran successfully").pipe(effect_Effect.annotateLogs({ migrations: executedMigrations.map(([id, name]) => `${id}_${name}`) }));
4234
4620
  return executedMigrations;
4235
4621
  });
@@ -4238,7 +4624,7 @@ effect_Layer.effectDiscard(runMigrations());
4238
4624
  //#region src/persistence/Layers/Sqlite.ts
4239
4625
  const defaultSqliteClientLoaders = {
4240
4626
  bun: () => import("@effect/sql-sqlite-bun/SqliteClient"),
4241
- node: () => Promise.resolve().then(() => require("./NodeSqliteClient-B5aDHbNc.cjs"))
4627
+ node: () => Promise.resolve().then(() => require("./NodeSqliteClient-D1O-MR8w.cjs"))
4242
4628
  };
4243
4629
  const makeRuntimeSqliteLayer = effect.Effect.fn("makeRuntimeSqliteLayer")(function* (config) {
4244
4630
  const loader = defaultSqliteClientLoaders[process.versions.bun !== void 0 ? "bun" : "node"];
@@ -4304,10 +4690,10 @@ function normalizeAzureDevOpsPullRequestRecord(raw) {
4304
4690
  updatedAt: (raw.closedDate ?? effect.Option.none()).pipe(effect.Option.orElse(() => raw.creationDate ?? effect.Option.none()))
4305
4691
  };
4306
4692
  }
4307
- const decodeAzureDevOpsPullRequestList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
4308
- const decodeAzureDevOpsPullRequest = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(AzureDevOpsPullRequestSchema);
4693
+ const decodeAzureDevOpsPullRequestList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
4694
+ const decodeAzureDevOpsPullRequest = require_collectUint8StreamText.decodeJsonResult(AzureDevOpsPullRequestSchema);
4309
4695
  const decodeAzureDevOpsPullRequestEntry = effect.Schema.decodeUnknownExit(AzureDevOpsPullRequestSchema);
4310
- const formatAzureDevOpsJsonDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
4696
+ const formatAzureDevOpsJsonDecodeError = require_collectUint8StreamText.formatSchemaError;
4311
4697
  function decodeAzureDevOpsPullRequestListJson(raw) {
4312
4698
  const result = decodeAzureDevOpsPullRequestList(raw);
4313
4699
  if (effect.Result.isSuccess(result)) {
@@ -4345,7 +4731,7 @@ function flattenThreadComments(threads) {
4345
4731
  createdAt: c.publishedDate ?? ""
4346
4732
  }));
4347
4733
  }
4348
- const decodeAzurePullRequestDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Struct({
4734
+ const decodeAzurePullRequestDetail = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Struct({
4349
4735
  ...AzureDevOpsPullRequestSchema.fields,
4350
4736
  description: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String)),
4351
4737
  threads: effect.Schema.optional(effect.Schema.Array(AzureThreadSchema))
@@ -4360,7 +4746,7 @@ function decodeAzureDevOpsPullRequestDetailJson(raw) {
4360
4746
  comments: flattenThreadComments(result.success.threads ?? [])
4361
4747
  });
4362
4748
  }
4363
- const decodeThreadList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(AzureThreadSchema));
4749
+ const decodeThreadList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(AzureThreadSchema));
4364
4750
  function decodeAzureDevOpsPullRequestThreadsJson(raw) {
4365
4751
  if (raw.length === 0) return effect.Result.succeed([]);
4366
4752
  const result = decodeThreadList(raw);
@@ -4434,10 +4820,10 @@ function normalize(raw) {
4434
4820
  labels: labelsFromTags(raw.fields["System.Tags"])
4435
4821
  };
4436
4822
  }
4437
- const decodeWorkItemList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
4823
+ const decodeWorkItemList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
4438
4824
  const decodeWorkItemEntry = effect.Schema.decodeUnknownExit(AzureWorkItemSchema);
4439
- const decodeWorkItemDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(AzureWorkItemSchema);
4440
- const formatAzureDevOpsWorkItemDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
4825
+ const decodeWorkItemDetail = require_collectUint8StreamText.decodeJsonResult(AzureWorkItemSchema);
4826
+ const formatAzureDevOpsWorkItemDecodeError = require_collectUint8StreamText.formatSchemaError;
4441
4827
  function decodeAzureDevOpsWorkItemListJson(raw) {
4442
4828
  const result = decodeWorkItemList(raw);
4443
4829
  if (!effect.Result.isSuccess(result)) return effect.Result.fail(result.failure);
@@ -4855,9 +5241,9 @@ function normalizeBitbucketCommentList(decoded) {
4855
5241
  createdAt: c.created_on
4856
5242
  }));
4857
5243
  }
4858
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(BitbucketIssueListSchema);
4859
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(BitbucketIssueSchema);
4860
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(BitbucketCommentListSchema);
5244
+ require_collectUint8StreamText.decodeJsonResult(BitbucketIssueListSchema);
5245
+ require_collectUint8StreamText.decodeJsonResult(BitbucketIssueSchema);
5246
+ require_collectUint8StreamText.decodeJsonResult(BitbucketCommentListSchema);
4861
5247
  //#endregion
4862
5248
  //#region src/sourceControl/bitbucketPullRequests.ts
4863
5249
  const BitbucketRepositoryRefSchema = effect.Schema.Struct({
@@ -4940,7 +5326,7 @@ const BitbucketPullRequestDetailSchema = effect.Schema.Struct({
4940
5326
  ...BitbucketPullRequestSchema.fields,
4941
5327
  summary: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.Struct({ raw: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String)) })))
4942
5328
  });
4943
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(BitbucketPullRequestDetailSchema);
5329
+ require_collectUint8StreamText.decodeJsonResult(BitbucketPullRequestDetailSchema);
4944
5330
  const WORK_ITEM_KEY_PATTERN = /\b([A-Z][A-Z0-9]{1,9})-(\d+)\b/giu;
4945
5331
  function extractWorkItemKeys(input) {
4946
5332
  const keys = /* @__PURE__ */ new Set();
@@ -5023,7 +5409,7 @@ effect.Schema.Struct({
5023
5409
  var ProjectAtlassianLinkRepository = class extends effect.Context.Service()("ryco/persistence/Services/ProjectAtlassianLinks/ProjectAtlassianLinkRepository") {};
5024
5410
  //#endregion
5025
5411
  //#region src/atlassian/AtlassianConnectionService.ts
5026
- const textEncoder$2 = new TextEncoder();
5412
+ const textEncoder$1 = new TextEncoder();
5027
5413
  var AtlassianConnectionService = class extends effect.Context.Service()("ryco/atlassian/AtlassianConnectionService") {};
5028
5414
  function atlassianError(operation, detail, cause) {
5029
5415
  return new require_collectUint8StreamText.AtlassianConnectionError({
@@ -5151,7 +5537,7 @@ const make$19 = effect.Effect.fn("makeAtlassianConnectionService")(function* ()
5151
5537
  createdAt,
5152
5538
  updatedAt: createdAt
5153
5539
  };
5154
- return secretStore.set(manualBitbucketTokenSecretName(connectionId), textEncoder$2.encode(input.token)).pipe(effect.Effect.flatMap(() => connections.upsert(record)), cleanupSecretOnFailure(manualBitbucketTokenSecretName(connectionId)), effect.Effect.as(toSummary(record)), effect.Effect.mapError(mapError$1("atlassian.saveManualBitbucketToken", "Failed to save the Bitbucket token.")));
5540
+ return secretStore.set(manualBitbucketTokenSecretName(connectionId), textEncoder$1.encode(input.token)).pipe(effect.Effect.flatMap(() => connections.upsert(record)), cleanupSecretOnFailure(manualBitbucketTokenSecretName(connectionId)), effect.Effect.as(toSummary(record)), effect.Effect.mapError(mapError$1("atlassian.saveManualBitbucketToken", "Failed to save the Bitbucket token.")));
5155
5541
  },
5156
5542
  saveManualJiraToken: (input) => {
5157
5543
  const createdAt = nowIso$2();
@@ -5187,7 +5573,7 @@ const make$19 = effect.Effect.fn("makeAtlassianConnectionService")(function* ()
5187
5573
  avatarUrl: null,
5188
5574
  updatedAt: createdAt
5189
5575
  };
5190
- return secretStore.set(manualJiraTokenSecretName(connectionId), textEncoder$2.encode(input.token)).pipe(effect.Effect.flatMap(() => connections.upsert(record)), effect.Effect.flatMap(() => resources.upsertForConnection({
5576
+ return secretStore.set(manualJiraTokenSecretName(connectionId), textEncoder$1.encode(input.token)).pipe(effect.Effect.flatMap(() => connections.upsert(record)), effect.Effect.flatMap(() => resources.upsertForConnection({
5191
5577
  connectionId,
5192
5578
  resources: [resource]
5193
5579
  })), cleanupSecretOnFailure(manualJiraTokenSecretName(connectionId)), effect.Effect.as(toSummary(record)), effect.Effect.mapError(mapError$1("atlassian.saveManualJiraToken", "Failed to save the Jira API token.")));
@@ -5303,6 +5689,11 @@ const NON_REPOSITORY_STATUS_DETAILS = Object.freeze({
5303
5689
  behindCount: 0,
5304
5690
  aheadOfDefaultCount: 0
5305
5691
  });
5692
+ const emptyDiffTotals = () => ({
5693
+ files: [],
5694
+ insertions: 0,
5695
+ deletions: 0
5696
+ });
5306
5697
  var StatusRemoteRefreshCacheKey = class extends effect.Data.Class {};
5307
5698
  function parseBranchAb(value) {
5308
5699
  const match = value.match(/^\+(\d+)\s+-(\d+)$/);
@@ -5502,7 +5893,7 @@ const createTrace2Monitor = effect.Effect.fn("createTrace2Monitor")(function* (i
5502
5893
  const handleTraceLine = effect.Effect.fn("handleTraceLine")(function* (line) {
5503
5894
  const trimmedLine = line.trim();
5504
5895
  if (trimmedLine.length === 0) return;
5505
- const traceRecord = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(Trace2Record)(trimmedLine);
5896
+ const traceRecord = require_collectUint8StreamText.decodeJsonResult(Trace2Record)(trimmedLine);
5506
5897
  if (effect.Result.isFailure(traceRecord)) {
5507
5898
  yield* effect.Effect.logDebug(`GitVcsDriver.trace2: failed to parse trace line for ${quoteGitCommand(input.args)} in ${input.cwd}`, traceRecord.failure);
5508
5899
  return;
@@ -5949,6 +6340,31 @@ const makeGitVcsDriverCore = effect.Effect.fn("makeGitVcsDriverCore")(function*
5949
6340
  const parsed = Number.parseInt(result.stdout.trim(), 10);
5950
6341
  return Number.isFinite(parsed) ? Math.max(0, parsed) : 0;
5951
6342
  });
6343
+ const readCommittedAgainstBase = effect.Effect.fn("readCommittedAgainstBase")(function* (cwd, refName) {
6344
+ const baseRef = yield* resolveBaseBranchForNoUpstream(cwd, refName).pipe(effect.Effect.catch(() => effect.Effect.succeed(null)));
6345
+ if (!baseRef) return emptyDiffTotals();
6346
+ const result = yield* executeGit("GitVcsDriver.readCommittedAgainstBase.diff", cwd, [
6347
+ "diff",
6348
+ "--numstat",
6349
+ `${baseRef}...HEAD`
6350
+ ], { allowNonZeroExit: true });
6351
+ if (result.exitCode !== 0) return emptyDiffTotals();
6352
+ let insertions = 0;
6353
+ let deletions = 0;
6354
+ return {
6355
+ files: parseNumstatEntries(result.stdout).map((entry) => {
6356
+ insertions += entry.insertions;
6357
+ deletions += entry.deletions;
6358
+ return {
6359
+ path: entry.path,
6360
+ insertions: entry.insertions,
6361
+ deletions: entry.deletions
6362
+ };
6363
+ }).toSorted((a, b) => a.path.localeCompare(b.path)),
6364
+ insertions,
6365
+ deletions
6366
+ };
6367
+ });
5952
6368
  const readBranchRecency = effect.Effect.fn("readBranchRecency")(function* (cwd) {
5953
6369
  const branchRecency = yield* executeGit("GitVcsDriver.readBranchRecency", cwd, [
5954
6370
  "for-each-ref",
@@ -6067,6 +6483,7 @@ const makeGitVcsDriverCore = effect.Effect.fn("makeGitVcsDriverCore")(function*
6067
6483
  });
6068
6484
  }
6069
6485
  files.sort((a, b) => a.path.localeCompare(b.path));
6486
+ const committed = refName && !isDefaultBranch ? yield* readCommittedAgainstBase(cwd, refName).pipe(effect.Effect.catch(() => effect.Effect.succeed(emptyDiffTotals()))) : void 0;
6070
6487
  return {
6071
6488
  isRepo: true,
6072
6489
  hasOriginRemote: hasPrimaryRemote,
@@ -6079,6 +6496,7 @@ const makeGitVcsDriverCore = effect.Effect.fn("makeGitVcsDriverCore")(function*
6079
6496
  insertions,
6080
6497
  deletions
6081
6498
  },
6499
+ ...committed ? { committed } : {},
6082
6500
  hasUpstream: upstreamRef !== null,
6083
6501
  aheadCount,
6084
6502
  behindCount,
@@ -6099,6 +6517,7 @@ const makeGitVcsDriverCore = effect.Effect.fn("makeGitVcsDriverCore")(function*
6099
6517
  refName: details.branch,
6100
6518
  hasWorkingTreeChanges: details.hasWorkingTreeChanges,
6101
6519
  workingTree: details.workingTree,
6520
+ ...details.committed ? { committed: details.committed } : {},
6102
6521
  hasUpstream: details.hasUpstream,
6103
6522
  aheadCount: details.aheadCount,
6104
6523
  behindCount: details.behindCount,
@@ -6991,7 +7410,7 @@ const layer$13 = effect.Layer.effect(VcsDriverRegistry, make$16()).pipe(effect.L
6991
7410
  //#endregion
6992
7411
  //#region src/sourceControl/BitbucketApi.ts
6993
7412
  const DEFAULT_API_BASE_URL = "https://api.bitbucket.org/2.0";
6994
- const textDecoder$2 = new TextDecoder();
7413
+ const textDecoder$1 = new TextDecoder();
6995
7414
  const BitbucketApiEnvConfig = effect.Config.all({
6996
7415
  baseUrl: effect.Config.string("RYCO_BITBUCKET_API_BASE_URL").pipe(effect.Config.withDefault(DEFAULT_API_BASE_URL)),
6997
7416
  accessToken: effect.Config.string("RYCO_BITBUCKET_ACCESS_TOKEN").pipe(effect.Config.option),
@@ -7336,7 +7755,7 @@ const make$15 = effect.Effect.fn("makeBitbucketApi")(function* () {
7336
7755
  cause
7337
7756
  })));
7338
7757
  if (!tokenBytes || tokenBytes.length === 0) continue;
7339
- const token = textDecoder$2.decode(tokenBytes).trim();
7758
+ const token = textDecoder$1.decode(tokenBytes).trim();
7340
7759
  if (!token) continue;
7341
7760
  return {
7342
7761
  source: "stored",
@@ -7741,9 +8160,9 @@ function normalizeForgejoIssueDetail(input) {
7741
8160
  comments: input.comments.map(normalizeForgejoComment)
7742
8161
  };
7743
8162
  }
7744
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
7745
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(ForgejoIssueSchema);
7746
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(ForgejoCommentListSchema);
8163
+ require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
8164
+ require_collectUint8StreamText.decodeJsonResult(ForgejoIssueSchema);
8165
+ require_collectUint8StreamText.decodeJsonResult(ForgejoCommentListSchema);
7747
8166
  effect.Schema.decodeUnknownExit(ForgejoIssueSchema);
7748
8167
  //#endregion
7749
8168
  //#region src/sourceControl/forgejoPullRequests.ts
@@ -7882,8 +8301,8 @@ function normalizeForgejoPullRequestDetail(input) {
7882
8301
  files
7883
8302
  };
7884
8303
  }
7885
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
7886
- require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(ForgejoPullRequestSchema);
8304
+ require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
8305
+ require_collectUint8StreamText.decodeJsonResult(ForgejoPullRequestSchema);
7887
8306
  effect.Schema.decodeUnknownExit(ForgejoPullRequestSchema);
7888
8307
  //#endregion
7889
8308
  //#region src/sourceControl/ForgejoApi.ts
@@ -7915,7 +8334,7 @@ const ForgejoCliLoginSchema = effect.Schema.Struct({
7915
8334
  name: effect.Schema.optional(require_collectUint8StreamText.TrimmedNonEmptyString),
7916
8335
  token: effect.Schema.optional(require_collectUint8StreamText.TrimmedNonEmptyString)
7917
8336
  });
7918
- const decodeForgejoCliKeysJson = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Struct({ hosts: effect.Schema.optional(effect.Schema.Record(effect.Schema.String, ForgejoCliLoginSchema)) }));
8337
+ const decodeForgejoCliKeysJson = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Struct({ hosts: effect.Schema.optional(effect.Schema.Record(effect.Schema.String, ForgejoCliLoginSchema)) }));
7919
8338
  var ForgejoApi = class extends effect.Context.Service()("ryco/source-control/ForgejoApi") {};
7920
8339
  function nonEmpty$1(value) {
7921
8340
  const trimmed = value?.trim();
@@ -8478,7 +8897,7 @@ const RawGitHubReactionGroupSchema = effect.Schema.Struct({
8478
8897
  reactors: effect.Schema.optional(effect.Schema.Struct({ totalCount: effect.Schema.optional(effect.Schema.Number) })),
8479
8898
  users: effect.Schema.optional(effect.Schema.Struct({ totalCount: effect.Schema.optional(effect.Schema.Number) }))
8480
8899
  });
8481
- const decodeReactionGroupsBySubject = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Struct({ data: effect.Schema.Struct({ nodes: effect.Schema.Array(effect.Schema.NullOr(effect.Schema.Struct({
8900
+ const decodeReactionGroupsBySubject = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Struct({ data: effect.Schema.Struct({ nodes: effect.Schema.Array(effect.Schema.NullOr(effect.Schema.Struct({
8482
8901
  id: effect.Schema.String,
8483
8902
  reactionGroups: effect.Schema.Array(RawGitHubReactionGroupSchema)
8484
8903
  }))) }) }));
@@ -8521,7 +8940,7 @@ function normalizeReactionGroups(raw) {
8521
8940
  }];
8522
8941
  });
8523
8942
  }
8524
- const formatGitHubReactionGroupsDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
8943
+ const formatGitHubReactionGroupsDecodeError = require_collectUint8StreamText.formatSchemaError;
8525
8944
  function decodeGitHubReactionGroupsBySubjectJson(raw) {
8526
8945
  const result = decodeReactionGroupsBySubject(raw);
8527
8946
  if (!effect.Result.isSuccess(result)) return effect.Result.fail(result.failure);
@@ -8599,8 +9018,8 @@ function normalizeGitHubIssueRecord(raw) {
8599
9018
  commentsCount
8600
9019
  };
8601
9020
  }
8602
- const decodeIssueList$1 = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
8603
- const decodeIssueDetail$1 = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitHubIssueSchema);
9021
+ const decodeIssueList$1 = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9022
+ const decodeIssueDetail$1 = require_collectUint8StreamText.decodeJsonResult(GitHubIssueSchema);
8604
9023
  const decodeIssueEntry$1 = effect.Schema.decodeUnknownExit(GitHubIssueSchema);
8605
9024
  const GitHubLabelSchema = effect.Schema.Struct({
8606
9025
  name: effect.Schema.String,
@@ -8612,9 +9031,9 @@ const GitHubAssigneeSchema = effect.Schema.Struct({
8612
9031
  name: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String)),
8613
9032
  avatar_url: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String))
8614
9033
  });
8615
- const decodeJsonLabelList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(GitHubLabelSchema));
8616
- const decodeJsonAssigneeList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(GitHubAssigneeSchema));
8617
- const formatGitHubIssueDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
9034
+ const decodeJsonLabelList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(GitHubLabelSchema));
9035
+ const decodeJsonAssigneeList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(GitHubAssigneeSchema));
9036
+ const formatGitHubIssueDecodeError = require_collectUint8StreamText.formatSchemaError;
8618
9037
  function decodeGitHubIssueListJson(raw) {
8619
9038
  const result = decodeIssueList$1(raw);
8620
9039
  if (!effect.Result.isSuccess(result)) return effect.Result.fail(result.failure);
@@ -8763,10 +9182,10 @@ function normalizePullRequestWorkflowContext(raw) {
8763
9182
  baseRepositoryNameWithOwner: optionalTrimmed(raw.base?.repo?.full_name)
8764
9183
  };
8765
9184
  }
8766
- const decodeWorkflowRunsResponse = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(RawGitHubWorkflowRunsResponseSchema);
8767
- const decodeWorkflowJobsResponse = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(RawGitHubWorkflowJobsPayloadSchema);
8768
- const decodePullRequestWorkflowContext = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(RawGitHubPullRequestWorkflowContextSchema);
8769
- const formatGitHubWorkflowDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
9185
+ const decodeWorkflowRunsResponse = require_collectUint8StreamText.decodeJsonResult(RawGitHubWorkflowRunsResponseSchema);
9186
+ const decodeWorkflowJobsResponse = require_collectUint8StreamText.decodeJsonResult(RawGitHubWorkflowJobsPayloadSchema);
9187
+ const decodePullRequestWorkflowContext = require_collectUint8StreamText.decodeJsonResult(RawGitHubPullRequestWorkflowContextSchema);
9188
+ const formatGitHubWorkflowDecodeError = require_collectUint8StreamText.formatSchemaError;
8770
9189
  function decodeGitHubWorkflowRunsJson(raw) {
8771
9190
  const result = decodeWorkflowRunsResponse(raw);
8772
9191
  return effect.Result.isSuccess(result) ? effect.Result.succeed(result.success.workflow_runs.map(normalizeWorkflowRun)) : effect.Result.fail(result.failure);
@@ -8974,10 +9393,10 @@ function normalizeGitHubPullRequestRecord(raw) {
8974
9393
  ...raw.statusCheckRollup ? { checkRollup: raw.statusCheckRollup.map((item, index) => normalizeCheckRollupItem(item, index)) } : {}
8975
9394
  };
8976
9395
  }
8977
- const decodeGitHubPullRequestList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
8978
- const decodeGitHubPullRequest = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitHubPullRequestSchema);
9396
+ const decodeGitHubPullRequestList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9397
+ const decodeGitHubPullRequest = require_collectUint8StreamText.decodeJsonResult(GitHubPullRequestSchema);
8979
9398
  const decodeGitHubPullRequestEntry = effect.Schema.decodeUnknownExit(GitHubPullRequestSchema);
8980
- const formatGitHubJsonDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
9399
+ const formatGitHubJsonDecodeError = require_collectUint8StreamText.formatSchemaError;
8981
9400
  function decodeGitHubPullRequestListJson(raw) {
8982
9401
  const result = decodeGitHubPullRequestList(raw);
8983
9402
  if (effect.Result.isSuccess(result)) {
@@ -9296,6 +9715,7 @@ function workflowRunsEndpoint(input) {
9296
9715
  const params = new URLSearchParams();
9297
9716
  params.set("per_page", String(workflowRunLimit(input.limit)));
9298
9717
  if (input.headSha?.trim()) params.set("head_sha", input.headSha.trim());
9718
+ else if (input.branch?.trim()) params.set("branch", input.branch.trim());
9299
9719
  return `repos/{owner}/{repo}/actions/runs?${params.toString()}`;
9300
9720
  }
9301
9721
  function isWorkflowRerunStateError(error) {
@@ -9650,7 +10070,7 @@ const make$13 = effect.Effect.fn("makeGitHubCli")(function* () {
9650
10070
  ...l.description ? { description: l.description } : {}
9651
10071
  }))) : effect.Effect.fail(new GitHubCliError({
9652
10072
  operation: "listLabels",
9653
- detail: `Invalid label list output: ${require_SourceControlProviderDiscoveryCatalog.formatSchemaError(decoded.failure)}`,
10073
+ detail: `Invalid label list output: ${require_collectUint8StreamText.formatSchemaError(decoded.failure)}`,
9654
10074
  cause: decoded.failure
9655
10075
  })))))),
9656
10076
  listAssignees: (input) => execute({
@@ -9670,7 +10090,7 @@ const make$13 = effect.Effect.fn("makeGitHubCli")(function* () {
9670
10090
  ...u.avatar_url ? { avatarUrl: u.avatar_url } : {}
9671
10091
  }))) : effect.Effect.fail(new GitHubCliError({
9672
10092
  operation: "listAssignees",
9673
- detail: `Invalid assignees output: ${require_SourceControlProviderDiscoveryCatalog.formatSchemaError(decoded.failure)}`,
10093
+ detail: `Invalid assignees output: ${require_collectUint8StreamText.formatSchemaError(decoded.failure)}`,
9674
10094
  cause: decoded.failure
9675
10095
  })))))),
9676
10096
  listWorkflowRuns: (input) => execute({
@@ -9779,10 +10199,10 @@ function normalizeGitLabIssueRecord(raw) {
9779
10199
  labels: raw.labels ?? []
9780
10200
  };
9781
10201
  }
9782
- const decodeIssueList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9783
- const decodeIssueDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitLabIssueSchema);
10202
+ const decodeIssueList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
10203
+ const decodeIssueDetail = require_collectUint8StreamText.decodeJsonResult(GitLabIssueSchema);
9784
10204
  const decodeIssueEntry = effect.Schema.decodeUnknownExit(GitLabIssueSchema);
9785
- const formatGitLabIssueDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
10205
+ const formatGitLabIssueDecodeError = require_collectUint8StreamText.formatSchemaError;
9786
10206
  function decodeGitLabIssueListJson(raw) {
9787
10207
  const result = decodeIssueList(raw);
9788
10208
  if (!effect.Result.isSuccess(result)) return effect.Result.fail(result.failure);
@@ -9869,10 +10289,10 @@ function normalizeGitLabMergeRequestRecord(raw) {
9869
10289
  ...headRepositoryOwnerLogin ? { headRepositoryOwnerLogin } : {}
9870
10290
  };
9871
10291
  }
9872
- const decodeGitLabMergeRequestList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9873
- const decodeGitLabMergeRequest = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitLabMergeRequestSchema);
10292
+ const decodeGitLabMergeRequestList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
10293
+ const decodeGitLabMergeRequest = require_collectUint8StreamText.decodeJsonResult(GitLabMergeRequestSchema);
9874
10294
  const decodeGitLabMergeRequestEntry = effect.Schema.decodeUnknownExit(GitLabMergeRequestSchema);
9875
- const formatGitLabJsonDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
10295
+ const formatGitLabJsonDecodeError = require_collectUint8StreamText.formatSchemaError;
9876
10296
  function decodeGitLabMergeRequestListJson(raw) {
9877
10297
  const result = decodeGitLabMergeRequestList(raw);
9878
10298
  if (effect.Result.isSuccess(result)) {
@@ -9891,7 +10311,7 @@ function decodeGitLabMergeRequestJson(raw) {
9891
10311
  if (effect.Result.isSuccess(result)) return effect.Result.succeed(normalizeGitLabMergeRequestRecord(result.success));
9892
10312
  return effect.Result.fail(result.failure);
9893
10313
  }
9894
- const decodeGitLabMergeRequestDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Struct({
10314
+ const decodeGitLabMergeRequestDetail = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Struct({
9895
10315
  ...GitLabMergeRequestSchema.fields,
9896
10316
  description: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String)),
9897
10317
  notes: effect.Schema.optional(effect.Schema.Array(effect.Schema.Struct({
@@ -10561,26 +10981,26 @@ const make$11 = effect.Effect.fn("makeSourceControlProviderRegistry")(function*
10561
10981
  const azureDevOpsCli = yield* AzureDevOpsCli;
10562
10982
  const fileSystem = yield* effect.FileSystem.FileSystem;
10563
10983
  const github = yield* makeLazyProvider("github", effect.Effect.tryPromise({
10564
- try: () => Promise.resolve().then(() => require("./GitHubSourceControlProvider-9lJFzjsu.cjs")),
10984
+ try: () => Promise.resolve().then(() => require("./GitHubSourceControlProvider-B1Tp0TbY.cjs")),
10565
10985
  catch: (cause) => providerLoadError("github", cause)
10566
10986
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(GitHubCli, githubCli), effect.Effect.provideService(effect.FileSystem.FileSystem, fileSystem)));
10567
10987
  const gitlab = yield* makeLazyProvider("gitlab", effect.Effect.tryPromise({
10568
- try: () => Promise.resolve().then(() => require("./GitLabSourceControlProvider-CvKA74lg.cjs")),
10988
+ try: () => Promise.resolve().then(() => require("./GitLabSourceControlProvider-D3vvKrB4.cjs")),
10569
10989
  catch: (cause) => providerLoadError("gitlab", cause)
10570
10990
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(GitLabCli, gitlabCli)));
10571
10991
  const bitbucket = yield* makeLazyProvider("bitbucket", effect.Effect.tryPromise({
10572
- try: () => Promise.resolve().then(() => require("./BitbucketSourceControlProvider-a0IsNFyG.cjs")),
10992
+ try: () => Promise.resolve().then(() => require("./BitbucketSourceControlProvider-D_7i4YQz.cjs")),
10573
10993
  catch: (cause) => providerLoadError("bitbucket", cause)
10574
10994
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(BitbucketApi, bitbucketApi)), {
10575
10995
  searchRepositories: true,
10576
10996
  cloneAuthentication: true
10577
10997
  });
10578
10998
  const forgejo = yield* makeLazyProvider("forgejo", effect.Effect.tryPromise({
10579
- try: () => Promise.resolve().then(() => require("./ForgejoSourceControlProvider-DwAog76b.cjs")),
10999
+ try: () => Promise.resolve().then(() => require("./ForgejoSourceControlProvider-DYFUGvBk.cjs")),
10580
11000
  catch: (cause) => providerLoadError("forgejo", cause)
10581
11001
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(ForgejoApi, forgejoApi)));
10582
11002
  const azureDevOps = yield* makeLazyProvider("azure-devops", effect.Effect.tryPromise({
10583
- try: () => Promise.resolve().then(() => require("./AzureDevOpsSourceControlProvider-6holYS_D.cjs")),
11003
+ try: () => Promise.resolve().then(() => require("./AzureDevOpsSourceControlProvider-CYZmJqSM.cjs")),
10584
11004
  catch: (cause) => providerLoadError("azure-devops", cause)
10585
11005
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(AzureDevOpsCli, azureDevOpsCli)));
10586
11006
  const bitbucketDiscovery = require_SourceControlProviderDiscoveryCatalog.makeBitbucketDiscovery(bitbucketApi);
@@ -10703,22 +11123,6 @@ var CheckpointDiffQuery = class extends effect.Context.Service()("ryco/checkpoin
10703
11123
  //#region src/diagnostics/Services/Diagnostics.ts
10704
11124
  var Diagnostics = class extends effect.Context.Service()("ryco/diagnostics/Services/Diagnostics") {};
10705
11125
  //#endregion
10706
- //#region src/atomicWrite.ts
10707
- const writeFileStringAtomically = (input) => effect.Effect.scoped(effect.Effect.gen(function* () {
10708
- const fs = yield* effect.FileSystem.FileSystem;
10709
- const path = yield* effect.Path.Path;
10710
- const tempFileId = yield* effect_Random.nextUUIDv4;
10711
- const targetDirectory = path.dirname(input.filePath);
10712
- yield* fs.makeDirectory(targetDirectory, { recursive: true });
10713
- const tempDirectory = yield* fs.makeTempDirectoryScoped({
10714
- directory: targetDirectory,
10715
- prefix: `${path.basename(input.filePath)}.`
10716
- });
10717
- const tempPath = path.join(tempDirectory, `${tempFileId}.tmp`);
10718
- yield* fs.writeFileString(tempPath, input.contents);
10719
- yield* fs.rename(tempPath, input.filePath);
10720
- }));
10721
- //#endregion
10722
11126
  //#region ../../packages/shared/src/keybindings.ts
10723
11127
  const DEFAULT_KEYBINDINGS = [
10724
11128
  {
@@ -10789,6 +11193,11 @@ const DEFAULT_KEYBINDINGS = [
10789
11193
  key: "mod+o",
10790
11194
  command: "editor.openFavorite"
10791
11195
  },
11196
+ {
11197
+ key: "mod+f",
11198
+ command: "thread.find",
11199
+ when: "!terminalFocus"
11200
+ },
10792
11201
  {
10793
11202
  key: "mod+shift+[",
10794
11203
  command: "thread.previous"
@@ -11069,7 +11478,7 @@ function encodeWhenAst(node) {
11069
11478
  case "or": return `(${encodeWhenAst(node.left)} || ${encodeWhenAst(node.right)})`;
11070
11479
  }
11071
11480
  }
11072
- const RawKeybindingsEntries = require_SourceControlProviderDiscoveryCatalog.fromLenientJson(effect.Schema.Array(effect.Schema.Unknown));
11481
+ const RawKeybindingsEntries = require_collectUint8StreamText.fromLenientJson(effect.Schema.Array(effect.Schema.Unknown));
11073
11482
  const KeybindingsConfigJson = effect.Schema.fromJsonString(require_collectUint8StreamText.KeybindingsConfig);
11074
11483
  const PrettyJsonString = effect.SchemaGetter.parseJson().compose(effect.SchemaGetter.stringifyJson({ space: 2 }));
11075
11484
  const KeybindingsConfigPrettyJson = KeybindingsConfigJson.pipe(effect.Schema.encode({
@@ -11610,14 +12019,7 @@ const ClientRequest__PluginsMigration = effect_Schema.Struct({
11610
12019
  marketplaceName: effect_Schema.String,
11611
12020
  pluginNames: effect_Schema.Array(effect_Schema.String)
11612
12021
  });
11613
- const ClientRequest__ReasoningEffort = effect_Schema.Literals([
11614
- "none",
11615
- "minimal",
11616
- "low",
11617
- "medium",
11618
- "high",
11619
- "xhigh"
11620
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
12022
+ const ClientRequest__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
11621
12023
  const ClientRequest__ReasoningItemContent = effect_Schema.Union([effect_Schema.Struct({
11622
12024
  text: effect_Schema.String,
11623
12025
  type: effect_Schema.Literal("reasoning_text").annotate({ title: "ReasoningTextReasoningItemContentType" })
@@ -12193,14 +12595,7 @@ const ServerNotification__RateLimitWindow = effect_Schema.Struct({
12193
12595
  windowDurationMins: effect_Schema.optionalKey(effect_Schema.Union([effect_Schema.Number.annotate({ format: "int64" }).check(effect_Schema.isInt()), effect_Schema.Null]))
12194
12596
  });
12195
12597
  const ServerNotification__RealtimeConversationVersion = effect_Schema.Literals(["v1", "v2"]);
12196
- const ServerNotification__ReasoningEffort = effect_Schema.Literals([
12197
- "none",
12198
- "minimal",
12199
- "low",
12200
- "medium",
12201
- "high",
12202
- "xhigh"
12203
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
12598
+ const ServerNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12204
12599
  const ServerNotification__ReasoningSummaryPartAddedNotification = effect_Schema.Struct({
12205
12600
  itemId: effect_Schema.String,
12206
12601
  summaryIndex: effect_Schema.Number.annotate({ format: "int64" }).check(effect_Schema.isInt()),
@@ -12579,14 +12974,7 @@ const V2ConfigReadResponse__AskForApproval = effect_Schema.Union([effect_Schema.
12579
12974
  skill_approval: effect_Schema.optionalKey(effect_Schema.Boolean.annotate({ default: false }))
12580
12975
  }) }).annotate({ title: "GranularAskForApproval" })], { mode: "oneOf" });
12581
12976
  const V2ConfigReadResponse__ForcedLoginMethod = effect_Schema.Literals(["chatgpt", "api"]);
12582
- const V2ConfigReadResponse__ReasoningEffort = effect_Schema.Literals([
12583
- "none",
12584
- "minimal",
12585
- "low",
12586
- "medium",
12587
- "high",
12588
- "xhigh"
12589
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
12977
+ const V2ConfigReadResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12590
12978
  const V2ConfigReadResponse__ReasoningSummary = effect_Schema.Union([effect_Schema.Literals([
12591
12979
  "auto",
12592
12980
  "concise",
@@ -12904,14 +13292,7 @@ const V2ItemCompletedNotification__PatchChangeKind = effect_Schema.Union([
12904
13292
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
12905
13293
  }).annotate({ title: "UpdatePatchChangeKind" })
12906
13294
  ], { mode: "oneOf" });
12907
- const V2ItemCompletedNotification__ReasoningEffort = effect_Schema.Literals([
12908
- "none",
12909
- "minimal",
12910
- "low",
12911
- "medium",
12912
- "high",
12913
- "xhigh"
12914
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13295
+ const V2ItemCompletedNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
12915
13296
  const V2ItemCompletedNotification__TextElement = effect_Schema.Struct({
12916
13297
  byteRange: effect_Schema.Struct({
12917
13298
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -13099,14 +13480,7 @@ const V2ItemStartedNotification__PatchChangeKind = effect_Schema.Union([
13099
13480
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13100
13481
  }).annotate({ title: "UpdatePatchChangeKind" })
13101
13482
  ], { mode: "oneOf" });
13102
- const V2ItemStartedNotification__ReasoningEffort = effect_Schema.Literals([
13103
- "none",
13104
- "minimal",
13105
- "low",
13106
- "medium",
13107
- "high",
13108
- "xhigh"
13109
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13483
+ const V2ItemStartedNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13110
13484
  const V2ItemStartedNotification__TextElement = effect_Schema.Struct({
13111
13485
  byteRange: effect_Schema.Struct({
13112
13486
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -13194,14 +13568,7 @@ const V2ModelListResponse__ModelUpgradeInfo = effect_Schema.Struct({
13194
13568
  modelLink: effect_Schema.optionalKey(effect_Schema.Union([effect_Schema.String, effect_Schema.Null])),
13195
13569
  upgradeCopy: effect_Schema.optionalKey(effect_Schema.Union([effect_Schema.String, effect_Schema.Null]))
13196
13570
  });
13197
- const V2ModelListResponse__ReasoningEffort = effect_Schema.Literals([
13198
- "none",
13199
- "minimal",
13200
- "low",
13201
- "medium",
13202
- "high",
13203
- "xhigh"
13204
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13571
+ const V2ModelListResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13205
13572
  const V2ModelReroutedNotification__ModelRerouteReason = effect_Schema.Literal("highRiskCyberActivity");
13206
13573
  const V2PluginInstallParams__AbsolutePathBuf = effect_Schema.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." });
13207
13574
  const V2PluginInstallResponse__AppSummary = effect_Schema.Struct({
@@ -13384,14 +13751,7 @@ const V2ReviewStartResponse__PatchChangeKind = effect_Schema.Union([
13384
13751
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13385
13752
  }).annotate({ title: "UpdatePatchChangeKind" })
13386
13753
  ], { mode: "oneOf" });
13387
- const V2ReviewStartResponse__ReasoningEffort = effect_Schema.Literals([
13388
- "none",
13389
- "minimal",
13390
- "low",
13391
- "medium",
13392
- "high",
13393
- "xhigh"
13394
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13754
+ const V2ReviewStartResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13395
13755
  const V2ReviewStartResponse__TextElement = effect_Schema.Struct({
13396
13756
  byteRange: effect_Schema.Struct({
13397
13757
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -13555,14 +13915,7 @@ const V2ThreadForkResponse__PatchChangeKind = effect_Schema.Union([
13555
13915
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13556
13916
  }).annotate({ title: "UpdatePatchChangeKind" })
13557
13917
  ], { mode: "oneOf" });
13558
- const V2ThreadForkResponse__ReasoningEffort = effect_Schema.Literals([
13559
- "none",
13560
- "minimal",
13561
- "low",
13562
- "medium",
13563
- "high",
13564
- "xhigh"
13565
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
13918
+ const V2ThreadForkResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13566
13919
  const V2ThreadForkResponse__ServiceTier = effect_Schema.Literals([
13567
13920
  "fast",
13568
13921
  "flex",
@@ -13685,14 +14038,7 @@ const V2ThreadListResponse__PatchChangeKind = effect_Schema.Union([
13685
14038
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13686
14039
  }).annotate({ title: "UpdatePatchChangeKind" })
13687
14040
  ], { mode: "oneOf" });
13688
- const V2ThreadListResponse__ReasoningEffort = effect_Schema.Literals([
13689
- "none",
13690
- "minimal",
13691
- "low",
13692
- "medium",
13693
- "high",
13694
- "xhigh"
13695
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14041
+ const V2ThreadListResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13696
14042
  const V2ThreadListResponse__TextElement = effect_Schema.Struct({
13697
14043
  byteRange: effect_Schema.Struct({
13698
14044
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -13801,14 +14147,7 @@ const V2ThreadMetadataUpdateResponse__PatchChangeKind = effect_Schema.Union([
13801
14147
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13802
14148
  }).annotate({ title: "UpdatePatchChangeKind" })
13803
14149
  ], { mode: "oneOf" });
13804
- const V2ThreadMetadataUpdateResponse__ReasoningEffort = effect_Schema.Literals([
13805
- "none",
13806
- "minimal",
13807
- "low",
13808
- "medium",
13809
- "high",
13810
- "xhigh"
13811
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14150
+ const V2ThreadMetadataUpdateResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13812
14151
  const V2ThreadMetadataUpdateResponse__TextElement = effect_Schema.Struct({
13813
14152
  byteRange: effect_Schema.Struct({
13814
14153
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -13912,14 +14251,7 @@ const V2ThreadReadResponse__PatchChangeKind = effect_Schema.Union([
13912
14251
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
13913
14252
  }).annotate({ title: "UpdatePatchChangeKind" })
13914
14253
  ], { mode: "oneOf" });
13915
- const V2ThreadReadResponse__ReasoningEffort = effect_Schema.Literals([
13916
- "none",
13917
- "minimal",
13918
- "low",
13919
- "medium",
13920
- "high",
13921
- "xhigh"
13922
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14254
+ const V2ThreadReadResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
13923
14255
  const V2ThreadReadResponse__TextElement = effect_Schema.Struct({
13924
14256
  byteRange: effect_Schema.Struct({
13925
14257
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14125,14 +14457,7 @@ const V2ThreadResumeResponse__PatchChangeKind = effect_Schema.Union([
14125
14457
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14126
14458
  }).annotate({ title: "UpdatePatchChangeKind" })
14127
14459
  ], { mode: "oneOf" });
14128
- const V2ThreadResumeResponse__ReasoningEffort = effect_Schema.Literals([
14129
- "none",
14130
- "minimal",
14131
- "low",
14132
- "medium",
14133
- "high",
14134
- "xhigh"
14135
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14460
+ const V2ThreadResumeResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14136
14461
  const V2ThreadResumeResponse__ServiceTier = effect_Schema.Literals([
14137
14462
  "fast",
14138
14463
  "flex",
@@ -14241,14 +14566,7 @@ const V2ThreadRollbackResponse__PatchChangeKind = effect_Schema.Union([
14241
14566
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14242
14567
  }).annotate({ title: "UpdatePatchChangeKind" })
14243
14568
  ], { mode: "oneOf" });
14244
- const V2ThreadRollbackResponse__ReasoningEffort = effect_Schema.Literals([
14245
- "none",
14246
- "minimal",
14247
- "low",
14248
- "medium",
14249
- "high",
14250
- "xhigh"
14251
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14569
+ const V2ThreadRollbackResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14252
14570
  const V2ThreadRollbackResponse__TextElement = effect_Schema.Struct({
14253
14571
  byteRange: effect_Schema.Struct({
14254
14572
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14352,14 +14670,7 @@ const V2ThreadStartedNotification__PatchChangeKind = effect_Schema.Union([
14352
14670
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14353
14671
  }).annotate({ title: "UpdatePatchChangeKind" })
14354
14672
  ], { mode: "oneOf" });
14355
- const V2ThreadStartedNotification__ReasoningEffort = effect_Schema.Literals([
14356
- "none",
14357
- "minimal",
14358
- "low",
14359
- "medium",
14360
- "high",
14361
- "xhigh"
14362
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14673
+ const V2ThreadStartedNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14363
14674
  const V2ThreadStartedNotification__TextElement = effect_Schema.Struct({
14364
14675
  byteRange: effect_Schema.Struct({
14365
14676
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14504,14 +14815,7 @@ const V2ThreadStartResponse__PatchChangeKind = effect_Schema.Union([
14504
14815
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14505
14816
  }).annotate({ title: "UpdatePatchChangeKind" })
14506
14817
  ], { mode: "oneOf" });
14507
- const V2ThreadStartResponse__ReasoningEffort = effect_Schema.Literals([
14508
- "none",
14509
- "minimal",
14510
- "low",
14511
- "medium",
14512
- "high",
14513
- "xhigh"
14514
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14818
+ const V2ThreadStartResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14515
14819
  const V2ThreadStartResponse__ServiceTier = effect_Schema.Literals([
14516
14820
  "fast",
14517
14821
  "flex",
@@ -14623,14 +14927,7 @@ const V2ThreadTurnsListResponse__PatchChangeKind = effect_Schema.Union([
14623
14927
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14624
14928
  }).annotate({ title: "UpdatePatchChangeKind" })
14625
14929
  ], { mode: "oneOf" });
14626
- const V2ThreadTurnsListResponse__ReasoningEffort = effect_Schema.Literals([
14627
- "none",
14628
- "minimal",
14629
- "low",
14630
- "medium",
14631
- "high",
14632
- "xhigh"
14633
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
14930
+ const V2ThreadTurnsListResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14634
14931
  const V2ThreadTurnsListResponse__TextElement = effect_Schema.Struct({
14635
14932
  byteRange: effect_Schema.Struct({
14636
14933
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14732,14 +15029,7 @@ const V2ThreadUnarchiveResponse__PatchChangeKind = effect_Schema.Union([
14732
15029
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14733
15030
  }).annotate({ title: "UpdatePatchChangeKind" })
14734
15031
  ], { mode: "oneOf" });
14735
- const V2ThreadUnarchiveResponse__ReasoningEffort = effect_Schema.Literals([
14736
- "none",
14737
- "minimal",
14738
- "low",
14739
- "medium",
14740
- "high",
14741
- "xhigh"
14742
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15032
+ const V2ThreadUnarchiveResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14743
15033
  const V2ThreadUnarchiveResponse__TextElement = effect_Schema.Struct({
14744
15034
  byteRange: effect_Schema.Struct({
14745
15035
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14842,14 +15132,7 @@ const V2TurnCompletedNotification__PatchChangeKind = effect_Schema.Union([
14842
15132
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14843
15133
  }).annotate({ title: "UpdatePatchChangeKind" })
14844
15134
  ], { mode: "oneOf" });
14845
- const V2TurnCompletedNotification__ReasoningEffort = effect_Schema.Literals([
14846
- "none",
14847
- "minimal",
14848
- "low",
14849
- "medium",
14850
- "high",
14851
- "xhigh"
14852
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15135
+ const V2TurnCompletedNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14853
15136
  const V2TurnCompletedNotification__TextElement = effect_Schema.Struct({
14854
15137
  byteRange: effect_Schema.Struct({
14855
15138
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -14950,14 +15233,7 @@ const V2TurnStartedNotification__PatchChangeKind = effect_Schema.Union([
14950
15233
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
14951
15234
  }).annotate({ title: "UpdatePatchChangeKind" })
14952
15235
  ], { mode: "oneOf" });
14953
- const V2TurnStartedNotification__ReasoningEffort = effect_Schema.Literals([
14954
- "none",
14955
- "minimal",
14956
- "low",
14957
- "medium",
14958
- "high",
14959
- "xhigh"
14960
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15236
+ const V2TurnStartedNotification__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
14961
15237
  const V2TurnStartedNotification__TextElement = effect_Schema.Struct({
14962
15238
  byteRange: effect_Schema.Struct({
14963
15239
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -15008,14 +15284,7 @@ const V2TurnStartParams__Personality = effect_Schema.Literals([
15008
15284
  "friendly",
15009
15285
  "pragmatic"
15010
15286
  ]);
15011
- const V2TurnStartParams__ReasoningEffort = effect_Schema.Literals([
15012
- "none",
15013
- "minimal",
15014
- "low",
15015
- "medium",
15016
- "high",
15017
- "xhigh"
15018
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15287
+ const V2TurnStartParams__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
15019
15288
  const V2TurnStartParams__ReasoningSummary = effect_Schema.Union([effect_Schema.Literals([
15020
15289
  "auto",
15021
15290
  "concise",
@@ -15098,14 +15367,7 @@ const V2TurnStartResponse__PatchChangeKind = effect_Schema.Union([
15098
15367
  type: effect_Schema.Literal("update").annotate({ title: "UpdatePatchChangeKindType" })
15099
15368
  }).annotate({ title: "UpdatePatchChangeKind" })
15100
15369
  ], { mode: "oneOf" });
15101
- const V2TurnStartResponse__ReasoningEffort = effect_Schema.Literals([
15102
- "none",
15103
- "minimal",
15104
- "low",
15105
- "medium",
15106
- "high",
15107
- "xhigh"
15108
- ]).annotate({ description: "See https://platform.openai.com/docs/guides/reasoning?api-mode=responses#get-started-with-reasoning" });
15370
+ const V2TurnStartResponse__ReasoningEffort = effect_Schema.String.check(effect_Schema.isNonEmpty()).annotate({ description: "A non-empty reasoning effort value advertised by the model." });
15109
15371
  const V2TurnStartResponse__TextElement = effect_Schema.Struct({
15110
15372
  byteRange: effect_Schema.Struct({
15111
15373
  end: effect_Schema.Number.annotate({ format: "uint" }).check(effect_Schema.isInt()).check(effect_Schema.isGreaterThanOrEqualTo(0)),
@@ -26104,300 +26366,6 @@ async function runProcess(command, args, options = {}) {
26104
26366
  });
26105
26367
  }
26106
26368
  //#endregion
26107
- //#region src/serverSettings.ts
26108
- /**
26109
- * ServerSettings - Server-authoritative settings service.
26110
- *
26111
- * Owns persistence, validation, and change notification of settings that affect
26112
- * server-side behavior (binary paths, streaming mode, env mode, custom models,
26113
- * text generation model selection).
26114
- *
26115
- * Follows the same pattern as `keybindings.ts`: JSON file + Cache + PubSub +
26116
- * Semaphore + FileSystem.watch for concurrency and external edit detection.
26117
- *
26118
- * @module ServerSettings
26119
- */
26120
- const textEncoder$1 = new TextEncoder();
26121
- const textDecoder$1 = new TextDecoder();
26122
- function providerEnvironmentSecretName(input) {
26123
- return `provider-env-${Buffer.from(input.instanceId, "utf8").toString("base64url")}-${Buffer.from(input.name, "utf8").toString("base64url")}`;
26124
- }
26125
- function redactProviderEnvironmentVariable(variable) {
26126
- if (!variable.sensitive) {
26127
- const { valueRedacted: _omit, ...rest } = variable;
26128
- return rest;
26129
- }
26130
- return {
26131
- ...variable,
26132
- value: "",
26133
- ...variable.value.length > 0 || variable.valueRedacted ? { valueRedacted: true } : {}
26134
- };
26135
- }
26136
- function redactServerSettingsForClient(settings) {
26137
- const providerInstances = Object.fromEntries(Object.entries(settings.providerInstances).map(([instanceId, instance]) => [instanceId, instance.environment ? {
26138
- ...instance,
26139
- environment: instance.environment.map(redactProviderEnvironmentVariable)
26140
- } : instance]));
26141
- return {
26142
- ...settings,
26143
- providerInstances
26144
- };
26145
- }
26146
- var ServerSettingsService = class ServerSettingsService extends effect.Context.Service()("ryco/serverSettings/ServerSettingsService") {
26147
- static layerTest = (overrides = {}) => effect.Layer.effect(ServerSettingsService, effect.Effect.gen(function* () {
26148
- const currentSettingsRef = yield* effect.Ref.make(deepMerge(require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS, overrides));
26149
- return {
26150
- start: effect.Effect.void,
26151
- ready: effect.Effect.void,
26152
- getSettings: effect.Ref.get(currentSettingsRef),
26153
- updateSettings: (patch) => effect.Ref.get(currentSettingsRef).pipe(effect.Effect.flatMap((currentSettings) => effect.Schema.decodeEffect(require_collectUint8StreamText.ServerSettings)(applyServerSettingsPatch(currentSettings, patch)).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26154
- settingsPath: "<memory>",
26155
- detail: `failed to normalize server settings: ${effect.SchemaIssue.makeFormatterDefault()(cause.issue)}`,
26156
- cause
26157
- })))), effect.Effect.tap((nextSettings) => effect.Ref.set(currentSettingsRef, nextSettings))),
26158
- streamChanges: effect.Stream.empty
26159
- };
26160
- }));
26161
- };
26162
- const ServerSettingsJson = require_SourceControlProviderDiscoveryCatalog.fromLenientJson(require_collectUint8StreamText.ServerSettings);
26163
- const getLegacyProviderSettings = (settings, provider) => settings.providers[provider];
26164
- /**
26165
- * Ensure the `textGenerationModelSelection` points to an enabled provider.
26166
- * If the selected provider is disabled, fall back to the first enabled
26167
- * provider with its default model. This is applied at read-time so the
26168
- * persisted preference is preserved for when a provider is re-enabled.
26169
- */
26170
- function resolveTextGenerationProvider(settings) {
26171
- const selection = settings.textGenerationModelSelection;
26172
- const instanceConfig = settings.providerInstances[selection.instanceId];
26173
- if (instanceConfig !== void 0) return instanceConfig.enabled ?? true ? settings : fallbackTextGenerationProvider(settings);
26174
- if (require_collectUint8StreamText.isProviderDriverKind(selection.instanceId) && getLegacyProviderSettings(settings, selection.instanceId)?.enabled) return settings;
26175
- return fallbackTextGenerationProvider(settings);
26176
- }
26177
- function fallbackTextGenerationProvider(settings) {
26178
- const fallbackEntry = Object.entries(settings.providers).find(([, provider]) => provider.enabled);
26179
- const fallback = fallbackEntry ? require_collectUint8StreamText.ProviderDriverKind.make(fallbackEntry[0]) : void 0;
26180
- if (!fallback) return settings;
26181
- return {
26182
- ...settings,
26183
- textGenerationModelSelection: {
26184
- instanceId: require_collectUint8StreamText.ProviderInstanceId.make(fallback),
26185
- model: require_collectUint8StreamText.DEFAULT_GIT_TEXT_GENERATION_MODEL_BY_PROVIDER[fallback] ?? "gpt-5.4-mini"
26186
- }
26187
- };
26188
- }
26189
- const ATOMIC_SETTINGS_KEYS = new Set(["textGenerationModelSelection"]);
26190
- function stripDefaultServerSettings(current, defaults) {
26191
- if (Array.isArray(current) || Array.isArray(defaults)) return effect.Equal.equals(current, defaults) ? void 0 : current;
26192
- if (current !== null && defaults !== null && typeof current === "object" && typeof defaults === "object") {
26193
- const currentRecord = current;
26194
- const defaultsRecord = defaults;
26195
- const next = {};
26196
- for (const key of Object.keys(currentRecord)) if (ATOMIC_SETTINGS_KEYS.has(key)) {
26197
- if (!effect.Equal.equals(currentRecord[key], defaultsRecord[key])) next[key] = currentRecord[key];
26198
- } else {
26199
- const stripped = stripDefaultServerSettings(currentRecord[key], defaultsRecord[key]);
26200
- if (stripped !== void 0) next[key] = stripped;
26201
- }
26202
- return Object.keys(next).length > 0 ? next : void 0;
26203
- }
26204
- return Object.is(current, defaults) ? void 0 : current;
26205
- }
26206
- const makeServerSettings = effect.Effect.gen(function* () {
26207
- const { settingsPath } = yield* require_Metrics.ServerConfig;
26208
- const fs = yield* effect.FileSystem.FileSystem;
26209
- const pathService = yield* effect.Path.Path;
26210
- const secretStore = yield* ServerSecretStore;
26211
- const writeSemaphore = yield* effect_Semaphore.make(1);
26212
- const cacheKey = "settings";
26213
- const changesPubSub = yield* effect.PubSub.unbounded();
26214
- const startedRef = yield* effect.Ref.make(false);
26215
- const startedDeferred = yield* effect.Deferred.make();
26216
- const watcherScope = yield* effect.Scope.make("sequential");
26217
- yield* effect.Effect.addFinalizer(() => effect.Scope.close(watcherScope, effect.Exit.void));
26218
- const emitChange = (settings) => effect.PubSub.publish(changesPubSub, settings).pipe(effect.Effect.asVoid);
26219
- const readConfigExists = fs.exists(settingsPath).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26220
- settingsPath,
26221
- detail: "failed to check settings file existence",
26222
- cause
26223
- })));
26224
- const readRawConfig = fs.readFileString(settingsPath).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26225
- settingsPath,
26226
- detail: "failed to read settings file",
26227
- cause
26228
- })));
26229
- const loadSettingsFromDisk = effect.Effect.gen(function* () {
26230
- if (!(yield* readConfigExists)) return require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS;
26231
- const raw = yield* readRawConfig;
26232
- const decoded = effect.Schema.decodeUnknownExit(ServerSettingsJson)(raw);
26233
- if (decoded._tag === "Failure") {
26234
- yield* effect.Effect.logWarning("failed to parse settings.json, using defaults", {
26235
- path: settingsPath,
26236
- issues: effect.Cause.pretty(decoded.cause)
26237
- });
26238
- return require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS;
26239
- }
26240
- return decoded.value;
26241
- });
26242
- const settingsCache = yield* effect.Cache.make({
26243
- capacity: 1,
26244
- lookup: () => loadSettingsFromDisk
26245
- });
26246
- const getSettingsFromCache = effect.Cache.get(settingsCache, cacheKey);
26247
- const toSettingsError = (detail, cause) => new require_collectUint8StreamText.ServerSettingsError({
26248
- settingsPath,
26249
- detail,
26250
- cause
26251
- });
26252
- const materializeProviderEnvironmentSecrets = (settings) => effect.Effect.gen(function* () {
26253
- const providerInstances = { ...settings.providerInstances };
26254
- for (const [instanceId, instance] of Object.entries(settings.providerInstances)) {
26255
- if (!instance.environment) continue;
26256
- const environment = [];
26257
- for (const variable of instance.environment) {
26258
- if (!variable.sensitive || !variable.valueRedacted) {
26259
- environment.push(variable);
26260
- continue;
26261
- }
26262
- const secret = yield* secretStore.get(providerEnvironmentSecretName({
26263
- instanceId,
26264
- name: variable.name
26265
- })).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to read sensitive environment variable ${variable.name}`, cause)));
26266
- environment.push({
26267
- ...variable,
26268
- value: secret ? textDecoder$1.decode(secret) : ""
26269
- });
26270
- }
26271
- providerInstances[instanceId] = {
26272
- ...instance,
26273
- environment
26274
- };
26275
- }
26276
- return {
26277
- ...settings,
26278
- providerInstances
26279
- };
26280
- });
26281
- const persistProviderEnvironmentSecrets = (current, next) => effect.Effect.gen(function* () {
26282
- const providerInstances = { ...next.providerInstances };
26283
- const nextSecretKeys = /* @__PURE__ */ new Set();
26284
- for (const [instanceId, instance] of Object.entries(next.providerInstances)) {
26285
- if (!instance.environment) continue;
26286
- const environment = [];
26287
- for (const variable of instance.environment) {
26288
- const secretName = providerEnvironmentSecretName({
26289
- instanceId,
26290
- name: variable.name
26291
- });
26292
- if (!variable.sensitive) {
26293
- yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
26294
- environment.push(redactProviderEnvironmentVariable(variable));
26295
- continue;
26296
- }
26297
- nextSecretKeys.add(secretName);
26298
- if (!variable.valueRedacted) {
26299
- if (variable.value.length > 0) {
26300
- yield* secretStore.set(secretName, textEncoder$1.encode(variable.value)).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to persist environment secret ${variable.name}`, cause)));
26301
- environment.push({
26302
- ...variable,
26303
- value: "",
26304
- valueRedacted: true
26305
- });
26306
- } else {
26307
- yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove environment secret ${variable.name}`, cause)));
26308
- const { valueRedacted: _omit, ...rest } = variable;
26309
- environment.push(rest);
26310
- }
26311
- continue;
26312
- }
26313
- environment.push(redactProviderEnvironmentVariable(variable));
26314
- }
26315
- providerInstances[instanceId] = {
26316
- ...instance,
26317
- environment
26318
- };
26319
- }
26320
- for (const [instanceId, instance] of Object.entries(current.providerInstances)) for (const variable of instance.environment ?? []) {
26321
- if (!variable.sensitive) continue;
26322
- const secretName = providerEnvironmentSecretName({
26323
- instanceId,
26324
- name: variable.name
26325
- });
26326
- if (nextSecretKeys.has(secretName)) continue;
26327
- yield* secretStore.remove(secretName).pipe(effect.Effect.mapError((cause) => toSettingsError(`failed to remove stale environment secret ${variable.name}`, cause)));
26328
- }
26329
- return {
26330
- ...next,
26331
- providerInstances
26332
- };
26333
- });
26334
- const writeSettingsAtomically = (settings) => {
26335
- const sparseSettings = stripDefaultServerSettings(settings, require_collectUint8StreamText.DEFAULT_SERVER_SETTINGS) ?? {};
26336
- return writeFileStringAtomically({
26337
- filePath: settingsPath,
26338
- contents: `${JSON.stringify(sparseSettings, null, 2)}\n`
26339
- }).pipe(effect.Effect.provideService(effect.FileSystem.FileSystem, fs), effect.Effect.provideService(effect.Path.Path, pathService), effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26340
- settingsPath,
26341
- detail: "failed to write settings file",
26342
- cause
26343
- })));
26344
- };
26345
- const revalidateAndEmit = writeSemaphore.withPermits(1)(effect.Effect.gen(function* () {
26346
- yield* effect.Cache.invalidate(settingsCache, cacheKey);
26347
- yield* emitChange(yield* getSettingsFromCache);
26348
- }));
26349
- const startWatcher = effect.Effect.gen(function* () {
26350
- const settingsDir = pathService.dirname(settingsPath);
26351
- const settingsFile = pathService.basename(settingsPath);
26352
- const settingsPathResolved = pathService.resolve(settingsPath);
26353
- yield* fs.makeDirectory(settingsDir, { recursive: true }).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26354
- settingsPath,
26355
- detail: "failed to prepare settings directory",
26356
- cause
26357
- })));
26358
- const revalidateAndEmitSafely = revalidateAndEmit.pipe(effect.Effect.ignoreCause({ log: true }));
26359
- const debouncedSettingsEvents = fs.watch(settingsDir).pipe(effect.Stream.filter((event) => {
26360
- return event.path === settingsFile || event.path === settingsPath || pathService.resolve(settingsDir, event.path) === settingsPathResolved;
26361
- }), effect.Stream.debounce(effect.Duration.millis(100)));
26362
- yield* effect.Stream.runForEach(debouncedSettingsEvents, () => revalidateAndEmitSafely).pipe(effect.Effect.ignoreCause({ log: true }), effect.Effect.forkIn(watcherScope), effect.Effect.asVoid);
26363
- });
26364
- return {
26365
- start: effect.Effect.gen(function* () {
26366
- if (!(yield* effect.Ref.modify(startedRef, (started) => [!started, true]))) return yield* effect.Deferred.await(startedDeferred);
26367
- const startup = effect.Effect.gen(function* () {
26368
- yield* startWatcher;
26369
- yield* effect.Cache.invalidate(settingsCache, cacheKey);
26370
- yield* getSettingsFromCache;
26371
- });
26372
- const startupExit = yield* effect.Effect.exit(startup);
26373
- if (startupExit._tag === "Failure") {
26374
- yield* effect.Deferred.failCause(startedDeferred, startupExit.cause).pipe(effect.Effect.orDie);
26375
- return yield* effect.Effect.failCause(startupExit.cause);
26376
- }
26377
- yield* effect.Deferred.succeed(startedDeferred, void 0).pipe(effect.Effect.orDie);
26378
- }),
26379
- ready: effect.Deferred.await(startedDeferred),
26380
- getSettings: getSettingsFromCache.pipe(effect.Effect.flatMap(materializeProviderEnvironmentSecrets), effect.Effect.map(resolveTextGenerationProvider)),
26381
- updateSettings: (patch) => writeSemaphore.withPermits(1)(effect.Effect.gen(function* () {
26382
- const current = yield* getSettingsFromCache;
26383
- const nextPersisted = yield* persistProviderEnvironmentSecrets(current, applyServerSettingsPatch(current, patch));
26384
- const next = yield* effect.Schema.decodeEffect(require_collectUint8StreamText.ServerSettings)(nextPersisted).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.ServerSettingsError({
26385
- settingsPath: "<memory>",
26386
- detail: `failed to normalize server settings: ${effect.SchemaIssue.makeFormatterDefault()(cause.issue)}`,
26387
- cause
26388
- })));
26389
- yield* writeSettingsAtomically(next);
26390
- yield* effect.Cache.set(settingsCache, cacheKey, next);
26391
- yield* emitChange(next);
26392
- return resolveTextGenerationProvider(yield* materializeProviderEnvironmentSecrets(next));
26393
- })),
26394
- get streamChanges() {
26395
- return effect.Stream.fromPubSub(changesPubSub).pipe(effect.Stream.mapEffect((settings) => materializeProviderEnvironmentSecrets(settings).pipe(effect.Effect.catch((error) => effect.Effect.logWarning("failed to materialize provider environment secrets", { detail: error.detail }).pipe(effect.Effect.as(settings))))), effect.Stream.map(resolveTextGenerationProvider));
26396
- }
26397
- };
26398
- });
26399
- const ServerSettingsLive = effect.Layer.effect(ServerSettingsService, makeServerSettings).pipe(effect.Layer.provide(ServerSecretStoreLive));
26400
- //#endregion
26401
26369
  //#region src/pathExpansion.ts
26402
26370
  /**
26403
26371
  * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the
@@ -26718,7 +26686,7 @@ const BUILT_IN_DRIVERS = [
26718
26686
  configSchema: require_collectUint8StreamText.CodexSettings,
26719
26687
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CodexSettings)({}),
26720
26688
  create: (input) => effect.Effect.tryPromise({
26721
- try: () => Promise.resolve().then(() => require("./CodexDriver-ms_YgFD7.cjs")),
26689
+ try: () => Promise.resolve().then(() => require("./CodexDriver-CtCCc3CC.cjs")),
26722
26690
  catch: (cause) => driverImportError(codexDriverKind, input, cause)
26723
26691
  }).pipe(effect.Effect.flatMap(({ CodexDriver }) => CodexDriver.create(input)))
26724
26692
  },
@@ -26731,7 +26699,7 @@ const BUILT_IN_DRIVERS = [
26731
26699
  configSchema: require_collectUint8StreamText.ClaudeSettings,
26732
26700
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.ClaudeSettings)({}),
26733
26701
  create: (input) => effect.Effect.tryPromise({
26734
- try: () => Promise.resolve().then(() => require("./ClaudeDriver-C6tQNEWO.cjs")),
26702
+ try: () => Promise.resolve().then(() => require("./ClaudeDriver-ScqmbPKW.cjs")),
26735
26703
  catch: (cause) => driverImportError(claudeDriverKind, input, cause)
26736
26704
  }).pipe(effect.Effect.flatMap(({ ClaudeDriver }) => ClaudeDriver.create(input)))
26737
26705
  },
@@ -26744,7 +26712,7 @@ const BUILT_IN_DRIVERS = [
26744
26712
  configSchema: require_collectUint8StreamText.CopilotSettings,
26745
26713
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CopilotSettings)({}),
26746
26714
  create: (input) => effect.Effect.tryPromise({
26747
- try: () => Promise.resolve().then(() => require("./CopilotDriver-B0--ZwIK.cjs")),
26715
+ try: () => Promise.resolve().then(() => require("./CopilotDriver-DLN2KEoc.cjs")),
26748
26716
  catch: (cause) => driverImportError(copilotDriverKind, input, cause)
26749
26717
  }).pipe(effect.Effect.flatMap(({ CopilotDriver }) => CopilotDriver.create(input)))
26750
26718
  },
@@ -26757,7 +26725,7 @@ const BUILT_IN_DRIVERS = [
26757
26725
  configSchema: require_collectUint8StreamText.CursorSettings,
26758
26726
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CursorSettings)({}),
26759
26727
  create: (input) => effect.Effect.tryPromise({
26760
- try: () => Promise.resolve().then(() => require("./CursorDriver-DTVPXZ-R.cjs")),
26728
+ try: () => Promise.resolve().then(() => require("./CursorDriver-BlvQbOEZ.cjs")),
26761
26729
  catch: (cause) => driverImportError(cursorDriverKind, input, cause)
26762
26730
  }).pipe(effect.Effect.flatMap(({ CursorDriver }) => CursorDriver.create(input)))
26763
26731
  },
@@ -26770,7 +26738,7 @@ const BUILT_IN_DRIVERS = [
26770
26738
  configSchema: require_collectUint8StreamText.OpenCodeSettings,
26771
26739
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.OpenCodeSettings)({}),
26772
26740
  create: (input) => effect.Effect.tryPromise({
26773
- try: () => Promise.resolve().then(() => require("./OpenCodeDriver-CXUsnl5o.cjs")),
26741
+ try: () => Promise.resolve().then(() => require("./OpenCodeDriver-mbR9mF54.cjs")),
26774
26742
  catch: (cause) => driverImportError(openCodeDriverKind, input, cause)
26775
26743
  }).pipe(effect.Effect.flatMap(({ OpenCodeDriver }) => OpenCodeDriver.create(input)))
26776
26744
  }
@@ -26870,6 +26838,7 @@ function buildServerProvider(input) {
26870
26838
  displayName: input.presentation.displayName,
26871
26839
  ...input.presentation.badgeLabel ? { badgeLabel: input.presentation.badgeLabel } : {},
26872
26840
  ...typeof input.presentation.showInteractionModeToggle === "boolean" ? { showInteractionModeToggle: input.presentation.showInteractionModeToggle } : {},
26841
+ ...typeof input.presentation.supportsAskMode === "boolean" ? { supportsAskMode: input.presentation.supportsAskMode } : {},
26873
26842
  enabled: input.enabled,
26874
26843
  installed: input.probe.installed,
26875
26844
  version: input.probe.version,
@@ -27483,13 +27452,14 @@ const scopedSafeTeardown = (label) => (effect$15) => effect.Effect.gen(function*
27483
27452
  });
27484
27453
  //#endregion
27485
27454
  //#region package.json
27486
- var version = "0.1.6";
27455
+ var version = "0.1.8";
27487
27456
  //#endregion
27488
27457
  //#region src/provider/Layers/CodexProvider.ts
27489
27458
  const PROVIDER_PROBE_TIMEOUT_MS = 8e3;
27490
27459
  const CODEX_PRESENTATION = {
27491
27460
  displayName: "Codex",
27492
- showInteractionModeToggle: true
27461
+ showInteractionModeToggle: true,
27462
+ supportsAskMode: true
27493
27463
  };
27494
27464
  const REASONING_EFFORT_LABELS = {
27495
27465
  none: "None",
@@ -27497,8 +27467,15 @@ const REASONING_EFFORT_LABELS = {
27497
27467
  low: "Low",
27498
27468
  medium: "Medium",
27499
27469
  high: "High",
27500
- xhigh: "Extra High"
27470
+ xhigh: "Extra High",
27471
+ max: "Max",
27472
+ ultra: "Ultra"
27501
27473
  };
27474
+ function formatReasoningEffortLabel(reasoningEffort) {
27475
+ const known = REASONING_EFFORT_LABELS[reasoningEffort];
27476
+ if (known) return known;
27477
+ return reasoningEffort.replace(/[_-]+/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
27478
+ }
27502
27479
  function codexAccountAuthLabel(account) {
27503
27480
  if (!account) return void 0;
27504
27481
  if (account.type === "apiKey") return "OpenAI API Key";
@@ -27527,11 +27504,11 @@ function codexAccountEmail(account) {
27527
27504
  function mapCodexModelCapabilities(model) {
27528
27505
  const reasoningOptions = model.supportedReasoningEfforts.map(({ reasoningEffort }) => reasoningEffort === model.defaultReasoningEffort ? {
27529
27506
  id: reasoningEffort,
27530
- label: REASONING_EFFORT_LABELS[reasoningEffort],
27507
+ label: formatReasoningEffortLabel(reasoningEffort),
27531
27508
  isDefault: true
27532
27509
  } : {
27533
27510
  id: reasoningEffort,
27534
- label: REASONING_EFFORT_LABELS[reasoningEffort]
27511
+ label: formatReasoningEffortLabel(reasoningEffort)
27535
27512
  });
27536
27513
  const defaultReasoning = reasoningOptions.find((option) => option.isDefault)?.id;
27537
27514
  const supportsFastMode = (model.additionalSpeedTiers ?? []).includes("fast");
@@ -28520,6 +28497,425 @@ var OrchestrationEngineService = class extends effect.Context.Service()("ryco/or
28520
28497
  */
28521
28498
  var ProjectionSnapshotQuery = class extends effect.Context.Service()("ryco/orchestration/Services/ProjectionSnapshotQuery") {};
28522
28499
  //#endregion
28500
+ //#region src/statistics/StatisticsQuery.ts
28501
+ /**
28502
+ * StatisticsQuery - usage analytics over the orchestration projection tables.
28503
+ *
28504
+ * Aggregates the existing read models (threads, sessions, token-usage
28505
+ * activities, turns/checkpoints, worktrees) into a {@link StatisticsSnapshot}
28506
+ * on demand. No dedicated statistics tables exist; everything is derived from
28507
+ * data already persisted by the projection pipeline.
28508
+ *
28509
+ * Token figures prefer per-turn attribution: for each `(thread, turn)` with
28510
+ * exact `last*` breakdowns we read the final `context-window.updated` activity
28511
+ * and bucket its provider-reported turn total by day/project/provider/model.
28512
+ * Threads without exact deltas (older data / providers that only report
28513
+ * cumulative context usage) fall back to attributing the latest cumulative
28514
+ * total to the thread's primary model and last-activity day.
28515
+ *
28516
+ * @module StatisticsQuery
28517
+ */
28518
+ /**
28519
+ * StatisticsQuery - service tag.
28520
+ */
28521
+ var StatisticsQuery = class extends effect.Context.Service()("ryco/statistics/StatisticsQuery") {};
28522
+ function toPersistenceSqlOrDecodeError$7(sqlOperation, decodeOperation) {
28523
+ return (cause) => effect.Schema.isSchemaError(cause) ? toPersistenceDecodeError(decodeOperation)(cause) : toPersistenceSqlError(sqlOperation)(cause);
28524
+ }
28525
+ const ThreadRow = effect.Schema.Struct({
28526
+ threadId: effect.Schema.String,
28527
+ projectId: require_collectUint8StreamText.ProjectId,
28528
+ model: effect.Schema.NullOr(effect.Schema.String),
28529
+ providerInstanceId: effect.Schema.NullOr(effect.Schema.String),
28530
+ createdAt: effect.Schema.String,
28531
+ providerName: effect.Schema.NullOr(effect.Schema.String)
28532
+ });
28533
+ const ProjectRow = effect.Schema.Struct({
28534
+ projectId: require_collectUint8StreamText.ProjectId,
28535
+ title: effect.Schema.String,
28536
+ deletedAt: effect.Schema.NullOr(effect.Schema.String)
28537
+ });
28538
+ const TokenActivityRow = effect.Schema.Struct({
28539
+ threadId: effect.Schema.String,
28540
+ turnId: effect.Schema.NullOr(effect.Schema.String),
28541
+ sequence: effect.Schema.NullOr(effect.Schema.Number),
28542
+ createdAt: effect.Schema.String,
28543
+ payloadJson: effect.Schema.String
28544
+ });
28545
+ const ToolUseRow = effect.Schema.Struct({
28546
+ threadId: effect.Schema.String,
28547
+ date: effect.Schema.String,
28548
+ count: effect.Schema.Number
28549
+ });
28550
+ const TurnRow = effect.Schema.Struct({
28551
+ threadId: effect.Schema.String,
28552
+ startedAt: effect.Schema.NullOr(effect.Schema.String),
28553
+ completedAt: effect.Schema.NullOr(effect.Schema.String),
28554
+ checkpointTurnCount: effect.Schema.NullOr(effect.Schema.Number),
28555
+ filesJson: effect.Schema.String
28556
+ });
28557
+ const WorktreeRow = effect.Schema.Struct({
28558
+ createdAt: effect.Schema.String,
28559
+ archivedAt: effect.Schema.NullOr(effect.Schema.String),
28560
+ prNumber: effect.Schema.NullOr(effect.Schema.Number)
28561
+ });
28562
+ function num(value) {
28563
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
28564
+ }
28565
+ function dayOf(iso) {
28566
+ return iso.length >= 10 ? iso.slice(0, 10) : null;
28567
+ }
28568
+ function bestPerTurnTotalTokenCount(payload) {
28569
+ const reportedTotal = num(payload?.lastUsedTokens);
28570
+ if (reportedTotal > 0) return reportedTotal;
28571
+ return num(payload?.lastInputTokens) + num(payload?.lastOutputTokens);
28572
+ }
28573
+ function bestCumulativeTotalTokenCount(payload) {
28574
+ const reportedTotal = num(payload?.usedTokens);
28575
+ if (reportedTotal > 0) return reportedTotal;
28576
+ return num(payload?.inputTokens) + num(payload?.outputTokens);
28577
+ }
28578
+ function hasExactPerTurnBreakdown(payload) {
28579
+ return num(payload?.lastInputTokens) + num(payload?.lastOutputTokens) + num(payload?.lastCachedInputTokens) + num(payload?.lastReasoningOutputTokens) > 0;
28580
+ }
28581
+ function addPerTurnUsage(bucket, payload) {
28582
+ bucket.inputTokens += num(payload?.lastInputTokens);
28583
+ bucket.outputTokens += num(payload?.lastOutputTokens);
28584
+ bucket.cachedInputTokens += num(payload?.lastCachedInputTokens);
28585
+ bucket.reasoningTokens += num(payload?.lastReasoningOutputTokens);
28586
+ bucket.totalTokens += bestPerTurnTotalTokenCount(payload);
28587
+ }
28588
+ function addCumulativeUsage(bucket, payload) {
28589
+ bucket.inputTokens += num(payload?.inputTokens);
28590
+ bucket.outputTokens += num(payload?.outputTokens);
28591
+ bucket.cachedInputTokens += num(payload?.cachedInputTokens);
28592
+ bucket.reasoningTokens += num(payload?.reasoningOutputTokens);
28593
+ bucket.totalTokens += bestCumulativeTotalTokenCount(payload);
28594
+ }
28595
+ const makeStatisticsQuery = effect.Effect.gen(function* () {
28596
+ const sql = yield* effect_unstable_sql_SqlClient.SqlClient;
28597
+ const listThreadRows = effect_unstable_sql_SqlSchema.findAll({
28598
+ Request: effect.Schema.Void,
28599
+ Result: ThreadRow,
28600
+ execute: () => sql`
28601
+ SELECT
28602
+ t.thread_id AS "threadId",
28603
+ t.project_id AS "projectId",
28604
+ json_extract(t.model_selection_json, '$.model') AS "model",
28605
+ COALESCE(
28606
+ json_extract(t.model_selection_json, '$.instanceId'),
28607
+ json_extract(t.model_selection_json, '$.provider')
28608
+ ) AS "providerInstanceId",
28609
+ t.created_at AS "createdAt",
28610
+ COALESCE(
28611
+ s.provider_name,
28612
+ json_extract(t.model_selection_json, '$.instanceId'),
28613
+ json_extract(t.model_selection_json, '$.provider')
28614
+ ) AS "providerName"
28615
+ FROM projection_threads t
28616
+ LEFT JOIN projection_thread_sessions s ON s.thread_id = t.thread_id
28617
+ `
28618
+ });
28619
+ const listProjectRows = effect_unstable_sql_SqlSchema.findAll({
28620
+ Request: effect.Schema.Void,
28621
+ Result: ProjectRow,
28622
+ execute: () => sql`
28623
+ SELECT
28624
+ project_id AS "projectId",
28625
+ title,
28626
+ deleted_at AS "deletedAt"
28627
+ FROM projection_projects
28628
+ `
28629
+ });
28630
+ const listTokenActivityRows = effect_unstable_sql_SqlSchema.findAll({
28631
+ Request: effect.Schema.Void,
28632
+ Result: TokenActivityRow,
28633
+ execute: () => sql`
28634
+ SELECT
28635
+ thread_id AS "threadId",
28636
+ turn_id AS "turnId",
28637
+ sequence,
28638
+ created_at AS "createdAt",
28639
+ payload_json AS "payloadJson"
28640
+ FROM projection_thread_activities
28641
+ WHERE kind = 'context-window.updated'
28642
+ ORDER BY thread_id ASC, turn_id ASC, sequence ASC, created_at ASC, activity_id ASC
28643
+ `
28644
+ });
28645
+ const listToolUseRows = effect_unstable_sql_SqlSchema.findAll({
28646
+ Request: effect.Schema.Void,
28647
+ Result: ToolUseRow,
28648
+ execute: () => sql`
28649
+ SELECT
28650
+ thread_id AS "threadId",
28651
+ substr(created_at, 1, 10) AS "date",
28652
+ COUNT(*) AS "count"
28653
+ FROM projection_thread_activities
28654
+ WHERE kind = 'tool.completed'
28655
+ GROUP BY thread_id, substr(created_at, 1, 10)
28656
+ `
28657
+ });
28658
+ const listTurnRows = effect_unstable_sql_SqlSchema.findAll({
28659
+ Request: effect.Schema.Void,
28660
+ Result: TurnRow,
28661
+ execute: () => sql`
28662
+ SELECT
28663
+ thread_id AS "threadId",
28664
+ started_at AS "startedAt",
28665
+ completed_at AS "completedAt",
28666
+ checkpoint_turn_count AS "checkpointTurnCount",
28667
+ checkpoint_files_json AS "filesJson"
28668
+ FROM projection_turns
28669
+ `
28670
+ });
28671
+ const listWorktreeRows = effect_unstable_sql_SqlSchema.findAll({
28672
+ Request: effect.Schema.Void,
28673
+ Result: WorktreeRow,
28674
+ execute: () => sql`
28675
+ SELECT
28676
+ created_at AS "createdAt",
28677
+ archived_at AS "archivedAt",
28678
+ pr_number AS "prNumber"
28679
+ FROM projection_worktrees
28680
+ `
28681
+ });
28682
+ const getStatistics = () => effect.Effect.gen(function* () {
28683
+ const [threadRows, projectRows, tokenRows, toolRows, turnRows, worktreeRows] = yield* effect.Effect.all([
28684
+ listThreadRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listThreads:query", "StatisticsQuery.getStatistics:listThreads:decodeRows"))),
28685
+ listProjectRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listProjects:query", "StatisticsQuery.getStatistics:listProjects:decodeRows"))),
28686
+ listTokenActivityRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTokenActivities:query", "StatisticsQuery.getStatistics:listTokenActivities:decodeRows"))),
28687
+ listToolUseRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listToolUses:query", "StatisticsQuery.getStatistics:listToolUses:decodeRows"))),
28688
+ listTurnRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTurns:query", "StatisticsQuery.getStatistics:listTurns:decodeRows"))),
28689
+ listWorktreeRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listWorktrees:query", "StatisticsQuery.getStatistics:listWorktrees:decodeRows")))
28690
+ ], { concurrency: "unbounded" });
28691
+ const threadMeta = /* @__PURE__ */ new Map();
28692
+ const modelRefs = /* @__PURE__ */ new Map();
28693
+ for (const row of threadRows) {
28694
+ const provider = row.providerName?.trim() || row.providerInstanceId?.trim() || void 0;
28695
+ const model = (row.model ?? "").trim() || "unknown";
28696
+ threadMeta.set(row.threadId, {
28697
+ projectId: row.projectId,
28698
+ model,
28699
+ provider,
28700
+ createdAt: row.createdAt
28701
+ });
28702
+ const modelKey = `${provider ?? ""}\u0000${model}`;
28703
+ modelRefs.set(modelKey, provider ? {
28704
+ model,
28705
+ provider
28706
+ } : { model });
28707
+ }
28708
+ const buckets = /* @__PURE__ */ new Map();
28709
+ const getBucket = (date, projectId, model, provider) => {
28710
+ const key = `${date}\u0000${projectId}\u0000${provider ?? ""}\u0000${model}`;
28711
+ let bucket = buckets.get(key);
28712
+ if (!bucket) {
28713
+ bucket = {
28714
+ date,
28715
+ projectId,
28716
+ model,
28717
+ provider,
28718
+ inputTokens: 0,
28719
+ outputTokens: 0,
28720
+ cachedInputTokens: 0,
28721
+ reasoningTokens: 0,
28722
+ totalTokens: 0,
28723
+ turns: 0,
28724
+ activeMs: 0,
28725
+ toolUses: 0,
28726
+ filesChanged: 0,
28727
+ additions: 0,
28728
+ deletions: 0,
28729
+ commits: 0,
28730
+ pushes: 0,
28731
+ threadsCreated: 0
28732
+ };
28733
+ buckets.set(key, bucket);
28734
+ }
28735
+ return bucket;
28736
+ };
28737
+ let earliest;
28738
+ const considerIso = (iso) => {
28739
+ if (iso && (earliest === void 0 || iso < earliest)) earliest = iso;
28740
+ };
28741
+ const lastPerTurn = /* @__PURE__ */ new Map();
28742
+ const isLater = (a, b) => {
28743
+ if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt;
28744
+ return (a.sequence ?? -1) >= (b.sequence ?? -1);
28745
+ };
28746
+ for (const row of tokenRows) {
28747
+ considerIso(row.createdAt);
28748
+ const turnKey = `${row.threadId}\u0000${row.turnId ?? ""}`;
28749
+ const prevTurn = lastPerTurn.get(turnKey);
28750
+ if (!prevTurn || isLater(row, prevTurn)) lastPerTurn.set(turnKey, row);
28751
+ }
28752
+ const firstExactPerThread = /* @__PURE__ */ new Map();
28753
+ for (const row of lastPerTurn.values()) if (hasExactPerTurnBreakdown(safeParse(row.payloadJson))) {
28754
+ const prev = firstExactPerThread.get(row.threadId);
28755
+ if (!prev || isLater(prev, row)) firstExactPerThread.set(row.threadId, row);
28756
+ }
28757
+ const cumulativeFallbackByThread = /* @__PURE__ */ new Map();
28758
+ for (const row of lastPerTurn.values()) {
28759
+ if (hasExactPerTurnBreakdown(safeParse(row.payloadJson))) continue;
28760
+ const firstExact = firstExactPerThread.get(row.threadId);
28761
+ if (firstExact && !isLater(firstExact, row)) continue;
28762
+ const prev = cumulativeFallbackByThread.get(row.threadId);
28763
+ if (!prev || isLater(row, prev)) cumulativeFallbackByThread.set(row.threadId, row);
28764
+ }
28765
+ let usedExactPerTurn = false;
28766
+ let usedCumulativeFallback = false;
28767
+ for (const row of lastPerTurn.values()) {
28768
+ const payload = safeParse(row.payloadJson);
28769
+ if (!hasExactPerTurnBreakdown(payload)) continue;
28770
+ const meta = threadMeta.get(row.threadId);
28771
+ const date = dayOf(row.createdAt);
28772
+ if (!meta || !date) continue;
28773
+ addPerTurnUsage(getBucket(date, meta.projectId, meta.model, meta.provider), payload);
28774
+ usedExactPerTurn = true;
28775
+ }
28776
+ for (const row of cumulativeFallbackByThread.values()) {
28777
+ const meta = threadMeta.get(row.threadId);
28778
+ const date = dayOf(row.createdAt);
28779
+ if (!meta || !date) continue;
28780
+ const payload = safeParse(row.payloadJson);
28781
+ if (bestCumulativeTotalTokenCount(payload) <= 0) continue;
28782
+ addCumulativeUsage(getBucket(date, meta.projectId, meta.model, meta.provider), payload);
28783
+ usedCumulativeFallback = true;
28784
+ }
28785
+ const attribution = usedExactPerTurn && usedCumulativeFallback ? "mixed" : usedCumulativeFallback ? "thread-cumulative" : "per-turn-delta";
28786
+ for (const row of turnRows) {
28787
+ const meta = threadMeta.get(row.threadId);
28788
+ if (!meta) continue;
28789
+ considerIso(row.startedAt);
28790
+ considerIso(row.completedAt);
28791
+ const anchor = row.completedAt ?? row.startedAt;
28792
+ const date = anchor ? dayOf(anchor) : null;
28793
+ if (!date) continue;
28794
+ const bucket = getBucket(date, meta.projectId, meta.model, meta.provider);
28795
+ if (row.completedAt) {
28796
+ bucket.turns += 1;
28797
+ if (row.startedAt) {
28798
+ const delta = Date.parse(row.completedAt) - Date.parse(row.startedAt);
28799
+ if (Number.isFinite(delta) && delta > 0) bucket.activeMs += delta;
28800
+ }
28801
+ }
28802
+ const files = safeParseArray(row.filesJson);
28803
+ if (files.length > 0) {
28804
+ bucket.filesChanged += files.length;
28805
+ for (const file of files) {
28806
+ bucket.additions += num(file?.additions);
28807
+ bucket.deletions += num(file?.deletions);
28808
+ }
28809
+ }
28810
+ }
28811
+ for (const row of toolRows) {
28812
+ const meta = threadMeta.get(row.threadId);
28813
+ if (!meta) continue;
28814
+ const bucket = getBucket(row.date, meta.projectId, meta.model, meta.provider);
28815
+ bucket.toolUses += num(row.count);
28816
+ }
28817
+ for (const meta of threadMeta.values()) {
28818
+ considerIso(meta.createdAt);
28819
+ const date = dayOf(meta.createdAt);
28820
+ if (!date) continue;
28821
+ getBucket(date, meta.projectId, meta.model, meta.provider).threadsCreated += 1;
28822
+ }
28823
+ let wtCreated = 0;
28824
+ let wtArchived = 0;
28825
+ let wtOpenPrs = 0;
28826
+ for (const row of worktreeRows) {
28827
+ considerIso(row.createdAt);
28828
+ wtCreated += 1;
28829
+ if (row.archivedAt) wtArchived += 1;
28830
+ else if (row.prNumber !== null) wtOpenPrs += 1;
28831
+ }
28832
+ const dailyBuckets = [];
28833
+ const totals = {
28834
+ inputTokens: 0,
28835
+ outputTokens: 0,
28836
+ cachedInputTokens: 0,
28837
+ reasoningTokens: 0,
28838
+ totalTokens: 0,
28839
+ turns: 0,
28840
+ activeMs: 0,
28841
+ toolUses: 0,
28842
+ filesChanged: 0,
28843
+ additions: 0,
28844
+ deletions: 0,
28845
+ commits: 0,
28846
+ pushes: 0,
28847
+ threads: threadMeta.size,
28848
+ projects: 0
28849
+ };
28850
+ for (const bucket of buckets.values()) {
28851
+ const { provider, ...rest } = bucket;
28852
+ dailyBuckets.push(provider ? {
28853
+ ...rest,
28854
+ provider
28855
+ } : rest);
28856
+ totals.inputTokens += bucket.inputTokens;
28857
+ totals.outputTokens += bucket.outputTokens;
28858
+ totals.cachedInputTokens += bucket.cachedInputTokens;
28859
+ totals.reasoningTokens += bucket.reasoningTokens;
28860
+ totals.turns += bucket.turns;
28861
+ totals.activeMs += bucket.activeMs;
28862
+ totals.toolUses += bucket.toolUses;
28863
+ totals.filesChanged += bucket.filesChanged;
28864
+ totals.additions += bucket.additions;
28865
+ totals.deletions += bucket.deletions;
28866
+ totals.commits += bucket.commits;
28867
+ totals.pushes += bucket.pushes;
28868
+ totals.totalTokens += bucket.totalTokens;
28869
+ }
28870
+ 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));
28871
+ const projectTitle = /* @__PURE__ */ new Map();
28872
+ for (const row of projectRows) projectTitle.set(row.projectId, row.title);
28873
+ const projectIdsInData = /* @__PURE__ */ new Set();
28874
+ for (const meta of threadMeta.values()) projectIdsInData.add(meta.projectId);
28875
+ for (const bucket of buckets.values()) projectIdsInData.add(bucket.projectId);
28876
+ totals.projects = projectIdsInData.size;
28877
+ const projects = [...projectIdsInData].map((id) => ({
28878
+ id,
28879
+ title: projectTitle.get(id)?.trim() || id
28880
+ }));
28881
+ projects.sort((a, b) => a.title.localeCompare(b.title) || a.id.localeCompare(b.id));
28882
+ const models = [...modelRefs.values()].toSorted((a, b) => (a.provider ?? "").localeCompare(b.provider ?? "") || a.model.localeCompare(b.model));
28883
+ return {
28884
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
28885
+ ...earliest ? { earliestActivityAt: earliest } : {},
28886
+ projects,
28887
+ models,
28888
+ dailyBuckets,
28889
+ worktrees: {
28890
+ created: wtCreated,
28891
+ archived: wtArchived,
28892
+ active: wtCreated - wtArchived,
28893
+ openPrs: wtOpenPrs
28894
+ },
28895
+ totals,
28896
+ tokenAttribution: attribution
28897
+ };
28898
+ });
28899
+ return { getStatistics };
28900
+ });
28901
+ function safeParse(json) {
28902
+ try {
28903
+ const parsed = JSON.parse(json);
28904
+ return parsed && typeof parsed === "object" ? parsed : null;
28905
+ } catch {
28906
+ return null;
28907
+ }
28908
+ }
28909
+ function safeParseArray(json) {
28910
+ try {
28911
+ const parsed = JSON.parse(json);
28912
+ return Array.isArray(parsed) ? parsed : [];
28913
+ } catch {
28914
+ return [];
28915
+ }
28916
+ }
28917
+ const StatisticsQueryLive = effect.Layer.effect(StatisticsQuery, makeStatisticsQuery);
28918
+ //#endregion
28523
28919
  //#region src/serverLifecycleEvents.ts
28524
28920
  var ServerLifecycleEvents = class extends effect.Context.Service()("ryco/serverLifecycleEvents") {};
28525
28921
  const ServerLifecycleEventsLive = effect.Layer.effect(ServerLifecycleEvents, effect.Effect.gen(function* () {
@@ -30409,7 +30805,8 @@ const makeGitManager = effect.Effect.fn("makeGitManager")(function* () {
30409
30805
  isDefaultRef: details.isDefaultBranch,
30410
30806
  refName: details.branch,
30411
30807
  hasWorkingTreeChanges: details.hasWorkingTreeChanges,
30412
- workingTree: details.workingTree
30808
+ workingTree: details.workingTree,
30809
+ ...details.committed ? { committed: details.committed } : {}
30413
30810
  };
30414
30811
  });
30415
30812
  const localStatusResultCache = yield* effect.Cache.makeWith(readLocalStatus, {
@@ -32543,6 +32940,8 @@ const layer = effect.Layer.effect(JiraWorkItemService, make$5());
32543
32940
  //#region src/remote/Services/AdvertisedEndpointRegistry.ts
32544
32941
  var AdvertisedEndpointRegistry = class extends effect.Context.Service()("ryco/remote/Services/AdvertisedEndpointRegistry") {};
32545
32942
  const ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT = 1e3;
32943
+ const ORCHESTRATION_LEGACY_REPLAY_MAX_EVENTS = ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT;
32944
+ const ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS = 1e3;
32546
32945
  //#endregion
32547
32946
  //#region src/ws/context/gitErrors.ts
32548
32947
  function gitErrorText(error) {
@@ -32576,6 +32975,10 @@ const failGitWorkflow = (operation, detail, cause) => effect.Effect.fail(toGitMa
32576
32975
  const wsOrchestrationReplayDepth = effect.Metric.gauge("t3_ws_orchestration_replay_depth", { description: "Current persisted orchestration event replay depth for a replayable WebSocket subscription." });
32577
32976
  const wsOrchestrationLiveBufferDepth = effect.Metric.gauge("t3_ws_orchestration_live_buffer_depth", { description: "Current live orchestration event buffer depth for a replayable WebSocket subscription." });
32578
32977
  const wsOrchestrationLiveBufferHighWater = effect.Metric.gauge("t3_ws_orchestration_live_buffer_high_water", { description: "Highest observed live orchestration event buffer depth for a replayable WebSocket subscription." });
32978
+ const wsOrchestrationLiveBufferOverflowsTotal = effect.Metric.counter("t3_ws_orchestration_live_buffer_overflows_total", {
32979
+ description: "Total live orchestration event buffer overflows that forced replayable WebSocket subscription resync.",
32980
+ incremental: true
32981
+ });
32579
32982
  const wsOrchestrationReplayLag = effect.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." });
32580
32983
  const normalizeSequence = (sequence) => Number.isFinite(sequence) ? Math.max(0, Math.floor(sequence)) : 0;
32581
32984
  const updateGauge$1 = (metric, attributes, value) => effect.Metric.update(effect.Metric.withAttributes(metric, attributes), value);
@@ -32613,6 +33016,18 @@ const makeWsReplayMetrics = (input) => effect.Effect.gen(function* () {
32613
33016
  yield* recordLiveBuffer(yield* effect.Ref.updateAndGet(liveBufferDepth, (current) => Math.max(0, current - 1)), yield* effect.Ref.get(liveBufferHighWater));
32614
33017
  yield* markDrained(sequence);
32615
33018
  }),
33019
+ recordLiveOverflow: (sequence, capacity) => effect.Effect.gen(function* () {
33020
+ const normalizedSequence = normalizeSequence(sequence);
33021
+ const normalizedCapacity = Math.max(0, Math.floor(capacity));
33022
+ yield* effect.Ref.update(latestLiveSequence, (current) => Math.max(current, normalizedSequence));
33023
+ yield* effect.Ref.set(liveBufferDepth, normalizedCapacity);
33024
+ const highWater = yield* effect.Ref.updateAndGet(liveBufferHighWater, (current) => Math.max(current, normalizedCapacity));
33025
+ yield* effect.Effect.all([
33026
+ recordLiveBuffer(normalizedCapacity, highWater),
33027
+ updateGauge$1(wsOrchestrationLiveBufferOverflowsTotal, attributes, 1),
33028
+ recordLag
33029
+ ], { discard: true });
33030
+ }),
32616
33031
  reset: effect.Effect.all([
32617
33032
  updateGauge$1(wsOrchestrationReplayDepth, attributes, 0),
32618
33033
  updateGauge$1(wsOrchestrationLiveBufferDepth, attributes, 0),
@@ -32663,6 +33078,29 @@ const buildWorkItemBranchNameMessage = (input) => {
32663
33078
  };
32664
33079
  //#endregion
32665
33080
  //#region src/ws/context/orchestrationStreams.ts
33081
+ const makeLiveQueueOverflowError = (input) => new require_collectUint8StreamText.OrchestrationGetSnapshotError({
33082
+ message: `Orchestration ${input.stream} live event queue overflowed; reconnect to resynchronize`,
33083
+ cause: /* @__PURE__ */ new Error(`orchestration ${input.stream} live queue overflow at sequence ${input.sequence} with capacity ${input.capacity}`)
33084
+ });
33085
+ const offerOrchestrationLiveEventOrFail = (input) => effect.Effect.gen(function* () {
33086
+ const capacity = input.capacity ?? 1e3;
33087
+ if (effect.Queue.offerUnsafe(input.liveQueue, input.event)) {
33088
+ yield* input.replayMetrics.recordLiveEnqueued(input.event.sequence);
33089
+ return;
33090
+ }
33091
+ if (!(yield* effect.Ref.modify(input.overflowedRef, (overflowed) => [!overflowed, true]))) return;
33092
+ yield* input.replayMetrics.recordLiveOverflow(input.event.sequence, capacity);
33093
+ yield* effect.Effect.logWarning("orchestration live stream queue overflow; forcing resync", {
33094
+ stream: input.stream,
33095
+ sequence: input.event.sequence,
33096
+ capacity
33097
+ });
33098
+ yield* effect.Queue.fail(input.liveQueue, makeLiveQueueOverflowError({
33099
+ stream: input.stream,
33100
+ sequence: input.event.sequence,
33101
+ capacity
33102
+ }));
33103
+ });
32666
33104
  const makeOrchestrationStreamHelpers = (deps) => {
32667
33105
  const { orchestrationEngine, projectionSnapshotQuery, repositoryIdentityResolver } = deps;
32668
33106
  const enrichProjectEvent = (event) => {
@@ -32782,8 +33220,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32782
33220
  const loadedSnapshot = yield* snapshot;
32783
33221
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32784
33222
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32785
- const liveQueue = yield* effect.Queue.unbounded();
33223
+ const liveQueue = yield* effect.Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32786
33224
  const lastSequenceRef = yield* effect.Ref.make(snapshotSequence);
33225
+ const overflowedRef = yield* effect.Ref.make(false);
32787
33226
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32788
33227
  const replayMetrics = yield* makeWsReplayMetrics({
32789
33228
  stream: "shell",
@@ -32792,8 +33231,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32792
33231
  });
32793
33232
  yield* effect.Stream.fromSubscription(liveSubscription).pipe(effect.Stream.runForEach((event) => effect.Effect.gen(function* () {
32794
33233
  yield* replayBoundary.recordLiveSequence(event.sequence);
32795
- yield* effect.Queue.offer(liveQueue, event);
32796
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33234
+ yield* offerOrchestrationLiveEventOrFail({
33235
+ stream: "shell",
33236
+ event,
33237
+ liveQueue,
33238
+ overflowedRef,
33239
+ replayMetrics
33240
+ });
32797
33241
  })), effect.Effect.ensuring(effect.Queue.shutdown(liveQueue)), effect.Effect.ignoreCause({ log: true }), effect.Effect.forkScoped);
32798
33242
  const replayStream = makeBoundedReplayStream({
32799
33243
  snapshotSequence,
@@ -32811,8 +33255,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32811
33255
  const loadedSnapshot = yield* snapshot;
32812
33256
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32813
33257
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32814
- const liveQueue = yield* effect.Queue.unbounded();
33258
+ const liveQueue = yield* effect.Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32815
33259
  const lastSequenceRef = yield* effect.Ref.make(snapshotSequence);
33260
+ const overflowedRef = yield* effect.Ref.make(false);
32816
33261
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32817
33262
  const replayMetrics = yield* makeWsReplayMetrics({
32818
33263
  stream: "thread",
@@ -32825,8 +33270,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32825
33270
  })));
32826
33271
  yield* effect.Stream.fromSubscription(liveSubscription).pipe(effect.Stream.runForEach((event) => effect.Effect.gen(function* () {
32827
33272
  yield* replayBoundary.recordLiveSequence(event.sequence);
32828
- yield* effect.Queue.offer(liveQueue, event);
32829
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33273
+ yield* offerOrchestrationLiveEventOrFail({
33274
+ stream: "thread",
33275
+ event,
33276
+ liveQueue,
33277
+ overflowedRef,
33278
+ replayMetrics
33279
+ });
32830
33280
  })), effect.Effect.ensuring(effect.Queue.shutdown(liveQueue)), effect.Effect.ignoreCause({ log: true }), effect.Effect.forkScoped);
32831
33281
  const replayStream = makeBoundedReplayStream({
32832
33282
  snapshotSequence,
@@ -32858,6 +33308,13 @@ const makeWorktreeOperations = (deps) => {
32858
33308
  onNone: () => failGitWorkflow(operation, `Worktree ${worktreeId} not found.`),
32859
33309
  onSome: effect.Effect.succeed
32860
33310
  })));
33311
+ const isProjectRootPath = (candidate, projectRoot) => {
33312
+ const normalize = (value) => {
33313
+ const resolved = node_path.default.resolve(value).replace(/[\\/]+$/g, "");
33314
+ return process.platform === "win32" || process.platform === "darwin" ? resolved.toLowerCase() : resolved;
33315
+ };
33316
+ return normalize(candidate) === normalize(projectRoot);
33317
+ };
32861
33318
  const dispatchWorktreeCommand = (command, operation) => dispatchNormalizedCommand(command).pipe(effect.Effect.mapError((cause) => toGitManagerError(operation, "Failed to dispatch orchestration command.", cause)), effect.Effect.asVoid);
32862
33319
  const launchSetupScriptForWorktreeInBackground = (input) => effect.Effect.gen(function* () {
32863
33320
  const requestedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -33081,20 +33538,27 @@ const makeWorktreeOperations = (deps) => {
33081
33538
  break;
33082
33539
  }
33083
33540
  }
33084
- const worktreePath = preparedWorktreePath ?? (yield* gitWorkflow.createWorktree({
33085
- cwd: project.workspaceRoot,
33086
- refName,
33087
- ...newRefName !== void 0 ? { newRefName } : {},
33088
- path: resolveWorktreeCheckoutPath({
33541
+ let worktreePath;
33542
+ if (preparedWorktreePath !== null) {
33543
+ if (isProjectRootPath(preparedWorktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot create a worktree at the project root.");
33544
+ worktreePath = preparedWorktreePath;
33545
+ } else {
33546
+ const targetPath = resolveWorktreeCheckoutPath({
33089
33547
  location: input.worktreeLocation,
33090
33548
  appWorktreesRoot: config.worktreesDir,
33091
33549
  projectId: input.projectId,
33092
33550
  workspaceRoot: project.workspaceRoot,
33093
33551
  projectMetadataDir: project.projectMetadataDir,
33094
33552
  branchName: branch
33095
- })
33096
- })).worktree.path;
33097
- if (preparedWorktreePath === null) {
33553
+ });
33554
+ if (isProjectRootPath(targetPath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot create a worktree at the project root.");
33555
+ worktreePath = (yield* gitWorkflow.createWorktree({
33556
+ cwd: project.workspaceRoot,
33557
+ refName,
33558
+ ...newRefName !== void 0 ? { newRefName } : {},
33559
+ path: targetPath
33560
+ })).worktree.path;
33561
+ if (isProjectRootPath(worktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Refusing to register a worktree that resolved to the project root.");
33098
33562
  ownedWorktreePath = worktreePath;
33099
33563
  if (newRefName !== void 0) ownedBranchName = newRefName;
33100
33564
  }
@@ -33235,6 +33699,7 @@ const makeWorktreeOperations = (deps) => {
33235
33699
  const worktree = yield* loadWorktreeForGitWorkflow(operation, input.worktreeId);
33236
33700
  if (worktree.origin === "main") return yield* failGitWorkflow(operation, "Cannot delete the main worktree.");
33237
33701
  const project = yield* loadProjectForGitWorkflow(operation, worktree.projectId);
33702
+ if (worktree.worktreePath !== null && isProjectRootPath(worktree.worktreePath, project.workspaceRoot)) return yield* failGitWorkflow(operation, "Cannot delete a worktree that points at the project root.");
33238
33703
  if (input.force) {
33239
33704
  if (worktree.worktreePath !== null) {
33240
33705
  if ((0, node_fs.existsSync)(worktree.worktreePath)) return yield* failGitWorkflow(operation, "Cannot force delete: the worktree path still exists on disk. Use a regular delete instead.");
@@ -33367,6 +33832,7 @@ function toDiagnosticsProviderProcess(provider) {
33367
33832
  const makeWsRpcContext = (session) => effect.Effect.gen(function* () {
33368
33833
  const currentSessionId = session.sessionId;
33369
33834
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
33835
+ const statisticsQuery = yield* StatisticsQuery;
33370
33836
  const orchestrationEngine = yield* OrchestrationEngineService;
33371
33837
  const checkpointDiffQuery = yield* CheckpointDiffQuery;
33372
33838
  const keybindings = yield* Keybindings;
@@ -33690,6 +34156,7 @@ const makeWsRpcContext = (session) => effect.Effect.gen(function* () {
33690
34156
  return {
33691
34157
  currentSessionId,
33692
34158
  projectionSnapshotQuery,
34159
+ statisticsQuery,
33693
34160
  orchestrationEngine,
33694
34161
  checkpointDiffQuery,
33695
34162
  keybindings,
@@ -33967,9 +34434,9 @@ function recordServerPerfPayload(label, value, record) {
33967
34434
  //#endregion
33968
34435
  //#region src/ws/orchestrationRpc.ts
33969
34436
  const makeOrchestrationHandlers = (ctx) => {
33970
- const { withAccess, ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
34437
+ const { ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
33971
34438
  return defineWsHandlers({
33972
- [require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand]: (command) => observeRpcEffect(require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand, withAccess("authenticated", require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand, effect.Effect.gen(function* () {
34439
+ [require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand]: (command) => observeRpcEffect(require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand, ownerEffect(require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.dispatchCommand, effect.Effect.gen(function* () {
33973
34440
  const normalizedCommand = yield* normalizeDispatchCommand(command);
33974
34441
  const shouldStopSessionAfterArchive = normalizedCommand.type === "thread.archive" ? yield* projectionSnapshotQuery.getThreadShellById(normalizedCommand.threadId).pipe(effect.Effect.map(effect.Option.match({
33975
34442
  onNone: () => false,
@@ -34006,10 +34473,20 @@ const makeOrchestrationHandlers = (ctx) => {
34006
34473
  message: "Failed to load full thread diff",
34007
34474
  cause
34008
34475
  }))), { "rpc.aggregate": "orchestration" }),
34476
+ [require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.searchThreadMessages]: (input) => observeRpcEffect(require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.searchThreadMessages, projectionSnapshotQuery.searchThreadMessages({
34477
+ ...input,
34478
+ limit: (0, effect_Number.clamp)(input.limit, {
34479
+ maximum: 50,
34480
+ minimum: 1
34481
+ })
34482
+ }).pipe(effect.Effect.mapError((cause) => new require_collectUint8StreamText.OrchestrationGetSnapshotError({
34483
+ message: "Failed to search thread messages",
34484
+ cause
34485
+ }))), { "rpc.aggregate": "orchestration" }),
34009
34486
  [require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.replayEvents]: (input) => observeRpcEffect(require_collectUint8StreamText.ORCHESTRATION_WS_METHODS.replayEvents, effect.Stream.runCollect(orchestrationEngine.readEvents((0, effect_Number.clamp)(input.fromSequenceExclusive, {
34010
34487
  maximum: Number.MAX_SAFE_INTEGER,
34011
34488
  minimum: 0
34012
- }))).pipe(effect.Effect.map((events) => Array.from(events)), effect.Effect.flatMap(enrichOrchestrationEvents), effect.Effect.mapError((cause) => new require_collectUint8StreamText.OrchestrationReplayEventsError({
34489
+ }), ORCHESTRATION_LEGACY_REPLAY_MAX_EVENTS)).pipe(effect.Effect.map((events) => Array.from(events)), effect.Effect.flatMap(enrichOrchestrationEvents), effect.Effect.mapError((cause) => new require_collectUint8StreamText.OrchestrationReplayEventsError({
34013
34490
  message: "Failed to replay orchestration events",
34014
34491
  cause
34015
34492
  }))), { "rpc.aggregate": "orchestration" }),
@@ -34065,7 +34542,22 @@ const makeOrchestrationHandlers = (ctx) => {
34065
34542
  threadId: input.threadId,
34066
34543
  position: input.position,
34067
34544
  changedAt: (/* @__PURE__ */ new Date()).toISOString()
34068
- }, "threads.setManualPosition").pipe(effect.Effect.as({}))), { "rpc.aggregate": "orchestration" })
34545
+ }, "threads.setManualPosition").pipe(effect.Effect.as({}))), { "rpc.aggregate": "orchestration" }),
34546
+ [require_collectUint8StreamText.WS_METHODS.searchThreadMessages]: (input) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.searchThreadMessages, ownerEffect(require_collectUint8StreamText.WS_METHODS.searchThreadMessages, effect.Effect.gen(function* () {
34547
+ const query = input.query.trim();
34548
+ if (query.length === 0) return { results: [] };
34549
+ return { results: yield* projectionSnapshotQuery.searchThreadMessages({
34550
+ query,
34551
+ ...input.projectId ? { projectId: input.projectId } : {},
34552
+ limit: (0, effect_Number.clamp)(input.limit ?? 20, {
34553
+ maximum: 50,
34554
+ minimum: 1
34555
+ })
34556
+ }) };
34557
+ }).pipe(effect.Effect.mapError(() => new require_collectUint8StreamText.AuthRpcError({
34558
+ message: "Failed to search thread messages.",
34559
+ status: 403
34560
+ })))), { "rpc.aggregate": "orchestration" })
34069
34561
  });
34070
34562
  };
34071
34563
  //#endregion
@@ -34140,10 +34632,25 @@ const makeTerminalHandlers = (ctx) => {
34140
34632
  [require_collectUint8StreamText.WS_METHODS.terminalClear]: (input) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.terminalClear, ownerEffect(require_collectUint8StreamText.WS_METHODS.terminalClear, terminalManager.clear(input)), { "rpc.aggregate": "terminal" }),
34141
34633
  [require_collectUint8StreamText.WS_METHODS.terminalRestart]: (input) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.terminalRestart, ownerEffect(require_collectUint8StreamText.WS_METHODS.terminalRestart, terminalManager.restart(input)), { "rpc.aggregate": "terminal" }),
34142
34634
  [require_collectUint8StreamText.WS_METHODS.terminalClose]: (input) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.terminalClose, ownerEffect(require_collectUint8StreamText.WS_METHODS.terminalClose, terminalManager.close(input)), { "rpc.aggregate": "terminal" }),
34143
- [require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents]: (_input) => observeRpcStream(require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents, ownerStream(require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents, effect.Stream.callback((queue) => effect.Effect.acquireRelease(terminalManager.subscribe((event) => effect.Effect.gen(function* () {
34144
- recordServerPerfPayload("server.ws.terminal.events", event);
34145
- yield* effect.Queue.offer(queue, event);
34146
- })), (unsubscribe) => effect.Effect.sync(unsubscribe)))), { "rpc.aggregate": "terminal" })
34635
+ [require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents]: (_input) => observeRpcStream(require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents, ownerStream(require_collectUint8StreamText.WS_METHODS.subscribeTerminalEvents, effect.Stream.callback((queue) => effect.Effect.acquireRelease(effect.Effect.gen(function* () {
34636
+ const unsubscribe = yield* terminalManager.subscribe((event) => effect.Effect.gen(function* () {
34637
+ recordServerPerfPayload("server.ws.terminal.events", event);
34638
+ yield* effect.Queue.offer(queue, event);
34639
+ }));
34640
+ const snapshots = yield* terminalManager.listSessions;
34641
+ yield* effect.Effect.forEach(snapshots.filter((snapshot) => snapshot.status === "running"), (snapshot) => {
34642
+ const event = {
34643
+ type: "started",
34644
+ threadId: snapshot.threadId,
34645
+ terminalId: snapshot.terminalId,
34646
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
34647
+ snapshot
34648
+ };
34649
+ recordServerPerfPayload("server.ws.terminal.events", event);
34650
+ return effect.Queue.offer(queue, event);
34651
+ }, { discard: true });
34652
+ return unsubscribe;
34653
+ }), (unsubscribe) => effect.Effect.sync(unsubscribe)))), { "rpc.aggregate": "terminal" })
34147
34654
  });
34148
34655
  };
34149
34656
  //#endregion
@@ -34238,7 +34745,7 @@ const makeSourceControlHandlers = (ctx) => {
34238
34745
  cwd,
34239
34746
  reason: "sourceControl.searchIssues"
34240
34747
  })))), { "rpc.aggregate": "source-control" }),
34241
- [require_collectUint8StreamText.WS_METHODS.sourceControlListChangeRequests]: ({ cwd, state, limit, query }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(effect.Effect.flatMap((provider) => {
34748
+ [require_collectUint8StreamText.WS_METHODS.sourceControlListChangeRequests]: ({ cwd, state, limit, query }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListChangeRequests, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(effect.Effect.flatMap((provider) => {
34242
34749
  const trimmedQuery = query?.trim() ?? "";
34243
34750
  if (trimmedQuery.length > 0) return provider.searchChangeRequests({
34244
34751
  cwd,
@@ -34254,7 +34761,7 @@ const makeSourceControlHandlers = (ctx) => {
34254
34761
  }), effect.Effect.tap(() => refreshLinkedWorktreeSourceControlStates({
34255
34762
  cwd,
34256
34763
  reason: "sourceControl.listChangeRequests"
34257
- }))), { "rpc.aggregate": "source-control" }),
34764
+ })))), { "rpc.aggregate": "source-control" }),
34258
34765
  [require_collectUint8StreamText.WS_METHODS.sourceControlSearchChangeRequests]: ({ cwd, query, limit }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlSearchChangeRequests, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlSearchChangeRequests, sourceControlRegistry.resolve({ cwd }).pipe(effect.Effect.flatMap((provider) => provider.searchChangeRequests({
34259
34766
  cwd,
34260
34767
  query,
@@ -34332,7 +34839,7 @@ const makeSourceControlHandlers = (ctx) => {
34332
34839
  })), { "rpc.aggregate": "source-control" }),
34333
34840
  [require_collectUint8StreamText.WS_METHODS.sourceControlListIssueLabels]: ({ cwd }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListIssueLabels, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListIssueLabels, sourceControlRegistry.resolve({ cwd }).pipe(effect.Effect.flatMap((provider) => provider.listLabels({ cwd })))), { "rpc.aggregate": "source-control" }),
34334
34841
  [require_collectUint8StreamText.WS_METHODS.sourceControlListIssueAssignees]: ({ cwd }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListIssueAssignees, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListIssueAssignees, sourceControlRegistry.resolve({ cwd }).pipe(effect.Effect.flatMap((provider) => provider.listAssignees({ cwd })))), { "rpc.aggregate": "source-control" }),
34335
- [require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns]: ({ cwd, pullRequestNumber, commitSha, limit }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns, callSourceControlWorkflowMethod({
34842
+ [require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns]: ({ cwd, pullRequestNumber, commitSha, branch, limit }) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns, ownerEffect(require_collectUint8StreamText.WS_METHODS.sourceControlListWorkflowRuns, callSourceControlWorkflowMethod({
34336
34843
  cwd,
34337
34844
  operation: "listWorkflowRuns",
34338
34845
  invoke: (provider) => {
@@ -34341,6 +34848,7 @@ const makeSourceControlHandlers = (ctx) => {
34341
34848
  cwd,
34342
34849
  ...pullRequestNumber !== void 0 ? { pullRequestNumber } : {},
34343
34850
  ...commitSha !== void 0 ? { commitSha } : {},
34851
+ ...branch !== void 0 ? { branch } : {},
34344
34852
  ...limit !== void 0 ? { limit } : {}
34345
34853
  });
34346
34854
  }
@@ -35004,12 +35512,19 @@ const makeProviderHandlers = (ctx) => {
35004
35512
  });
35005
35513
  };
35006
35514
  //#endregion
35515
+ //#region src/ws/statisticsRpc.ts
35516
+ const makeStatisticsHandlers = (ctx) => {
35517
+ const { statisticsQuery, ownerEffect } = ctx;
35518
+ return defineWsHandlers({ [require_collectUint8StreamText.WS_METHODS.serverGetStatistics]: (_input) => observeRpcEffect(require_collectUint8StreamText.WS_METHODS.serverGetStatistics, ownerEffect(require_collectUint8StreamText.WS_METHODS.serverGetStatistics, statisticsQuery.getStatistics().pipe(effect.Effect.orDie)), { "rpc.aggregate": "server" }) });
35519
+ };
35520
+ //#endregion
35007
35521
  //#region src/ws/index.ts
35008
35522
  const makeWsRpcLayer = (session) => require_collectUint8StreamText.WsRpcGroup.toLayer(effect.Effect.gen(function* () {
35009
35523
  const ctx = yield* makeWsRpcContext(session);
35010
35524
  return require_collectUint8StreamText.WsRpcGroup.of({
35011
35525
  ...makeOrchestrationHandlers(ctx),
35012
35526
  ...makeProviderHandlers(ctx),
35527
+ ...makeStatisticsHandlers(ctx),
35013
35528
  ...makeSourceControlHandlers(ctx),
35014
35529
  ...makeProjectHandlers(ctx),
35015
35530
  ...makeGitHandlers(ctx),
@@ -35100,7 +35615,7 @@ const TelemetryEnvConfig = effect.Config.all({
35100
35615
  maxBufferedEvents: effect.Config.number("RYCO_TELEMETRY_MAX_BUFFERED_EVENTS").pipe(effect.Config.withDefault(1e3))
35101
35616
  });
35102
35617
  const makeAnalyticsService = effect.Effect.gen(function* () {
35103
- const telemetryConfig = yield* TelemetryEnvConfig.asEffect();
35618
+ const telemetryConfig = yield* TelemetryEnvConfig;
35104
35619
  const httpClient = yield* effect_unstable_http.HttpClient.HttpClient;
35105
35620
  const serverConfig = yield* require_Metrics.ServerConfig;
35106
35621
  const identifier = yield* getTelemetryIdentifier;
@@ -37427,6 +37942,8 @@ const DEFAULT_PERSIST_DEBOUNCE_MS = 40;
37427
37942
  const DEFAULT_SUBPROCESS_POLL_INTERVAL_MS = 1e3;
37428
37943
  const DEFAULT_PROCESS_KILL_GRACE_MS = 1e3;
37429
37944
  const DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS = 128;
37945
+ const DEFAULT_MAX_PENDING_PROCESS_EVENTS = 2048;
37946
+ const DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES = 2 * 1024 * 1024;
37430
37947
  const DEFAULT_OPEN_COLS = 120;
37431
37948
  const DEFAULT_OPEN_ROWS = 30;
37432
37949
  const TERMINAL_ENV_BLOCKLIST = new Set([
@@ -37483,13 +38000,86 @@ function cleanupProcessHandles(session) {
37483
38000
  session.unsubscribeExit?.();
37484
38001
  session.unsubscribeExit = null;
37485
38002
  }
37486
- function enqueueProcessEvent(session, expectedPid, event) {
38003
+ function clearPendingProcessEvents(session) {
38004
+ session.pendingProcessEvents = [];
38005
+ session.pendingProcessEventIndex = 0;
38006
+ session.pendingProcessOutputBytes = 0;
38007
+ session.processEventDrainRunning = false;
38008
+ }
38009
+ function enqueueProcessEvent(session, expectedPid, event, limits) {
37487
38010
  if (!session.process || session.status !== "running" || session.pid !== expectedPid) return false;
38011
+ if (event.type === "output" && session.pendingProcessEvents.some((pendingEvent) => pendingEvent.type === "exit")) return false;
38012
+ compactPendingProcessEvents(session);
37488
38013
  session.pendingProcessEvents.push(event);
38014
+ if (event.type === "output") session.pendingProcessOutputBytes += event.byteLength;
38015
+ enforcePendingProcessEventLimits(session, limits);
37489
38016
  if (session.processEventDrainRunning) return false;
37490
38017
  session.processEventDrainRunning = true;
37491
38018
  return true;
37492
38019
  }
38020
+ function compactPendingProcessEvents(session) {
38021
+ if (session.pendingProcessEventIndex <= 0) return;
38022
+ session.pendingProcessEvents = session.pendingProcessEvents.slice(session.pendingProcessEventIndex);
38023
+ session.pendingProcessEventIndex = 0;
38024
+ }
38025
+ function coalesceOldestPendingOutputPair(session) {
38026
+ for (let index = 0; index < session.pendingProcessEvents.length - 1; index += 1) {
38027
+ const current = session.pendingProcessEvents[index];
38028
+ const next = session.pendingProcessEvents[index + 1];
38029
+ if (current?.type !== "output" || next?.type !== "output") continue;
38030
+ session.pendingProcessEvents.splice(index, 2, {
38031
+ type: "output",
38032
+ data: `${current.data}${next.data}`,
38033
+ byteLength: current.byteLength + next.byteLength
38034
+ });
38035
+ return true;
38036
+ }
38037
+ return false;
38038
+ }
38039
+ function trimOutputDataToApproxBytes(data, maxBytes) {
38040
+ if (maxBytes <= 0 || data.length === 0) return {
38041
+ data: "",
38042
+ byteLength: 0
38043
+ };
38044
+ let nextData = data;
38045
+ let byteLength = approximateTextBytes(nextData);
38046
+ while (byteLength > maxBytes && nextData.length > 0) {
38047
+ const excessBytes = byteLength - maxBytes;
38048
+ const sliceStart = Math.min(nextData.length, Math.max(1, excessBytes));
38049
+ nextData = nextData.slice(sliceStart);
38050
+ byteLength = approximateTextBytes(nextData);
38051
+ }
38052
+ return {
38053
+ data: nextData,
38054
+ byteLength
38055
+ };
38056
+ }
38057
+ function trimPendingOutputBytesToLimit(session, maxPendingProcessOutputBytes) {
38058
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38059
+ for (let index = 0; index < session.pendingProcessEvents.length; index += 1) {
38060
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38061
+ const event = session.pendingProcessEvents[index];
38062
+ if (event?.type !== "output") continue;
38063
+ const targetEventBytes = Math.max(0, event.byteLength - (session.pendingProcessOutputBytes - maxPendingProcessOutputBytes));
38064
+ if (targetEventBytes <= 0) {
38065
+ session.pendingProcessEvents.splice(index, 1);
38066
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - event.byteLength);
38067
+ index -= 1;
38068
+ continue;
38069
+ }
38070
+ const trimmed = trimOutputDataToApproxBytes(event.data, targetEventBytes);
38071
+ session.pendingProcessEvents[index] = {
38072
+ type: "output",
38073
+ data: trimmed.data,
38074
+ byteLength: trimmed.byteLength
38075
+ };
38076
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - (event.byteLength - trimmed.byteLength));
38077
+ }
38078
+ }
38079
+ function enforcePendingProcessEventLimits(session, limits) {
38080
+ while (session.pendingProcessEvents.length > limits.maxPendingProcessEvents && coalesceOldestPendingOutputPair(session));
38081
+ trimPendingOutputBytesToLimit(session, limits.maxPendingProcessOutputBytes);
38082
+ }
37493
38083
  function defaultShellResolver(platform = process.platform, env = process.env) {
37494
38084
  if (platform === "win32") return "pwsh.exe";
37495
38085
  return env.SHELL ?? "bash";
@@ -37856,6 +38446,10 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
37856
38446
  const subprocessPollIntervalMs = options.subprocessPollIntervalMs ?? DEFAULT_SUBPROCESS_POLL_INTERVAL_MS;
37857
38447
  const processKillGraceMs = options.processKillGraceMs ?? DEFAULT_PROCESS_KILL_GRACE_MS;
37858
38448
  const maxRetainedInactiveSessions = options.maxRetainedInactiveSessions ?? DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS;
38449
+ const pendingProcessEventLimits = {
38450
+ maxPendingProcessEvents: Math.max(1, options.maxPendingProcessEvents ?? DEFAULT_MAX_PENDING_PROCESS_EVENTS),
38451
+ maxPendingProcessOutputBytes: Math.max(1, options.maxPendingProcessOutputBytes ?? DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES)
38452
+ };
37859
38453
  yield* fileSystem.makeDirectory(logsDir, { recursive: true }).pipe(effect.Effect.orDie);
37860
38454
  const managerStateRef = yield* effect.SynchronizedRef.make({
37861
38455
  sessions: /* @__PURE__ */ new Map(),
@@ -38092,16 +38686,12 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38092
38686
  while (true) {
38093
38687
  const action = yield* effect.Effect.sync(() => {
38094
38688
  if (session.pid !== expectedPid || !session.process || session.status !== "running") {
38095
- session.pendingProcessEvents = [];
38096
- session.pendingProcessEventIndex = 0;
38097
- session.processEventDrainRunning = false;
38689
+ clearPendingProcessEvents(session);
38098
38690
  return { type: "idle" };
38099
38691
  }
38100
38692
  const nextEvent = session.pendingProcessEvents[session.pendingProcessEventIndex];
38101
38693
  if (!nextEvent) {
38102
- session.pendingProcessEvents = [];
38103
- session.pendingProcessEventIndex = 0;
38104
- session.processEventDrainRunning = false;
38694
+ clearPendingProcessEvents(session);
38105
38695
  return { type: "idle" };
38106
38696
  }
38107
38697
  session.pendingProcessEventIndex += 1;
@@ -38110,6 +38700,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38110
38700
  session.pendingProcessEventIndex = 0;
38111
38701
  }
38112
38702
  if (nextEvent.type === "output") {
38703
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - nextEvent.byteLength);
38113
38704
  const sanitized = sanitizeTerminalHistoryChunk(session.pendingHistoryControlSequence, nextEvent.data);
38114
38705
  session.pendingHistoryControlSequence = sanitized.pendingControlSequence;
38115
38706
  if (sanitized.visibleText.length > 0) session.history = capHistory(`${session.history}${sanitized.visibleText}`, historyLineLimit);
@@ -38129,9 +38720,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38129
38720
  session.hasRunningSubprocess = false;
38130
38721
  session.status = "exited";
38131
38722
  session.pendingHistoryControlSequence = "";
38132
- session.pendingProcessEvents = [];
38133
- session.pendingProcessEventIndex = 0;
38134
- session.processEventDrainRunning = false;
38723
+ clearPendingProcessEvents(session);
38135
38724
  session.exitCode = Number.isInteger(nextEvent.event.exitCode) ? nextEvent.event.exitCode : null;
38136
38725
  session.exitSignal = Number.isInteger(nextEvent.event.signal) ? nextEvent.event.signal : null;
38137
38726
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -38179,9 +38768,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38179
38768
  session.hasRunningSubprocess = false;
38180
38769
  session.status = "exited";
38181
38770
  session.pendingHistoryControlSequence = "";
38182
- session.pendingProcessEvents = [];
38183
- session.pendingProcessEventIndex = 0;
38184
- session.processEventDrainRunning = false;
38771
+ clearPendingProcessEvents(session);
38185
38772
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38186
38773
  return [void 0, state];
38187
38774
  });
@@ -38233,9 +38820,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38233
38820
  session.exitCode = null;
38234
38821
  session.exitSignal = null;
38235
38822
  session.hasRunningSubprocess = false;
38236
- session.pendingProcessEvents = [];
38237
- session.pendingProcessEventIndex = 0;
38238
- session.processEventDrainRunning = false;
38823
+ clearPendingProcessEvents(session);
38239
38824
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38240
38825
  return [void 0, state];
38241
38826
  });
@@ -38249,15 +38834,16 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38249
38834
  const unsubscribeData = ptyProcess.onData((data) => {
38250
38835
  if (!enqueueProcessEvent(session, processPid, {
38251
38836
  type: "output",
38252
- data
38253
- })) return;
38837
+ data,
38838
+ byteLength: approximateTextBytes(data)
38839
+ }, pendingProcessEventLimits)) return;
38254
38840
  runFork(drainProcessEvents(session, processPid));
38255
38841
  });
38256
38842
  const unsubscribeExit = ptyProcess.onExit((event) => {
38257
38843
  if (!enqueueProcessEvent(session, processPid, {
38258
38844
  type: "exit",
38259
38845
  event
38260
- })) return;
38846
+ }, pendingProcessEventLimits)) return;
38261
38847
  runFork(drainProcessEvents(session, processPid));
38262
38848
  });
38263
38849
  yield* modifyManagerState((state) => {
@@ -38289,9 +38875,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38289
38875
  session.unsubscribeData = null;
38290
38876
  session.unsubscribeExit = null;
38291
38877
  session.hasRunningSubprocess = false;
38292
- session.pendingProcessEvents = [];
38293
- session.pendingProcessEventIndex = 0;
38294
- session.processEventDrainRunning = false;
38878
+ clearPendingProcessEvents(session);
38295
38879
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38296
38880
  return [void 0, state];
38297
38881
  });
@@ -38401,6 +38985,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38401
38985
  pendingHistoryControlSequence: "",
38402
38986
  pendingProcessEvents: [],
38403
38987
  pendingProcessEventIndex: 0,
38988
+ pendingProcessOutputBytes: 0,
38404
38989
  processEventDrainRunning: false,
38405
38990
  exitCode: null,
38406
38991
  exitSignal: null,
@@ -38447,18 +39032,14 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38447
39032
  liveSession.runtimeEnv = nextRuntimeEnv;
38448
39033
  liveSession.history = "";
38449
39034
  liveSession.pendingHistoryControlSequence = "";
38450
- liveSession.pendingProcessEvents = [];
38451
- liveSession.pendingProcessEventIndex = 0;
38452
- liveSession.processEventDrainRunning = false;
39035
+ clearPendingProcessEvents(liveSession);
38453
39036
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38454
39037
  } else if (liveSession.status === "exited" || liveSession.status === "error") {
38455
39038
  liveSession.runtimeEnv = nextRuntimeEnv;
38456
39039
  liveSession.worktreePath = input.worktreePath ?? null;
38457
39040
  liveSession.history = "";
38458
39041
  liveSession.pendingHistoryControlSequence = "";
38459
- liveSession.pendingProcessEvents = [];
38460
- liveSession.pendingProcessEventIndex = 0;
38461
- liveSession.processEventDrainRunning = false;
39042
+ clearPendingProcessEvents(liveSession);
38462
39043
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38463
39044
  }
38464
39045
  if (!liveSession.process) {
@@ -38512,9 +39093,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38512
39093
  const session = yield* requireSession(input.threadId, terminalId);
38513
39094
  session.history = "";
38514
39095
  session.pendingHistoryControlSequence = "";
38515
- session.pendingProcessEvents = [];
38516
- session.pendingProcessEventIndex = 0;
38517
- session.processEventDrainRunning = false;
39096
+ clearPendingProcessEvents(session);
38518
39097
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38519
39098
  yield* persistHistory(input.threadId, terminalId, session.history);
38520
39099
  yield* publishEvent({
@@ -38545,6 +39124,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38545
39124
  pendingHistoryControlSequence: "",
38546
39125
  pendingProcessEvents: [],
38547
39126
  pendingProcessEventIndex: 0,
39127
+ pendingProcessOutputBytes: 0,
38548
39128
  processEventDrainRunning: false,
38549
39129
  exitCode: null,
38550
39130
  exitSignal: null,
@@ -38578,9 +39158,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38578
39158
  const rows = input.rows ?? session.rows;
38579
39159
  session.history = "";
38580
39160
  session.pendingHistoryControlSequence = "";
38581
- session.pendingProcessEvents = [];
38582
- session.pendingProcessEventIndex = 0;
38583
- session.processEventDrainRunning = false;
39161
+ clearPendingProcessEvents(session);
38584
39162
  yield* persistHistory(input.threadId, terminalId, session.history);
38585
39163
  yield* startSession(session, {
38586
39164
  threadId: input.threadId,
@@ -38602,6 +39180,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38602
39180
  yield* effect.Effect.forEach(threadSessions, (session) => closeSession(input.threadId, session.terminalId, false), { discard: true });
38603
39181
  if (input.deleteHistory) yield* deleteAllHistoryForThread(input.threadId);
38604
39182
  }));
39183
+ const listSessions = effect.SynchronizedRef.get(managerStateRef).pipe(effect.Effect.map((state) => [...state.sessions.values()].map(snapshot).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))));
38605
39184
  return {
38606
39185
  open,
38607
39186
  write,
@@ -38610,6 +39189,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38610
39189
  restart,
38611
39190
  close,
38612
39191
  listDiagnostics: effect.SynchronizedRef.get(managerStateRef).pipe(effect.Effect.map((state) => [...state.sessions.values()].map(diagnosticsSnapshot).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt)))),
39192
+ listSessions,
38613
39193
  subscribe: (listener) => effect.Effect.sync(() => {
38614
39194
  terminalEventListeners.add(listener);
38615
39195
  return () => {
@@ -39105,7 +39685,7 @@ function isGitRepository(cwd) {
39105
39685
  //#endregion
39106
39686
  //#region src/orchestration/Layers/ProviderRuntimeIngestion.ts
39107
39687
  const providerTurnKey = (threadId, turnId) => `${threadId}:${turnId}`;
39108
- const providerCommandId = (event, tag) => require_collectUint8StreamText.CommandId.make(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`);
39688
+ const providerCommandId = (event, tag, discriminator) => require_collectUint8StreamText.CommandId.make(discriminator === void 0 ? `provider:${event.eventId}:${tag}` : `provider:${event.eventId}:${tag}:${discriminator}`);
39109
39689
  const liveAssistantDeltaBufferKey = (threadId, messageId) => `${threadId}:${messageId}`;
39110
39690
  const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 1e4;
39111
39691
  const TURN_MESSAGE_IDS_BY_TURN_TTL = effect.Duration.minutes(120);
@@ -39179,6 +39759,7 @@ function rawPayloadThreadId(event) {
39179
39759
  return asTrimmedString(asRecord(event.raw?.payload)?.threadId);
39180
39760
  }
39181
39761
  function collabReceiverThreadIdsFromRuntimeEvent(event) {
39762
+ if (event.type === "subagent.started" || event.type === "subagent.updated" || event.type === "subagent.completed") return event.payload.subagent.providerThreadId ? [event.payload.subagent.providerThreadId] : [];
39182
39763
  if (event.type !== "item.started" && event.type !== "item.updated" && event.type !== "item.completed") return [];
39183
39764
  if (event.payload.itemType !== "collab_agent_tool_call") return [];
39184
39765
  const item = asRecord(asRecord(event.payload.data)?.item);
@@ -39236,6 +39817,9 @@ function normalizeRuntimeTurnState(value) {
39236
39817
  default: return "completed";
39237
39818
  }
39238
39819
  }
39820
+ function isTerminalProjectionTurnState(state) {
39821
+ return state === "completed" || state === "error" || state === "interrupted";
39822
+ }
39239
39823
  function orchestrationSessionStatusFromRuntimeState(state) {
39240
39824
  switch (state) {
39241
39825
  case "starting": return "starting";
@@ -39423,6 +40007,53 @@ function runtimeEventToActivities(event) {
39423
40007
  turnId: toTurnId$1(event.turnId) ?? null,
39424
40008
  ...maybeSequence
39425
40009
  }];
40010
+ case "subagent.started": return [{
40011
+ id: event.eventId,
40012
+ createdAt: event.createdAt,
40013
+ tone: "tool",
40014
+ kind: "subagent.started",
40015
+ summary: `${event.payload.subagent.label ?? "Subagent"} started`,
40016
+ payload: {
40017
+ itemType: "collab_agent_tool_call",
40018
+ status: "running",
40019
+ subagent: event.payload.subagent
40020
+ },
40021
+ turnId: toTurnId$1(event.turnId) ?? null,
40022
+ ...maybeSequence
40023
+ }];
40024
+ case "subagent.updated": return [{
40025
+ id: event.eventId,
40026
+ createdAt: event.createdAt,
40027
+ tone: "tool",
40028
+ kind: "subagent.updated",
40029
+ summary: event.payload.summary ?? `${event.payload.subagent.label ?? "Subagent"} updated`,
40030
+ payload: {
40031
+ itemType: "collab_agent_tool_call",
40032
+ ...event.payload.status ? { status: event.payload.status } : {},
40033
+ subagent: event.payload.subagent,
40034
+ ...event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {},
40035
+ ...event.payload.summary ? { summary: truncateDetail(event.payload.summary) } : {},
40036
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40037
+ },
40038
+ turnId: toTurnId$1(event.turnId) ?? null,
40039
+ ...maybeSequence
40040
+ }];
40041
+ case "subagent.completed": return [{
40042
+ id: event.eventId,
40043
+ createdAt: event.createdAt,
40044
+ tone: event.payload.status === "failed" ? "error" : "tool",
40045
+ kind: "subagent.completed",
40046
+ 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`,
40047
+ payload: {
40048
+ itemType: "collab_agent_tool_call",
40049
+ status: event.payload.status,
40050
+ subagent: event.payload.subagent,
40051
+ ...event.payload.summary ? { detail: truncateDetail(event.payload.summary) } : {},
40052
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40053
+ },
40054
+ turnId: toTurnId$1(event.turnId) ?? null,
40055
+ ...maybeSequence
40056
+ }];
39426
40057
  case "thread.state.changed":
39427
40058
  if (event.payload.state !== "compacted") return [];
39428
40059
  return [{
@@ -39615,7 +40246,7 @@ const make$3 = effect.Effect.gen(function* () {
39615
40246
  }
39616
40247
  yield* orchestrationEngine.dispatch({
39617
40248
  type: "thread.message.assistant.delta",
39618
- commandId: providerCommandId(buffer.event, input.commandTag),
40249
+ commandId: providerCommandId(buffer.event, input.commandTag, String(buffer.messageId)),
39619
40250
  threadId: buffer.threadId,
39620
40251
  messageId: buffer.messageId,
39621
40252
  delta: buffer.text,
@@ -39694,9 +40325,10 @@ const make$3 = effect.Effect.gen(function* () {
39694
40325
  };
39695
40326
  const upsertSubagentMessageActivity = (input) => effect.Effect.gen(function* () {
39696
40327
  const sessionSequence = input.event.sessionSequence;
40328
+ const commandKey = `${input.providerThreadId}:${input.providerItemId}`;
39697
40329
  yield* orchestrationEngine.dispatch({
39698
40330
  type: "thread.activity.append",
39699
- commandId: providerCommandId(input.event, "subagent-message-upsert"),
40331
+ commandId: providerCommandId(input.event, "subagent-message-upsert", commandKey),
39700
40332
  threadId: input.threadId,
39701
40333
  activity: {
39702
40334
  id: subagentMessageActivityId({
@@ -39714,6 +40346,7 @@ const make$3 = effect.Effect.gen(function* () {
39714
40346
  streaming: input.streaming,
39715
40347
  providerThreadId: input.providerThreadId,
39716
40348
  providerItemId: input.providerItemId,
40349
+ ...input.subagentId ? { subagentId: input.subagentId } : {},
39717
40350
  ...input.event.providerRefs ? { providerRefs: input.event.providerRefs } : {}
39718
40351
  },
39719
40352
  turnId: toTurnId$1(input.event.turnId) ?? null,
@@ -39753,6 +40386,16 @@ const make$3 = effect.Effect.gen(function* () {
39753
40386
  streaming: false
39754
40387
  });
39755
40388
  });
40389
+ const bufferedSubagentProviderItemIds = (input) => {
40390
+ const prefix = subagentMessageBufferKey({
40391
+ threadId: input.threadId,
40392
+ providerThreadId: input.providerThreadId,
40393
+ providerItemId: ""
40394
+ });
40395
+ const providerItemIds = /* @__PURE__ */ new Set();
40396
+ for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) providerItemIds.add(key.slice(prefix.length));
40397
+ return [...providerItemIds];
40398
+ };
39756
40399
  const clearSubagentMessageBuffersForThread = (threadId) => effect.Effect.sync(() => {
39757
40400
  const prefix = `${threadId}:`;
39758
40401
  for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) subagentMessageTextByKey.delete(key);
@@ -39786,7 +40429,7 @@ const make$3 = effect.Effect.gen(function* () {
39786
40429
  if (!hasRenderableAssistantText(bufferedText)) return false;
39787
40430
  yield* orchestrationEngine.dispatch({
39788
40431
  type: "thread.message.assistant.delta",
39789
- commandId: providerCommandId(input.event, input.commandTag),
40432
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39790
40433
  threadId: input.threadId,
39791
40434
  messageId: input.messageId,
39792
40435
  delta: bufferedText,
@@ -39814,7 +40457,7 @@ const make$3 = effect.Effect.gen(function* () {
39814
40457
  const hasRenderableText = hasRenderableAssistantText(text);
39815
40458
  if (hasRenderableText) yield* orchestrationEngine.dispatch({
39816
40459
  type: "thread.message.assistant.delta",
39817
- commandId: providerCommandId(input.event, input.finalDeltaCommandTag),
40460
+ commandId: providerCommandId(input.event, input.finalDeltaCommandTag, String(input.messageId)),
39818
40461
  threadId: input.threadId,
39819
40462
  messageId: input.messageId,
39820
40463
  delta: text,
@@ -39823,7 +40466,7 @@ const make$3 = effect.Effect.gen(function* () {
39823
40466
  });
39824
40467
  if (input.hasProjectedMessage || hasRenderableText) yield* orchestrationEngine.dispatch({
39825
40468
  type: "thread.message.assistant.complete",
39826
- commandId: providerCommandId(input.event, input.commandTag),
40469
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39827
40470
  threadId: input.threadId,
39828
40471
  messageId: input.messageId,
39829
40472
  ...input.turnId ? { turnId: input.turnId } : {},
@@ -39857,7 +40500,7 @@ const make$3 = effect.Effect.gen(function* () {
39857
40500
  const existingPlan = findProposedPlanById(input.threadProposedPlans, input.planId);
39858
40501
  yield* orchestrationEngine.dispatch({
39859
40502
  type: "thread.proposed-plan.upsert",
39860
- commandId: providerCommandId(input.event, "proposed-plan-upsert"),
40503
+ commandId: providerCommandId(input.event, "proposed-plan-upsert", input.planId),
39861
40504
  threadId: input.threadId,
39862
40505
  proposedPlan: {
39863
40506
  id: input.planId,
@@ -39924,13 +40567,13 @@ const make$3 = effect.Effect.gen(function* () {
39924
40567
  if (!sameId$1(yield* getExpectedProviderTurnIdForThread(threadId), eventTurnId)) return null;
39925
40568
  return yield* getSourceProposedPlanReferenceForPendingTurnStart(threadId);
39926
40569
  });
39927
- const markSourceProposedPlanImplemented = effect.Effect.fn("markSourceProposedPlanImplemented")(function* (sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
40570
+ const markSourceProposedPlanImplemented = effect.Effect.fn("markSourceProposedPlanImplemented")(function* (event, sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
39928
40571
  const sourceThread = yield* resolveThreadDetail(sourceThreadId);
39929
40572
  const sourcePlan = sourceThread?.proposedPlans.find((entry) => entry.id === sourcePlanId);
39930
40573
  if (!sourceThread || !sourcePlan || sourcePlan.implementedAt !== null) return;
39931
40574
  yield* orchestrationEngine.dispatch({
39932
40575
  type: "thread.proposed-plan.upsert",
39933
- commandId: require_collectUint8StreamText.CommandId.make(`provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`),
40576
+ commandId: providerCommandId(event, "source-proposed-plan-implemented", `${sourceThreadId}:${sourcePlanId}:${implementationThreadId}`),
39934
40577
  threadId: sourceThread.id,
39935
40578
  proposedPlan: {
39936
40579
  ...sourcePlan,
@@ -39955,6 +40598,12 @@ const make$3 = effect.Effect.gen(function* () {
39955
40598
  const activeTurnId = thread.session?.activeTurnId ?? null;
39956
40599
  const conflictsWithActiveTurn = activeTurnId !== null && eventTurnId !== void 0 && !sameId$1(activeTurnId, eventTurnId);
39957
40600
  const missingTurnForActiveTurn = activeTurnId !== null && eventTurnId === void 0;
40601
+ const existingEventTurn = event.type === "turn.started" && eventTurnId !== void 0 ? yield* projectionTurnRepository.getByTurnId({
40602
+ threadId: thread.id,
40603
+ turnId: eventTurnId
40604
+ }) : effect.Option.none();
40605
+ const eventTurnAlreadyTerminal = effect.Option.isSome(existingEventTurn) && isTerminalProjectionTurnState(existingEventTurn.value.state);
40606
+ const eventTurnAlreadyProjectedWithoutActiveSession = activeTurnId === null && effect.Option.isSome(existingEventTurn);
39958
40607
  if (!isAssistantTextDeltaEvent(event)) yield* flushLiveAssistantDeltaBuffersSafely({ commandTag: "assistant-delta-flush-before-runtime-event" });
39959
40608
  yield* rememberSubagentProviderThreadIds(thread.id, event);
39960
40609
  const eventProviderThreadId = rawPayloadThreadId(event);
@@ -39965,8 +40614,9 @@ const make$3 = effect.Effect.gen(function* () {
39965
40614
  case "session.exited": return true;
39966
40615
  case "session.started":
39967
40616
  case "thread.started": return true;
39968
- case "turn.started": return !conflictsWithActiveTurn;
40617
+ case "turn.started": return !conflictsWithActiveTurn && !eventTurnAlreadyTerminal && !eventTurnAlreadyProjectedWithoutActiveSession;
39969
40618
  case "turn.completed":
40619
+ case "turn.aborted":
39970
40620
  if (conflictsWithActiveTurn || missingTurnForActiveTurn) return false;
39971
40621
  if (activeTurnId !== null && eventTurnId !== void 0) return sameId$1(activeTurnId, eventTurnId);
39972
40622
  return true;
@@ -39974,21 +40624,22 @@ const make$3 = effect.Effect.gen(function* () {
39974
40624
  }
39975
40625
  })();
39976
40626
  const acceptedTurnStartedSourcePlan = event.type === "turn.started" && shouldApplyThreadLifecycle ? yield* getSourceProposedPlanReferenceForAcceptedTurnStart(thread.id, eventTurnId) : null;
39977
- 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") {
39978
- const nextActiveTurnId = event.type === "turn.started" ? eventTurnId ?? null : event.type === "turn.completed" || event.type === "session.exited" ? null : activeTurnId;
40627
+ 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") {
40628
+ const nextActiveTurnId = event.type === "turn.started" ? eventTurnId ?? null : event.type === "turn.completed" || event.type === "turn.aborted" || event.type === "session.exited" ? null : activeTurnId;
39979
40629
  const status = (() => {
39980
40630
  switch (event.type) {
39981
40631
  case "session.state.changed": return orchestrationSessionStatusFromRuntimeState(event.payload.state);
39982
40632
  case "turn.started": return "running";
39983
40633
  case "session.exited": return "stopped";
39984
40634
  case "turn.completed": return normalizeRuntimeTurnState(event.payload.state) === "failed" ? "error" : "ready";
40635
+ case "turn.aborted": return "ready";
39985
40636
  case "session.started":
39986
40637
  case "thread.started": return activeTurnId !== null ? "running" : "ready";
39987
40638
  }
39988
40639
  })();
39989
40640
  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;
39990
40641
  if (shouldApplyThreadLifecycle) {
39991
- if (event.type === "turn.started" && acceptedTurnStartedSourcePlan !== null) yield* markSourceProposedPlanImplemented(acceptedTurnStartedSourcePlan.sourceThreadId, acceptedTurnStartedSourcePlan.sourcePlanId, thread.id, now).pipe(effect.Effect.catchCause((cause) => effect.Effect.logWarning("provider runtime ingestion failed to mark source proposed plan", {
40642
+ if (event.type === "turn.started" && acceptedTurnStartedSourcePlan !== null) yield* markSourceProposedPlanImplemented(event, acceptedTurnStartedSourcePlan.sourceThreadId, acceptedTurnStartedSourcePlan.sourcePlanId, thread.id, now).pipe(effect.Effect.catchCause((cause) => effect.Effect.logWarning("provider runtime ingestion failed to mark source proposed plan", {
39992
40643
  eventId: event.eventId,
39993
40644
  eventType: event.type,
39994
40645
  cause: effect.Cause.pretty(cause)
@@ -40010,11 +40661,30 @@ const make$3 = effect.Effect.gen(function* () {
40010
40661
  },
40011
40662
  createdAt: now
40012
40663
  });
40664
+ if (event.type === "turn.aborted" && eventTurnId !== void 0) yield* orchestrationEngine.dispatch({
40665
+ type: "thread.turn.interrupt",
40666
+ commandId: providerCommandId(event, "thread-turn-interrupt", String(eventTurnId)),
40667
+ threadId: thread.id,
40668
+ turnId: eventTurnId,
40669
+ createdAt: now
40670
+ });
40013
40671
  }
40014
40672
  }
40015
40673
  const assistantDelta = isAssistantTextDeltaEvent(event) ? event.payload.delta : void 0;
40674
+ const subagentDelta = event.type === "subagent.message.delta" ? event.payload.delta : void 0;
40016
40675
  const proposedPlanDelta = event.type === "turn.proposed.delta" ? event.payload.delta : void 0;
40017
- if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
40676
+ if (event.type === "subagent.message.delta" && subagentDelta && subagentDelta.length > 0) {
40677
+ const subagentPayload = event.payload;
40678
+ yield* appendSubagentMessageDelta({
40679
+ event,
40680
+ threadId: thread.id,
40681
+ subagentId: String(subagentPayload.subagentId),
40682
+ providerThreadId: subagentPayload.providerThreadId ?? String(subagentPayload.subagentId),
40683
+ providerItemId: subagentPayload.providerMessageId ?? String(subagentPayload.subagentId),
40684
+ delta: subagentDelta,
40685
+ createdAt: now
40686
+ });
40687
+ } else if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
40018
40688
  event,
40019
40689
  threadId: thread.id,
40020
40690
  providerThreadId: eventProviderThreadId,
@@ -40034,7 +40704,7 @@ const make$3 = effect.Effect.gen(function* () {
40034
40704
  const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, assistantDelta);
40035
40705
  if (spillChunk.length > 0) yield* orchestrationEngine.dispatch({
40036
40706
  type: "thread.message.assistant.delta",
40037
- commandId: providerCommandId(event, "assistant-delta-buffer-spill"),
40707
+ commandId: providerCommandId(event, "assistant-delta-buffer-spill", String(assistantMessageId)),
40038
40708
  threadId: thread.id,
40039
40709
  messageId: assistantMessageId,
40040
40710
  delta: spillChunk,
@@ -40119,6 +40789,26 @@ const make$3 = effect.Effect.gen(function* () {
40119
40789
  ...subagentMessageCompletion.fallbackText !== void 0 ? { fallbackText: subagentMessageCompletion.fallbackText } : {},
40120
40790
  createdAt: now
40121
40791
  });
40792
+ if (event.type === "subagent.completed") {
40793
+ const subagentId = String(event.payload.subagent.subagentId);
40794
+ const providerThreadId = event.payload.subagent.providerThreadId ?? subagentId;
40795
+ const providerItemIds = bufferedSubagentProviderItemIds({
40796
+ threadId: thread.id,
40797
+ providerThreadId
40798
+ });
40799
+ yield* effect.Effect.forEach(providerItemIds.length > 0 ? providerItemIds : [subagentId], (providerItemId) => completeSubagentMessage({
40800
+ event,
40801
+ threadId: thread.id,
40802
+ subagentId,
40803
+ providerThreadId,
40804
+ providerItemId,
40805
+ ...event.payload.summary !== void 0 ? { fallbackText: event.payload.summary } : {},
40806
+ createdAt: now
40807
+ }), {
40808
+ concurrency: 1,
40809
+ discard: true
40810
+ });
40811
+ }
40122
40812
  if (proposedPlanCompletion) {
40123
40813
  const detailedThread = yield* getLoadedThreadDetail();
40124
40814
  yield* finalizeBufferedProposedPlan({
@@ -40131,7 +40821,7 @@ const make$3 = effect.Effect.gen(function* () {
40131
40821
  updatedAt: now
40132
40822
  });
40133
40823
  }
40134
- if (event.type === "turn.completed") {
40824
+ if (event.type === "turn.completed" || event.type === "turn.aborted") {
40135
40825
  const detailedThread = yield* getLoadedThreadDetail();
40136
40826
  const messages = detailedThread?.messages ?? [];
40137
40827
  const proposedPlans = detailedThread?.proposedPlans ?? [];
@@ -40201,7 +40891,7 @@ const make$3 = effect.Effect.gen(function* () {
40201
40891
  const assistantMessageId = require_collectUint8StreamText.MessageId.make(`assistant:${event.itemId ?? event.turnId ?? event.eventId}`);
40202
40892
  yield* orchestrationEngine.dispatch({
40203
40893
  type: "thread.turn.diff.complete",
40204
- commandId: providerCommandId(event, "thread-turn-diff-complete"),
40894
+ commandId: providerCommandId(event, "thread-turn-diff-complete", String(turnId)),
40205
40895
  threadId: thread.id,
40206
40896
  turnId,
40207
40897
  completedAt: now,
@@ -40217,7 +40907,7 @@ const make$3 = effect.Effect.gen(function* () {
40217
40907
  const activities = runtimeEventToActivities(event);
40218
40908
  yield* effect.Effect.forEach(activities, (activity) => orchestrationEngine.dispatch({
40219
40909
  type: "thread.activity.append",
40220
- commandId: providerCommandId(event, "thread-activity-append"),
40910
+ commandId: providerCommandId(event, "thread-activity-append", String(activity.id)),
40221
40911
  threadId: thread.id,
40222
40912
  activity,
40223
40913
  createdAt: activity.createdAt
@@ -40281,6 +40971,7 @@ function mapProviderSessionStatusToOrchestrationStatus(status) {
40281
40971
  }
40282
40972
  const turnStartKeyForEvent = (event) => event.commandId !== null ? `command:${event.commandId}` : `event:${event.eventId}`;
40283
40973
  const serverCommandId$1 = (tag) => require_collectUint8StreamText.CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
40974
+ const isProviderOriginatedCommandId = (commandId) => commandId !== null && String(commandId).startsWith("provider:");
40284
40975
  const HANDLED_TURN_START_KEY_MAX = 1e4;
40285
40976
  const HANDLED_TURN_START_KEY_TTL = effect.Duration.minutes(30);
40286
40977
  const DEFAULT_RUNTIME_MODE = "full-access";
@@ -40632,6 +41323,17 @@ const make$2 = effect.Effect.gen(function* () {
40632
41323
  if (yield* hasHandledTurnStartRecently(turnStartKeyForEvent(event))) return;
40633
41324
  const thread = yield* resolveThread(event.payload.threadId);
40634
41325
  if (!thread) return;
41326
+ if (thread.session?.status === "running" && thread.session.activeTurnId !== null) {
41327
+ yield* appendProviderFailureActivity({
41328
+ threadId: event.payload.threadId,
41329
+ kind: "provider.turn.start.failed",
41330
+ summary: "Provider turn start rejected",
41331
+ 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.`,
41332
+ turnId: null,
41333
+ createdAt: event.payload.createdAt
41334
+ });
41335
+ return;
41336
+ }
40635
41337
  const message = thread.messages.find((entry) => entry.id === event.payload.messageId);
40636
41338
  if (!message || message.role !== "user") {
40637
41339
  yield* appendProviderFailureActivity({
@@ -40702,6 +41404,7 @@ const make$2 = effect.Effect.gen(function* () {
40702
41404
  yield* providerService.sendTurn(sendTurnRequest.value).pipe(effect.Effect.catchCause(recoverTurnStartFailure), effect.Effect.forkScoped);
40703
41405
  });
40704
41406
  const processTurnInterruptRequested = effect.Effect.fn("processTurnInterruptRequested")(function* (event) {
41407
+ if (isProviderOriginatedCommandId(event.commandId)) return;
40705
41408
  const thread = yield* resolveThread(event.payload.threadId);
40706
41409
  if (!thread) return;
40707
41410
  if (!(thread.session && thread.session.status !== "stopped")) return yield* appendProviderFailureActivity({
@@ -46998,6 +47701,10 @@ const decideOrchestrationCommand = effect.Effect.fn("decideOrchestrationCommand"
46998
47701
  command,
46999
47702
  threadId: command.threadId
47000
47703
  });
47704
+ if (targetThread.session?.status === "running" && targetThread.session.activeTurnId !== null) return yield* new OrchestrationCommandInvariantError({
47705
+ commandType: command.type,
47706
+ detail: `Thread '${command.threadId}' already has active turn '${targetThread.session.activeTurnId}' and cannot start another turn until it finishes.`
47707
+ });
47001
47708
  const sourceProposedPlan = command.sourceProposedPlan;
47002
47709
  const sourceThread = sourceProposedPlan ? yield* requireThread({
47003
47710
  readModel,
@@ -49845,8 +50552,20 @@ const WorkspaceRootLookupInput = effect.Schema.Struct({ workspaceRoot: effect.Sc
49845
50552
  const ProjectIdLookupInput = effect.Schema.Struct({ projectId: require_collectUint8StreamText.ProjectId });
49846
50553
  const ThreadIdLookupInput = effect.Schema.Struct({ threadId: require_collectUint8StreamText.ThreadId });
49847
50554
  const WorktreeIdLookupInput = effect.Schema.Struct({ worktreeId: require_collectUint8StreamText.WorktreeId });
50555
+ const ThreadMessageSearchQueryInput = effect.Schema.Struct({
50556
+ likePattern: effect.Schema.String,
50557
+ projectId: effect.Schema.NullOr(require_collectUint8StreamText.ProjectId),
50558
+ limit: require_collectUint8StreamText.NonNegativeInt
50559
+ });
49848
50560
  const ProjectionProjectLookupRowSchema = ProjectionProjectDbRowSchema;
49849
50561
  const ProjectionThreadIdLookupRowSchema = effect.Schema.Struct({ threadId: require_collectUint8StreamText.ThreadId });
50562
+ const ProjectionThreadMessageSearchRowSchema = effect.Schema.Struct({
50563
+ threadId: require_collectUint8StreamText.ThreadId,
50564
+ messageId: require_collectUint8StreamText.MessageId,
50565
+ text: effect.Schema.String,
50566
+ createdAt: require_collectUint8StreamText.IsoDateTime,
50567
+ updatedAt: require_collectUint8StreamText.IsoDateTime
50568
+ });
49850
50569
  const ProjectionThreadCheckpointContextThreadRowSchema = effect.Schema.Struct({
49851
50570
  threadId: require_collectUint8StreamText.ThreadId,
49852
50571
  projectId: require_collectUint8StreamText.ProjectId,
@@ -49878,6 +50597,20 @@ function computeSnapshotSequence(stateRows) {
49878
50597
  }
49879
50598
  return Number.isFinite(minSequence) ? minSequence : 0;
49880
50599
  }
50600
+ function escapeSqlLikePattern(value) {
50601
+ return value.replace(/[\\%_]/g, (match) => `\\${match}`);
50602
+ }
50603
+ function buildMessageSearchSnippet(input) {
50604
+ const maxLength = input.maxLength ?? 180;
50605
+ const normalizedQuery = input.query.trim().toLowerCase();
50606
+ const normalizedText = input.text.toLowerCase();
50607
+ const matchIndex = normalizedQuery.length > 0 ? normalizedText.indexOf(normalizedQuery) : -1;
50608
+ const contextLength = Math.floor(maxLength / 2);
50609
+ const start = matchIndex <= contextLength ? 0 : Math.max(0, matchIndex - Math.floor(contextLength / 2));
50610
+ const end = Math.min(input.text.length, start + maxLength);
50611
+ const snippet = input.text.slice(start, end).replace(/\s+/g, " ").trim();
50612
+ return `${start > 0 ? "..." : ""}${snippet}${end < input.text.length ? "..." : ""}`;
50613
+ }
49881
50614
  function mapLatestTurn(row) {
49882
50615
  return {
49883
50616
  turnId: row.turnId,
@@ -50347,6 +51080,30 @@ const makeProjectionSnapshotQuery = effect.Effect.gen(function* () {
50347
51080
  FROM projection_thread_messages
50348
51081
  WHERE thread_id = ${threadId}
50349
51082
  ORDER BY created_at ASC, message_id ASC
51083
+ `
51084
+ });
51085
+ const searchThreadMessageRows = effect_unstable_sql_SqlSchema.findAll({
51086
+ Request: ThreadMessageSearchQueryInput,
51087
+ Result: ProjectionThreadMessageSearchRowSchema,
51088
+ execute: ({ likePattern, projectId, limit }) => sql`
51089
+ SELECT
51090
+ messages.thread_id AS "threadId",
51091
+ messages.message_id AS "messageId",
51092
+ messages.text,
51093
+ messages.created_at AS "createdAt",
51094
+ messages.updated_at AS "updatedAt"
51095
+ FROM projection_thread_messages AS messages
51096
+ INNER JOIN projection_threads AS threads
51097
+ ON threads.thread_id = messages.thread_id
51098
+ INNER JOIN projection_projects AS projects
51099
+ ON projects.project_id = threads.project_id
51100
+ WHERE threads.deleted_at IS NULL
51101
+ AND projects.deleted_at IS NULL
51102
+ AND messages.role IN ('user', 'assistant')
51103
+ AND (${projectId} IS NULL OR threads.project_id = ${projectId})
51104
+ AND lower(messages.text) LIKE ${likePattern} ESCAPE '\\'
51105
+ ORDER BY messages.created_at DESC, messages.message_id DESC
51106
+ LIMIT ${limit}
50350
51107
  `
50351
51108
  });
50352
51109
  const listThreadProposedPlanRowsByThread = effect_unstable_sql_SqlSchema.findAll({
@@ -50935,6 +51692,24 @@ const makeProjectionSnapshotQuery = effect.Effect.gen(function* () {
50935
51692
  };
50936
51693
  return effect.Option.some(yield* decodeThread(thread).pipe(effect.Effect.mapError(toPersistenceDecodeError("ProjectionSnapshotQuery.getThreadDetailById:decodeThread"))));
50937
51694
  });
51695
+ const searchThreadMessages = (input) => {
51696
+ const query = input.query.trim();
51697
+ if (query.length === 0) return effect.Effect.succeed([]);
51698
+ const limit = Math.min(Math.max(1, input.limit), 50);
51699
+ return searchThreadMessageRows({
51700
+ likePattern: `%${escapeSqlLikePattern(query.toLowerCase())}%`,
51701
+ projectId: input.projectId ?? null,
51702
+ limit
51703
+ }).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError("ProjectionSnapshotQuery.searchThreadMessages:query", "ProjectionSnapshotQuery.searchThreadMessages:decodeRows")), effect.Effect.map((rows) => rows.map((row) => ({
51704
+ threadId: row.threadId,
51705
+ messageId: row.messageId,
51706
+ snippet: buildMessageSearchSnippet({
51707
+ text: row.text,
51708
+ query
51709
+ }),
51710
+ timestamp: row.createdAt
51711
+ }))));
51712
+ };
50938
51713
  return {
50939
51714
  getCommandReadModel,
50940
51715
  getSnapshot,
@@ -50947,7 +51722,8 @@ const makeProjectionSnapshotQuery = effect.Effect.gen(function* () {
50947
51722
  getThreadCheckpointContext,
50948
51723
  getThreadShellById,
50949
51724
  getWorktreeShellById,
50950
- getThreadDetailById
51725
+ getThreadDetailById,
51726
+ searchThreadMessages
50951
51727
  };
50952
51728
  });
50953
51729
  const OrchestrationProjectionSnapshotQueryLive = effect.Layer.effect(ProjectionSnapshotQuery, makeProjectionSnapshotQuery);
@@ -50955,7 +51731,7 @@ const OrchestrationProjectionSnapshotQueryLive = effect.Layer.effect(ProjectionS
50955
51731
  //#region src/orchestration/runtimeLayer.ts
50956
51732
  const OrchestrationEventInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationEventStoreLive, OrchestrationCommandReceiptRepositoryLive);
50957
51733
  const OrchestrationProjectionPipelineLayerLive = OrchestrationProjectionPipelineLive.pipe(effect.Layer.provide(OrchestrationEventStoreLive));
50958
- const OrchestrationInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
51734
+ const OrchestrationInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, StatisticsQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
50959
51735
  const OrchestrationLayerLive = effect.Layer.mergeAll(OrchestrationInfrastructureLayerLive, OrchestrationEngineLive.pipe(effect.Layer.provide(OrchestrationInfrastructureLayerLive)));
50960
51736
  //#endregion
50961
51737
  //#region src/serverRuntimeState.ts
@@ -51762,6 +52538,7 @@ exports.ServerRequest__DynamicToolCallParams = ServerRequest__DynamicToolCallPar
51762
52538
  exports.ServerRequest__ExecCommandApprovalParams = ServerRequest__ExecCommandApprovalParams;
51763
52539
  exports.ServerRequest__FileChangeRequestApprovalParams = ServerRequest__FileChangeRequestApprovalParams;
51764
52540
  exports.ServerRequest__ToolRequestUserInputParams = ServerRequest__ToolRequestUserInputParams;
52541
+ exports.ServerSettingsService = ServerSettingsService;
51765
52542
  exports.ToolRequestUserInputParams = ToolRequestUserInputParams;
51766
52543
  exports.ToolRequestUserInputResponse = ToolRequestUserInputResponse;
51767
52544
  exports.V2AccountRateLimitsUpdatedNotification = V2AccountRateLimitsUpdatedNotification;