ryco-cli 0.1.6 → 0.1.7

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 (152) hide show
  1. package/dist/{AzureDevOpsSourceControlProvider-6holYS_D.cjs → AzureDevOpsSourceControlProvider-5exivY-1.cjs} +2 -2
  2. package/dist/{AzureDevOpsSourceControlProvider-CC2_COxP.mjs → AzureDevOpsSourceControlProvider-CF5LuCHp.mjs} +2 -2
  3. package/dist/{BitbucketSourceControlProvider-a0IsNFyG.cjs → BitbucketSourceControlProvider-1GWENqcl.cjs} +2 -2
  4. package/dist/{BitbucketSourceControlProvider-CYR_BD1d.mjs → BitbucketSourceControlProvider-CKH09tfT.mjs} +2 -2
  5. package/dist/{ClaudeDriver-C6tQNEWO.cjs → ClaudeDriver-BXLdjrht.cjs} +177 -4
  6. package/dist/{ClaudeDriver-D3nsxwH9.mjs → ClaudeDriver-WwWFTu61.mjs} +178 -5
  7. package/dist/{CodexDriver-BILrJG8o.mjs → CodexDriver-93-TmCrO.mjs} +6 -5
  8. package/dist/{CodexDriver-ms_YgFD7.cjs → CodexDriver-BboMT9uk.cjs} +5 -4
  9. package/dist/{CodexTextGeneration-CHEgoANm.cjs → CodexTextGeneration-0pRbJji3.cjs} +2 -2
  10. package/dist/{CodexTextGeneration-BIKHl7E3.mjs → CodexTextGeneration-BU9Myn87.mjs} +3 -3
  11. package/dist/{CopilotDriver-CmD3Ffyl.mjs → CopilotDriver-BASi8pJ0.mjs} +4 -4
  12. package/dist/{CopilotDriver-B0--ZwIK.cjs → CopilotDriver-DCsv-17W.cjs} +3 -3
  13. package/dist/{CursorDriver-DTVPXZ-R.cjs → CursorDriver-BTSiM3A8.cjs} +161 -14
  14. package/dist/{CursorDriver-CbeaIbdb.mjs → CursorDriver-D5LvIqUk.mjs} +162 -15
  15. package/dist/{ForgejoSourceControlProvider-DwAog76b.cjs → ForgejoSourceControlProvider-BTXRDCGB.cjs} +2 -2
  16. package/dist/{ForgejoSourceControlProvider-DX6d9gvz.mjs → ForgejoSourceControlProvider-e-4hvvZ_.mjs} +2 -2
  17. package/dist/{GitHubSourceControlProvider-C-LGwho3.mjs → GitHubSourceControlProvider-26IofDAO.mjs} +2 -2
  18. package/dist/{GitHubSourceControlProvider-9lJFzjsu.cjs → GitHubSourceControlProvider-DWmwkA_y.cjs} +2 -2
  19. package/dist/{GitLabSourceControlProvider-CvKA74lg.cjs → GitLabSourceControlProvider-D-YHnxRe.cjs} +2 -2
  20. package/dist/{GitLabSourceControlProvider-CkeT2aEx.mjs → GitLabSourceControlProvider-DePct78W.mjs} +2 -2
  21. package/dist/{OpenCodeDriver-ClWtCk0j.mjs → OpenCodeDriver-BhvLOmvn.mjs} +325 -38
  22. package/dist/{OpenCodeDriver-CXUsnl5o.cjs → OpenCodeDriver-CVeZ4n9-.cjs} +324 -37
  23. package/dist/{SourceControlProviderDiscoveryCatalog-_rg0mc1o.cjs → SourceControlProviderDiscoveryCatalog-DeFR5xH3.cjs} +1 -64
  24. package/dist/{SourceControlProviderDiscoveryCatalog-yMDer_Yh.mjs → SourceControlProviderDiscoveryCatalog-mJV_Xitr.mjs} +3 -48
  25. package/dist/bin.cjs +1280 -519
  26. package/dist/bin.mjs +1234 -474
  27. package/dist/client/assets/{AnimatedHeight-DgItDSis.js → AnimatedHeight-BSU0lzYs.js} +1 -1
  28. package/dist/client/assets/AppearanceSettings-D7q0ujGK.js +1 -0
  29. package/dist/client/assets/{BranchToolbar.logic-C_xHEt9C.js → BranchToolbar.logic-CZIFSvhd.js} +1 -1
  30. package/dist/client/assets/ChatRightPanel-CWHOIuiQ.js +188 -0
  31. package/dist/client/assets/{ChatThreadRouteView-CV6ETYG5.js → ChatThreadRouteView-bA7xWXzc.js} +1 -1
  32. package/dist/client/assets/{CommandPaletteDialog-C_H7R8S5.js → CommandPaletteDialog-DNYaVOZO.js} +1 -1
  33. package/dist/client/assets/CompositeRoot-B_KEfCUE.js +1 -0
  34. package/dist/client/assets/ConnectionsSettings-CLZPbx_K.js +1 -0
  35. package/dist/client/assets/{DiagnosticsSettings-B340S_mq.js → DiagnosticsSettings-DtzPCb6N.js} +1 -1
  36. package/dist/client/assets/{DialogHandle-BJY7cHCV.js → DialogHandle-DuHnmWoE.js} +1 -1
  37. package/dist/client/assets/{DialogRoot-Ej1FVk-_.js → DialogRoot-BV25qAcA.js} +1 -1
  38. package/dist/client/assets/DialogTitle-DQsi-RTa.js +1 -0
  39. package/dist/client/assets/DialogTrigger-D0fA01C6.js +1 -0
  40. package/dist/client/assets/{DialogViewport-DvjAl76K.js → DialogViewport-CMV_BePU.js} +1 -1
  41. package/dist/client/assets/{DraftChatThreadRouteView-pOkyeKPS.js → DraftChatThreadRouteView-BoiKnrii.js} +1 -1
  42. package/dist/client/assets/{JetBrainsIcons-DTTF47I0.js → JetBrainsIcons-BcPxJlMf.js} +1 -1
  43. package/dist/client/assets/{KeybindingsSettings-CW3RRi-7.js → KeybindingsSettings-LuI98pdF.js} +1 -1
  44. package/dist/client/assets/McpServersSettings-BFMuWYVV.js +6 -0
  45. package/dist/client/assets/{NoActiveThreadState-DVHwrkCp.js → NoActiveThreadState-C2q_O1kP.js} +1 -1
  46. package/dist/client/assets/{OpinionatedPluginsSettings-Cjj23Fs4.js → OpinionatedPluginsSettings-Bqwct37O.js} +1 -1
  47. package/dist/client/assets/{ProjectFavicon-TDP7HRfn.js → ProjectFavicon-D2imiJ8M.js} +1 -1
  48. package/dist/client/assets/{ProviderUpdateLaunchNotification.logic-6TpFsmiI.js → ProviderUpdateLaunchNotification.logic-CJgA--nK.js} +1 -1
  49. package/dist/client/assets/ProvidersSettingsPanel-B97vbeu6.js +1 -0
  50. package/dist/client/assets/{RedactedSensitiveText-BfoVjPsE.js → RedactedSensitiveText-B2GFTyfq.js} +1 -1
  51. package/dist/client/assets/RootAppShell-C1xTR98P.js +12 -0
  52. package/dist/client/assets/SettingsDialog-Q0Q8O5oH.js +2 -0
  53. package/dist/client/assets/SourceControlSettings-KAvoPTNi.js +1 -0
  54. package/dist/client/assets/StatisticsPanel-BzsQ444K.js +64 -0
  55. package/dist/client/assets/{ThreadStatusIndicators-B0leIJGD.js → ThreadStatusIndicators-Cw_jLVQf.js} +1 -1
  56. package/dist/client/assets/{ThreadTerminalDrawer-COq2Fk17.js → ThreadTerminalDrawer-D1WnSemH.js} +17 -17
  57. package/dist/client/assets/ThreadWorkspacePanel-L4qCPTOA.js +166 -0
  58. package/dist/client/assets/{alert-dialog-lMfKpG2t.js → alert-dialog-CNmKa0BK.js} +1 -1
  59. package/dist/client/assets/chart-column-BWSKYZ_a.js +1 -0
  60. package/dist/client/assets/{chatThreadActions-PPyTj_x0.js → chatThreadActions-TtDsiwDC.js} +1 -1
  61. package/dist/client/assets/chevron-up-zopJ_99l.js +1 -0
  62. package/dist/client/assets/{clamp-JiD4IZ0Z.js → clamp-Ds6WJu8j.js} +1 -1
  63. package/dist/client/assets/{clipboard-copy-CYBLSG2q.js → clipboard-copy-BoJGpdmJ.js} +1 -1
  64. package/dist/client/assets/clock-CzUHThzk.js +1 -0
  65. package/dist/client/assets/{commandPaletteStore-uVfCD7zE.js → commandPaletteStore-KebG835b.js} +1 -1
  66. package/dist/client/assets/composerDraftStore-DpPEQ6BN.js +21 -0
  67. package/dist/client/assets/{desktopUpdateAtoms-X6uM5SoD.js → desktopUpdateAtoms-BQ0GGEfM.js} +1 -1
  68. package/dist/client/assets/{download-DLBlpp4h.js → download-CUiRE0N9.js} +1 -1
  69. package/dist/client/assets/{draft-input-dLlVMl5P.js → draft-input-CwpGDtoT.js} +1 -1
  70. package/dist/client/assets/editorPreferences-jN7DKbyt.js +1 -0
  71. package/dist/client/assets/{empty-DlLY-Hhj.js → empty-CBuXePtg.js} +1 -1
  72. package/dist/client/assets/{group-zDBPF7rR.js → group-BpZvxjG5.js} +1 -1
  73. package/dist/client/assets/{index-Dq8sT2lF.js → index-BKzXTNJ_.js} +2 -2
  74. package/dist/client/assets/{input-B9-RWT56.js → input-CgDawd6k.js} +1 -1
  75. package/dist/client/assets/kbd-QYSL97CK.js +1 -0
  76. package/dist/client/assets/{keybindings-Dx4Nc1vY.js → keybindings-CUTRYI_A.js} +1 -1
  77. package/dist/client/assets/{keyboard-DDTWQkr0.js → keyboard-DFFiI7JY.js} +1 -1
  78. package/dist/client/assets/{keyedQuery-Ds4OGhX2.js → keyedQuery-DiOCbGH0.js} +0 -0
  79. package/dist/client/assets/{main-DxfpmoKJ.js → main-BgFGju3e.js} +3 -3
  80. package/dist/client/assets/main-D0UFdwts.css +2 -0
  81. package/dist/client/assets/{palette-BQcz-ZYV.js → palette-BXCL909c.js} +1 -1
  82. package/dist/client/assets/{play-QloDGe4R.js → play-DI2LZohg.js} +1 -1
  83. package/dist/client/assets/plus-DzgIfHUF.js +1 -0
  84. package/dist/client/assets/projectAtoms-DXXpuERK.js +1 -0
  85. package/dist/client/assets/providerDriverMeta-CRNNp0q3.js +1 -0
  86. package/dist/client/assets/providerIconUtils-D-DFhem2.js +47 -0
  87. package/dist/client/assets/radio-group-_PPYg3YY.js +1 -0
  88. package/dist/client/assets/{refresh-cw-DkBMGllm.js → refresh-cw-BsQTNBXl.js} +1 -1
  89. package/dist/client/assets/{resolveAriaLabelledBy-DDfIbj05.js → resolveAriaLabelledBy-CRHo_s16.js} +1 -1
  90. package/dist/client/assets/{rightPanelRouteSearch-DfuWVvXN.js → rightPanelRouteSearch-CfmZ7osj.js} +1 -1
  91. package/dist/client/assets/{scale-BActunFu.js → scale-DDEJ-tvn.js} +1 -1
  92. package/dist/client/assets/{scroll-area-D-YOUG02.js → scroll-area-HgsI-ErG.js} +1 -1
  93. package/dist/client/assets/{separator-TInKeIDo.js → separator-ClnL4mUB.js} +1 -1
  94. package/dist/client/assets/{server-BtEG3NAw.js → server-Bbs3L-6_.js} +1 -1
  95. package/dist/client/assets/{session-logic-DNf1aXq_.js → session-logic-CKQkgIwo.js} +1 -1
  96. package/dist/client/assets/settingsDialogStore-SG-W1q0A.js +1 -0
  97. package/dist/client/assets/{settingsLayout-D22Mm0u3.js → settingsLayout-G5xVm-ZT.js} +1 -1
  98. package/dist/client/assets/sidebar-_u7SGgmC.js +1 -0
  99. package/dist/client/assets/sidebarStatusText-BTYnwflG.js +1 -0
  100. package/dist/client/assets/{skeleton-DWaac_qp.js → skeleton-CmUvhZGP.js} +1 -1
  101. package/dist/client/assets/{storeSelectors-Cx1IYPA4.js → storeSelectors-DhJRpdiw.js} +1 -1
  102. package/dist/client/assets/threadSelectionStore-ygmA4DNA.js +1 -0
  103. package/dist/client/assets/toast-DmA_ziPT.js +1 -0
  104. package/dist/client/assets/toggle-DGS7tKuj.js +1 -0
  105. package/dist/client/assets/{toggle-group-CnLjaHSL.js → toggle-group-Dtv1ZUFW.js} +1 -1
  106. package/dist/client/assets/{tokenModePresentation-BSYdIvBm.js → tokenModePresentation-Dor1DnBx.js} +1 -1
  107. package/dist/client/assets/{trash-2-BI5xcbsm.js → trash-2-C2G3VGoV.js} +1 -1
  108. package/dist/client/assets/{triangle-alert-DJef_Uvh.js → triangle-alert-Bpz3xGu5.js} +1 -1
  109. package/dist/client/assets/{undo-2-DeZ2cMGS.js → undo-2-C3rtxuqZ.js} +1 -1
  110. package/dist/client/assets/{useHandleNewThread-XB82O6SA.js → useHandleNewThread-ypSbzG0P.js} +1 -1
  111. package/dist/client/assets/useLabelableId-Bc7KEnHU.js +1 -0
  112. package/dist/client/assets/{useNavigate-B2tgshhW.js → useNavigate-_4hWGSey.js} +1 -1
  113. package/dist/client/assets/useTheme-HcKmBfpF.js +1 -0
  114. package/dist/client/assets/{vscode-icons-DM9qFoEr.js → vscode-icons-DdSTtxYd.js} +1 -1
  115. package/dist/client/assets/{wrench-je4QOyG-.js → wrench-C4jdis40.js} +1 -1
  116. package/dist/client/assets/x-CG2WU63t.js +1 -0
  117. package/dist/client/index.html +1 -1
  118. package/dist/{collectUint8StreamText-C2z5Y8tF.mjs → collectUint8StreamText-Lv4GGI79.mjs} +269 -14
  119. package/dist/{collectUint8StreamText-TCG6xW_P.cjs → collectUint8StreamText-uNXVRHOf.cjs} +291 -12
  120. package/dist/{makeManagedServerProvider-DPFw9gV_.cjs → makeManagedServerProvider-3SD-Q5xh.cjs} +1 -1
  121. package/dist/{makeManagedServerProvider-BXO8QvzC.mjs → makeManagedServerProvider-CkPEptun.mjs} +1 -1
  122. package/package.json +1 -1
  123. package/dist/client/assets/AppearanceSettings-BlsMFBvJ.js +0 -1
  124. package/dist/client/assets/ChatRightPanel-CThudBb1.js +0 -188
  125. package/dist/client/assets/CompositeRoot-DjnzUIwC.js +0 -1
  126. package/dist/client/assets/ConnectionsSettings-hArR4Oe3.js +0 -1
  127. package/dist/client/assets/DialogTitle-H1yIMmA9.js +0 -1
  128. package/dist/client/assets/DialogTrigger-BHMq_K2G.js +0 -1
  129. package/dist/client/assets/McpServersSettings-dQ3BzOWY.js +0 -6
  130. package/dist/client/assets/ProvidersSettingsPanel-D15EeWAq.js +0 -1
  131. package/dist/client/assets/RootAppShell-CWBPSA2w.js +0 -12
  132. package/dist/client/assets/SettingsDialog-oJCE5foT.js +0 -2
  133. package/dist/client/assets/SourceControlSettings-CPnlMxNm.js +0 -1
  134. package/dist/client/assets/ThreadWorkspacePanel-qF9y6QT9.js +0 -166
  135. package/dist/client/assets/composerDraftStore-D12lUSfh.js +0 -21
  136. package/dist/client/assets/editorPreferences-BLeEBSD_.js +0 -1
  137. package/dist/client/assets/folder-open-DMjz0lpx.js +0 -1
  138. package/dist/client/assets/kbd-aeSsjiPI.js +0 -1
  139. package/dist/client/assets/main-C57RhHZ_.css +0 -2
  140. package/dist/client/assets/plus-4Oy56d-3.js +0 -1
  141. package/dist/client/assets/projectAtoms-DUEBb7Zz.js +0 -1
  142. package/dist/client/assets/providerDriverMeta-Dwi6Zqgd.js +0 -1
  143. package/dist/client/assets/providerIconUtils-uhWb4bdg.js +0 -47
  144. package/dist/client/assets/radio-group-DHyQlLeX.js +0 -1
  145. package/dist/client/assets/settingsDialogStore-BINi7pdD.js +0 -1
  146. package/dist/client/assets/sidebar-DHwNQmWp.js +0 -1
  147. package/dist/client/assets/threadSelectionStore-C0ig4405.js +0 -1
  148. package/dist/client/assets/toast-DE7a4wkW.js +0 -1
  149. package/dist/client/assets/toggle-BIW4AyPY.js +0 -1
  150. package/dist/client/assets/useLabelableId-DBAgv9qQ.js +0 -1
  151. package/dist/client/assets/useTheme-Dv42Z8uy.js +0 -1
  152. 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-uNXVRHOf.cjs");
