specrails-desktop 2.61.0 → 2.63.0

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 (230) hide show
  1. package/cli/dist/specrails-desktop.js +39 -5
  2. package/client/dist/assets/ActivityFeedPage-Bn2MpG5M.js +1 -0
  3. package/client/dist/assets/{AgentBrowserCapture-CkNWDZT3.js → AgentBrowserCapture-CHlXWdXq.js} +1 -1
  4. package/client/dist/assets/AgentModeAnalyticsPane-yj-YCgrK.js +2 -0
  5. package/client/dist/assets/AgentModeCodePane-Xczcyghb.js +2 -0
  6. package/client/dist/assets/{AgentModeJobsPane-U1J6PaBo.js → AgentModeJobsPane-DtOW01ln.js} +1 -1
  7. package/client/dist/assets/AgentsPage-BMfJ4mzU.js +118 -0
  8. package/client/dist/assets/AnalyticsPage-CGn5cgtc.js +1 -0
  9. package/client/dist/assets/BarChart-BDMqVFJy.js +55 -0
  10. package/client/dist/assets/CodePage-C8NIginr.js +3 -0
  11. package/client/dist/assets/DesktopAnalyticsPage-C87r4y26.js +1 -0
  12. package/client/dist/assets/{DocsDialog-CSlMPemn.js → DocsDialog-Z50a_KPv.js} +1 -1
  13. package/client/dist/assets/{DocsPage-BKlYAvWq.js → DocsPage-DIArbEPl.js} +1 -1
  14. package/client/dist/assets/{ExportDropdown-CweP_1GP.js → ExportDropdown-CJdCqYd7.js} +1 -1
  15. package/client/dist/assets/IntegrationsPage-EAipiDWm.js +1 -0
  16. package/client/dist/assets/InteractiveJobComposer-BxLh4me-.js +19 -0
  17. package/client/dist/assets/{JobDetailModal-DYyGq1Vp.js → JobDetailModal-C7dWoRu6.js} +1 -1
  18. package/client/dist/assets/JobDetailPage-Ilu294WS.js +1 -0
  19. package/client/dist/assets/JobsPage-fPq1hefb.js +1 -0
  20. package/client/dist/assets/LoopBuilderPage-5M7IypEP.js +1 -0
  21. package/client/dist/assets/{LoopPreviewModal-DkLRfsYX.js → LoopPreviewModal-ChI3NQC7.js} +1 -1
  22. package/client/dist/assets/{LoopWindowSurface-CuACkXFV.js → LoopWindowSurface-D1WB9i-9.js} +1 -1
  23. package/client/dist/assets/LoopsPage-CCwc0Biu.js +2 -0
  24. package/client/dist/assets/MinimizedChatsContext-eH_KaAZ1.js +1 -0
  25. package/client/dist/assets/PluginsPage-DfDfEfbW.js +2 -0
  26. package/client/dist/assets/ProjectSettingsDialog-9NHnG8JQ.js +1 -0
  27. package/client/dist/assets/RepositoryDeliveries-DgzNnJmn.js +5 -0
  28. package/client/dist/assets/{RepositoryScopeSelector-CeCr6Jz-.js → RepositoryScopeSelector-BSgUqk_u.js} +1 -1
  29. package/client/dist/assets/ReviewPacketPage-D34bGpSA.js +4 -0
  30. package/client/dist/assets/{TemplatePreviewModal-B8yd1AGU.js → TemplatePreviewModal-CcfSxWQ7.js} +1 -1
  31. package/client/dist/assets/TicketDetailModalContext-BoafEcm4.js +16 -0
  32. package/client/dist/assets/Trans-CVSryAL1.js +1 -0
  33. package/client/dist/assets/agent-5bSixVzz.js +1 -0
  34. package/client/dist/assets/agent-BR4EHfhq.js +1 -0
  35. package/client/dist/assets/agent-C0IKkJP8.js +1 -0
  36. package/client/dist/assets/agent-C7li9ayC.js +1 -0
  37. package/client/dist/assets/agent-D0dRmEen.js +1 -0
  38. package/client/dist/assets/{agent-BjttZLIl.js → agent-DZVGHm7H.js} +1 -1
  39. package/client/dist/assets/agent-Dip4h2YE.js +1 -0
  40. package/client/dist/assets/agent-Pp-P-n1p.js +1 -0
  41. package/client/dist/assets/ban-wqfdzoXt.js +1 -0
  42. package/client/dist/assets/{button-D1xExSTF.js → button-4FS1ZDTp.js} +1 -1
  43. package/client/dist/assets/chevron-up-6EFqEHBj.js +1 -0
  44. package/client/dist/assets/chunk-OB3PAWPO-B1EMk45A.js +3 -0
  45. package/client/dist/assets/circle-x-D2ttKotB.js +1 -0
  46. package/client/dist/assets/createLucideIcon-DtODIU64.js +1 -0
  47. package/client/dist/assets/{dialog-CMgps2MY.js → dialog-DtdR05v1.js} +1 -1
  48. package/client/dist/assets/download-C9wP_42h.js +1 -0
  49. package/client/dist/assets/external-link-CZ6Q-v1-.js +1 -0
  50. package/client/dist/assets/file-text-C0blZHLh.js +1 -0
  51. package/client/dist/assets/{formatDistanceToNow-DZ3VWsHq.js → formatDistanceToNow-NVM6rK3i.js} +1 -1
  52. package/client/dist/assets/{getTimezoneOffsetInMilliseconds-9JiGlFrx.js → getTimezoneOffsetInMilliseconds-ZHg7WpdH.js} +1 -1
  53. package/client/dist/assets/git-branch-yf6LE1Bw.js +1 -0
  54. package/client/dist/assets/index-Culan6y7.js +83 -0
  55. package/client/dist/assets/index-D1v33giW.css +2 -0
  56. package/client/dist/assets/jira-api-Bl8xEIpk.js +1 -0
  57. package/client/dist/assets/jsx-runtime-BbYvbtY8.js +1 -0
  58. package/client/dist/assets/layers-BCLTgHXO.js +1 -0
  59. package/client/dist/assets/{lib-C7Mhm-gT.js → lib-CQJT8S6Q.js} +1 -1
  60. package/client/dist/assets/{lib-CGdyxIWg.js → lib-DWJfei6y.js} +1 -1
  61. package/client/dist/assets/loader-circle-BqV9xYGf.js +1 -0
  62. package/client/dist/assets/{loop-layout-BF8xMiNv.js → loop-layout-CyXj9223.js} +1 -1
  63. package/client/dist/assets/pencil-CxrRbDwb.js +1 -0
  64. package/client/dist/assets/plus-fHDdjBk1.js +1 -0
  65. package/client/dist/assets/{project-repositories-CadLZOnr.js → project-repositories-CvJf1a6H.js} +1 -1
  66. package/client/dist/assets/react-dom-DDD8FYaU.js +1 -0
  67. package/client/dist/assets/rotate-ccw-DalJAQQX.js +1 -0
  68. package/client/dist/assets/save-LmPSfsvZ.js +1 -0
  69. package/client/dist/assets/search-BDVp3fkH.js +1 -0
  70. package/client/dist/assets/{settings-Dn-pXukr.js → settings-BO2N-oPf.js} +1 -1
  71. package/client/dist/assets/{settings-ByVE7jdZ.js → settings-BQChAs_w.js} +1 -1
  72. package/client/dist/assets/{settings-BBj_kZaD.js → settings-BzmYlcQB.js} +1 -1
  73. package/client/dist/assets/{settings-COlH_P33.js → settings-CGjXb6TX.js} +1 -1
  74. package/client/dist/assets/{settings-D77mC_Lb.js → settings-CMxEWY46.js} +1 -1
  75. package/client/dist/assets/{settings-DZKy7MqN.js → settings-C_dRSgI2.js} +1 -1
  76. package/client/dist/assets/{settings-B5Hy_Kol.js → settings-Hw7av-B1.js} +1 -1
  77. package/client/dist/assets/settings-nryN6wuS.js +1 -0
  78. package/client/dist/assets/sparkles-CCfUdFGY.js +1 -0
  79. package/client/dist/assets/spending-BfjSJjBd.js +1 -0
  80. package/client/dist/assets/square-D0JTbRSR.js +1 -0
  81. package/client/dist/assets/trash-D8zYUmTg.js +1 -0
  82. package/client/dist/assets/triangle-alert-Cxpn8bqr.js +1 -0
  83. package/client/dist/assets/upload-CCHvUkRs.js +1 -0
  84. package/client/dist/assets/{useDesktop-DKNbOF0u.js → useDesktop-C5JVBeur.js} +1 -1
  85. package/client/dist/assets/{useRuntimeRuns-D3YMw9qx.js → useRuntimeRuns-DohBmeg8.js} +1 -1
  86. package/client/dist/assets/useSharedWebSocket-CNOvmgTr.js +2 -0
  87. package/client/dist/assets/{value-s-gzBeyF.js → value-DtOOK0wQ.js} +1 -1
  88. package/client/dist/assets/x-Bu7X_eR2.js +1 -0
  89. package/client/dist/assets/zap-bugHyWzQ.js +1 -0
  90. package/client/dist/index.html +44 -41
  91. package/docs/ci-cd.md +8 -1
  92. package/docs/customizing.md +1 -0
  93. package/docs/getting-started.md +1 -1
  94. package/docs/guide/de/integrations/6-agent-chat.md +11 -0
  95. package/docs/guide/de/settings/5-project-environment.md +27 -0
  96. package/docs/guide/en/integrations/6-agent-chat.md +11 -0
  97. package/docs/guide/en/settings/5-project-environment.md +27 -0
  98. package/docs/guide/es/integrations/6-agent-chat.md +11 -0
  99. package/docs/guide/es/settings/5-project-environment.md +27 -0
  100. package/docs/guide/fr/integrations/6-agent-chat.md +11 -0
  101. package/docs/guide/fr/settings/5-project-environment.md +27 -0
  102. package/docs/guide/it/integrations/6-agent-chat.md +11 -0
  103. package/docs/guide/it/settings/5-project-environment.md +27 -0
  104. package/docs/guide/ja/integrations/6-agent-chat.md +11 -0
  105. package/docs/guide/ja/settings/5-project-environment.md +27 -0
  106. package/docs/guide/pt/integrations/6-agent-chat.md +11 -0
  107. package/docs/guide/pt/settings/5-project-environment.md +27 -0
  108. package/docs/guide/zh/integrations/6-agent-chat.md +11 -0
  109. package/docs/guide/zh/settings/5-project-environment.md +27 -0
  110. package/docs/internals/README.md +1 -0
  111. package/docs/internals/agent-sessions.md +157 -0
  112. package/docs/internals/api-reference.md +35 -4
  113. package/docs/internals/configuration.md +80 -5
  114. package/docs/internals/core-runtime-updates.md +10 -3
  115. package/docs/internals/operations-runbook.md +11 -0
  116. package/docs/internals/programmatic-agent-runtime.md +1 -1
  117. package/docs/internals/safe-pr-review-flow.md +15 -0
  118. package/docs/internals/source-map.md +42 -1
  119. package/docs/mcp.md +1 -1
  120. package/docs/platforms/macos.md +2 -0
  121. package/docs/platforms/windows-parity.md +4 -4
  122. package/docs/platforms/windows.md +9 -9
  123. package/local-runner/dist/specrails-local-runner.js +1 -1
  124. package/mcp-bridge/dist/specrails-mcp.js +82 -15
  125. package/package.json +8 -9
  126. package/server/dist/agent-mcp-config.js +41 -0
  127. package/server/dist/core-compat.js +2 -0
  128. package/server/dist/desktop-db.js +101 -4
  129. package/server/dist/desktop-router.js +31 -2
  130. package/server/dist/index.js +58 -0
  131. package/server/dist/mcp/tools/app-settings.js +11 -0
  132. package/server/dist/mcp/tools/env.js +38 -3
  133. package/server/dist/mcp/tools/mission.js +45 -2
  134. package/server/dist/mcp/tools/specs.js +2 -2
  135. package/server/dist/modules/accounting/runtime/result-event.js +9 -1
  136. package/server/dist/modules/agent-runtime/runtime/agent-runtime-controls.js +25 -0
  137. package/server/dist/modules/agent-sessions/adapters/host-process.js +73 -0
  138. package/server/dist/modules/agent-sessions/adapters/http.js +31 -0
  139. package/server/dist/modules/agent-sessions/adapters/rpc-client.js +147 -0
  140. package/server/dist/modules/agent-sessions/application/session-event-pump.js +99 -0
  141. package/server/dist/modules/agent-sessions/domain/availability.js +25 -0
  142. package/server/dist/modules/agent-sessions/domain/errors.js +23 -0
  143. package/server/dist/modules/agent-sessions/domain/host-state.js +80 -0
  144. package/server/dist/modules/agent-sessions/domain/projection.js +147 -0
  145. package/server/dist/modules/agent-sessions/domain/protocol.js +59 -0
  146. package/server/dist/modules/agent-sessions/domain/subagent-policy.js +34 -0
  147. package/server/dist/modules/agent-sessions/index.js +31 -0
  148. package/server/dist/modules/agent-sessions/ports.js +2 -0
  149. package/server/dist/modules/agent-sessions/runtime/core-sessions-availability.js +43 -0
  150. package/server/dist/modules/agent-sessions/runtime/session-host-registry.js +226 -0
  151. package/server/dist/modules/agents/runtime/agent-store.js +3 -1
  152. package/server/dist/modules/execution/runtime/queue-manager.js +8 -0
  153. package/server/dist/modules/loops/runtime/loop-executors.js +1 -0
  154. package/server/dist/modules/loops/runtime/loop-implement-recipe.js +15 -4
  155. package/server/dist/modules/loops/runtime/loop-run-manager.js +11 -0
  156. package/server/dist/modules/missions/runtime/agent-chat-manager.js +137 -40
  157. package/server/dist/modules/missions/runtime/agent-chat-router.js +68 -0
  158. package/server/dist/modules/missions/runtime/agent-operator-prompt.js +8 -9
  159. package/server/dist/modules/missions/runtime/agent-session-store.js +133 -0
  160. package/server/dist/modules/missions/runtime/core-session-runner.js +204 -0
  161. package/server/dist/modules/missions/runtime/mission-core-sessions.js +286 -0
  162. package/server/dist/modules/missions/runtime/mission-session-projector.js +168 -0
  163. package/server/dist/modules/project-settings/adapters/http.js +38 -2
  164. package/server/dist/modules/project-settings/adapters/sqlite.js +30 -2
  165. package/server/dist/modules/project-settings/domain.js +24 -0
  166. package/server/dist/modules/project-settings/index.js +2 -1
  167. package/server/dist/modules/specs/runtime/spec-models.js +1 -1
  168. package/server/dist/path-resolver.js +135 -20
  169. package/server/dist/plugins/codex-spawn.js +15 -7
  170. package/server/dist/project-env.js +265 -17
  171. package/server/dist/project-registry.js +46 -0
  172. package/server/dist/project-router-settings.js +24 -1
  173. package/server/dist/providers/claude-adapter.js +6 -1
  174. package/server/dist/worktree-node-modules.js +95 -1
  175. package/client/dist/assets/ActivityFeedPage-Wi5XNgoG.js +0 -1
  176. package/client/dist/assets/AgentModeAnalyticsPane-CXz2j-Bt.js +0 -2
  177. package/client/dist/assets/AgentModeCodePane-DnsBPtpS.js +0 -2
  178. package/client/dist/assets/AgentsPage-CXrqu5vo.js +0 -118
  179. package/client/dist/assets/AnalyticsPage-Cvtt-LOO.js +0 -1
  180. package/client/dist/assets/BarChart-nBOMNfiy.js +0 -33
  181. package/client/dist/assets/CodePage-D35o-MFO.js +0 -3
  182. package/client/dist/assets/DesktopAnalyticsPage-CsDFy5rp.js +0 -1
  183. package/client/dist/assets/IntegrationsPage-Bhy58lnQ.js +0 -1
  184. package/client/dist/assets/InteractiveJobComposer-CFKvbQeJ.js +0 -19
  185. package/client/dist/assets/JobDetailPage-C6gpdYvj.js +0 -1
  186. package/client/dist/assets/JobsPage-CgS0pnKd.js +0 -1
  187. package/client/dist/assets/LoopBuilderPage-jUp20DSZ.js +0 -1
  188. package/client/dist/assets/LoopsPage-ClSrWzDd.js +0 -2
  189. package/client/dist/assets/MinimizedChatsContext-CroAs6Kh.js +0 -1
  190. package/client/dist/assets/PluginsPage-nCKQOwRL.js +0 -2
  191. package/client/dist/assets/ProjectSettingsDialog-BVDYwllU.js +0 -1
  192. package/client/dist/assets/RepositoryDeliveries-DJCm1pZr.js +0 -5
  193. package/client/dist/assets/ReviewPacketPage-kTErrEjx.js +0 -4
  194. package/client/dist/assets/TicketDetailModalContext-YBJX3RmP.js +0 -16
  195. package/client/dist/assets/Trans-D5eOjfvG.js +0 -1
  196. package/client/dist/assets/agent--8QAfoXL.js +0 -1
  197. package/client/dist/assets/agent-3HxXkIWX.js +0 -1
  198. package/client/dist/assets/agent-CpFCz32T.js +0 -1
  199. package/client/dist/assets/agent-DVnw6ybO.js +0 -1
  200. package/client/dist/assets/agent-Z5s_GTP0.js +0 -1
  201. package/client/dist/assets/agent-hv57-zyG.js +0 -1
  202. package/client/dist/assets/agent-xGrZ_aN1.js +0 -1
  203. package/client/dist/assets/chunk-KS7C4IRE-DEKu087k.js +0 -3
  204. package/client/dist/assets/circle-x-CMYsN4ZH.js +0 -1
  205. package/client/dist/assets/createLucideIcon-Bjbbp_KS.js +0 -1
  206. package/client/dist/assets/download-CFHyyHcw.js +0 -1
  207. package/client/dist/assets/external-link-D6XxC_Cu.js +0 -1
  208. package/client/dist/assets/file-text-BcLCfcWZ.js +0 -1
  209. package/client/dist/assets/git-branch-BRM0F5GN.js +0 -1
  210. package/client/dist/assets/index-Bfunvi6w.css +0 -2
  211. package/client/dist/assets/index-lTGJJTuk.js +0 -83
  212. package/client/dist/assets/jira-api-Bp0zXwjg.js +0 -1
  213. package/client/dist/assets/jsx-runtime-zu2_FqZY.js +0 -1
  214. package/client/dist/assets/loader-circle-DcygOQpC.js +0 -1
  215. package/client/dist/assets/pencil-C9RkrIPZ.js +0 -1
  216. package/client/dist/assets/plus-CsnjfkmX.js +0 -1
  217. package/client/dist/assets/react-dom-DiWzBDP9.js +0 -1
  218. package/client/dist/assets/rotate-ccw-Wh4id13q.js +0 -1
  219. package/client/dist/assets/save-OsUqVn99.js +0 -1
  220. package/client/dist/assets/search-Cq_gE3Ik.js +0 -1
  221. package/client/dist/assets/settings-Dbyd6gmj.js +0 -1
  222. package/client/dist/assets/sparkles-DrY8hb0X.js +0 -1
  223. package/client/dist/assets/spending-DQDNtM8q.js +0 -1
  224. package/client/dist/assets/square-Ci-PFcFe.js +0 -1
  225. package/client/dist/assets/trash-2-BOU3WdZZ.js +0 -1
  226. package/client/dist/assets/triangle-alert-H9T4jAmh.js +0 -1
  227. package/client/dist/assets/upload-DYMjAQ6M.js +0 -1
  228. package/client/dist/assets/useSharedWebSocket-DaWuiO0S.js +0 -2
  229. package/client/dist/assets/x-CBMI9ejn.js +0 -1
  230. package/client/dist/assets/zap-CUOlvtT1.js +0 -1
@@ -1,37 +1,237 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PROJECT_ENV_FAILURE_TTL_MS = exports.PROJECT_ENV_SUCCESS_TTL_MS = void 0;
4
+ exports.refreshProjectEnv = refreshProjectEnv;
5
+ exports.ensureProjectEnvFresh = ensureProjectEnvFresh;
6
+ exports.startProjectEnvWarmup = startProjectEnvWarmup;
7
+ exports.stopProjectEnvWarmup = stopProjectEnvWarmup;
3
8
  exports.resolveWorktreeEnvPassthrough = resolveWorktreeEnvPassthrough;
4
9
  exports.applyWorktreeEnvPassthrough = applyWorktreeEnvPassthrough;
10
+ exports.getProjectEnvStatus = getProjectEnvStatus;
11
+ exports.worktreeEnvPassthroughWarning = worktreeEnvPassthroughWarning;
5
12
  const db_1 = require("./db");
6
13
  const path_resolver_1 = require("./path-resolver");
7
- // Values stay in the owning project's memory, never process.env or SQLite.
8
- // Both successful and missing lookups expire so profiles can recover/rotate.
9
- const LOGIN_SHELL_CACHE_MS = 30_000;
10
- const shellValues = new WeakMap();
14
+ /** Successful recoveries stay valid for 10 minutes; the background refresh
15
+ * rotates them well inside that window. */
16
+ exports.PROJECT_ENV_SUCCESS_TTL_MS = 10 * 60_000;
17
+ /** Failed or empty lookups are retried after 30 seconds. */
18
+ exports.PROJECT_ENV_FAILURE_TTL_MS = 30_000;
19
+ /** The background refresh runs this long before a success expires. */
20
+ const PROJECT_ENV_REFRESH_LEAD_MS = 60_000;
21
+ const states = new WeakMap();
22
+ function shellIdentity() {
23
+ return JSON.stringify([process.env.SHELL, process.env.HOME, process.env.ZDOTDIR]);
24
+ }
25
+ function stateFor(db) {
26
+ const identity = shellIdentity();
27
+ let state = states.get(db);
28
+ if (!state) {
29
+ state = { identity, records: new Map(), values: new Map(), inflight: null, queued: null, timer: null, active: false };
30
+ states.set(db, state);
31
+ }
32
+ else if (state.identity !== identity) {
33
+ state.identity = identity;
34
+ state.records.clear();
35
+ state.values.clear();
36
+ }
37
+ return state;
38
+ }
39
+ function configuredNames(db) {
40
+ try {
41
+ return (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
42
+ }
43
+ catch {
44
+ // A closed/unloaded project connection has no configured names.
45
+ return [];
46
+ }
47
+ }
48
+ function missingNames(names) {
49
+ return names.filter(name => !process.env[name]);
50
+ }
51
+ /** Drop records for names no longer configured or now inherited. */
52
+ function prune(state, names) {
53
+ for (const name of [...state.records.keys()]) {
54
+ if (!names.includes(name) || process.env[name]) {
55
+ state.records.delete(name);
56
+ state.values.delete(name);
57
+ }
58
+ }
59
+ }
60
+ function isValid(record, now) {
61
+ return Boolean(record && now < record.expiresAt);
62
+ }
63
+ function statusFromProbe(probe, value) {
64
+ if (value)
65
+ return 'recovered';
66
+ if (probe.status === 'timeout')
67
+ return 'probe-timeout';
68
+ if (probe.status === 'failed')
69
+ return 'probe-failed';
70
+ // `ok` without a value, or `skipped` (Windows / no login-shell recovery).
71
+ return 'not-defined';
72
+ }
73
+ function recordProbe(state, probe, names, now) {
74
+ for (const name of names) {
75
+ const value = probe.values[name];
76
+ const status = statusFromProbe(probe, value);
77
+ const previous = state.records.get(name);
78
+ // A transient probe failure never discards a still-valid recovered value.
79
+ if (status !== 'recovered' && previous?.status === 'recovered' && isValid(previous, now) && probe.status !== 'ok')
80
+ continue;
81
+ state.records.set(name, {
82
+ status,
83
+ shell: probe.shell,
84
+ checkedAt: now,
85
+ expiresAt: now + (status === 'recovered' ? exports.PROJECT_ENV_SUCCESS_TTL_MS : exports.PROJECT_ENV_FAILURE_TTL_MS),
86
+ exitCode: probe.exitCode,
87
+ });
88
+ if (value)
89
+ state.values.set(name, value);
90
+ else
91
+ state.values.delete(name);
92
+ }
93
+ }
94
+ function scheduleRefresh(db, state) {
95
+ if (state.timer) {
96
+ clearTimeout(state.timer);
97
+ state.timer = null;
98
+ }
99
+ if (!state.active || states.get(db) !== state)
100
+ return;
101
+ let earliest = Infinity;
102
+ for (const record of state.records.values()) {
103
+ if (record.status === 'recovered')
104
+ earliest = Math.min(earliest, record.expiresAt);
105
+ }
106
+ if (earliest === Infinity)
107
+ return;
108
+ const now = Date.now();
109
+ const at = Math.max(now + exports.PROJECT_ENV_FAILURE_TTL_MS, earliest - PROJECT_ENV_REFRESH_LEAD_MS);
110
+ state.timer = setTimeout(() => {
111
+ state.timer = null;
112
+ if (state.active && states.get(db) === state)
113
+ void refreshProjectEnv(db);
114
+ }, at - now);
115
+ state.timer.unref?.();
116
+ }
117
+ /** Probe the login shell asynchronously for every configured name missing from
118
+ * the process environment and update this project's records. De-duplicated
119
+ * while a probe is in flight: a call during a probe resolves after one more
120
+ * probe that starts when the current one ends, so a settings change or an
121
+ * explicit recheck is never answered with stale results. Never rejects. */
122
+ function refreshProjectEnv(db) {
123
+ const state = stateFor(db);
124
+ if (state.inflight) {
125
+ // A request during a probe (settings change, recheck) gets a fresh probe
126
+ // after the current one; concurrent requests share that single follow-up.
127
+ if (!state.queued) {
128
+ state.queued = state.inflight.then(() => {
129
+ state.queued = null;
130
+ return refreshProjectEnv(db);
131
+ });
132
+ }
133
+ return state.queued;
134
+ }
135
+ const names = configuredNames(db);
136
+ prune(state, names);
137
+ const missing = missingNames(names);
138
+ if (missing.length === 0) {
139
+ scheduleRefresh(db, state);
140
+ return Promise.resolve();
141
+ }
142
+ const run = (0, path_resolver_1.readEnvFromLoginShell)(missing, { timeoutMs: (0, path_resolver_1.resolveLoginShellProbeTimeoutMs)() })
143
+ .then(probe => {
144
+ if (states.get(db) !== state)
145
+ return;
146
+ const current = configuredNames(db);
147
+ prune(state, current);
148
+ // Only names still configured and still missing are recorded.
149
+ const stillMissing = missingNames(current);
150
+ recordProbe(state, probe, missing.filter(name => stillMissing.includes(name)), Date.now());
151
+ })
152
+ .catch(() => { })
153
+ .finally(() => {
154
+ state.inflight = null;
155
+ scheduleRefresh(db, state);
156
+ });
157
+ state.inflight = run;
158
+ return run;
159
+ }
160
+ /** Probe only when a configured missing name has no valid record yet. */
161
+ function ensureProjectEnvFresh(db) {
162
+ const state = stateFor(db);
163
+ if (state.inflight)
164
+ return state.inflight;
165
+ const names = configuredNames(db);
166
+ prune(state, names);
167
+ const now = Date.now();
168
+ const stale = missingNames(names).some(name => !isValid(state.records.get(name), now));
169
+ return stale ? refreshProjectEnv(db) : Promise.resolve();
170
+ }
171
+ /** Start background warm-up for a loaded project: probe now, then refresh
172
+ * before successful results expire, until `stopProjectEnvWarmup`. */
173
+ function startProjectEnvWarmup(db) {
174
+ const state = stateFor(db);
175
+ state.active = true;
176
+ void refreshProjectEnv(db);
177
+ }
178
+ /** Stop the background refresh and forget this project's recovered values. */
179
+ function stopProjectEnvWarmup(db) {
180
+ const state = states.get(db);
181
+ if (!state)
182
+ return;
183
+ state.active = false;
184
+ if (state.timer) {
185
+ clearTimeout(state.timer);
186
+ state.timer = null;
187
+ }
188
+ states.delete(db);
189
+ }
11
190
  function recoverProjectEnv(db, names) {
12
- const missing = names.filter(name => !process.env[name]);
13
- if (!missing.length) {
14
- shellValues.delete(db);
191
+ const state = stateFor(db);
192
+ prune(state, names);
193
+ const missing = missingNames(names);
194
+ if (!missing.length)
15
195
  return {};
196
+ const now = Date.now();
197
+ // Cold fallback: a name with no record (or an expired success) gets the
198
+ // short synchronous probe. Expired failures are retried asynchronously.
199
+ const cold = missing.filter(name => {
200
+ const record = state.records.get(name);
201
+ return !record || (record.status === 'recovered' && !isValid(record, now));
202
+ });
203
+ let retry = missing.some(name => {
204
+ const record = state.records.get(name);
205
+ return record !== undefined && record.status !== 'recovered' && !isValid(record, now);
206
+ });
207
+ if (cold.length) {
208
+ const probe = (0, path_resolver_1.readEnvFromLoginShellSyncDetailed)(cold);
209
+ recordProbe(state, probe, cold, now);
210
+ // The async probe has a larger budget than the cold sync one.
211
+ if (probe.status === 'timeout' || probe.status === 'failed')
212
+ retry = true;
213
+ }
214
+ if (retry)
215
+ void refreshProjectEnv(db);
216
+ const out = {};
217
+ for (const name of missing) {
218
+ const value = state.values.get(name);
219
+ if (value && isValid(state.records.get(name), now))
220
+ out[name] = value;
16
221
  }
17
- const identity = JSON.stringify([names, missing, process.env.SHELL, process.env.HOME, process.env.ZDOTDIR]);
18
- const cached = shellValues.get(db);
19
- if (cached?.identity === identity && Date.now() < cached.expiresAt)
20
- return cached.env;
21
- const env = (0, path_resolver_1.readEnvFromLoginShellSync)(missing);
22
- shellValues.set(db, { identity, expiresAt: Date.now() + LOGIN_SHELL_CACHE_MS, env });
23
- return env;
222
+ return out;
24
223
  }
25
224
  /** Resolve the per-project env passthrough overlay for a spawn.
26
225
  *
27
226
  * The project setting stores names only. Values are read from `sourceEnv` at
28
227
  * spawn time so credentials can rotate without touching SQLite. When the
29
- * source is `process.env`, missing configured names get bounded login-shell
30
- * recovery with a short, project-owned cache. Unresolved names remain absent.
228
+ * source is `process.env`, missing configured names come from the project's
229
+ * warm login-shell cache; the short synchronous probe runs only when cold.
230
+ * Unresolved names remain absent.
31
231
  */
32
232
  function resolveWorktreeEnvPassthrough(db, sourceEnv = process.env) {
33
233
  const out = {};
34
- const names = (0, db_1.getProjectSettings)(db).worktreeEnvPassthrough;
234
+ const names = configuredNames(db);
35
235
  const recovered = sourceEnv === process.env ? recoverProjectEnv(db, names) : {};
36
236
  for (const name of names) {
37
237
  const value = sourceEnv[name] || recovered[name] || sourceEnv[name];
@@ -44,3 +244,51 @@ function applyWorktreeEnvPassthrough(db, env, sourceEnv = process.env) {
44
244
  const passthrough = resolveWorktreeEnvPassthrough(db, sourceEnv);
45
245
  return Object.keys(passthrough).length === 0 ? env : { ...env, ...passthrough };
46
246
  }
247
+ /** Value-free status of every configured name for this project. */
248
+ function getProjectEnvStatus(db) {
249
+ const state = stateFor(db);
250
+ const names = configuredNames(db);
251
+ prune(state, names);
252
+ const nowIso = new Date(Date.now()).toISOString();
253
+ return {
254
+ loginShellRecovery: process.platform !== 'win32',
255
+ timeoutMs: (0, path_resolver_1.resolveLoginShellProbeTimeoutMs)(),
256
+ checking: state.inflight !== null,
257
+ names: names.map((name) => {
258
+ if (process.env[name])
259
+ return { name, status: 'inherited', shell: null, checkedAt: nowIso, exitCode: null };
260
+ const record = state.records.get(name);
261
+ if (!record)
262
+ return { name, status: 'pending', shell: null, checkedAt: null, exitCode: null };
263
+ return {
264
+ name,
265
+ status: record.status,
266
+ shell: record.shell,
267
+ checkedAt: new Date(record.checkedAt).toISOString(),
268
+ exitCode: record.exitCode,
269
+ };
270
+ }),
271
+ };
272
+ }
273
+ /** One aggregated, value-free run-log line naming every configured name that
274
+ * is absent from `env`, or null when all configured names resolved. */
275
+ function worktreeEnvPassthroughWarning(db, env) {
276
+ const names = configuredNames(db);
277
+ const unresolved = names.filter(name => !env[name]);
278
+ if (!unresolved.length)
279
+ return null;
280
+ const state = states.get(db);
281
+ const now = Date.now();
282
+ const parts = unresolved.map(name => {
283
+ const record = state?.records.get(name);
284
+ // An expired success no longer counts as recovered; failures keep their cause.
285
+ const status = !record ? 'not-defined' : record.status === 'recovered' && now >= record.expiresAt ? 'not-defined' : record.status;
286
+ return { name, status };
287
+ });
288
+ const hint = process.platform === 'win32'
289
+ ? 'set it in the environment Specrails starts with'
290
+ : 'configure it in the login shell or launch from a terminal';
291
+ if (parts.length === 1)
292
+ return `[environment] ${parts[0].name} not available (${parts[0].status}); ${hint}`;
293
+ return `[environment] ${parts.map(p => `${p.name} (${p.status})`).join(', ')} not available; ${hint.replace(' it ', ' them ')}`;
294
+ }
@@ -110,6 +110,9 @@ class ProjectRegistry {
110
110
  // M9: projects whose per-project DB failed to load at startup (corrupt, locked,
111
111
  // or migration-stuck). They stay registered but have no live context.
112
112
  _failedProjects;
113
+ /** Settings that running agent sessions depend on changed (`projectId: null` = app-global). */
114
+ _settingsListeners = new Set();
115
+ _removalListeners = new Set();
113
116
  _loadRetryTimers = new Map();
114
117
  _shuttingDown = false;
115
118
  // App-wide shared browser context for "Add Spec from a website": ONE persistent
@@ -387,6 +390,11 @@ class ProjectRegistry {
387
390
  (0, telemetry_receiver_1.dropBlobStatesForProject)(id);
388
391
  }
389
392
  catch { /* ignore */ }
393
+ // Stop the login-shell env refresh and forget recovered values.
394
+ try {
395
+ (0, project_env_1.stopProjectEnvWarmup)(ctx.db);
396
+ }
397
+ catch { /* ignore */ }
390
398
  // Close the DB connection BEFORE removing the project's data dir below.
391
399
  try {
392
400
  ctx.db.close();
@@ -443,6 +451,35 @@ class ProjectRegistry {
443
451
  }
444
452
  this._failedProjects.delete(id);
445
453
  (0, desktop_db_1.removeProject)(this._desktopDb, id);
454
+ if (persistedProject) {
455
+ for (const listener of this._removalListeners) {
456
+ try {
457
+ listener({ id, slug: persistedProject.slug });
458
+ }
459
+ catch (err) {
460
+ console.error('[project-registry] project removal listener failed (non-fatal):', err);
461
+ }
462
+ }
463
+ }
464
+ }
465
+ /** Observe project removal (e.g. to stop the project's Core session host). */
466
+ onSettingsChanged(listener) {
467
+ this._settingsListeners.add(listener);
468
+ return () => { this._settingsListeners.delete(listener); };
469
+ }
470
+ notifySettingsChanged(scope) {
471
+ for (const listener of this._settingsListeners) {
472
+ try {
473
+ listener(scope);
474
+ }
475
+ catch (error) {
476
+ console.error('[project-registry] settings listener failed:', error);
477
+ }
478
+ }
479
+ }
480
+ onProjectRemoved(listener) {
481
+ this._removalListeners.add(listener);
482
+ return () => { this._removalListeners.delete(listener); };
446
483
  }
447
484
  getContext(id) {
448
485
  return this._contexts.get(id);
@@ -607,6 +644,10 @@ class ProjectRegistry {
607
644
  ctx.fileSummaryManager.dispose();
608
645
  }
609
646
  catch { /* ignore */ }
647
+ try {
648
+ (0, project_env_1.stopProjectEnvWarmup)(ctx.db);
649
+ }
650
+ catch { /* ignore */ }
610
651
  ctx.ticketWatcher.close().catch(() => { });
611
652
  }
612
653
  // Close the shared browser context once, after every per-project manager has
@@ -1120,6 +1161,8 @@ class ProjectRegistry {
1120
1161
  sourcePath: () => (0, project_repositories_1.resolveProjectRepository)(project).path,
1121
1162
  workspacePaths: () => { const primary = (0, project_repositories_1.resolveProjectRepository)(project); return primary.workspacePaths ?? (primary.workspacePath ? [primary.workspacePath] : undefined); },
1122
1163
  env: () => (0, workspace_resolution_1.resolveLoopBaseEnv)({ slug: project.slug, path: project.path }, undefined, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
1164
+ // One value-free run-log line when configured names are unresolved.
1165
+ environmentNotice: () => (0, project_env_1.worktreeEnvPassthroughWarning)(db, (0, project_env_1.applyWorktreeEnvPassthrough)(db, process.env)),
1123
1166
  // Named rail profiles and global per-agent defaults ride the same
1124
1167
  // SPECRAILS_PROFILE_PATH snapshot in both AI transports. An explicit null
1125
1168
  // rail selection opts out; omitted selections keep Core's default rules.
@@ -1454,6 +1497,9 @@ class ProjectRegistry {
1454
1497
  const ctx = { project, db, queueManager, chatManager, setupManager, proposalManager, agentRefineManager, fileSummaryManager, specLauncherManager, ticketWatcher, browserCaptureManager, jiraSyncManager, stuckRunDetector, broadcast: boundBroadcast, railJobs, loopRunManager, railLoopRuns, onLoopRunFinished, getTicketSpec, desktopDb: this._desktopDb, milestoneProgress };
1455
1498
  ctx.milestoneChains = milestoneChains;
1456
1499
  this._contexts.set(project.id, ctx);
1500
+ // Warm the project's login-shell env cache off the event loop so the first
1501
+ // rail/loop spawn reads recovered credentials instead of a cold 1.5 s probe.
1502
+ (0, project_env_1.startProjectEnvWarmup)(db);
1457
1503
  const recoverAfterProbe = (definitionStates) => {
1458
1504
  if (this._contexts.get(project.id) !== ctx)
1459
1505
  return;
@@ -9,6 +9,7 @@ exports.registerSettingsRoutes = registerSettingsRoutes;
9
9
  const project_settings_1 = require("./modules/project-settings");
10
10
  const sqlite_1 = require("./modules/project-settings/adapters/sqlite");
11
11
  const http_1 = require("./modules/project-settings/adapters/http");
12
+ const project_env_1 = require("./project-env");
12
13
  const fs_1 = __importDefault(require("fs"));
13
14
  const path_1 = __importDefault(require("path"));
14
15
  const db_1 = require("./db");
@@ -29,7 +30,29 @@ function registerSettingsRoutes(deps) {
29
30
  (0, agent_runtime_controls_router_1.registerAgentRuntimeControlRoutes)(deps);
30
31
  const { router, registry, ctx } = deps;
31
32
  // ─── Project settings (pipeline telemetry) ───────────────────────────────────
32
- (0, http_1.registerProjectSettingsHttp)(router, req => (0, project_settings_1.createProjectSettingsService)((0, sqlite_1.createSqliteProjectSettingsRepository)(ctx(req).db)));
33
+ (0, http_1.registerProjectSettingsHttp)(router, req => (0, project_settings_1.createProjectSettingsService)((0, sqlite_1.createSqliteProjectSettingsRepository)(ctx(req).db)),
34
+ // Open agent sessions of this project re-read their sub-agent policy.
35
+ (req, settings, previous) => {
36
+ const runtimeChanged = JSON.stringify(settings.subagentRuntime) !== JSON.stringify(previous.subagentRuntime);
37
+ if (settings.allowSubagents !== previous.allowSubagents || runtimeChanged)
38
+ registry.notifySettingsChanged({ projectId: String(req.params.projectId) });
39
+ // Configured env names changed: re-warm this project's login-shell cache.
40
+ if (JSON.stringify(settings.worktreeEnvPassthrough) !== JSON.stringify(previous.worktreeEnvPassthrough))
41
+ void (0, project_env_1.refreshProjectEnv)(ctx(req).db);
42
+ });
43
+ // Value-free resolution status of the configured env names (+ recheck).
44
+ (0, http_1.registerEnvPassthroughStatusHttp)(router, {
45
+ read: async (req) => {
46
+ const { db } = ctx(req);
47
+ await (0, project_env_1.ensureProjectEnvFresh)(db);
48
+ return (0, project_env_1.getProjectEnvStatus)(db);
49
+ },
50
+ recheck: async (req) => {
51
+ const { db } = ctx(req);
52
+ await (0, project_env_1.refreshProjectEnv)(db);
53
+ return (0, project_env_1.getProjectEnvStatus)(db);
54
+ },
55
+ });
33
56
  // ─── Per-project Quick mode Contract Refine last-used value ─────────────────
34
57
  router.get('/:projectId/add-spec-quick-contract-refine-last', (req, res) => {
35
58
  res.json({
@@ -22,7 +22,7 @@ const CLAUDE_MODELS = [
22
22
  { value: 'sonnet', label: 'Sonnet 5.5', default: true },
23
23
  { value: 'fable', label: 'Fable 5.1' },
24
24
  { value: 'opus', label: 'Claude Opus 5.5' },
25
- { value: 'haiku', label: 'Claude Haiku' },
25
+ { value: 'haiku', label: 'Claude Haiku 5.5' },
26
26
  ];
27
27
  /** Mirror of ChatManager.normalizeClaudeCodeModel — pinned model strings the
28
28
  * CLI accepts collapse to the short alias. */
@@ -44,6 +44,8 @@ function normaliseModel(model) {
44
44
  case 'claude-opus-4-1-20250805':
45
45
  case 'claude-opus-4-20250514':
46
46
  return 'opus';
47
+ case 'claude-haiku-5-5':
48
+ case 'claude-haiku-5':
47
49
  case 'claude-haiku-4-5-20251001':
48
50
  case 'claude-3-5-haiku-20241022':
49
51
  case 'claude-3-5-haiku-latest':
@@ -65,6 +67,7 @@ function normaliseModel(model) {
65
67
  const PINNED_ALIAS_MODEL_IDS = {
66
68
  sonnet: 'claude-sonnet-5-5',
67
69
  opus: 'claude-opus-5-5',
70
+ haiku: 'claude-haiku-5-5',
68
71
  };
69
72
  /** Catalog value (or concrete id) in, spawn model id out. */
70
73
  function resolveSpawnModel(model) {
@@ -482,6 +485,8 @@ exports.claudeAdapter = {
482
485
  nativeOtelEnv: true,
483
486
  profileEnvSupport: true,
484
487
  systemPromptArg: true,
488
+ // Missions already run with --dangerously-skip-permissions; Specrails tiers gate actions.
489
+ sessionPermissions: 'bypass',
485
490
  persistentStdin: true,
486
491
  liveInputTransport: 'claude-stream-json',
487
492
  supportsReasoningEffort: true,
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.WORKTREE_DEPENDENCY_CACHES = void 0;
36
+ exports.DEPENDENCY_LOCKFILES = exports.WORKTREE_DEPENDENCY_CACHES = void 0;
37
37
  exports.isWorktreeNodeModulesEnabled = isWorktreeNodeModulesEnabled;
38
38
  exports.discoverPackageDirs = discoverPackageDirs;
39
+ exports.dependencyInputs = dependencyInputs;
40
+ exports.differingDependencyInput = differingDependencyInput;
39
41
  exports.authenticateWarmNodeModulesLinks = authenticateWarmNodeModulesLinks;
40
42
  exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
41
43
  /**
@@ -48,7 +50,14 @@ exports.linkNodeModulesIntoWorktree = linkNodeModulesIntoWorktree;
48
50
  * Cleanup authority covers individual source-anchored links, never the entire
49
51
  * writable directory. Replaced packages and newly created files therefore keep
50
52
  * the existing ignored-artifact / recoverable-work settlement guarantees.
53
+ *
54
+ * Freshness guard: a package directory is linked only when its dependency inputs
55
+ * (nearest lockfile up to the Git top level + the manifest's dependency fields)
56
+ * match between the base checkout and the worktree. Otherwise the directory is
57
+ * left without node_modules so the runtime installs it cold from the worktree's
58
+ * own lockfile. The base checkout is only ever read.
51
59
  */
60
+ const crypto_1 = require("crypto");
52
61
  const fs = __importStar(require("fs"));
53
62
  const path = __importStar(require("path"));
54
63
  const worktree_overlay_1 = require("./worktree-overlay");
@@ -112,6 +121,81 @@ function discoverPackageDirs(baseRepo) {
112
121
  walk('', 0);
113
122
  return found;
114
123
  }
124
+ /** Lockfiles in lookup priority order; the first one found in a directory wins. */
125
+ exports.DEPENDENCY_LOCKFILES = ['pnpm-lock.yaml', 'yarn.lock', 'package-lock.json', 'npm-shrinkwrap.json', 'bun.lock'];
126
+ const DEPENDENCY_FIELDS = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'];
127
+ function sha256(data) {
128
+ return (0, crypto_1.createHash)('sha256').update(data).digest('hex');
129
+ }
130
+ function canonicalJson(value) {
131
+ if (Array.isArray(value))
132
+ return `[${value.map(canonicalJson).join(',')}]`;
133
+ if (value && typeof value === 'object') {
134
+ const record = value;
135
+ return `{${Object.keys(record).sort().map(key => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(',')}}`;
136
+ }
137
+ return JSON.stringify(value) ?? 'null';
138
+ }
139
+ /** Git top level of a checkout: nearest ancestor holding a `.git` entry (dir
140
+ * for a clone, file for a linked worktree). Outside Git, the checkout itself
141
+ * bounds the lockfile search so it never escapes into unrelated parents. */
142
+ function gitTopLevel(checkoutRoot) {
143
+ const start = path.resolve(checkoutRoot);
144
+ for (let current = start;; current = path.dirname(current)) {
145
+ if (exists(path.join(current, '.git')))
146
+ return current;
147
+ if (path.dirname(current) === current)
148
+ return start;
149
+ }
150
+ }
151
+ /**
152
+ * Fingerprint the inputs that decide what a package directory installs. Never
153
+ * throws: unreadable inputs yield null digests, which compare as different.
154
+ */
155
+ function dependencyInputs(checkoutRoot, pkgRel) {
156
+ const top = gitTopLevel(checkoutRoot);
157
+ const pkgDir = pkgRel === '' ? path.resolve(checkoutRoot) : path.resolve(checkoutRoot, ...pkgRel.split('/'));
158
+ let lockfile = null;
159
+ for (let current = pkgDir; lockfile === null; current = path.dirname(current)) {
160
+ for (const name of exports.DEPENDENCY_LOCKFILES) {
161
+ const file = path.join(current, name);
162
+ if (!exists(file))
163
+ continue;
164
+ let digest = null;
165
+ try {
166
+ digest = sha256(fs.readFileSync(file));
167
+ }
168
+ catch { /* unreadable: never equal */ }
169
+ lockfile = { name, location: path.relative(pkgDir, current).split(path.sep).join('/'), digest };
170
+ break;
171
+ }
172
+ if (current === top || path.dirname(current) === current)
173
+ break;
174
+ }
175
+ let manifestDigest = null;
176
+ try {
177
+ const manifest = JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf8'));
178
+ const fields = {};
179
+ for (const field of DEPENDENCY_FIELDS)
180
+ if (manifest?.[field] !== undefined)
181
+ fields[field] = manifest[field];
182
+ manifestDigest = sha256(canonicalJson(fields));
183
+ }
184
+ catch { /* missing or unparsable manifest: never equal */ }
185
+ return { lockfile, manifestDigest };
186
+ }
187
+ /** Name of the first dependency input that differs, or null when warm reuse is safe. */
188
+ function differingDependencyInput(base, worktree) {
189
+ const a = base.lockfile, b = worktree.lockfile;
190
+ if (a || b) {
191
+ if (!a || !b || a.name !== b.name || a.location !== b.location || a.digest === null || a.digest !== b.digest) {
192
+ return (a ?? b).name;
193
+ }
194
+ }
195
+ if (base.manifestDigest === null || base.manifestDigest !== worktree.manifestDigest)
196
+ return 'package.json';
197
+ return null;
198
+ }
115
199
  /** Resolve a path through symlinks when possible; the literal path otherwise
116
200
  * (a nonexistent target must still compare, and must still compare EQUAL to an
117
201
  * equally-nonexistent expectation rather than silently authorizing). */
@@ -305,6 +389,16 @@ function linkNodeModulesIntoWorktree(baseRepo, worktreePath) {
305
389
  }
306
390
  const dest = path.join(worktreePath, ...rel.split('/'));
307
391
  const legacy = authenticateWarmLink(baseRepo, worktreePath, rel, sourceRel);
392
+ if (!exists(dest)) {
393
+ // Freshness guard, only when a NEW link would be prepared: an existing
394
+ // (resumed or legacy) link keeps its authentication below.
395
+ const differing = differingDependencyInput(dependencyInputs(baseRepo, pkgRel), dependencyInputs(prefix ? path.join(worktreePath, ...prefix.split('/')) : worktreePath, pkgRel));
396
+ if (differing) {
397
+ const pkgDir = [prefix, pkgRel].filter(Boolean).join('/') || '.';
398
+ result.warnings.push(`${pkgDir}: ${differing} differs from the base checkout; dependencies will be installed in the worktree`);
399
+ continue;
400
+ }
401
+ }
308
402
  if (!exists(dest) || legacy) {
309
403
  let staging;
310
404
  let removedLegacy = false;
@@ -1 +0,0 @@
1
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{h as r,r as i}from"./useSharedWebSocket-DaWuiO0S.js";import{n as a}from"./createLucideIcon-Bjbbp_KS.js";import{a as o,r as s}from"./useDesktop-DKNbOF0u.js";import{n as c,t as l}from"./zap-CUOlvtT1.js";import{t as u}from"./circle-x-CMYsN4ZH.js";import{t as d}from"./loader-circle-DcygOQpC.js";import{$t as f,Gt as p}from"./index-lTGJJTuk.js";var m=e(t(),1);function h(e){let t=new Set;return e.filter(e=>{let n=`${e.type}:${e.jobId}`;return t.has(n)?!1:(t.add(n),!0)})}function g({activeProjectId:e,limit:t=50}){let[n,a]=(0,m.useState)([]),[s,c]=(0,m.useState)(!1),[l,u]=(0,m.useState)(!0),d=(0,m.useRef)(e);(0,m.useEffect)(()=>{d.current=e},[e]);let{registerHandler:f,unregisterHandler:p}=i();(0,m.useEffect)(()=>{if(!e){a([]),u(!0);return}let n=!1;c(!0),a([]),u(!0);async function r(){let e=o();try{let r=await fetch(`${e}/activity?limit=${t}`);if(!r.ok||n)return;let i=await r.json();if(n)return;a(i),u(i.length===t)}catch{}finally{n||c(!1)}}return r(),()=>{n=!0}},[e,t]);let g=(0,m.useCallback)(async()=>{if(s||!l)return;c(!0);let e=o(),n=d.current;try{a(r=>{let i=r[r.length-1];if(!i)return r;let o=encodeURIComponent(i.timestamp);return fetch(`${e}/activity?limit=${t}&before=${o}`).then(e=>e.json()).then(e=>{if(d.current!==n){c(!1);return}a(t=>h([...t,...e])),u(e.length===t),c(!1)}).catch(()=>c(!1)),r})}catch{c(!1)}},[s,l,t]),_=(0,m.useCallback)(e=>{let t=e,n=d.current;if(!(!t||typeof t.type!=`string`)&&!(t.projectId&&t.projectId!==n)){if(t.type===`queue`&&Array.isArray(t.jobs)){let e=[];for(let n of t.jobs){let t=n.status===`completed`?`job_completed`:n.status===`failed`?`job_failed`:n.status===`canceled`?`job_canceled`:`job_started`;e.push({id:`${t}:${n.id}`,type:t,jobId:n.id,jobCommand:n.command??``,timestamp:n.startedAt??new Date().toISOString(),summary:`${t.replace(`_`,` `)}: ${n.command??``}`,costUsd:null})}e.length>0&&a(t=>h([...e,...t]))}if(t.type===`phase`&&t.phase&&t.state&&t.timestamp){let e={id:`phase:${t.phase}:${t.state}:${t.timestamp}`,type:`job_started`,jobId:`phase-${t.phase}`,jobCommand:r.t(`activity:feed.phaseCommand`,{phase:t.phase,state:t.state}),timestamp:t.timestamp,summary:`Phase ${t.phase} is ${t.state}`,costUsd:null};a(t=>h([e,...t]))}}},[]);return(0,m.useLayoutEffect)(()=>(f(`activity`,_),()=>p(`activity`)),[_,f,p]),{items:n,loading:s,hasMore:l,loadMore:g}}var _=n();function v(e,t){let n=Date.now()-new Date(e).getTime(),r=Math.floor(n/1e3);if(r<60)return t(`feed.relativeTime.seconds`,{value:r});let i=Math.floor(r/60);if(i<60)return t(`feed.relativeTime.minutes`,{value:i});let a=Math.floor(i/60);return a<24?t(`feed.relativeTime.hours`,{value:a}):t(`feed.relativeTime.days`,{value:Math.floor(a/24)})}function y({type:e}){switch(e){case`job_completed`:return(0,_.jsx)(p,{className:`w-4 h-4 text-green-500 aurora-light:text-accent-success flex-shrink-0`});case`job_failed`:return(0,_.jsx)(u,{className:`w-4 h-4 text-red-500 aurora-light:text-destructive flex-shrink-0`});case`job_canceled`:return(0,_.jsx)(c,{className:`w-4 h-4 text-muted-foreground flex-shrink-0`});default:return(0,_.jsx)(l,{className:`w-4 h-4 text-blue-500 aurora-light:text-accent-info flex-shrink-0`})}}function b(e,t){switch(e){case`job_completed`:return t(`common:status.completed`);case`job_failed`:return t(`common:status.failed`);case`job_canceled`:return t(`common:status.canceled`);default:return t(`feed.typeStarted`)}}function x(e){switch(e){case`job_completed`:return`text-green-500 aurora-light:text-accent-success`;case`job_failed`:return`text-red-500 aurora-light:text-destructive`;case`job_canceled`:return`text-muted-foreground`;default:return`text-blue-500 aurora-light:text-accent-info`}}function S(){let{t:e}=a(`activity`),{activeProjectId:t}=s(),{items:n,loading:r,hasMore:i,loadMore:o}=g({activeProjectId:t}),c=(0,m.useRef)(null);return(0,m.useEffect)(()=>{let e=c.current;if(!e)return;let t=new IntersectionObserver(e=>{e[0].isIntersecting&&i&&!r&&o()},{threshold:.1});return t.observe(e),()=>t.disconnect()},[i,r,o]),(0,_.jsxs)(`div`,{className:`flex flex-col h-full overflow-hidden`,children:[(0,_.jsxs)(`div`,{className:`flex items-center gap-2 px-4 py-3 border-b border-border bg-background/50`,children:[(0,_.jsx)(f,{className:`w-4 h-4 text-muted-foreground`}),(0,_.jsx)(`h1`,{className:`text-sm font-medium`,children:e(`feed.title`)})]}),(0,_.jsxs)(`div`,{className:`flex-1 overflow-y-auto`,children:[r&&n.length===0?(0,_.jsx)(`div`,{className:`flex items-center justify-center h-32`,children:(0,_.jsx)(d,{className:`w-4 h-4 animate-spin text-muted-foreground`})}):n.length===0?(0,_.jsxs)(`div`,{className:`flex flex-col items-center justify-center h-48 gap-2 text-muted-foreground`,children:[(0,_.jsx)(f,{className:`w-8 h-8 opacity-40`}),(0,_.jsx)(`p`,{className:`text-sm`,children:e(`feed.emptyTitle`)}),(0,_.jsx)(`p`,{className:`text-xs opacity-70`,children:e(`feed.emptyHint`)})]}):(0,_.jsx)(`ul`,{className:`divide-y divide-border/50`,children:n.map(t=>(0,_.jsxs)(`li`,{className:`flex items-center gap-3 px-4 py-2.5 hover:bg-accent/30 transition-colors`,children:[(0,_.jsx)(y,{type:t.type}),(0,_.jsxs)(`div`,{className:`flex-1 min-w-0`,children:[(0,_.jsx)(`p`,{className:`text-xs text-foreground truncate`,title:t.jobCommand,children:t.jobCommand}),(0,_.jsxs)(`div`,{className:`flex items-center gap-2 mt-0.5`,children:[(0,_.jsx)(`span`,{className:`text-xs font-medium ${x(t.type)}`,children:b(t.type,e)}),t.costUsd!=null&&(0,_.jsxs)(`span`,{className:`text-xs text-muted-foreground`,children:[`$`,t.costUsd.toFixed(4)]})]})]}),(0,_.jsx)(`span`,{className:`text-xs text-muted-foreground flex-shrink-0 tabular-nums`,children:v(t.timestamp,e)})]},`${t.type}:${t.jobId}`))}),(0,_.jsx)(`div`,{ref:c,className:`h-1`}),r&&n.length>0&&(0,_.jsx)(`div`,{className:`flex justify-center py-3`,children:(0,_.jsx)(d,{className:`w-4 h-4 animate-spin text-muted-foreground`})}),!i&&n.length>0&&(0,_.jsx)(`p`,{className:`text-center text-xs text-muted-foreground py-3`,children:e(`feed.allLoaded`)})]})]})}export{S as default};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/AnalyticsPage-Cvtt-LOO.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/preload-helper-Czpn1I53.js","assets/useSharedWebSocket-DaWuiO0S.js","assets/activity-DiyjsgQV.js","assets/addspec-BOvgmcgk.js","assets/agent-Z5s_GTP0.js","assets/agentRuntime-C_yLPKDy.js","assets/agents-Doe1sHY5.js","assets/agentstudio-BzugqB_N.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BWynXPEm.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-CpS9liE9.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-Bdi0qN4S.js","assets/dashboard-XGdQ5mjw.js","assets/explore-DgBACn39.js","assets/integrations-Dwl9KQp0.js","assets/jira-FXr7u2lh.js","assets/jobs-De3EqaO0.js","assets/loops-WyWDkhok.js","assets/mcp-CUAN6ORK.js","assets/narration-DbHnz0qV.js","assets/nav--u7iPYlS.js","assets/packet-B0H15elv.js","assets/settings-Dbyd6gmj.js","assets/setup-CKGUxKr-.js","assets/specs-DfIdoU5L.js","assets/subscriptionUsage-CdlE5Mg1.js","assets/terminal-BAzpHtpY.js","assets/tickets-D41ZMQpf.js","assets/createLucideIcon-Bjbbp_KS.js","assets/Trans-D5eOjfvG.js","assets/useDesktop-DKNbOF0u.js","assets/react-dom-DiWzBDP9.js","assets/provider-capabilities-lePfeT_Y.js","assets/spending-DQDNtM8q.js","assets/clsx-CjueKrWZ.js","assets/formatDistanceToNow-DZ3VWsHq.js","assets/getTimezoneOffsetInMilliseconds-9JiGlFrx.js","assets/index-lTGJJTuk.js","assets/tauri-shell-DE6LfGPw.js","assets/TicketDetailModalContext-YBJX3RmP.js","assets/save-OsUqVn99.js","assets/MinimizedChatsContext-CroAs6Kh.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/zap-CUOlvtT1.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/external-link-D6XxC_Cu.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/rotate-ccw-Wh4id13q.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-Bp0zXwjg.js","assets/dialog-CMgps2MY.js","assets/utils-BC4DHlz2.js","assets/lib-CGdyxIWg.js","assets/button-D1xExSTF.js","assets/RepositoryScopeSelector-CeCr6Jz-.js","assets/project-repositories-CadLZOnr.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-DJCm1pZr.js","assets/useRuntimeRuns-D3YMw9qx.js","assets/event-Bt7f3wXe.js","assets/index-Bfunvi6w.css","assets/ExportDropdown-CweP_1GP.js","assets/BarChart-nBOMNfiy.js","assets/value-s-gzBeyF.js"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./loader-circle-DcygOQpC.js";import{t as o}from"./x-CBMI9ejn.js";import{t as s}from"./utils-BC4DHlz2.js";import{At as c,Q as l,kt as u}from"./index-lTGJJTuk.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./AnalyticsPage-Cvtt-LOO.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81]))),m=420,h=560;function g(){let{t:e}=i(`agent`),{closeAnalyticsPane:t}=l(),[n,r]=(0,d.useState)(h),[g,_]=(0,d.useState)(!1),v=(0,d.useCallback)(e=>{let t=e.clientX,i=n,a=window.innerWidth-m;try{e.currentTarget.setPointerCapture(e.pointerId)}catch{}let o=e=>{let n=Math.min(Math.max(i+(t-e.clientX),m),a);r(n)},s=()=>{window.removeEventListener(`pointermove`,o),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s)};window.addEventListener(`pointermove`,o),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s)},[n]);return(0,f.jsxs)(`div`,{className:s(`flex h-full flex-col overflow-hidden border-l border-border bg-background`,g&&`absolute inset-0 z-30 border-l-0`),style:g?void 0:{width:n,flexShrink:0},"data-testid":`agent-analytics-pane`,children:[(0,f.jsx)(`div`,{className:`relative flex items-center`,children:!g&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:v,className:`absolute left-0 top-0 h-full w-1.5 -translate-x-full cursor-col-resize select-none touch-none hover:bg-accent-primary/20`,title:e(`workspace.resizeAnalytics`)})}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:e(`workspace.analytics`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>_(e=>!e),"aria-label":e(g?`restore`:`maximize`),title:e(g?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:g?(0,f.jsx)(u,{className:`h-4 w-4`}):(0,f.jsx)(c,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:t,"aria-label":e(`workspace.closeAnalytics`),title:e(`workspace.closeAnalytics`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(o,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-y-auto`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-24 items-center justify-center text-muted-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4 animate-spin`})}),children:(0,f.jsx)(p,{})})})]})}export{g as AgentModeAnalyticsPane};
@@ -1,2 +0,0 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/CodePage-D35o-MFO.js","assets/rolldown-runtime-CNC7AqOf.js","assets/jsx-runtime-zu2_FqZY.js","assets/react-dom-DiWzBDP9.js","assets/preload-helper-Czpn1I53.js","assets/chunk-KS7C4IRE-DEKu087k.js","assets/useSharedWebSocket-DaWuiO0S.js","assets/activity-DiyjsgQV.js","assets/addspec-BOvgmcgk.js","assets/agent-Z5s_GTP0.js","assets/agentRuntime-C_yLPKDy.js","assets/agents-Doe1sHY5.js","assets/agentstudio-BzugqB_N.js","assets/aiedit-iIjz2_Qk.js","assets/analytics-BWynXPEm.js","assets/attachments-B53PG733.js","assets/browser-CG0PE1j_.js","assets/builder-CpS9liE9.js","assets/chat-BGD7MXAy.js","assets/code-DaZ-ZwCT.js","assets/commands-BAWSYHjQ.js","assets/common-Bdi0qN4S.js","assets/dashboard-XGdQ5mjw.js","assets/explore-DgBACn39.js","assets/integrations-Dwl9KQp0.js","assets/jira-FXr7u2lh.js","assets/jobs-De3EqaO0.js","assets/loops-WyWDkhok.js","assets/mcp-CUAN6ORK.js","assets/narration-DbHnz0qV.js","assets/nav--u7iPYlS.js","assets/packet-B0H15elv.js","assets/settings-Dbyd6gmj.js","assets/setup-CKGUxKr-.js","assets/specs-DfIdoU5L.js","assets/subscriptionUsage-CdlE5Mg1.js","assets/terminal-BAzpHtpY.js","assets/tickets-D41ZMQpf.js","assets/createLucideIcon-Bjbbp_KS.js","assets/useDesktop-DKNbOF0u.js","assets/TicketDetailModalContext-YBJX3RmP.js","assets/tauri-shell-DE6LfGPw.js","assets/Trans-D5eOjfvG.js","assets/provider-capabilities-lePfeT_Y.js","assets/save-OsUqVn99.js","assets/spending-DQDNtM8q.js","assets/MinimizedChatsContext-CroAs6Kh.js","assets/sparkles-DrY8hb0X.js","assets/x-CBMI9ejn.js","assets/zap-CUOlvtT1.js","assets/file-text-BcLCfcWZ.js","assets/circle-x-CMYsN4ZH.js","assets/download-CFHyyHcw.js","assets/external-link-D6XxC_Cu.js","assets/git-branch-BRM0F5GN.js","assets/loader-circle-DcygOQpC.js","assets/pencil-C9RkrIPZ.js","assets/square-Ci-PFcFe.js","assets/plus-CsnjfkmX.js","assets/rotate-ccw-Wh4id13q.js","assets/search-Cq_gE3Ik.js","assets/trash-2-BOU3WdZZ.js","assets/triangle-alert-H9T4jAmh.js","assets/jira-api-Bp0zXwjg.js","assets/dialog-CMgps2MY.js","assets/utils-BC4DHlz2.js","assets/clsx-CjueKrWZ.js","assets/lib-CGdyxIWg.js","assets/formatDistanceToNow-DZ3VWsHq.js","assets/getTimezoneOffsetInMilliseconds-9JiGlFrx.js","assets/button-D1xExSTF.js","assets/RepositoryScopeSelector-CeCr6Jz-.js","assets/project-repositories-CadLZOnr.js","assets/index-lTGJJTuk.js","assets/core-B3mKBIh5.js","assets/RepositoryDeliveries-DJCm1pZr.js","assets/useRuntimeRuns-D3YMw9qx.js","assets/event-Bt7f3wXe.js","assets/index-Bfunvi6w.css"])))=>i.map(i=>d[i]);
2
- import{a as e}from"./rolldown-runtime-CNC7AqOf.js";import{n as t,t as n}from"./jsx-runtime-zu2_FqZY.js";import{t as r}from"./preload-helper-Czpn1I53.js";import{n as i}from"./createLucideIcon-Bjbbp_KS.js";import{t as a}from"./x-CBMI9ejn.js";import{At as o,Q as s,dt as c,kt as l,ut as u}from"./index-lTGJJTuk.js";var d=e(t(),1),f=n(),p=(0,d.lazy)(()=>r(()=>import(`./CodePage-D35o-MFO.js`),__vite__mapDeps([0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78]))),m=420,h=560;function g({projectId:e,conversationId:t}){let{t:n}=i(`agent`),{closeCodePane:r}=s(),[g,_]=(0,d.useState)(h),[v,y]=(0,d.useState)(()=>window.innerWidth),[b,x]=(0,d.useState)(!1),S=(0,d.useRef)(null),C=(0,d.useRef)(null),w=Math.max(0,v-Math.min(m,v/2)),T=Math.min(m,w),E=Math.min(w,Math.max(T,g)),D=(0,d.useCallback)(e=>Math.min(w,Math.max(T,e)),[w,T]),O=`${e}:${t}`,k=(0,d.useCallback)(n=>{c(e,t,{path:n})},[e,t]);(0,d.useLayoutEffect)(()=>{let e=S.current,t=e?.parentElement;if(!e||!t)return;let n=()=>{let n=t.getBoundingClientRect().width||t.clientWidth||window.innerWidth,r=0;for(let n of t.children){if(n===e||!(n instanceof HTMLElement))continue;let t=getComputedStyle(n);t.display===`none`||t.position===`absolute`||t.position===`fixed`||Number(t.flexGrow)>0||(r+=n.getBoundingClientRect().width)}y(Math.max(0,n-r))},r=new ResizeObserver(n),i=()=>{r.disconnect(),r.observe(t);for(let n of t.children)n!==e&&r.observe(n);n()},a=new MutationObserver(i);return a.observe(t,{childList:!0}),i(),window.addEventListener(`resize`,n),()=>{r.disconnect(),a.disconnect(),window.removeEventListener(`resize`,n)}},[O]),(0,d.useEffect)(()=>()=>{C.current?.()},[O,b,w]);let A=(0,d.useCallback)(e=>{if(e.button!==0)return;e.preventDefault(),C.current?.();let t=e.clientX,n=E,r=e.pointerId,i=e.currentTarget;try{i.setPointerCapture(r)}catch{}let a=e=>{e.pointerId===r&&_(D(n+t-e.clientX))},o=()=>{window.removeEventListener(`pointermove`,a),window.removeEventListener(`pointerup`,s),window.removeEventListener(`pointercancel`,s),window.removeEventListener(`blur`,o),i.removeEventListener(`lostpointercapture`,o);try{i.hasPointerCapture(r)&&i.releasePointerCapture(r)}catch{}C.current=null},s=e=>{e.pointerId===r&&o()};C.current=o,window.addEventListener(`pointermove`,a),window.addEventListener(`pointerup`,s),window.addEventListener(`pointercancel`,s),window.addEventListener(`blur`,o),i.addEventListener(`lostpointercapture`,o)},[E,D]);return(0,f.jsxs)(`div`,{ref:S,className:`flex h-full min-w-0 flex-col overflow-hidden border-l border-border bg-background${b?` absolute inset-0 z-30 border-l-0`:` relative`}`,style:b?void 0:{width:E,maxWidth:`100%`,flexShrink:0},"data-testid":`agent-code-pane`,children:[!b&&(0,f.jsx)(`div`,{role:`separator`,"aria-orientation":`vertical`,onPointerDown:A,tabIndex:0,"aria-label":n(`workspace.resizeCode`),"aria-valuemin":Math.round(T),"aria-valuemax":Math.round(w),"aria-valuenow":Math.round(E),onKeyDown:e=>{let t=e.key===`ArrowLeft`?E+32:e.key===`ArrowRight`?E-32:e.key===`Home`?T:e.key===`End`?w:null;t!==null&&(e.preventDefault(),_(D(t)))},className:`absolute inset-y-0 left-0 z-20 w-1.5 cursor-col-resize select-none touch-none hover:bg-accent-primary/20 focus-visible:bg-accent-primary/30 focus-visible:outline-none`,title:n(`workspace.resizeCode`)}),(0,f.jsxs)(`div`,{className:`flex items-center gap-2 border-b border-border/60 bg-surface/40 px-3 py-1.5`,children:[(0,f.jsx)(`span`,{className:`text-xs font-medium text-foreground/70`,children:n(`workspace.files`)}),(0,f.jsxs)(`div`,{className:`ml-auto flex items-center gap-1`,children:[(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),x(e=>!e)},"aria-label":n(b?`restore`:`maximize`),title:n(b?`restore`:`maximize`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:b?(0,f.jsx)(l,{className:`h-4 w-4`}):(0,f.jsx)(o,{className:`h-4 w-4`})}),(0,f.jsx)(`button`,{type:`button`,onClick:()=>{C.current?.(),r()},"aria-label":n(`workspace.closeFiles`),title:n(`workspace.closeFiles`),className:`rounded-md p-1 text-foreground/60 hover:bg-surface hover:text-foreground`,children:(0,f.jsx)(a,{className:`h-4 w-4`})})]})]}),(0,f.jsx)(`div`,{className:`min-h-0 flex-1 overflow-hidden`,children:(0,f.jsx)(d.Suspense,{fallback:(0,f.jsx)(`div`,{className:`flex h-full items-center justify-center text-sm text-muted-foreground`,children:`…`}),children:(0,f.jsx)(p,{embedded:!0,initialRepositoryId:u(e,t).repositoryId,onRepositoryChange:n=>c(e,t,{repositoryId:n}),initialPath:u(e,t).path,onSelectedPathChange:k},O)})})]},O)}export{g as AgentModeCodePane};