7
6
  const require_Metrics = require("./Metrics-CIW1QH7M.cjs");
7
+ const require_SourceControlProviderDiscoveryCatalog = require("./SourceControlProviderDiscoveryCatalog-DeFR5xH3.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
  });
@@ -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)) {
@@ -9650,7 +10069,7 @@ const make$13 = effect.Effect.fn("makeGitHubCli")(function* () {
9650
10069
  ...l.description ? { description: l.description } : {}
9651
10070
  }))) : effect.Effect.fail(new GitHubCliError({
9652
10071
  operation: "listLabels",
9653
- detail: `Invalid label list output: ${require_SourceControlProviderDiscoveryCatalog.formatSchemaError(decoded.failure)}`,
10072
+ detail: `Invalid label list output: ${require_collectUint8StreamText.formatSchemaError(decoded.failure)}`,
9654
10073
  cause: decoded.failure
9655
10074
  })))))),
9656
10075
  listAssignees: (input) => execute({
@@ -9670,7 +10089,7 @@ const make$13 = effect.Effect.fn("makeGitHubCli")(function* () {
9670
10089
  ...u.avatar_url ? { avatarUrl: u.avatar_url } : {}
9671
10090
  }))) : effect.Effect.fail(new GitHubCliError({
9672
10091
  operation: "listAssignees",
9673
- detail: `Invalid assignees output: ${require_SourceControlProviderDiscoveryCatalog.formatSchemaError(decoded.failure)}`,
10092
+ detail: `Invalid assignees output: ${require_collectUint8StreamText.formatSchemaError(decoded.failure)}`,
9674
10093
  cause: decoded.failure
9675
10094
  })))))),
9676
10095
  listWorkflowRuns: (input) => execute({
@@ -9779,10 +10198,10 @@ function normalizeGitLabIssueRecord(raw) {
9779
10198
  labels: raw.labels ?? []
9780
10199
  };
9781
10200
  }
9782
- const decodeIssueList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9783
- const decodeIssueDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitLabIssueSchema);
10201
+ const decodeIssueList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
10202
+ const decodeIssueDetail = require_collectUint8StreamText.decodeJsonResult(GitLabIssueSchema);
9784
10203
  const decodeIssueEntry = effect.Schema.decodeUnknownExit(GitLabIssueSchema);
9785
- const formatGitLabIssueDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
10204
+ const formatGitLabIssueDecodeError = require_collectUint8StreamText.formatSchemaError;
9786
10205
  function decodeGitLabIssueListJson(raw) {
9787
10206
  const result = decodeIssueList(raw);
9788
10207
  if (!effect.Result.isSuccess(result)) return effect.Result.fail(result.failure);
@@ -9869,10 +10288,10 @@ function normalizeGitLabMergeRequestRecord(raw) {
9869
10288
  ...headRepositoryOwnerLogin ? { headRepositoryOwnerLogin } : {}
9870
10289
  };
9871
10290
  }
9872
- const decodeGitLabMergeRequestList = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
9873
- const decodeGitLabMergeRequest = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(GitLabMergeRequestSchema);
10291
+ const decodeGitLabMergeRequestList = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Array(effect.Schema.Unknown));
10292
+ const decodeGitLabMergeRequest = require_collectUint8StreamText.decodeJsonResult(GitLabMergeRequestSchema);
9874
10293
  const decodeGitLabMergeRequestEntry = effect.Schema.decodeUnknownExit(GitLabMergeRequestSchema);
9875
- const formatGitLabJsonDecodeError = require_SourceControlProviderDiscoveryCatalog.formatSchemaError;
10294
+ const formatGitLabJsonDecodeError = require_collectUint8StreamText.formatSchemaError;
9876
10295
  function decodeGitLabMergeRequestListJson(raw) {
9877
10296
  const result = decodeGitLabMergeRequestList(raw);
9878
10297
  if (effect.Result.isSuccess(result)) {
@@ -9891,7 +10310,7 @@ function decodeGitLabMergeRequestJson(raw) {
9891
10310
  if (effect.Result.isSuccess(result)) return effect.Result.succeed(normalizeGitLabMergeRequestRecord(result.success));
9892
10311
  return effect.Result.fail(result.failure);
9893
10312
  }
9894
- const decodeGitLabMergeRequestDetail = require_SourceControlProviderDiscoveryCatalog.decodeJsonResult(effect.Schema.Struct({
10313
+ const decodeGitLabMergeRequestDetail = require_collectUint8StreamText.decodeJsonResult(effect.Schema.Struct({
9895
10314
  ...GitLabMergeRequestSchema.fields,
9896
10315
  description: effect.Schema.optional(effect.Schema.NullOr(effect.Schema.String)),
9897
10316
  notes: effect.Schema.optional(effect.Schema.Array(effect.Schema.Struct({
@@ -10561,26 +10980,26 @@ const make$11 = effect.Effect.fn("makeSourceControlProviderRegistry")(function*
10561
10980
  const azureDevOpsCli = yield* AzureDevOpsCli;
10562
10981
  const fileSystem = yield* effect.FileSystem.FileSystem;
10563
10982
  const github = yield* makeLazyProvider("github", effect.Effect.tryPromise({
10564
- try: () => Promise.resolve().then(() => require("./GitHubSourceControlProvider-9lJFzjsu.cjs")),
10983
+ try: () => Promise.resolve().then(() => require("./GitHubSourceControlProvider-DWmwkA_y.cjs")),
10565
10984
  catch: (cause) => providerLoadError("github", cause)
10566
10985
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(GitHubCli, githubCli), effect.Effect.provideService(effect.FileSystem.FileSystem, fileSystem)));
10567
10986
  const gitlab = yield* makeLazyProvider("gitlab", effect.Effect.tryPromise({
10568
- try: () => Promise.resolve().then(() => require("./GitLabSourceControlProvider-CvKA74lg.cjs")),
10987
+ try: () => Promise.resolve().then(() => require("./GitLabSourceControlProvider-D-YHnxRe.cjs")),
10569
10988
  catch: (cause) => providerLoadError("gitlab", cause)
10570
10989
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(GitLabCli, gitlabCli)));
10571
10990
  const bitbucket = yield* makeLazyProvider("bitbucket", effect.Effect.tryPromise({
10572
- try: () => Promise.resolve().then(() => require("./BitbucketSourceControlProvider-a0IsNFyG.cjs")),
10991
+ try: () => Promise.resolve().then(() => require("./BitbucketSourceControlProvider-1GWENqcl.cjs")),
10573
10992
  catch: (cause) => providerLoadError("bitbucket", cause)
10574
10993
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(BitbucketApi, bitbucketApi)), {
10575
10994
  searchRepositories: true,
10576
10995
  cloneAuthentication: true
10577
10996
  });
10578
10997
  const forgejo = yield* makeLazyProvider("forgejo", effect.Effect.tryPromise({
10579
- try: () => Promise.resolve().then(() => require("./ForgejoSourceControlProvider-DwAog76b.cjs")),
10998
+ try: () => Promise.resolve().then(() => require("./ForgejoSourceControlProvider-BTXRDCGB.cjs")),
10580
10999
  catch: (cause) => providerLoadError("forgejo", cause)
10581
11000
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(ForgejoApi, forgejoApi)));
10582
11001
  const azureDevOps = yield* makeLazyProvider("azure-devops", effect.Effect.tryPromise({
10583
- try: () => Promise.resolve().then(() => require("./AzureDevOpsSourceControlProvider-6holYS_D.cjs")),
11002
+ try: () => Promise.resolve().then(() => require("./AzureDevOpsSourceControlProvider-5exivY-1.cjs")),
10584
11003
  catch: (cause) => providerLoadError("azure-devops", cause)
10585
11004
  }).pipe(effect.Effect.flatMap((module) => module.make()), effect.Effect.provideService(AzureDevOpsCli, azureDevOpsCli)));
10586
11005
  const bitbucketDiscovery = require_SourceControlProviderDiscoveryCatalog.makeBitbucketDiscovery(bitbucketApi);
@@ -10703,22 +11122,6 @@ var CheckpointDiffQuery = class extends effect.Context.Service()("ryco/checkpoin
10703
11122
  //#region src/diagnostics/Services/Diagnostics.ts
10704
11123
  var Diagnostics = class extends effect.Context.Service()("ryco/diagnostics/Services/Diagnostics") {};
10705
11124
  //#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
11125
  //#region ../../packages/shared/src/keybindings.ts
10723
11126
  const DEFAULT_KEYBINDINGS = [
10724
11127
  {
@@ -11069,7 +11472,7 @@ function encodeWhenAst(node) {
11069
11472
  case "or": return `(${encodeWhenAst(node.left)} || ${encodeWhenAst(node.right)})`;
11070
11473
  }
11071
11474
  }
11072
- const RawKeybindingsEntries = require_SourceControlProviderDiscoveryCatalog.fromLenientJson(effect.Schema.Array(effect.Schema.Unknown));
11475
+ const RawKeybindingsEntries = require_collectUint8StreamText.fromLenientJson(effect.Schema.Array(effect.Schema.Unknown));
11073
11476
  const KeybindingsConfigJson = effect.Schema.fromJsonString(require_collectUint8StreamText.KeybindingsConfig);
11074
11477
  const PrettyJsonString = effect.SchemaGetter.parseJson().compose(effect.SchemaGetter.stringifyJson({ space: 2 }));
11075
11478
  const KeybindingsConfigPrettyJson = KeybindingsConfigJson.pipe(effect.Schema.encode({
@@ -26104,300 +26507,6 @@ async function runProcess(command, args, options = {}) {
26104
26507
  });
26105
26508
  }
26106
26509
  //#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
26510
  //#region src/pathExpansion.ts
26402
26511
  /**
26403
26512
  * Expand a leading `~` (or `~/…`, `~\…`) in a user-supplied path to the
@@ -26718,7 +26827,7 @@ const BUILT_IN_DRIVERS = [
26718
26827
  configSchema: require_collectUint8StreamText.CodexSettings,
26719
26828
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CodexSettings)({}),
26720
26829
  create: (input) => effect.Effect.tryPromise({
26721
- try: () => Promise.resolve().then(() => require("./CodexDriver-ms_YgFD7.cjs")),
26830
+ try: () => Promise.resolve().then(() => require("./CodexDriver-BboMT9uk.cjs")),
26722
26831
  catch: (cause) => driverImportError(codexDriverKind, input, cause)
26723
26832
  }).pipe(effect.Effect.flatMap(({ CodexDriver }) => CodexDriver.create(input)))
26724
26833
  },
@@ -26731,7 +26840,7 @@ const BUILT_IN_DRIVERS = [
26731
26840
  configSchema: require_collectUint8StreamText.ClaudeSettings,
26732
26841
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.ClaudeSettings)({}),
26733
26842
  create: (input) => effect.Effect.tryPromise({
26734
- try: () => Promise.resolve().then(() => require("./ClaudeDriver-C6tQNEWO.cjs")),
26843
+ try: () => Promise.resolve().then(() => require("./ClaudeDriver-BXLdjrht.cjs")),
26735
26844
  catch: (cause) => driverImportError(claudeDriverKind, input, cause)
26736
26845
  }).pipe(effect.Effect.flatMap(({ ClaudeDriver }) => ClaudeDriver.create(input)))
26737
26846
  },
@@ -26744,7 +26853,7 @@ const BUILT_IN_DRIVERS = [
26744
26853
  configSchema: require_collectUint8StreamText.CopilotSettings,
26745
26854
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CopilotSettings)({}),
26746
26855
  create: (input) => effect.Effect.tryPromise({
26747
- try: () => Promise.resolve().then(() => require("./CopilotDriver-B0--ZwIK.cjs")),
26856
+ try: () => Promise.resolve().then(() => require("./CopilotDriver-DCsv-17W.cjs")),
26748
26857
  catch: (cause) => driverImportError(copilotDriverKind, input, cause)
26749
26858
  }).pipe(effect.Effect.flatMap(({ CopilotDriver }) => CopilotDriver.create(input)))
26750
26859
  },
@@ -26757,7 +26866,7 @@ const BUILT_IN_DRIVERS = [
26757
26866
  configSchema: require_collectUint8StreamText.CursorSettings,
26758
26867
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.CursorSettings)({}),
26759
26868
  create: (input) => effect.Effect.tryPromise({
26760
- try: () => Promise.resolve().then(() => require("./CursorDriver-DTVPXZ-R.cjs")),
26869
+ try: () => Promise.resolve().then(() => require("./CursorDriver-BTSiM3A8.cjs")),
26761
26870
  catch: (cause) => driverImportError(cursorDriverKind, input, cause)
26762
26871
  }).pipe(effect.Effect.flatMap(({ CursorDriver }) => CursorDriver.create(input)))
26763
26872
  },
@@ -26770,7 +26879,7 @@ const BUILT_IN_DRIVERS = [
26770
26879
  configSchema: require_collectUint8StreamText.OpenCodeSettings,
26771
26880
  defaultConfig: () => effect.Schema.decodeSync(require_collectUint8StreamText.OpenCodeSettings)({}),
26772
26881
  create: (input) => effect.Effect.tryPromise({
26773
- try: () => Promise.resolve().then(() => require("./OpenCodeDriver-CXUsnl5o.cjs")),
26882
+ try: () => Promise.resolve().then(() => require("./OpenCodeDriver-CVeZ4n9-.cjs")),
26774
26883
  catch: (cause) => driverImportError(openCodeDriverKind, input, cause)
26775
26884
  }).pipe(effect.Effect.flatMap(({ OpenCodeDriver }) => OpenCodeDriver.create(input)))
26776
26885
  }
@@ -27483,7 +27592,7 @@ const scopedSafeTeardown = (label) => (effect$15) => effect.Effect.gen(function*
27483
27592
  });
27484
27593
  //#endregion
27485
27594
  //#region package.json
27486
- var version = "0.1.6";
27595
+ var version = "0.1.7";
27487
27596
  //#endregion
27488
27597
  //#region src/provider/Layers/CodexProvider.ts
27489
27598
  const PROVIDER_PROBE_TIMEOUT_MS = 8e3;
@@ -28520,6 +28629,387 @@ var OrchestrationEngineService = class extends effect.Context.Service()("ryco/or
28520
28629
  */
28521
28630
  var ProjectionSnapshotQuery = class extends effect.Context.Service()("ryco/orchestration/Services/ProjectionSnapshotQuery") {};
28522
28631
  //#endregion
28632
+ //#region src/statistics/StatisticsQuery.ts
28633
+ /**
28634
+ * StatisticsQuery - usage analytics over the orchestration projection tables.
28635
+ *
28636
+ * Aggregates the existing read models (threads, sessions, token-usage
28637
+ * activities, turns/checkpoints, worktrees) into a {@link StatisticsSnapshot}
28638
+ * on demand. No dedicated statistics tables exist; everything is derived from
28639
+ * data already persisted by the projection pipeline.
28640
+ *
28641
+ * Token figures use a per-turn delta attribution: for each `(thread, turn)` we
28642
+ * read the final `context-window.updated` activity and sum its `last*` deltas,
28643
+ * bucketed by day/project/model. When deltas are absent (older data / providers
28644
+ * that only report cumulative totals) we fall back to attributing a thread's
28645
+ * cumulative total to its primary model and last-activity day.
28646
+ *
28647
+ * @module StatisticsQuery
28648
+ */
28649
+ /**
28650
+ * StatisticsQuery - service tag.
28651
+ */
28652
+ var StatisticsQuery = class extends effect.Context.Service()("ryco/statistics/StatisticsQuery") {};
28653
+ function toPersistenceSqlOrDecodeError$7(sqlOperation, decodeOperation) {
28654
+ return (cause) => effect.Schema.isSchemaError(cause) ? toPersistenceDecodeError(decodeOperation)(cause) : toPersistenceSqlError(sqlOperation)(cause);
28655
+ }
28656
+ const ThreadRow = effect.Schema.Struct({
28657
+ threadId: effect.Schema.String,
28658
+ projectId: require_collectUint8StreamText.ProjectId,
28659
+ model: effect.Schema.NullOr(effect.Schema.String),
28660
+ createdAt: effect.Schema.String,
28661
+ providerName: effect.Schema.NullOr(effect.Schema.String)
28662
+ });
28663
+ const ProjectRow = effect.Schema.Struct({
28664
+ projectId: require_collectUint8StreamText.ProjectId,
28665
+ title: effect.Schema.String,
28666
+ deletedAt: effect.Schema.NullOr(effect.Schema.String)
28667
+ });
28668
+ const TokenActivityRow = effect.Schema.Struct({
28669
+ threadId: effect.Schema.String,
28670
+ turnId: effect.Schema.NullOr(effect.Schema.String),
28671
+ sequence: effect.Schema.NullOr(effect.Schema.Number),
28672
+ createdAt: effect.Schema.String,
28673
+ payloadJson: effect.Schema.String
28674
+ });
28675
+ const ToolUseRow = effect.Schema.Struct({
28676
+ threadId: effect.Schema.String,
28677
+ date: effect.Schema.String,
28678
+ count: effect.Schema.Number
28679
+ });
28680
+ const TurnRow = effect.Schema.Struct({
28681
+ threadId: effect.Schema.String,
28682
+ startedAt: effect.Schema.NullOr(effect.Schema.String),
28683
+ completedAt: effect.Schema.NullOr(effect.Schema.String),
28684
+ checkpointTurnCount: effect.Schema.NullOr(effect.Schema.Number),
28685
+ filesJson: effect.Schema.String
28686
+ });
28687
+ const WorktreeRow = effect.Schema.Struct({
28688
+ createdAt: effect.Schema.String,
28689
+ archivedAt: effect.Schema.NullOr(effect.Schema.String),
28690
+ prNumber: effect.Schema.NullOr(effect.Schema.Number)
28691
+ });
28692
+ function num(value) {
28693
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
28694
+ }
28695
+ function dayOf(iso) {
28696
+ return iso.length >= 10 ? iso.slice(0, 10) : null;
28697
+ }
28698
+ const makeStatisticsQuery = effect.Effect.gen(function* () {
28699
+ const sql = yield* effect_unstable_sql_SqlClient.SqlClient;
28700
+ const listThreadRows = effect_unstable_sql_SqlSchema.findAll({
28701
+ Request: effect.Schema.Void,
28702
+ Result: ThreadRow,
28703
+ execute: () => sql`
28704
+ SELECT
28705
+ t.thread_id AS "threadId",
28706
+ t.project_id AS "projectId",
28707
+ json_extract(t.model_selection_json, '$.model') AS "model",
28708
+ t.created_at AS "createdAt",
28709
+ COALESCE(
28710
+ s.provider_name,
28711
+ json_extract(t.model_selection_json, '$.provider')
28712
+ ) AS "providerName"
28713
+ FROM projection_threads t
28714
+ LEFT JOIN projection_thread_sessions s ON s.thread_id = t.thread_id
28715
+ `
28716
+ });
28717
+ const listProjectRows = effect_unstable_sql_SqlSchema.findAll({
28718
+ Request: effect.Schema.Void,
28719
+ Result: ProjectRow,
28720
+ execute: () => sql`
28721
+ SELECT
28722
+ project_id AS "projectId",
28723
+ title,
28724
+ deleted_at AS "deletedAt"
28725
+ FROM projection_projects
28726
+ `
28727
+ });
28728
+ const listTokenActivityRows = effect_unstable_sql_SqlSchema.findAll({
28729
+ Request: effect.Schema.Void,
28730
+ Result: TokenActivityRow,
28731
+ execute: () => sql`
28732
+ SELECT
28733
+ thread_id AS "threadId",
28734
+ turn_id AS "turnId",
28735
+ sequence,
28736
+ created_at AS "createdAt",
28737
+ payload_json AS "payloadJson"
28738
+ FROM projection_thread_activities
28739
+ WHERE kind = 'context-window.updated'
28740
+ ORDER BY thread_id ASC, turn_id ASC, sequence ASC, created_at ASC, activity_id ASC
28741
+ `
28742
+ });
28743
+ const listToolUseRows = effect_unstable_sql_SqlSchema.findAll({
28744
+ Request: effect.Schema.Void,
28745
+ Result: ToolUseRow,
28746
+ execute: () => sql`
28747
+ SELECT
28748
+ thread_id AS "threadId",
28749
+ substr(created_at, 1, 10) AS "date",
28750
+ COUNT(*) AS "count"
28751
+ FROM projection_thread_activities
28752
+ WHERE kind = 'tool.completed'
28753
+ GROUP BY thread_id, substr(created_at, 1, 10)
28754
+ `
28755
+ });
28756
+ const listTurnRows = effect_unstable_sql_SqlSchema.findAll({
28757
+ Request: effect.Schema.Void,
28758
+ Result: TurnRow,
28759
+ execute: () => sql`
28760
+ SELECT
28761
+ thread_id AS "threadId",
28762
+ started_at AS "startedAt",
28763
+ completed_at AS "completedAt",
28764
+ checkpoint_turn_count AS "checkpointTurnCount",
28765
+ checkpoint_files_json AS "filesJson"
28766
+ FROM projection_turns
28767
+ `
28768
+ });
28769
+ const listWorktreeRows = effect_unstable_sql_SqlSchema.findAll({
28770
+ Request: effect.Schema.Void,
28771
+ Result: WorktreeRow,
28772
+ execute: () => sql`
28773
+ SELECT
28774
+ created_at AS "createdAt",
28775
+ archived_at AS "archivedAt",
28776
+ pr_number AS "prNumber"
28777
+ FROM projection_worktrees
28778
+ `
28779
+ });
28780
+ const getStatistics = () => effect.Effect.gen(function* () {
28781
+ const [threadRows, projectRows, tokenRows, toolRows, turnRows, worktreeRows] = yield* effect.Effect.all([
28782
+ listThreadRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listThreads:query", "StatisticsQuery.getStatistics:listThreads:decodeRows"))),
28783
+ listProjectRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listProjects:query", "StatisticsQuery.getStatistics:listProjects:decodeRows"))),
28784
+ listTokenActivityRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTokenActivities:query", "StatisticsQuery.getStatistics:listTokenActivities:decodeRows"))),
28785
+ listToolUseRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listToolUses:query", "StatisticsQuery.getStatistics:listToolUses:decodeRows"))),
28786
+ listTurnRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listTurns:query", "StatisticsQuery.getStatistics:listTurns:decodeRows"))),
28787
+ listWorktreeRows(void 0).pipe(effect.Effect.mapError(toPersistenceSqlOrDecodeError$7("StatisticsQuery.getStatistics:listWorktrees:query", "StatisticsQuery.getStatistics:listWorktrees:decodeRows")))
28788
+ ], { concurrency: "unbounded" });
28789
+ const threadMeta = /* @__PURE__ */ new Map();
28790
+ const modelProvider = /* @__PURE__ */ new Map();
28791
+ for (const row of threadRows) {
28792
+ const provider = row.providerName?.trim() || void 0;
28793
+ const model = (row.model ?? "").trim() || "unknown";
28794
+ threadMeta.set(row.threadId, {
28795
+ projectId: row.projectId,
28796
+ model,
28797
+ provider,
28798
+ createdAt: row.createdAt
28799
+ });
28800
+ if (!modelProvider.has(model) || modelProvider.get(model) === void 0 && provider) modelProvider.set(model, provider);
28801
+ }
28802
+ const buckets = /* @__PURE__ */ new Map();
28803
+ const getBucket = (date, projectId, model) => {
28804
+ const key = `${date}${projectId}${model}`;
28805
+ let bucket = buckets.get(key);
28806
+ if (!bucket) {
28807
+ bucket = {
28808
+ date,
28809
+ projectId,
28810
+ model,
28811
+ provider: modelProvider.get(model),
28812
+ inputTokens: 0,
28813
+ outputTokens: 0,
28814
+ cachedInputTokens: 0,
28815
+ reasoningTokens: 0,
28816
+ turns: 0,
28817
+ activeMs: 0,
28818
+ toolUses: 0,
28819
+ filesChanged: 0,
28820
+ additions: 0,
28821
+ deletions: 0,
28822
+ commits: 0,
28823
+ pushes: 0,
28824
+ threadsCreated: 0
28825
+ };
28826
+ buckets.set(key, bucket);
28827
+ }
28828
+ return bucket;
28829
+ };
28830
+ let earliest;
28831
+ const considerIso = (iso) => {
28832
+ if (iso && (earliest === void 0 || iso < earliest)) earliest = iso;
28833
+ };
28834
+ const lastPerTurn = /* @__PURE__ */ new Map();
28835
+ const lastPerThread = /* @__PURE__ */ new Map();
28836
+ const isLater = (a, b) => {
28837
+ if (a.createdAt !== b.createdAt) return a.createdAt > b.createdAt;
28838
+ return (a.sequence ?? -1) >= (b.sequence ?? -1);
28839
+ };
28840
+ for (const row of tokenRows) {
28841
+ considerIso(row.createdAt);
28842
+ lastPerTurn.set(`${row.threadId}${row.turnId ?? ""}`, row);
28843
+ const prevThread = lastPerThread.get(row.threadId);
28844
+ if (!prevThread || isLater(row, prevThread)) lastPerThread.set(row.threadId, row);
28845
+ }
28846
+ let perTurnDeltaSum = 0;
28847
+ for (const row of lastPerTurn.values()) {
28848
+ const payload = safeParse(row.payloadJson);
28849
+ perTurnDeltaSum += num(payload?.lastInputTokens) + num(payload?.lastOutputTokens) + num(payload?.lastCachedInputTokens) + num(payload?.lastReasoningOutputTokens);
28850
+ }
28851
+ let cumulativeSum = 0;
28852
+ for (const row of lastPerThread.values()) {
28853
+ const payload = safeParse(row.payloadJson);
28854
+ cumulativeSum += num(payload?.inputTokens) + num(payload?.outputTokens);
28855
+ }
28856
+ const attribution = perTurnDeltaSum === 0 && cumulativeSum > 0 ? "thread-cumulative" : "per-turn-delta";
28857
+ if (attribution === "per-turn-delta") for (const row of lastPerTurn.values()) {
28858
+ const meta = threadMeta.get(row.threadId);
28859
+ const date = dayOf(row.createdAt);
28860
+ if (!meta || !date) continue;
28861
+ const payload = safeParse(row.payloadJson);
28862
+ const bucket = getBucket(date, meta.projectId, meta.model);
28863
+ bucket.inputTokens += num(payload?.lastInputTokens);
28864
+ bucket.outputTokens += num(payload?.lastOutputTokens);
28865
+ bucket.cachedInputTokens += num(payload?.lastCachedInputTokens);
28866
+ bucket.reasoningTokens += num(payload?.lastReasoningOutputTokens);
28867
+ }
28868
+ else for (const row of lastPerThread.values()) {
28869
+ const meta = threadMeta.get(row.threadId);
28870
+ const date = dayOf(row.createdAt);
28871
+ if (!meta || !date) continue;
28872
+ const payload = safeParse(row.payloadJson);
28873
+ const bucket = getBucket(date, meta.projectId, meta.model);
28874
+ bucket.inputTokens += num(payload?.inputTokens);
28875
+ bucket.outputTokens += num(payload?.outputTokens);
28876
+ bucket.cachedInputTokens += num(payload?.cachedInputTokens);
28877
+ bucket.reasoningTokens += num(payload?.reasoningOutputTokens);
28878
+ }
28879
+ for (const row of turnRows) {
28880
+ const meta = threadMeta.get(row.threadId);
28881
+ if (!meta) continue;
28882
+ considerIso(row.startedAt);
28883
+ considerIso(row.completedAt);
28884
+ const anchor = row.completedAt ?? row.startedAt;
28885
+ const date = anchor ? dayOf(anchor) : null;
28886
+ if (!date) continue;
28887
+ const bucket = getBucket(date, meta.projectId, meta.model);
28888
+ if (row.completedAt) {
28889
+ bucket.turns += 1;
28890
+ if (row.startedAt) {
28891
+ const delta = Date.parse(row.completedAt) - Date.parse(row.startedAt);
28892
+ if (Number.isFinite(delta) && delta > 0) bucket.activeMs += delta;
28893
+ }
28894
+ }
28895
+ const files = safeParseArray(row.filesJson);
28896
+ if (files.length > 0) {
28897
+ bucket.filesChanged += files.length;
28898
+ for (const file of files) {
28899
+ bucket.additions += num(file?.additions);
28900
+ bucket.deletions += num(file?.deletions);
28901
+ }
28902
+ }
28903
+ }
28904
+ for (const row of toolRows) {
28905
+ const meta = threadMeta.get(row.threadId);
28906
+ if (!meta) continue;
28907
+ const bucket = getBucket(row.date, meta.projectId, meta.model);
28908
+ bucket.toolUses += num(row.count);
28909
+ }
28910
+ for (const meta of threadMeta.values()) {
28911
+ considerIso(meta.createdAt);
28912
+ const date = dayOf(meta.createdAt);
28913
+ if (!date) continue;
28914
+ getBucket(date, meta.projectId, meta.model).threadsCreated += 1;
28915
+ }
28916
+ let wtCreated = 0;
28917
+ let wtArchived = 0;
28918
+ let wtOpenPrs = 0;
28919
+ for (const row of worktreeRows) {
28920
+ considerIso(row.createdAt);
28921
+ wtCreated += 1;
28922
+ if (row.archivedAt) wtArchived += 1;
28923
+ else if (row.prNumber !== null) wtOpenPrs += 1;
28924
+ }
28925
+ const dailyBuckets = [];
28926
+ const totals = {
28927
+ inputTokens: 0,
28928
+ outputTokens: 0,
28929
+ cachedInputTokens: 0,
28930
+ reasoningTokens: 0,
28931
+ totalTokens: 0,
28932
+ turns: 0,
28933
+ activeMs: 0,
28934
+ toolUses: 0,
28935
+ filesChanged: 0,
28936
+ additions: 0,
28937
+ deletions: 0,
28938
+ commits: 0,
28939
+ pushes: 0,
28940
+ threads: threadMeta.size,
28941
+ projects: 0
28942
+ };
28943
+ for (const bucket of buckets.values()) {
28944
+ const { provider, ...rest } = bucket;
28945
+ dailyBuckets.push(provider ? {
28946
+ ...rest,
28947
+ provider
28948
+ } : rest);
28949
+ totals.inputTokens += bucket.inputTokens;
28950
+ totals.outputTokens += bucket.outputTokens;
28951
+ totals.cachedInputTokens += bucket.cachedInputTokens;
28952
+ totals.reasoningTokens += bucket.reasoningTokens;
28953
+ totals.turns += bucket.turns;
28954
+ totals.activeMs += bucket.activeMs;
28955
+ totals.toolUses += bucket.toolUses;
28956
+ totals.filesChanged += bucket.filesChanged;
28957
+ totals.additions += bucket.additions;
28958
+ totals.deletions += bucket.deletions;
28959
+ totals.commits += bucket.commits;
28960
+ totals.pushes += bucket.pushes;
28961
+ }
28962
+ totals.totalTokens = totals.inputTokens + totals.outputTokens;
28963
+ const projectTitle = /* @__PURE__ */ new Map();
28964
+ for (const row of projectRows) projectTitle.set(row.projectId, row.title);
28965
+ const projectIdsInData = /* @__PURE__ */ new Set();
28966
+ for (const meta of threadMeta.values()) projectIdsInData.add(meta.projectId);
28967
+ for (const bucket of buckets.values()) projectIdsInData.add(bucket.projectId);
28968
+ totals.projects = projectIdsInData.size;
28969
+ const projects = [...projectIdsInData].map((id) => ({
28970
+ id,
28971
+ title: projectTitle.get(id)?.trim() || id
28972
+ }));
28973
+ const models = [...modelProvider].map(([model, provider]) => provider ? {
28974
+ model,
28975
+ provider
28976
+ } : { model });
28977
+ return {
28978
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
28979
+ ...earliest ? { earliestActivityAt: earliest } : {},
28980
+ projects,
28981
+ models,
28982
+ dailyBuckets,
28983
+ worktrees: {
28984
+ created: wtCreated,
28985
+ archived: wtArchived,
28986
+ active: wtCreated - wtArchived,
28987
+ openPrs: wtOpenPrs
28988
+ },
28989
+ totals,
28990
+ tokenAttribution: attribution
28991
+ };
28992
+ });
28993
+ return { getStatistics };
28994
+ });
28995
+ function safeParse(json) {
28996
+ try {
28997
+ const parsed = JSON.parse(json);
28998
+ return parsed && typeof parsed === "object" ? parsed : null;
28999
+ } catch {
29000
+ return null;
29001
+ }
29002
+ }
29003
+ function safeParseArray(json) {
29004
+ try {
29005
+ const parsed = JSON.parse(json);
29006
+ return Array.isArray(parsed) ? parsed : [];
29007
+ } catch {
29008
+ return [];
29009
+ }
29010
+ }
29011
+ const StatisticsQueryLive = effect.Layer.effect(StatisticsQuery, makeStatisticsQuery);
29012
+ //#endregion
28523
29013
  //#region src/serverLifecycleEvents.ts
28524
29014
  var ServerLifecycleEvents = class extends effect.Context.Service()("ryco/serverLifecycleEvents") {};
28525
29015
  const ServerLifecycleEventsLive = effect.Layer.effect(ServerLifecycleEvents, effect.Effect.gen(function* () {
@@ -30409,7 +30899,8 @@ const makeGitManager = effect.Effect.fn("makeGitManager")(function* () {
30409
30899
  isDefaultRef: details.isDefaultBranch,
30410
30900
  refName: details.branch,
30411
30901
  hasWorkingTreeChanges: details.hasWorkingTreeChanges,
30412
- workingTree: details.workingTree
30902
+ workingTree: details.workingTree,
30903
+ ...details.committed ? { committed: details.committed } : {}
30413
30904
  };
30414
30905
  });
30415
30906
  const localStatusResultCache = yield* effect.Cache.makeWith(readLocalStatus, {
@@ -32543,6 +33034,8 @@ const layer = effect.Layer.effect(JiraWorkItemService, make$5());
32543
33034
  //#region src/remote/Services/AdvertisedEndpointRegistry.ts
32544
33035
  var AdvertisedEndpointRegistry = class extends effect.Context.Service()("ryco/remote/Services/AdvertisedEndpointRegistry") {};
32545
33036
  const ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT = 1e3;
33037
+ const ORCHESTRATION_LEGACY_REPLAY_MAX_EVENTS = ORCHESTRATION_REPLAY_PAGE_MAX_LIMIT;
33038
+ const ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS = 1e3;
32546
33039
  //#endregion
32547
33040
  //#region src/ws/context/gitErrors.ts
32548
33041
  function gitErrorText(error) {
@@ -32576,6 +33069,10 @@ const failGitWorkflow = (operation, detail, cause) => effect.Effect.fail(toGitMa
32576
33069
  const wsOrchestrationReplayDepth = effect.Metric.gauge("t3_ws_orchestration_replay_depth", { description: "Current persisted orchestration event replay depth for a replayable WebSocket subscription." });
32577
33070
  const wsOrchestrationLiveBufferDepth = effect.Metric.gauge("t3_ws_orchestration_live_buffer_depth", { description: "Current live orchestration event buffer depth for a replayable WebSocket subscription." });
32578
33071
  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." });
33072
+ const wsOrchestrationLiveBufferOverflowsTotal = effect.Metric.counter("t3_ws_orchestration_live_buffer_overflows_total", {
33073
+ description: "Total live orchestration event buffer overflows that forced replayable WebSocket subscription resync.",
33074
+ incremental: true
33075
+ });
32579
33076
  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
33077
  const normalizeSequence = (sequence) => Number.isFinite(sequence) ? Math.max(0, Math.floor(sequence)) : 0;
32581
33078
  const updateGauge$1 = (metric, attributes, value) => effect.Metric.update(effect.Metric.withAttributes(metric, attributes), value);
@@ -32613,6 +33110,18 @@ const makeWsReplayMetrics = (input) => effect.Effect.gen(function* () {
32613
33110
  yield* recordLiveBuffer(yield* effect.Ref.updateAndGet(liveBufferDepth, (current) => Math.max(0, current - 1)), yield* effect.Ref.get(liveBufferHighWater));
32614
33111
  yield* markDrained(sequence);
32615
33112
  }),
33113
+ recordLiveOverflow: (sequence, capacity) => effect.Effect.gen(function* () {
33114
+ const normalizedSequence = normalizeSequence(sequence);
33115
+ const normalizedCapacity = Math.max(0, Math.floor(capacity));
33116
+ yield* effect.Ref.update(latestLiveSequence, (current) => Math.max(current, normalizedSequence));
33117
+ yield* effect.Ref.set(liveBufferDepth, normalizedCapacity);
33118
+ const highWater = yield* effect.Ref.updateAndGet(liveBufferHighWater, (current) => Math.max(current, normalizedCapacity));
33119
+ yield* effect.Effect.all([
33120
+ recordLiveBuffer(normalizedCapacity, highWater),
33121
+ updateGauge$1(wsOrchestrationLiveBufferOverflowsTotal, attributes, 1),
33122
+ recordLag
33123
+ ], { discard: true });
33124
+ }),
32616
33125
  reset: effect.Effect.all([
32617
33126
  updateGauge$1(wsOrchestrationReplayDepth, attributes, 0),
32618
33127
  updateGauge$1(wsOrchestrationLiveBufferDepth, attributes, 0),
@@ -32663,6 +33172,29 @@ const buildWorkItemBranchNameMessage = (input) => {
32663
33172
  };
32664
33173
  //#endregion
32665
33174
  //#region src/ws/context/orchestrationStreams.ts
33175
+ const makeLiveQueueOverflowError = (input) => new require_collectUint8StreamText.OrchestrationGetSnapshotError({
33176
+ message: `Orchestration ${input.stream} live event queue overflowed; reconnect to resynchronize`,
33177
+ cause: /* @__PURE__ */ new Error(`orchestration ${input.stream} live queue overflow at sequence ${input.sequence} with capacity ${input.capacity}`)
33178
+ });
33179
+ const offerOrchestrationLiveEventOrFail = (input) => effect.Effect.gen(function* () {
33180
+ const capacity = input.capacity ?? 1e3;
33181
+ if (effect.Queue.offerUnsafe(input.liveQueue, input.event)) {
33182
+ yield* input.replayMetrics.recordLiveEnqueued(input.event.sequence);
33183
+ return;
33184
+ }
33185
+ if (!(yield* effect.Ref.modify(input.overflowedRef, (overflowed) => [!overflowed, true]))) return;
33186
+ yield* input.replayMetrics.recordLiveOverflow(input.event.sequence, capacity);
33187
+ yield* effect.Effect.logWarning("orchestration live stream queue overflow; forcing resync", {
33188
+ stream: input.stream,
33189
+ sequence: input.event.sequence,
33190
+ capacity
33191
+ });
33192
+ yield* effect.Queue.fail(input.liveQueue, makeLiveQueueOverflowError({
33193
+ stream: input.stream,
33194
+ sequence: input.event.sequence,
33195
+ capacity
33196
+ }));
33197
+ });
32666
33198
  const makeOrchestrationStreamHelpers = (deps) => {
32667
33199
  const { orchestrationEngine, projectionSnapshotQuery, repositoryIdentityResolver } = deps;
32668
33200
  const enrichProjectEvent = (event) => {
@@ -32782,8 +33314,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32782
33314
  const loadedSnapshot = yield* snapshot;
32783
33315
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32784
33316
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32785
- const liveQueue = yield* effect.Queue.unbounded();
33317
+ const liveQueue = yield* effect.Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32786
33318
  const lastSequenceRef = yield* effect.Ref.make(snapshotSequence);
33319
+ const overflowedRef = yield* effect.Ref.make(false);
32787
33320
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32788
33321
  const replayMetrics = yield* makeWsReplayMetrics({
32789
33322
  stream: "shell",
@@ -32792,8 +33325,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32792
33325
  });
32793
33326
  yield* effect.Stream.fromSubscription(liveSubscription).pipe(effect.Stream.runForEach((event) => effect.Effect.gen(function* () {
32794
33327
  yield* replayBoundary.recordLiveSequence(event.sequence);
32795
- yield* effect.Queue.offer(liveQueue, event);
32796
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33328
+ yield* offerOrchestrationLiveEventOrFail({
33329
+ stream: "shell",
33330
+ event,
33331
+ liveQueue,
33332
+ overflowedRef,
33333
+ replayMetrics
33334
+ });
32797
33335
  })), effect.Effect.ensuring(effect.Queue.shutdown(liveQueue)), effect.Effect.ignoreCause({ log: true }), effect.Effect.forkScoped);
32798
33336
  const replayStream = makeBoundedReplayStream({
32799
33337
  snapshotSequence,
@@ -32811,8 +33349,9 @@ const makeOrchestrationStreamHelpers = (deps) => {
32811
33349
  const loadedSnapshot = yield* snapshot;
32812
33350
  const snapshotSequence = loadedSnapshot.snapshotSequence;
32813
33351
  const liveSubscription = yield* orchestrationEngine.subscribeDomainEvents;
32814
- const liveQueue = yield* effect.Queue.unbounded();
33352
+ const liveQueue = yield* effect.Queue.bounded(ORCHESTRATION_LIVE_QUEUE_MAX_EVENTS);
32815
33353
  const lastSequenceRef = yield* effect.Ref.make(snapshotSequence);
33354
+ const overflowedRef = yield* effect.Ref.make(false);
32816
33355
  const replayBoundary = yield* makeReplayBoundaryTracker(snapshotSequence);
32817
33356
  const replayMetrics = yield* makeWsReplayMetrics({
32818
33357
  stream: "thread",
@@ -32825,8 +33364,13 @@ const makeOrchestrationStreamHelpers = (deps) => {
32825
33364
  })));
32826
33365
  yield* effect.Stream.fromSubscription(liveSubscription).pipe(effect.Stream.runForEach((event) => effect.Effect.gen(function* () {
32827
33366
  yield* replayBoundary.recordLiveSequence(event.sequence);
32828
- yield* effect.Queue.offer(liveQueue, event);
32829
- yield* replayMetrics.recordLiveEnqueued(event.sequence);
33367
+ yield* offerOrchestrationLiveEventOrFail({
33368
+ stream: "thread",
33369
+ event,
33370
+ liveQueue,
33371
+ overflowedRef,
33372
+ replayMetrics
33373
+ });
32830
33374
  })), effect.Effect.ensuring(effect.Queue.shutdown(liveQueue)), effect.Effect.ignoreCause({ log: true }), effect.Effect.forkScoped);
32831
33375
  const replayStream = makeBoundedReplayStream({
32832
33376
  snapshotSequence,
@@ -33367,6 +33911,7 @@ function toDiagnosticsProviderProcess(provider) {
33367
33911
  const makeWsRpcContext = (session) => effect.Effect.gen(function* () {
33368
33912
  const currentSessionId = session.sessionId;
33369
33913
  const projectionSnapshotQuery = yield* ProjectionSnapshotQuery;
33914
+ const statisticsQuery = yield* StatisticsQuery;
33370
33915
  const orchestrationEngine = yield* OrchestrationEngineService;
33371
33916
  const checkpointDiffQuery = yield* CheckpointDiffQuery;
33372
33917
  const keybindings = yield* Keybindings;
@@ -33690,6 +34235,7 @@ const makeWsRpcContext = (session) => effect.Effect.gen(function* () {
33690
34235
  return {
33691
34236
  currentSessionId,
33692
34237
  projectionSnapshotQuery,
34238
+ statisticsQuery,
33693
34239
  orchestrationEngine,
33694
34240
  checkpointDiffQuery,
33695
34241
  keybindings,
@@ -33967,9 +34513,9 @@ function recordServerPerfPayload(label, value, record) {
33967
34513
  //#endregion
33968
34514
  //#region src/ws/orchestrationRpc.ts
33969
34515
  const makeOrchestrationHandlers = (ctx) => {
33970
- const { withAccess, ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
34516
+ const { ownerEffect, projectionSnapshotQuery, dispatchNormalizedCommand, dispatchWorktreeCommand, serverCommandId, terminalManager, checkpointDiffQuery, orchestrationEngine, enrichOrchestrationEvents, makeReplayableShellStream, makeReplayableThreadStream } = ctx;
33971
34517
  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* () {
34518
+ [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
34519
  const normalizedCommand = yield* normalizeDispatchCommand(command);
33974
34520
  const shouldStopSessionAfterArchive = normalizedCommand.type === "thread.archive" ? yield* projectionSnapshotQuery.getThreadShellById(normalizedCommand.threadId).pipe(effect.Effect.map(effect.Option.match({
33975
34521
  onNone: () => false,
@@ -34009,7 +34555,7 @@ const makeOrchestrationHandlers = (ctx) => {
34009
34555
  [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
34556
  maximum: Number.MAX_SAFE_INTEGER,
34011
34557
  minimum: 0
34012
- }))).pipe(effect.Effect.map((events) => Array.from(events)), effect.Effect.flatMap(enrichOrchestrationEvents), effect.Effect.mapError((cause) => new require_collectUint8StreamText.OrchestrationReplayEventsError({
34558
+ }), 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
34559
  message: "Failed to replay orchestration events",
34014
34560
  cause
34015
34561
  }))), { "rpc.aggregate": "orchestration" }),
@@ -34140,10 +34686,25 @@ const makeTerminalHandlers = (ctx) => {
34140
34686
  [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
34687
  [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
34688
  [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" })
34689
+ [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* () {
34690
+ const unsubscribe = yield* terminalManager.subscribe((event) => effect.Effect.gen(function* () {
34691
+ recordServerPerfPayload("server.ws.terminal.events", event);
34692
+ yield* effect.Queue.offer(queue, event);
34693
+ }));
34694
+ const snapshots = yield* terminalManager.listSessions;
34695
+ yield* effect.Effect.forEach(snapshots.filter((snapshot) => snapshot.status === "running"), (snapshot) => {
34696
+ const event = {
34697
+ type: "started",
34698
+ threadId: snapshot.threadId,
34699
+ terminalId: snapshot.terminalId,
34700
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
34701
+ snapshot
34702
+ };
34703
+ recordServerPerfPayload("server.ws.terminal.events", event);
34704
+ return effect.Queue.offer(queue, event);
34705
+ }, { discard: true });
34706
+ return unsubscribe;
34707
+ }), (unsubscribe) => effect.Effect.sync(unsubscribe)))), { "rpc.aggregate": "terminal" })
34147
34708
  });
34148
34709
  };
34149
34710
  //#endregion
@@ -34238,7 +34799,7 @@ const makeSourceControlHandlers = (ctx) => {
34238
34799
  cwd,
34239
34800
  reason: "sourceControl.searchIssues"
34240
34801
  })))), { "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) => {
34802
+ [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
34803
  const trimmedQuery = query?.trim() ?? "";
34243
34804
  if (trimmedQuery.length > 0) return provider.searchChangeRequests({
34244
34805
  cwd,
@@ -34254,7 +34815,7 @@ const makeSourceControlHandlers = (ctx) => {
34254
34815
  }), effect.Effect.tap(() => refreshLinkedWorktreeSourceControlStates({
34255
34816
  cwd,
34256
34817
  reason: "sourceControl.listChangeRequests"
34257
- }))), { "rpc.aggregate": "source-control" }),
34818
+ })))), { "rpc.aggregate": "source-control" }),
34258
34819
  [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
34820
  cwd,
34260
34821
  query,
@@ -35004,12 +35565,19 @@ const makeProviderHandlers = (ctx) => {
35004
35565
  });
35005
35566
  };
35006
35567
  //#endregion
35568
+ //#region src/ws/statisticsRpc.ts
35569
+ const makeStatisticsHandlers = (ctx) => {
35570
+ const { statisticsQuery, ownerEffect } = ctx;
35571
+ 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" }) });
35572
+ };
35573
+ //#endregion
35007
35574
  //#region src/ws/index.ts
35008
35575
  const makeWsRpcLayer = (session) => require_collectUint8StreamText.WsRpcGroup.toLayer(effect.Effect.gen(function* () {
35009
35576
  const ctx = yield* makeWsRpcContext(session);
35010
35577
  return require_collectUint8StreamText.WsRpcGroup.of({
35011
35578
  ...makeOrchestrationHandlers(ctx),
35012
35579
  ...makeProviderHandlers(ctx),
35580
+ ...makeStatisticsHandlers(ctx),
35013
35581
  ...makeSourceControlHandlers(ctx),
35014
35582
  ...makeProjectHandlers(ctx),
35015
35583
  ...makeGitHandlers(ctx),
@@ -37427,6 +37995,8 @@ const DEFAULT_PERSIST_DEBOUNCE_MS = 40;
37427
37995
  const DEFAULT_SUBPROCESS_POLL_INTERVAL_MS = 1e3;
37428
37996
  const DEFAULT_PROCESS_KILL_GRACE_MS = 1e3;
37429
37997
  const DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS = 128;
37998
+ const DEFAULT_MAX_PENDING_PROCESS_EVENTS = 2048;
37999
+ const DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES = 2 * 1024 * 1024;
37430
38000
  const DEFAULT_OPEN_COLS = 120;
37431
38001
  const DEFAULT_OPEN_ROWS = 30;
37432
38002
  const TERMINAL_ENV_BLOCKLIST = new Set([
@@ -37483,13 +38053,86 @@ function cleanupProcessHandles(session) {
37483
38053
  session.unsubscribeExit?.();
37484
38054
  session.unsubscribeExit = null;
37485
38055
  }
37486
- function enqueueProcessEvent(session, expectedPid, event) {
38056
+ function clearPendingProcessEvents(session) {
38057
+ session.pendingProcessEvents = [];
38058
+ session.pendingProcessEventIndex = 0;
38059
+ session.pendingProcessOutputBytes = 0;
38060
+ session.processEventDrainRunning = false;
38061
+ }
38062
+ function enqueueProcessEvent(session, expectedPid, event, limits) {
37487
38063
  if (!session.process || session.status !== "running" || session.pid !== expectedPid) return false;
38064
+ if (event.type === "output" && session.pendingProcessEvents.some((pendingEvent) => pendingEvent.type === "exit")) return false;
38065
+ compactPendingProcessEvents(session);
37488
38066
  session.pendingProcessEvents.push(event);
38067
+ if (event.type === "output") session.pendingProcessOutputBytes += event.byteLength;
38068
+ enforcePendingProcessEventLimits(session, limits);
37489
38069
  if (session.processEventDrainRunning) return false;
37490
38070
  session.processEventDrainRunning = true;
37491
38071
  return true;
37492
38072
  }
38073
+ function compactPendingProcessEvents(session) {
38074
+ if (session.pendingProcessEventIndex <= 0) return;
38075
+ session.pendingProcessEvents = session.pendingProcessEvents.slice(session.pendingProcessEventIndex);
38076
+ session.pendingProcessEventIndex = 0;
38077
+ }
38078
+ function coalesceOldestPendingOutputPair(session) {
38079
+ for (let index = 0; index < session.pendingProcessEvents.length - 1; index += 1) {
38080
+ const current = session.pendingProcessEvents[index];
38081
+ const next = session.pendingProcessEvents[index + 1];
38082
+ if (current?.type !== "output" || next?.type !== "output") continue;
38083
+ session.pendingProcessEvents.splice(index, 2, {
38084
+ type: "output",
38085
+ data: `${current.data}${next.data}`,
38086
+ byteLength: current.byteLength + next.byteLength
38087
+ });
38088
+ return true;
38089
+ }
38090
+ return false;
38091
+ }
38092
+ function trimOutputDataToApproxBytes(data, maxBytes) {
38093
+ if (maxBytes <= 0 || data.length === 0) return {
38094
+ data: "",
38095
+ byteLength: 0
38096
+ };
38097
+ let nextData = data;
38098
+ let byteLength = approximateTextBytes(nextData);
38099
+ while (byteLength > maxBytes && nextData.length > 0) {
38100
+ const excessBytes = byteLength - maxBytes;
38101
+ const sliceStart = Math.min(nextData.length, Math.max(1, excessBytes));
38102
+ nextData = nextData.slice(sliceStart);
38103
+ byteLength = approximateTextBytes(nextData);
38104
+ }
38105
+ return {
38106
+ data: nextData,
38107
+ byteLength
38108
+ };
38109
+ }
38110
+ function trimPendingOutputBytesToLimit(session, maxPendingProcessOutputBytes) {
38111
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38112
+ for (let index = 0; index < session.pendingProcessEvents.length; index += 1) {
38113
+ if (session.pendingProcessOutputBytes <= maxPendingProcessOutputBytes) return;
38114
+ const event = session.pendingProcessEvents[index];
38115
+ if (event?.type !== "output") continue;
38116
+ const targetEventBytes = Math.max(0, event.byteLength - (session.pendingProcessOutputBytes - maxPendingProcessOutputBytes));
38117
+ if (targetEventBytes <= 0) {
38118
+ session.pendingProcessEvents.splice(index, 1);
38119
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - event.byteLength);
38120
+ index -= 1;
38121
+ continue;
38122
+ }
38123
+ const trimmed = trimOutputDataToApproxBytes(event.data, targetEventBytes);
38124
+ session.pendingProcessEvents[index] = {
38125
+ type: "output",
38126
+ data: trimmed.data,
38127
+ byteLength: trimmed.byteLength
38128
+ };
38129
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - (event.byteLength - trimmed.byteLength));
38130
+ }
38131
+ }
38132
+ function enforcePendingProcessEventLimits(session, limits) {
38133
+ while (session.pendingProcessEvents.length > limits.maxPendingProcessEvents && coalesceOldestPendingOutputPair(session));
38134
+ trimPendingOutputBytesToLimit(session, limits.maxPendingProcessOutputBytes);
38135
+ }
37493
38136
  function defaultShellResolver(platform = process.platform, env = process.env) {
37494
38137
  if (platform === "win32") return "pwsh.exe";
37495
38138
  return env.SHELL ?? "bash";
@@ -37856,6 +38499,10 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
37856
38499
  const subprocessPollIntervalMs = options.subprocessPollIntervalMs ?? DEFAULT_SUBPROCESS_POLL_INTERVAL_MS;
37857
38500
  const processKillGraceMs = options.processKillGraceMs ?? DEFAULT_PROCESS_KILL_GRACE_MS;
37858
38501
  const maxRetainedInactiveSessions = options.maxRetainedInactiveSessions ?? DEFAULT_MAX_RETAINED_INACTIVE_SESSIONS;
38502
+ const pendingProcessEventLimits = {
38503
+ maxPendingProcessEvents: Math.max(1, options.maxPendingProcessEvents ?? DEFAULT_MAX_PENDING_PROCESS_EVENTS),
38504
+ maxPendingProcessOutputBytes: Math.max(1, options.maxPendingProcessOutputBytes ?? DEFAULT_MAX_PENDING_PROCESS_OUTPUT_BYTES)
38505
+ };
37859
38506
  yield* fileSystem.makeDirectory(logsDir, { recursive: true }).pipe(effect.Effect.orDie);
37860
38507
  const managerStateRef = yield* effect.SynchronizedRef.make({
37861
38508
  sessions: /* @__PURE__ */ new Map(),
@@ -38092,16 +38739,12 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38092
38739
  while (true) {
38093
38740
  const action = yield* effect.Effect.sync(() => {
38094
38741
  if (session.pid !== expectedPid || !session.process || session.status !== "running") {
38095
- session.pendingProcessEvents = [];
38096
- session.pendingProcessEventIndex = 0;
38097
- session.processEventDrainRunning = false;
38742
+ clearPendingProcessEvents(session);
38098
38743
  return { type: "idle" };
38099
38744
  }
38100
38745
  const nextEvent = session.pendingProcessEvents[session.pendingProcessEventIndex];
38101
38746
  if (!nextEvent) {
38102
- session.pendingProcessEvents = [];
38103
- session.pendingProcessEventIndex = 0;
38104
- session.processEventDrainRunning = false;
38747
+ clearPendingProcessEvents(session);
38105
38748
  return { type: "idle" };
38106
38749
  }
38107
38750
  session.pendingProcessEventIndex += 1;
@@ -38110,6 +38753,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38110
38753
  session.pendingProcessEventIndex = 0;
38111
38754
  }
38112
38755
  if (nextEvent.type === "output") {
38756
+ session.pendingProcessOutputBytes = Math.max(0, session.pendingProcessOutputBytes - nextEvent.byteLength);
38113
38757
  const sanitized = sanitizeTerminalHistoryChunk(session.pendingHistoryControlSequence, nextEvent.data);
38114
38758
  session.pendingHistoryControlSequence = sanitized.pendingControlSequence;
38115
38759
  if (sanitized.visibleText.length > 0) session.history = capHistory(`${session.history}${sanitized.visibleText}`, historyLineLimit);
@@ -38129,9 +38773,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38129
38773
  session.hasRunningSubprocess = false;
38130
38774
  session.status = "exited";
38131
38775
  session.pendingHistoryControlSequence = "";
38132
- session.pendingProcessEvents = [];
38133
- session.pendingProcessEventIndex = 0;
38134
- session.processEventDrainRunning = false;
38776
+ clearPendingProcessEvents(session);
38135
38777
  session.exitCode = Number.isInteger(nextEvent.event.exitCode) ? nextEvent.event.exitCode : null;
38136
38778
  session.exitSignal = Number.isInteger(nextEvent.event.signal) ? nextEvent.event.signal : null;
38137
38779
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -38179,9 +38821,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38179
38821
  session.hasRunningSubprocess = false;
38180
38822
  session.status = "exited";
38181
38823
  session.pendingHistoryControlSequence = "";
38182
- session.pendingProcessEvents = [];
38183
- session.pendingProcessEventIndex = 0;
38184
- session.processEventDrainRunning = false;
38824
+ clearPendingProcessEvents(session);
38185
38825
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38186
38826
  return [void 0, state];
38187
38827
  });
@@ -38233,9 +38873,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38233
38873
  session.exitCode = null;
38234
38874
  session.exitSignal = null;
38235
38875
  session.hasRunningSubprocess = false;
38236
- session.pendingProcessEvents = [];
38237
- session.pendingProcessEventIndex = 0;
38238
- session.processEventDrainRunning = false;
38876
+ clearPendingProcessEvents(session);
38239
38877
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38240
38878
  return [void 0, state];
38241
38879
  });
@@ -38249,15 +38887,16 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38249
38887
  const unsubscribeData = ptyProcess.onData((data) => {
38250
38888
  if (!enqueueProcessEvent(session, processPid, {
38251
38889
  type: "output",
38252
- data
38253
- })) return;
38890
+ data,
38891
+ byteLength: approximateTextBytes(data)
38892
+ }, pendingProcessEventLimits)) return;
38254
38893
  runFork(drainProcessEvents(session, processPid));
38255
38894
  });
38256
38895
  const unsubscribeExit = ptyProcess.onExit((event) => {
38257
38896
  if (!enqueueProcessEvent(session, processPid, {
38258
38897
  type: "exit",
38259
38898
  event
38260
- })) return;
38899
+ }, pendingProcessEventLimits)) return;
38261
38900
  runFork(drainProcessEvents(session, processPid));
38262
38901
  });
38263
38902
  yield* modifyManagerState((state) => {
@@ -38289,9 +38928,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38289
38928
  session.unsubscribeData = null;
38290
38929
  session.unsubscribeExit = null;
38291
38930
  session.hasRunningSubprocess = false;
38292
- session.pendingProcessEvents = [];
38293
- session.pendingProcessEventIndex = 0;
38294
- session.processEventDrainRunning = false;
38931
+ clearPendingProcessEvents(session);
38295
38932
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38296
38933
  return [void 0, state];
38297
38934
  });
@@ -38401,6 +39038,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38401
39038
  pendingHistoryControlSequence: "",
38402
39039
  pendingProcessEvents: [],
38403
39040
  pendingProcessEventIndex: 0,
39041
+ pendingProcessOutputBytes: 0,
38404
39042
  processEventDrainRunning: false,
38405
39043
  exitCode: null,
38406
39044
  exitSignal: null,
@@ -38447,18 +39085,14 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38447
39085
  liveSession.runtimeEnv = nextRuntimeEnv;
38448
39086
  liveSession.history = "";
38449
39087
  liveSession.pendingHistoryControlSequence = "";
38450
- liveSession.pendingProcessEvents = [];
38451
- liveSession.pendingProcessEventIndex = 0;
38452
- liveSession.processEventDrainRunning = false;
39088
+ clearPendingProcessEvents(liveSession);
38453
39089
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38454
39090
  } else if (liveSession.status === "exited" || liveSession.status === "error") {
38455
39091
  liveSession.runtimeEnv = nextRuntimeEnv;
38456
39092
  liveSession.worktreePath = input.worktreePath ?? null;
38457
39093
  liveSession.history = "";
38458
39094
  liveSession.pendingHistoryControlSequence = "";
38459
- liveSession.pendingProcessEvents = [];
38460
- liveSession.pendingProcessEventIndex = 0;
38461
- liveSession.processEventDrainRunning = false;
39095
+ clearPendingProcessEvents(liveSession);
38462
39096
  yield* persistHistory(liveSession.threadId, liveSession.terminalId, liveSession.history);
38463
39097
  }
38464
39098
  if (!liveSession.process) {
@@ -38512,9 +39146,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38512
39146
  const session = yield* requireSession(input.threadId, terminalId);
38513
39147
  session.history = "";
38514
39148
  session.pendingHistoryControlSequence = "";
38515
- session.pendingProcessEvents = [];
38516
- session.pendingProcessEventIndex = 0;
38517
- session.processEventDrainRunning = false;
39149
+ clearPendingProcessEvents(session);
38518
39150
  session.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
38519
39151
  yield* persistHistory(input.threadId, terminalId, session.history);
38520
39152
  yield* publishEvent({
@@ -38545,6 +39177,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38545
39177
  pendingHistoryControlSequence: "",
38546
39178
  pendingProcessEvents: [],
38547
39179
  pendingProcessEventIndex: 0,
39180
+ pendingProcessOutputBytes: 0,
38548
39181
  processEventDrainRunning: false,
38549
39182
  exitCode: null,
38550
39183
  exitSignal: null,
@@ -38578,9 +39211,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38578
39211
  const rows = input.rows ?? session.rows;
38579
39212
  session.history = "";
38580
39213
  session.pendingHistoryControlSequence = "";
38581
- session.pendingProcessEvents = [];
38582
- session.pendingProcessEventIndex = 0;
38583
- session.processEventDrainRunning = false;
39214
+ clearPendingProcessEvents(session);
38584
39215
  yield* persistHistory(input.threadId, terminalId, session.history);
38585
39216
  yield* startSession(session, {
38586
39217
  threadId: input.threadId,
@@ -38602,6 +39233,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38602
39233
  yield* effect.Effect.forEach(threadSessions, (session) => closeSession(input.threadId, session.terminalId, false), { discard: true });
38603
39234
  if (input.deleteHistory) yield* deleteAllHistoryForThread(input.threadId);
38604
39235
  }));
39236
+ 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
39237
  return {
38606
39238
  open,
38607
39239
  write,
@@ -38610,6 +39242,7 @@ const makeTerminalManagerWithOptions = effect.Effect.fn("makeTerminalManagerWith
38610
39242
  restart,
38611
39243
  close,
38612
39244
  listDiagnostics: effect.SynchronizedRef.get(managerStateRef).pipe(effect.Effect.map((state) => [...state.sessions.values()].map(diagnosticsSnapshot).toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt)))),
39245
+ listSessions,
38613
39246
  subscribe: (listener) => effect.Effect.sync(() => {
38614
39247
  terminalEventListeners.add(listener);
38615
39248
  return () => {
@@ -39105,7 +39738,7 @@ function isGitRepository(cwd) {
39105
39738
  //#endregion
39106
39739
  //#region src/orchestration/Layers/ProviderRuntimeIngestion.ts
39107
39740
  const providerTurnKey = (threadId, turnId) => `${threadId}:${turnId}`;
39108
- const providerCommandId = (event, tag) => require_collectUint8StreamText.CommandId.make(`provider:${event.eventId}:${tag}:${crypto.randomUUID()}`);
39741
+ const providerCommandId = (event, tag, discriminator) => require_collectUint8StreamText.CommandId.make(discriminator === void 0 ? `provider:${event.eventId}:${tag}` : `provider:${event.eventId}:${tag}:${discriminator}`);
39109
39742
  const liveAssistantDeltaBufferKey = (threadId, messageId) => `${threadId}:${messageId}`;
39110
39743
  const TURN_MESSAGE_IDS_BY_TURN_CACHE_CAPACITY = 1e4;
39111
39744
  const TURN_MESSAGE_IDS_BY_TURN_TTL = effect.Duration.minutes(120);
@@ -39179,6 +39812,7 @@ function rawPayloadThreadId(event) {
39179
39812
  return asTrimmedString(asRecord(event.raw?.payload)?.threadId);
39180
39813
  }
39181
39814
  function collabReceiverThreadIdsFromRuntimeEvent(event) {
39815
+ if (event.type === "subagent.started" || event.type === "subagent.updated" || event.type === "subagent.completed") return event.payload.subagent.providerThreadId ? [event.payload.subagent.providerThreadId] : [];
39182
39816
  if (event.type !== "item.started" && event.type !== "item.updated" && event.type !== "item.completed") return [];
39183
39817
  if (event.payload.itemType !== "collab_agent_tool_call") return [];
39184
39818
  const item = asRecord(asRecord(event.payload.data)?.item);
@@ -39236,6 +39870,9 @@ function normalizeRuntimeTurnState(value) {
39236
39870
  default: return "completed";
39237
39871
  }
39238
39872
  }
39873
+ function isTerminalProjectionTurnState(state) {
39874
+ return state === "completed" || state === "error" || state === "interrupted";
39875
+ }
39239
39876
  function orchestrationSessionStatusFromRuntimeState(state) {
39240
39877
  switch (state) {
39241
39878
  case "starting": return "starting";
@@ -39423,6 +40060,53 @@ function runtimeEventToActivities(event) {
39423
40060
  turnId: toTurnId$1(event.turnId) ?? null,
39424
40061
  ...maybeSequence
39425
40062
  }];
40063
+ case "subagent.started": return [{
40064
+ id: event.eventId,
40065
+ createdAt: event.createdAt,
40066
+ tone: "tool",
40067
+ kind: "subagent.started",
40068
+ summary: `${event.payload.subagent.label ?? "Subagent"} started`,
40069
+ payload: {
40070
+ itemType: "collab_agent_tool_call",
40071
+ status: "running",
40072
+ subagent: event.payload.subagent
40073
+ },
40074
+ turnId: toTurnId$1(event.turnId) ?? null,
40075
+ ...maybeSequence
40076
+ }];
40077
+ case "subagent.updated": return [{
40078
+ id: event.eventId,
40079
+ createdAt: event.createdAt,
40080
+ tone: "tool",
40081
+ kind: "subagent.updated",
40082
+ summary: event.payload.summary ?? `${event.payload.subagent.label ?? "Subagent"} updated`,
40083
+ payload: {
40084
+ itemType: "collab_agent_tool_call",
40085
+ ...event.payload.status ? { status: event.payload.status } : {},
40086
+ subagent: event.payload.subagent,
40087
+ ...event.payload.detail ? { detail: truncateDetail(event.payload.detail) } : {},
40088
+ ...event.payload.summary ? { summary: truncateDetail(event.payload.summary) } : {},
40089
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40090
+ },
40091
+ turnId: toTurnId$1(event.turnId) ?? null,
40092
+ ...maybeSequence
40093
+ }];
40094
+ case "subagent.completed": return [{
40095
+ id: event.eventId,
40096
+ createdAt: event.createdAt,
40097
+ tone: event.payload.status === "failed" ? "error" : "tool",
40098
+ kind: "subagent.completed",
40099
+ 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`,
40100
+ payload: {
40101
+ itemType: "collab_agent_tool_call",
40102
+ status: event.payload.status,
40103
+ subagent: event.payload.subagent,
40104
+ ...event.payload.summary ? { detail: truncateDetail(event.payload.summary) } : {},
40105
+ ...event.payload.usage !== void 0 ? { usage: event.payload.usage } : {}
40106
+ },
40107
+ turnId: toTurnId$1(event.turnId) ?? null,
40108
+ ...maybeSequence
40109
+ }];
39426
40110
  case "thread.state.changed":
39427
40111
  if (event.payload.state !== "compacted") return [];
39428
40112
  return [{
@@ -39615,7 +40299,7 @@ const make$3 = effect.Effect.gen(function* () {
39615
40299
  }
39616
40300
  yield* orchestrationEngine.dispatch({
39617
40301
  type: "thread.message.assistant.delta",
39618
- commandId: providerCommandId(buffer.event, input.commandTag),
40302
+ commandId: providerCommandId(buffer.event, input.commandTag, String(buffer.messageId)),
39619
40303
  threadId: buffer.threadId,
39620
40304
  messageId: buffer.messageId,
39621
40305
  delta: buffer.text,
@@ -39694,9 +40378,10 @@ const make$3 = effect.Effect.gen(function* () {
39694
40378
  };
39695
40379
  const upsertSubagentMessageActivity = (input) => effect.Effect.gen(function* () {
39696
40380
  const sessionSequence = input.event.sessionSequence;
40381
+ const commandKey = `${input.providerThreadId}:${input.providerItemId}`;
39697
40382
  yield* orchestrationEngine.dispatch({
39698
40383
  type: "thread.activity.append",
39699
- commandId: providerCommandId(input.event, "subagent-message-upsert"),
40384
+ commandId: providerCommandId(input.event, "subagent-message-upsert", commandKey),
39700
40385
  threadId: input.threadId,
39701
40386
  activity: {
39702
40387
  id: subagentMessageActivityId({
@@ -39714,6 +40399,7 @@ const make$3 = effect.Effect.gen(function* () {
39714
40399
  streaming: input.streaming,
39715
40400
  providerThreadId: input.providerThreadId,
39716
40401
  providerItemId: input.providerItemId,
40402
+ ...input.subagentId ? { subagentId: input.subagentId } : {},
39717
40403
  ...input.event.providerRefs ? { providerRefs: input.event.providerRefs } : {}
39718
40404
  },
39719
40405
  turnId: toTurnId$1(input.event.turnId) ?? null,
@@ -39753,6 +40439,16 @@ const make$3 = effect.Effect.gen(function* () {
39753
40439
  streaming: false
39754
40440
  });
39755
40441
  });
40442
+ const bufferedSubagentProviderItemIds = (input) => {
40443
+ const prefix = subagentMessageBufferKey({
40444
+ threadId: input.threadId,
40445
+ providerThreadId: input.providerThreadId,
40446
+ providerItemId: ""
40447
+ });
40448
+ const providerItemIds = /* @__PURE__ */ new Set();
40449
+ for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) providerItemIds.add(key.slice(prefix.length));
40450
+ return [...providerItemIds];
40451
+ };
39756
40452
  const clearSubagentMessageBuffersForThread = (threadId) => effect.Effect.sync(() => {
39757
40453
  const prefix = `${threadId}:`;
39758
40454
  for (const key of subagentMessageTextByKey.keys()) if (key.startsWith(prefix)) subagentMessageTextByKey.delete(key);
@@ -39786,7 +40482,7 @@ const make$3 = effect.Effect.gen(function* () {
39786
40482
  if (!hasRenderableAssistantText(bufferedText)) return false;
39787
40483
  yield* orchestrationEngine.dispatch({
39788
40484
  type: "thread.message.assistant.delta",
39789
- commandId: providerCommandId(input.event, input.commandTag),
40485
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39790
40486
  threadId: input.threadId,
39791
40487
  messageId: input.messageId,
39792
40488
  delta: bufferedText,
@@ -39814,7 +40510,7 @@ const make$3 = effect.Effect.gen(function* () {
39814
40510
  const hasRenderableText = hasRenderableAssistantText(text);
39815
40511
  if (hasRenderableText) yield* orchestrationEngine.dispatch({
39816
40512
  type: "thread.message.assistant.delta",
39817
- commandId: providerCommandId(input.event, input.finalDeltaCommandTag),
40513
+ commandId: providerCommandId(input.event, input.finalDeltaCommandTag, String(input.messageId)),
39818
40514
  threadId: input.threadId,
39819
40515
  messageId: input.messageId,
39820
40516
  delta: text,
@@ -39823,7 +40519,7 @@ const make$3 = effect.Effect.gen(function* () {
39823
40519
  });
39824
40520
  if (input.hasProjectedMessage || hasRenderableText) yield* orchestrationEngine.dispatch({
39825
40521
  type: "thread.message.assistant.complete",
39826
- commandId: providerCommandId(input.event, input.commandTag),
40522
+ commandId: providerCommandId(input.event, input.commandTag, String(input.messageId)),
39827
40523
  threadId: input.threadId,
39828
40524
  messageId: input.messageId,
39829
40525
  ...input.turnId ? { turnId: input.turnId } : {},
@@ -39857,7 +40553,7 @@ const make$3 = effect.Effect.gen(function* () {
39857
40553
  const existingPlan = findProposedPlanById(input.threadProposedPlans, input.planId);
39858
40554
  yield* orchestrationEngine.dispatch({
39859
40555
  type: "thread.proposed-plan.upsert",
39860
- commandId: providerCommandId(input.event, "proposed-plan-upsert"),
40556
+ commandId: providerCommandId(input.event, "proposed-plan-upsert", input.planId),
39861
40557
  threadId: input.threadId,
39862
40558
  proposedPlan: {
39863
40559
  id: input.planId,
@@ -39924,13 +40620,13 @@ const make$3 = effect.Effect.gen(function* () {
39924
40620
  if (!sameId$1(yield* getExpectedProviderTurnIdForThread(threadId), eventTurnId)) return null;
39925
40621
  return yield* getSourceProposedPlanReferenceForPendingTurnStart(threadId);
39926
40622
  });
39927
- const markSourceProposedPlanImplemented = effect.Effect.fn("markSourceProposedPlanImplemented")(function* (sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
40623
+ const markSourceProposedPlanImplemented = effect.Effect.fn("markSourceProposedPlanImplemented")(function* (event, sourceThreadId, sourcePlanId, implementationThreadId, implementedAt) {
39928
40624
  const sourceThread = yield* resolveThreadDetail(sourceThreadId);
39929
40625
  const sourcePlan = sourceThread?.proposedPlans.find((entry) => entry.id === sourcePlanId);
39930
40626
  if (!sourceThread || !sourcePlan || sourcePlan.implementedAt !== null) return;
39931
40627
  yield* orchestrationEngine.dispatch({
39932
40628
  type: "thread.proposed-plan.upsert",
39933
- commandId: require_collectUint8StreamText.CommandId.make(`provider:source-proposed-plan-implemented:${implementationThreadId}:${crypto.randomUUID()}`),
40629
+ commandId: providerCommandId(event, "source-proposed-plan-implemented", `${sourceThreadId}:${sourcePlanId}:${implementationThreadId}`),
39934
40630
  threadId: sourceThread.id,
39935
40631
  proposedPlan: {
39936
40632
  ...sourcePlan,
@@ -39955,6 +40651,12 @@ const make$3 = effect.Effect.gen(function* () {
39955
40651
  const activeTurnId = thread.session?.activeTurnId ?? null;
39956
40652
  const conflictsWithActiveTurn = activeTurnId !== null && eventTurnId !== void 0 && !sameId$1(activeTurnId, eventTurnId);
39957
40653
  const missingTurnForActiveTurn = activeTurnId !== null && eventTurnId === void 0;
40654
+ const existingEventTurn = event.type === "turn.started" && eventTurnId !== void 0 ? yield* projectionTurnRepository.getByTurnId({
40655
+ threadId: thread.id,
40656
+ turnId: eventTurnId
40657
+ }) : effect.Option.none();
40658
+ const eventTurnAlreadyTerminal = effect.Option.isSome(existingEventTurn) && isTerminalProjectionTurnState(existingEventTurn.value.state);
40659
+ const eventTurnAlreadyProjectedWithoutActiveSession = activeTurnId === null && effect.Option.isSome(existingEventTurn);
39958
40660
  if (!isAssistantTextDeltaEvent(event)) yield* flushLiveAssistantDeltaBuffersSafely({ commandTag: "assistant-delta-flush-before-runtime-event" });
39959
40661
  yield* rememberSubagentProviderThreadIds(thread.id, event);
39960
40662
  const eventProviderThreadId = rawPayloadThreadId(event);
@@ -39965,8 +40667,9 @@ const make$3 = effect.Effect.gen(function* () {
39965
40667
  case "session.exited": return true;
39966
40668
  case "session.started":
39967
40669
  case "thread.started": return true;
39968
- case "turn.started": return !conflictsWithActiveTurn;
40670
+ case "turn.started": return !conflictsWithActiveTurn && !eventTurnAlreadyTerminal && !eventTurnAlreadyProjectedWithoutActiveSession;
39969
40671
  case "turn.completed":
40672
+ case "turn.aborted":
39970
40673
  if (conflictsWithActiveTurn || missingTurnForActiveTurn) return false;
39971
40674
  if (activeTurnId !== null && eventTurnId !== void 0) return sameId$1(activeTurnId, eventTurnId);
39972
40675
  return true;
@@ -39974,21 +40677,22 @@ const make$3 = effect.Effect.gen(function* () {
39974
40677
  }
39975
40678
  })();
39976
40679
  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;
40680
+ 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") {
40681
+ const nextActiveTurnId = event.type === "turn.started" ? eventTurnId ?? null : event.type === "turn.completed" || event.type === "turn.aborted" || event.type === "session.exited" ? null : activeTurnId;
39979
40682
  const status = (() => {
39980
40683
  switch (event.type) {
39981
40684
  case "session.state.changed": return orchestrationSessionStatusFromRuntimeState(event.payload.state);
39982
40685
  case "turn.started": return "running";
39983
40686
  case "session.exited": return "stopped";
39984
40687
  case "turn.completed": return normalizeRuntimeTurnState(event.payload.state) === "failed" ? "error" : "ready";
40688
+ case "turn.aborted": return "ready";
39985
40689
  case "session.started":
39986
40690
  case "thread.started": return activeTurnId !== null ? "running" : "ready";
39987
40691
  }
39988
40692
  })();
39989
40693
  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
40694
  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", {
40695
+ 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
40696
  eventId: event.eventId,
39993
40697
  eventType: event.type,
39994
40698
  cause: effect.Cause.pretty(cause)
@@ -40010,11 +40714,30 @@ const make$3 = effect.Effect.gen(function* () {
40010
40714
  },
40011
40715
  createdAt: now
40012
40716
  });
40717
+ if (event.type === "turn.aborted" && eventTurnId !== void 0) yield* orchestrationEngine.dispatch({
40718
+ type: "thread.turn.interrupt",
40719
+ commandId: providerCommandId(event, "thread-turn-interrupt", String(eventTurnId)),
40720
+ threadId: thread.id,
40721
+ turnId: eventTurnId,
40722
+ createdAt: now
40723
+ });
40013
40724
  }
40014
40725
  }
40015
40726
  const assistantDelta = isAssistantTextDeltaEvent(event) ? event.payload.delta : void 0;
40727
+ const subagentDelta = event.type === "subagent.message.delta" ? event.payload.delta : void 0;
40016
40728
  const proposedPlanDelta = event.type === "turn.proposed.delta" ? event.payload.delta : void 0;
40017
- if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
40729
+ if (event.type === "subagent.message.delta" && subagentDelta && subagentDelta.length > 0) {
40730
+ const subagentPayload = event.payload;
40731
+ yield* appendSubagentMessageDelta({
40732
+ event,
40733
+ threadId: thread.id,
40734
+ subagentId: String(subagentPayload.subagentId),
40735
+ providerThreadId: subagentPayload.providerThreadId ?? String(subagentPayload.subagentId),
40736
+ providerItemId: subagentPayload.providerMessageId ?? String(subagentPayload.subagentId),
40737
+ delta: subagentDelta,
40738
+ createdAt: now
40739
+ });
40740
+ } else if (assistantDelta && assistantDelta.length > 0 && isSubagentProviderThread && eventProviderThreadId) yield* appendSubagentMessageDelta({
40018
40741
  event,
40019
40742
  threadId: thread.id,
40020
40743
  providerThreadId: eventProviderThreadId,
@@ -40034,7 +40757,7 @@ const make$3 = effect.Effect.gen(function* () {
40034
40757
  const spillChunk = yield* appendBufferedAssistantText(assistantMessageId, assistantDelta);
40035
40758
  if (spillChunk.length > 0) yield* orchestrationEngine.dispatch({
40036
40759
  type: "thread.message.assistant.delta",
40037
- commandId: providerCommandId(event, "assistant-delta-buffer-spill"),
40760
+ commandId: providerCommandId(event, "assistant-delta-buffer-spill", String(assistantMessageId)),
40038
40761
  threadId: thread.id,
40039
40762
  messageId: assistantMessageId,
40040
40763
  delta: spillChunk,
@@ -40119,6 +40842,26 @@ const make$3 = effect.Effect.gen(function* () {
40119
40842
  ...subagentMessageCompletion.fallbackText !== void 0 ? { fallbackText: subagentMessageCompletion.fallbackText } : {},
40120
40843
  createdAt: now
40121
40844
  });
40845
+ if (event.type === "subagent.completed") {
40846
+ const subagentId = String(event.payload.subagent.subagentId);
40847
+ const providerThreadId = event.payload.subagent.providerThreadId ?? subagentId;
40848
+ const providerItemIds = bufferedSubagentProviderItemIds({
40849
+ threadId: thread.id,
40850
+ providerThreadId
40851
+ });
40852
+ yield* effect.Effect.forEach(providerItemIds.length > 0 ? providerItemIds : [subagentId], (providerItemId) => completeSubagentMessage({
40853
+ event,
40854
+ threadId: thread.id,
40855
+ subagentId,
40856
+ providerThreadId,
40857
+ providerItemId,
40858
+ ...event.payload.summary !== void 0 ? { fallbackText: event.payload.summary } : {},
40859
+ createdAt: now
40860
+ }), {
40861
+ concurrency: 1,
40862
+ discard: true
40863
+ });
40864
+ }
40122
40865
  if (proposedPlanCompletion) {
40123
40866
  const detailedThread = yield* getLoadedThreadDetail();
40124
40867
  yield* finalizeBufferedProposedPlan({
@@ -40131,7 +40874,7 @@ const make$3 = effect.Effect.gen(function* () {
40131
40874
  updatedAt: now
40132
40875
  });
40133
40876
  }
40134
- if (event.type === "turn.completed") {
40877
+ if (event.type === "turn.completed" || event.type === "turn.aborted") {
40135
40878
  const detailedThread = yield* getLoadedThreadDetail();
40136
40879
  const messages = detailedThread?.messages ?? [];
40137
40880
  const proposedPlans = detailedThread?.proposedPlans ?? [];
@@ -40201,7 +40944,7 @@ const make$3 = effect.Effect.gen(function* () {
40201
40944
  const assistantMessageId = require_collectUint8StreamText.MessageId.make(`assistant:${event.itemId ?? event.turnId ?? event.eventId}`);
40202
40945
  yield* orchestrationEngine.dispatch({
40203
40946
  type: "thread.turn.diff.complete",
40204
- commandId: providerCommandId(event, "thread-turn-diff-complete"),
40947
+ commandId: providerCommandId(event, "thread-turn-diff-complete", String(turnId)),
40205
40948
  threadId: thread.id,
40206
40949
  turnId,
40207
40950
  completedAt: now,
@@ -40217,7 +40960,7 @@ const make$3 = effect.Effect.gen(function* () {
40217
40960
  const activities = runtimeEventToActivities(event);
40218
40961
  yield* effect.Effect.forEach(activities, (activity) => orchestrationEngine.dispatch({
40219
40962
  type: "thread.activity.append",
40220
- commandId: providerCommandId(event, "thread-activity-append"),
40963
+ commandId: providerCommandId(event, "thread-activity-append", String(activity.id)),
40221
40964
  threadId: thread.id,
40222
40965
  activity,
40223
40966
  createdAt: activity.createdAt
@@ -40281,6 +41024,7 @@ function mapProviderSessionStatusToOrchestrationStatus(status) {
40281
41024
  }
40282
41025
  const turnStartKeyForEvent = (event) => event.commandId !== null ? `command:${event.commandId}` : `event:${event.eventId}`;
40283
41026
  const serverCommandId$1 = (tag) => require_collectUint8StreamText.CommandId.make(`server:${tag}:${crypto.randomUUID()}`);
41027
+ const isProviderOriginatedCommandId = (commandId) => commandId !== null && String(commandId).startsWith("provider:");
40284
41028
  const HANDLED_TURN_START_KEY_MAX = 1e4;
40285
41029
  const HANDLED_TURN_START_KEY_TTL = effect.Duration.minutes(30);
40286
41030
  const DEFAULT_RUNTIME_MODE = "full-access";
@@ -40632,6 +41376,17 @@ const make$2 = effect.Effect.gen(function* () {
40632
41376
  if (yield* hasHandledTurnStartRecently(turnStartKeyForEvent(event))) return;
40633
41377
  const thread = yield* resolveThread(event.payload.threadId);
40634
41378
  if (!thread) return;
41379
+ if (thread.session?.status === "running" && thread.session.activeTurnId !== null) {
41380
+ yield* appendProviderFailureActivity({
41381
+ threadId: event.payload.threadId,
41382
+ kind: "provider.turn.start.failed",
41383
+ summary: "Provider turn start rejected",
41384
+ 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.`,
41385
+ turnId: null,
41386
+ createdAt: event.payload.createdAt
41387
+ });
41388
+ return;
41389
+ }
40635
41390
  const message = thread.messages.find((entry) => entry.id === event.payload.messageId);
40636
41391
  if (!message || message.role !== "user") {
40637
41392
  yield* appendProviderFailureActivity({
@@ -40702,6 +41457,7 @@ const make$2 = effect.Effect.gen(function* () {
40702
41457
  yield* providerService.sendTurn(sendTurnRequest.value).pipe(effect.Effect.catchCause(recoverTurnStartFailure), effect.Effect.forkScoped);
40703
41458
  });
40704
41459
  const processTurnInterruptRequested = effect.Effect.fn("processTurnInterruptRequested")(function* (event) {
41460
+ if (isProviderOriginatedCommandId(event.commandId)) return;
40705
41461
  const thread = yield* resolveThread(event.payload.threadId);
40706
41462
  if (!thread) return;
40707
41463
  if (!(thread.session && thread.session.status !== "stopped")) return yield* appendProviderFailureActivity({
@@ -46998,6 +47754,10 @@ const decideOrchestrationCommand = effect.Effect.fn("decideOrchestrationCommand"
46998
47754
  command,
46999
47755
  threadId: command.threadId
47000
47756
  });
47757
+ if (targetThread.session?.status === "running" && targetThread.session.activeTurnId !== null) return yield* new OrchestrationCommandInvariantError({
47758
+ commandType: command.type,
47759
+ detail: `Thread '${command.threadId}' already has active turn '${targetThread.session.activeTurnId}' and cannot start another turn until it finishes.`
47760
+ });
47001
47761
  const sourceProposedPlan = command.sourceProposedPlan;
47002
47762
  const sourceThread = sourceProposedPlan ? yield* requireThread({
47003
47763
  readModel,
@@ -50955,7 +51715,7 @@ const OrchestrationProjectionSnapshotQueryLive = effect.Layer.effect(ProjectionS
50955
51715
  //#region src/orchestration/runtimeLayer.ts
50956
51716
  const OrchestrationEventInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationEventStoreLive, OrchestrationCommandReceiptRepositoryLive);
50957
51717
  const OrchestrationProjectionPipelineLayerLive = OrchestrationProjectionPipelineLive.pipe(effect.Layer.provide(OrchestrationEventStoreLive));
50958
- const OrchestrationInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
51718
+ const OrchestrationInfrastructureLayerLive = effect.Layer.mergeAll(OrchestrationProjectionSnapshotQueryLive, StatisticsQueryLive, OrchestrationEventInfrastructureLayerLive, OrchestrationProjectionPipelineLayerLive);
50959
51719
  const OrchestrationLayerLive = effect.Layer.mergeAll(OrchestrationInfrastructureLayerLive, OrchestrationEngineLive.pipe(effect.Layer.provide(OrchestrationInfrastructureLayerLive)));
50960
51720
  //#endregion
50961
51721
  //#region src/serverRuntimeState.ts
@@ -51762,6 +52522,7 @@ exports.ServerRequest__DynamicToolCallParams = ServerRequest__DynamicToolCallPar
51762
52522
  exports.ServerRequest__ExecCommandApprovalParams = ServerRequest__ExecCommandApprovalParams;
51763
52523
  exports.ServerRequest__FileChangeRequestApprovalParams = ServerRequest__FileChangeRequestApprovalParams;
51764
52524
  exports.ServerRequest__ToolRequestUserInputParams = ServerRequest__ToolRequestUserInputParams;
52525
+ exports.ServerSettingsService = ServerSettingsService;
51765
52526
  exports.ToolRequestUserInputParams = ToolRequestUserInputParams;
51766
52527
  exports.ToolRequestUserInputResponse = ToolRequestUserInputResponse;
51767
52528
  exports.V2AccountRateLimitsUpdatedNotification = V2AccountRateLimitsUpdatedNotification;