openfox 2.0.76 → 2.0.78

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 (76) hide show
  1. package/dist/{chat-handler-3BZ4PDL2.js → chat-handler-5ZHPYU24.js} +21 -20
  2. package/dist/{chunk-MF3UW36X.js → chunk-37WF2Z3T.js} +84 -17
  3. package/dist/{chunk-OBQPXUXI.js → chunk-5CQRB7AD.js} +2 -2
  4. package/dist/chunk-64TKWWNF.js +7 -0
  5. package/dist/{chunk-LXFLRM7M.js → chunk-6AE3JPDJ.js} +2 -2
  6. package/dist/chunk-AANVMTYU.js +327 -0
  7. package/dist/{chunk-YMFCFMMB.js → chunk-AFJLY43Z.js} +8 -3
  8. package/dist/{chunk-OCVRIPQP.js → chunk-CJDYSUU5.js} +3 -3
  9. package/dist/{chunk-OMWVB2XC.js → chunk-DI4SIPFT.js} +46 -27
  10. package/dist/{chunk-7772ENZN.js → chunk-EHVDAVYG.js} +4 -3
  11. package/dist/{chunk-DH3SSBZN.js → chunk-EYIWUONA.js} +2 -2
  12. package/dist/{chunk-NGJ5XJ7D.js → chunk-M7EFXRDS.js} +57 -290
  13. package/dist/{chunk-4QHDQP2N.js → chunk-MTUYDBSL.js} +2 -2
  14. package/dist/{chunk-HHUIXHHX.js → chunk-NUR2B4RU.js} +7 -7
  15. package/dist/{chunk-46RORSKU.js → chunk-NZIYELSQ.js} +9 -7
  16. package/dist/{chunk-7QXRQ5ZL.js → chunk-RGUUZD5B.js} +5 -1
  17. package/dist/{chunk-EZK7CGJ7.js → chunk-RMXPVQP4.js} +315 -45
  18. package/dist/{chunk-D6CI2B7B.js → chunk-SGBXBP3S.js} +253 -110
  19. package/dist/{chunk-O3NUNU4H.js → chunk-SJTG56CH.js} +5 -4
  20. package/dist/{chunk-DJASQ2ZB.js → chunk-TSVFRWSG.js} +24 -5
  21. package/dist/{chunk-XTK6E2QR.js → chunk-UIU7OGHP.js} +26 -10
  22. package/dist/{chunk-J7VCI5EX.js → chunk-UMX7MMNW.js} +652 -248
  23. package/dist/{chunk-HLHGNHMV.js → chunk-VGWJFXPR.js} +23 -11
  24. package/dist/{chunk-KVRSAC4Z.js → chunk-VPFUXGMM.js} +6 -1
  25. package/dist/{chunk-W4ZEG3LX.js → chunk-WL2WEJAK.js} +2 -2
  26. package/dist/{chunk-HAQV6AJP.js → chunk-WUSWNLTV.js} +3 -3
  27. package/dist/{chunk-5GOOSZX7.js → chunk-X6BXZFO7.js} +1 -1
  28. package/dist/{chunk-WGAAQASA.js → chunk-YFVSSMA3.js} +3 -2
  29. package/dist/cli/dev.js +1 -1
  30. package/dist/cli/index.js +1 -1
  31. package/dist/{client-TDPQVBQ7.js → client-V5DZRO2D.js} +3 -3
  32. package/dist/{client-pure-NMLDKRDN.js → client-pure-5YE5WFJP.js} +2 -2
  33. package/dist/compactor-G7B3V24F.js +24 -0
  34. package/dist/{config-EUMVEFHU.js → config-MD74ETY5.js} +2 -2
  35. package/dist/{events-FLWRLM6V.js → events-KNOZX7YG.js} +7 -4
  36. package/dist/{folding-ONH3D4OK.js → folding-OPP2BVUB.js} +2 -2
  37. package/dist/{inspect-proxy-JHZV34QR.js → inspect-proxy-DRYIIH4O.js} +4 -4
  38. package/dist/{manager-OPMCSYN2.js → manager-ATWZUL3O.js} +5 -5
  39. package/dist/{orchestrator-5IMCWX5P.js → orchestrator-L6LX5BV7.js} +20 -19
  40. package/dist/package.json +6 -1
  41. package/dist/{path-security-ZROWNL22.js → path-security-HUXQHEQ5.js} +10 -7
  42. package/dist/{platform-QGXO3CNR.js → platform-VBNNMTR5.js} +4 -4
  43. package/dist/{processor-3R4GS6VB.js → processor-UQX5KL56.js} +21 -20
  44. package/dist/{project-creator-C22J43BC.js → project-creator-NNUITNUI.js} +3 -3
  45. package/dist/{projects-WSVINK2C.js → projects-ZGJGICGT.js} +3 -3
  46. package/dist/{protocol-Sr6Jr4ZS.d.ts → protocol-BXxJmsOW.d.ts} +2 -1
  47. package/dist/{protocol-EWEVW65B.js → protocol-Q7X4NEME.js} +3 -3
  48. package/dist/provider/index.d.ts +4 -4
  49. package/dist/{provider-G2YC2IC4.js → provider-PI3RDDE4.js} +6 -6
  50. package/dist/{provider-manager-SQIELYLK.js → provider-manager-PD3BXUHY.js} +4 -4
  51. package/dist/{serve-MXKMKZYY.js → serve-QWC3U5MB.js} +26 -25
  52. package/dist/server/index.d.ts +27 -4
  53. package/dist/server/index.js +25 -24
  54. package/dist/server-3XUD2NB7.js +40 -0
  55. package/dist/sessions-SFG42M4W.js +42 -0
  56. package/dist/{settings-7XDMSPIC.js → settings-6M55N4Q2.js} +5 -3
  57. package/dist/shared/index.d.ts +3 -3
  58. package/dist/shared/index.js +1 -1
  59. package/dist/{tools-JRCUWPJO.js → tools-HGKUD327.js} +26 -23
  60. package/dist/{types-C4cG9CtC.d.ts → types-C308SU4B.d.ts} +3 -0
  61. package/dist/{types-DSZhFA8O.d.ts → types-o07LJcLI.d.ts} +1 -1
  62. package/dist/{update-OXDQ75M7.js → update-NWZALB5M.js} +2 -2
  63. package/dist/{update-server-4MTM7M5P.js → update-server-V6GJ43WH.js} +2 -2
  64. package/dist/web/assets/index-7_xKqeJc.js +305 -0
  65. package/dist/web/assets/index-DRkTjPo3.css +32 -0
  66. package/dist/web/index.html +4 -4
  67. package/dist/web/manifest.webmanifest +1 -1
  68. package/dist/web/registerSW.js +1 -1
  69. package/dist/web/sw.js +1 -1
  70. package/dist/{workspace-6QTTUMMW.js → workspace-J74ZMSOJ.js} +14 -2
  71. package/package.json +6 -1
  72. package/dist/chunk-KQEVAO2Q.js +0 -7
  73. package/dist/compactor-BB3YYVRP.js +0 -23
  74. package/dist/server-SPYFMFQE.js +0 -39
  75. package/dist/web/assets/index-BNVBxKnB.css +0 -32
  76. package/dist/web/assets/index-ghDkIdlf.js +0 -305
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-OMWVB2XC.js";
24
+ } from "./chunk-DI4SIPFT.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -66,7 +66,7 @@ import {
66
66
  setSkillEnabled,
67
67
  skillExists,
68
68
  updateOwnedSkill
69
- } from "./chunk-D6CI2B7B.js";
69
+ } from "./chunk-SGBXBP3S.js";
70
70
  import {
71
71
  createMcpTools
72
72
  } from "./chunk-NWO6GRYE.js";
@@ -74,14 +74,18 @@ import {
74
74
  deleteWorkspace,
75
75
  ensureWorkspace,
76
76
  getCommitsBehind,
77
+ getDefaultBranch,
77
78
  getGitBranch,
79
+ getGlobalDataDir,
78
80
  getWorkspacesDir,
79
81
  loadWorkspaceConfig,
82
+ resolveAndValidateSourceBranch,
80
83
  runGit,
81
84
  saveWorkspaceConfig,
85
+ validateRef,
82
86
  validateWorkspaceName,
83
87
  workspaceExists
84
- } from "./chunk-MF3UW36X.js";
88
+ } from "./chunk-37WF2Z3T.js";
85
89
  import {
86
90
  EventEmitter
87
91
  } from "./chunk-EU3WWTFH.js";
@@ -89,25 +93,23 @@ import {
89
93
  ALWAYS_ALLOWED,
90
94
  ALWAYS_ALLOWED_FOR_SUBAGENTS,
91
95
  TOP_LEVEL_ONLY_TOOLS
92
- } from "./chunk-7772ENZN.js";
93
- import {
94
- getProject
95
- } from "./chunk-LXFLRM7M.js";
96
+ } from "./chunk-EHVDAVYG.js";
96
97
  import {
97
98
  getPathSeparator,
98
99
  isAbsolutePath
99
- } from "./chunk-4QHDQP2N.js";
100
+ } from "./chunk-MTUYDBSL.js";
100
101
  import {
101
102
  deleteSetting,
102
103
  getSetting,
103
104
  setSetting
104
- } from "./chunk-YMFCFMMB.js";
105
+ } from "./chunk-AFJLY43Z.js";
106
+ import {
107
+ getProject
108
+ } from "./chunk-6AE3JPDJ.js";
105
109
  import {
106
110
  VERSION
107
- } from "./chunk-KQEVAO2Q.js";
111
+ } from "./chunk-64TKWWNF.js";
108
112
  import {
109
- createSession,
110
- deleteSession,
111
113
  emitAssistantMessageStart,
112
114
  emitContextState,
113
115
  emitCriteriaSet,
@@ -119,32 +121,37 @@ import {
119
121
  emitSessionInitialized,
120
122
  emitUserMessage,
121
123
  getEventStore,
122
- getSession,
123
- getSessionCachedPrompt,
124
124
  getSessionState,
125
125
  initEventStore,
126
+ setRuntimeConfig
127
+ } from "./chunk-M7EFXRDS.js";
128
+ import {
129
+ createSession,
130
+ deleteSession,
131
+ getSession,
132
+ getSessionCachedPrompt,
126
133
  listSessions,
127
134
  listSessionsByProject,
128
- setRuntimeConfig,
135
+ updateSessionBranch,
129
136
  updateSessionCachedPrompt,
130
137
  updateSessionDangerLevel,
131
138
  updateSessionMetadata,
132
139
  updateSessionProvider,
133
140
  updateSessionRunning,
134
141
  updateSessionWorkdir
135
- } from "./chunk-NGJ5XJ7D.js";
142
+ } from "./chunk-AANVMTYU.js";
136
143
  import {
137
144
  initDatabase
138
- } from "./chunk-7QXRQ5ZL.js";
145
+ } from "./chunk-RGUUZD5B.js";
139
146
  import {
140
147
  createContextStateMessage
141
- } from "./chunk-O3NUNU4H.js";
148
+ } from "./chunk-SJTG56CH.js";
142
149
  import {
143
150
  createProviderManager,
144
151
  detectModel,
145
152
  getLlmStatus,
146
153
  parseDefaultModelSelection
147
- } from "./chunk-OCVRIPQP.js";
154
+ } from "./chunk-CJDYSUU5.js";
148
155
  import {
149
156
  getModelProfile
150
157
  } from "./chunk-V4IE7HJY.js";
@@ -163,7 +170,7 @@ import {
163
170
  } from "./chunk-K44MW7JJ.js";
164
171
  import {
165
172
  createServerMessage
166
- } from "./chunk-5GOOSZX7.js";
173
+ } from "./chunk-X6BXZFO7.js";
167
174
  import {
168
175
  getGlobalConfigDir
169
176
  } from "./chunk-CQGTEGKL.js";
@@ -173,7 +180,7 @@ import express from "express";
173
180
  import cors from "cors";
174
181
  import { createServer as createHttpServer } from "http";
175
182
  import { fileURLToPath as fileURLToPath7 } from "url";
176
- import { dirname as dirname8, resolve as resolve7, join as join9 } from "path";
183
+ import { dirname as dirname8, resolve as resolve8, join as join10 } from "path";
177
184
  import { readFile as readFile4 } from "fs/promises";
178
185
  import { createServer as createViteServer } from "vite";
179
186
 
@@ -1490,6 +1497,26 @@ var McpManager = class {
1490
1497
  throw new Error(`Unsupported transport: ${entry.config.transport}`);
1491
1498
  }
1492
1499
  if (!transport) throw new Error("Failed to create transport");
1500
+ let sdkOnMessage = void 0;
1501
+ Object.defineProperty(transport, "onmessage", {
1502
+ get() {
1503
+ return sdkOnMessage;
1504
+ },
1505
+ set(fn) {
1506
+ sdkOnMessage = (message) => {
1507
+ const msg = message;
1508
+ if (msg && msg.result && Array.isArray(msg.result.tools)) {
1509
+ for (const tool of msg.result.tools) {
1510
+ if (tool && tool.outputSchema) {
1511
+ delete tool.outputSchema;
1512
+ }
1513
+ }
1514
+ }
1515
+ fn?.(message);
1516
+ };
1517
+ },
1518
+ configurable: true
1519
+ });
1493
1520
  await client.connect(transport);
1494
1521
  const { tools: mcpTools } = await client.listTools();
1495
1522
  const disabledSet = new Set(entry.config.disabledTools ?? []);
@@ -1585,7 +1612,27 @@ var McpManager = class {
1585
1612
  if (!entry) return { success: false, error: `MCP server '${serverName}' not found` };
1586
1613
  if (!entry.client) return { success: false, error: `MCP server '${serverName}' is not connected` };
1587
1614
  try {
1588
- const result = await entry.client.callTool({ name: toolName, arguments: args });
1615
+ const timeout = entry.config.timeout;
1616
+ const requestOptions = timeout !== void 0 && timeout > 0 ? { timeout: timeout * 1e3 } : void 0;
1617
+ let result;
1618
+ if (timeout !== void 0 && timeout > 0) {
1619
+ let timer;
1620
+ const timeoutPromise = new Promise((_, reject) => {
1621
+ timer = setTimeout(() => {
1622
+ reject(new Error(`MCP tool call timed out after ${timeout} seconds`));
1623
+ }, timeout * 1e3);
1624
+ });
1625
+ try {
1626
+ result = await Promise.race([
1627
+ entry.client.callTool({ name: toolName, arguments: args }, void 0, requestOptions),
1628
+ timeoutPromise
1629
+ ]);
1630
+ } finally {
1631
+ if (timer) clearTimeout(timer);
1632
+ }
1633
+ } else {
1634
+ result = await entry.client.callTool({ name: toolName, arguments: args });
1635
+ }
1589
1636
  const content = result.content;
1590
1637
  const textParts = content.filter((c) => c.type === "text").map((c) => c.text);
1591
1638
  return { success: !result.isError, output: textParts.join("\n") };
@@ -1731,8 +1778,8 @@ var LspServer = class {
1731
1778
  windowsHide: true,
1732
1779
  shell: needsShell
1733
1780
  });
1734
- await new Promise((resolve8, reject) => {
1735
- this.process.once("spawn", resolve8);
1781
+ await new Promise((resolve9, reject) => {
1782
+ this.process.once("spawn", resolve9);
1736
1783
  this.process.once("error", reject);
1737
1784
  });
1738
1785
  if (!this.process.stdin || !this.process.stdout) {
@@ -1952,19 +1999,19 @@ var LspServer = class {
1952
1999
  */
1953
2000
  async getDiagnosticsWithWait(path, timeoutMs = DIAGNOSTIC_WAIT_MS) {
1954
2001
  if (this.openDocuments.has(path)) {
1955
- return new Promise((resolve8) => {
2002
+ return new Promise((resolve9) => {
1956
2003
  const existing = this.diagnostics.get(path);
1957
2004
  if (existing !== void 0) {
1958
2005
  setTimeout(() => {
1959
- resolve8(this.diagnostics.get(path) ?? []);
2006
+ resolve9(this.diagnostics.get(path) ?? []);
1960
2007
  }, 100);
1961
2008
  return;
1962
2009
  }
1963
2010
  const timeout = setTimeout(() => {
1964
2011
  this.pendingDiagnostics.delete(path);
1965
- resolve8(this.diagnostics.get(path) ?? []);
2012
+ resolve9(this.diagnostics.get(path) ?? []);
1966
2013
  }, timeoutMs);
1967
- this.pendingDiagnostics.set(path, { resolve: resolve8, timeout });
2014
+ this.pendingDiagnostics.set(path, { resolve: resolve9, timeout });
1968
2015
  });
1969
2016
  }
1970
2017
  return this.diagnostics.get(path) ?? [];
@@ -2583,6 +2630,35 @@ var SessionManager = class {
2583
2630
  getCurrentModelContext() {
2584
2631
  return this.providerManager.getCurrentModelContext();
2585
2632
  }
2633
+ /**
2634
+ * Get the effective working directory for a session.
2635
+ * Uses workspace path when active, otherwise the project workdir.
2636
+ */
2637
+ getEffectiveWorkdir(sessionId) {
2638
+ const session = this.requireSession(sessionId);
2639
+ return session.workspace ?? session.workdir;
2640
+ }
2641
+ /**
2642
+ * Return (effectiveWorkdir, actualBranch) for a session.
2643
+ */
2644
+ async getActualBranchPair(sessionId) {
2645
+ const effectiveWorkdir = this.getEffectiveWorkdir(sessionId);
2646
+ const branch = await getGitBranch(effectiveWorkdir);
2647
+ return { workdir: effectiveWorkdir, branch };
2648
+ }
2649
+ /**
2650
+ * Check that the session's persisted branch matches the actual branch on disk.
2651
+ * If they differ (and persisted branch exists), returns a warning message.
2652
+ */
2653
+ async checkBranchConsistency(sessionId) {
2654
+ const session = this.getSession(sessionId);
2655
+ if (!session?.branch) return null;
2656
+ const actualBranch = await getGitBranch(session.workspace ?? session.workdir);
2657
+ if (actualBranch && actualBranch !== session.branch) {
2658
+ return `Branch mismatch: session expects "${session.branch}" but workspace is on "${actualBranch}". The workspace branch was changed externally.`;
2659
+ }
2660
+ return null;
2661
+ }
2586
2662
  // ============================================================================
2587
2663
  // Session Lifecycle
2588
2664
  // ============================================================================
@@ -2606,6 +2682,20 @@ var SessionManager = class {
2606
2682
  const contextWindowId = crypto.randomUUID();
2607
2683
  emitSessionInitialized(dbSession.id, projectId, effectiveWorkdir, contextWindowId, sessionTitle);
2608
2684
  const session = this.buildSessionFromDb(dbSession);
2685
+ getGitBranch(effectiveWorkdir).then((branch) => {
2686
+ if (branch) {
2687
+ updateSessionBranch(session.id, branch);
2688
+ const updatedDb = getSession(session.id);
2689
+ if (updatedDb) {
2690
+ this.emit({ type: "session_updated", session: this.buildSessionFromDb(updatedDb) });
2691
+ }
2692
+ }
2693
+ }).catch((err) => {
2694
+ logger.error("Failed to persist initial branch for session", {
2695
+ sessionId: session.id,
2696
+ error: err instanceof Error ? err.message : String(err)
2697
+ });
2698
+ });
2609
2699
  this.emit({ type: "session_created", session });
2610
2700
  return session;
2611
2701
  }
@@ -2699,6 +2789,16 @@ var SessionManager = class {
2699
2789
  this.emit({ type: "session_updated", session: updatedSession });
2700
2790
  return updatedSession;
2701
2791
  }
2792
+ /**
2793
+ * Emit session_updated for the given session.
2794
+ * Used by REST handlers after updating sibling session branches.
2795
+ */
2796
+ emitBranchChange(sessionId) {
2797
+ const session = this.getSession(sessionId);
2798
+ if (session) {
2799
+ this.emit({ type: "session_updated", session });
2800
+ }
2801
+ }
2702
2802
  /**
2703
2803
  * Change session phase. Emits phase.changed event.
2704
2804
  */
@@ -3209,6 +3309,24 @@ var SessionManager = class {
3209
3309
  const effectiveWorkdir = session.workspace ?? session.workdir;
3210
3310
  return getLspManager(sessionId, effectiveWorkdir);
3211
3311
  }
3312
+ async applyBranchIfNeeded(projectDir, projectName, workspaceName, branch, sourceBranch) {
3313
+ const wsPath = resolve2(await getWorkspacesDir(projectName, projectDir), workspaceName);
3314
+ const currentBranch = await getGitBranch(wsPath);
3315
+ if (currentBranch !== branch) {
3316
+ try {
3317
+ await validateRef(wsPath, branch);
3318
+ await runGit(wsPath, ["checkout", branch]).catch(async () => {
3319
+ const sb = sourceBranch ?? await getDefaultBranch(projectDir);
3320
+ const validated = sourceBranch ? await resolveAndValidateSourceBranch(wsPath, sourceBranch, projectDir) : sb;
3321
+ await runGit(wsPath, ["checkout", "-b", branch, validated]);
3322
+ });
3323
+ } catch (err) {
3324
+ throw new Error(
3325
+ `Failed to apply branch "${branch}" to workspace "${workspaceName}": ${err instanceof Error ? err.message : String(err)}`
3326
+ );
3327
+ }
3328
+ }
3329
+ }
3212
3330
  // ============================================================================
3213
3331
  // Workspace Lifecycle
3214
3332
  // ============================================================================
@@ -3218,44 +3336,55 @@ var SessionManager = class {
3218
3336
  * Emits a single type of event — switching is always "opening" a workspace.
3219
3337
  * Switches are serialized per-session to prevent race conditions.
3220
3338
  */
3221
- async switchWorkspace(sessionId, target, branch) {
3222
- if (target !== "original") {
3223
- validateWorkspaceName(target);
3224
- }
3339
+ async switchWorkspace(sessionId, target, branch, sourceBranch) {
3340
+ if (target !== "original") validateWorkspaceName(target);
3225
3341
  const existingLock = this.switchLocks.get(sessionId);
3226
- if (existingLock) {
3227
- await existingLock;
3228
- }
3342
+ if (existingLock) await existingLock;
3229
3343
  const lockPromise = (async () => {
3230
3344
  const session = this.requireSession(sessionId);
3231
3345
  const project = getProject(session.projectId);
3232
3346
  if (!project) throw new Error(`Project not found: ${session.projectId}`);
3233
- if (target === "original" && !session.workspace) return session;
3234
- if (target !== "original" && session.workspace?.split("/").pop() === target) return session;
3347
+ const isBranchChangeOnly = target !== "original" && session.workspace?.split("/").pop() === target && branch !== void 0;
3348
+ if (target === "original" && !session.workspace && !branch) return session;
3349
+ if (target !== "original" && session.workspace?.split("/").pop() === target && !branch) return session;
3350
+ if (isBranchChangeOnly) {
3351
+ const currentBranch = await getGitBranch(session.workspace ?? session.workdir);
3352
+ if (currentBranch === branch) return session;
3353
+ }
3235
3354
  const previousPath = session.workspace;
3236
- if (previousPath) {
3237
- try {
3238
- await devServerManager.stop(previousPath);
3239
- } catch (err) {
3240
- logger.error("Error stopping dev server for workspace switch", {
3241
- sessionId,
3242
- workspace: previousPath,
3243
- error: err
3355
+ if (previousPath && !isBranchChangeOnly) {
3356
+ const otherSessionsUsingPath = this.listSessions().filter(
3357
+ (s) => s.id !== sessionId && s.workspace === previousPath
3358
+ );
3359
+ if (otherSessionsUsingPath.length === 0) {
3360
+ try {
3361
+ await devServerManager.stop(previousPath);
3362
+ } catch (err) {
3363
+ logger.error("Error stopping dev server for workspace switch", {
3364
+ sessionId,
3365
+ workspace: previousPath,
3366
+ error: err
3367
+ });
3368
+ }
3369
+ } else {
3370
+ logger.info("Skipping dev server stop \u2014 other sessions still use path", {
3371
+ path: previousPath,
3372
+ otherSessions: otherSessionsUsingPath.map((s) => s.id)
3244
3373
  });
3245
3374
  }
3246
3375
  }
3247
3376
  if (target === "original") {
3248
3377
  updateSessionWorkdir(sessionId, project.workdir, null);
3249
- } else {
3378
+ } else if (!isBranchChangeOnly) {
3250
3379
  const createLockKey = `${project.name}:${target}`;
3251
3380
  const existingCreateLock = this.workspaceCreationLocks.get(createLockKey);
3252
- if (existingCreateLock) {
3253
- await existingCreateLock;
3254
- }
3381
+ if (existingCreateLock) await existingCreateLock;
3255
3382
  const createLockPromise = (async () => {
3256
- const exists = await workspaceExists(project.name, target);
3383
+ const exists = await workspaceExists(project.name, target, project.workdir);
3257
3384
  if (!exists) {
3258
- await ensureWorkspace(project.workdir, target, project.name, branch);
3385
+ await ensureWorkspace(project.workdir, target, project.name, branch, sourceBranch);
3386
+ } else if (branch) {
3387
+ await this.applyBranchIfNeeded(project.workdir, project.name, target, branch, sourceBranch);
3259
3388
  }
3260
3389
  })();
3261
3390
  this.workspaceCreationLocks.set(createLockKey, createLockPromise);
@@ -3264,16 +3393,31 @@ var SessionManager = class {
3264
3393
  } finally {
3265
3394
  this.workspaceCreationLocks.delete(createLockKey);
3266
3395
  }
3267
- const wsPath = resolve2(getWorkspacesDir(project.name), target);
3396
+ const wsDir = await getWorkspacesDir(project.name, project.workdir);
3397
+ const wsPath = resolve2(wsDir, target);
3268
3398
  updateSessionWorkdir(sessionId, project.workdir, wsPath);
3399
+ } else {
3400
+ await this.applyBranchIfNeeded(project.workdir, project.name, target, branch, sourceBranch);
3269
3401
  }
3270
3402
  try {
3271
3403
  await shutdownLspManager(sessionId);
3272
3404
  } catch (err) {
3273
3405
  logger.error("Error shutting down LSP for workspace switch", { sessionId, error: err });
3274
3406
  }
3275
- const effectiveWorkdir = target === "original" ? project.workdir : resolve2(getWorkspacesDir(project.name), target);
3407
+ const wsDirForBranch = await getWorkspacesDir(project.name, project.workdir);
3408
+ const effectiveWorkdir = target === "original" ? project.workdir : resolve2(wsDirForBranch, target);
3276
3409
  const actualBranch = await getGitBranch(effectiveWorkdir);
3410
+ if (actualBranch) {
3411
+ updateSessionBranch(sessionId, actualBranch);
3412
+ const otherSessionsOnWorkspace = this.listSessions().filter(
3413
+ (s) => s.id !== sessionId && s.workspace === effectiveWorkdir
3414
+ );
3415
+ for (const other of otherSessionsOnWorkspace) {
3416
+ updateSessionBranch(other.id, actualBranch);
3417
+ const updated2 = this.getSession(other.id);
3418
+ if (updated2) this.emit({ type: "session_updated", session: updated2 });
3419
+ }
3420
+ }
3277
3421
  let stalenessHint = "";
3278
3422
  if (target !== "original" && actualBranch) {
3279
3423
  await runGit(effectiveWorkdir, ["fetch", "origin", "--no-tags", "--quiet"]).catch(() => {
@@ -3310,9 +3454,7 @@ All file and git operations should use this directory.
3310
3454
  })();
3311
3455
  this.switchLocks.set(sessionId, lockPromise);
3312
3456
  lockPromise.finally(() => {
3313
- if (this.switchLocks.get(sessionId) === lockPromise) {
3314
- this.switchLocks.delete(sessionId);
3315
- }
3457
+ if (this.switchLocks.get(sessionId) === lockPromise) this.switchLocks.delete(sessionId);
3316
3458
  });
3317
3459
  return lockPromise;
3318
3460
  }
@@ -3340,12 +3482,13 @@ All file and git operations should use this directory.
3340
3482
  if (currentWsName === target) {
3341
3483
  await this.switchWorkspace(sessionId, "original");
3342
3484
  }
3343
- const effectivePath = resolve2(getWorkspacesDir(project.name), target);
3485
+ const wsDir = await getWorkspacesDir(project.name, project.workdir);
3486
+ const effectivePath = resolve2(wsDir, target);
3344
3487
  try {
3345
3488
  await devServerManager.stop(effectivePath);
3346
3489
  } catch {
3347
3490
  }
3348
- await deleteWorkspace(project.name, target);
3491
+ await deleteWorkspace(project.name, target, project.workdir);
3349
3492
  const updated = this.requireSession(sessionId);
3350
3493
  this.emit({ type: "session_updated", session: updated });
3351
3494
  return updated;
@@ -4114,6 +4257,85 @@ function createDevServerRoutes() {
4114
4257
 
4115
4258
  // src/server/routes/workspace-config.ts
4116
4259
  import { Router as Router5 } from "express";
4260
+ import { stat as stat2, mkdir as mkdir4, readdir, access as access3 } from "fs/promises";
4261
+ import { constants as constants3 } from "fs";
4262
+ import { resolve as resolve4, isAbsolute as isAbsolute3, join as join6 } from "path";
4263
+
4264
+ // src/shared/workspace.ts
4265
+ var BLOCKED_EXACT_PATHS = [
4266
+ "/",
4267
+ "/etc",
4268
+ "/bin",
4269
+ "/sbin",
4270
+ "/lib",
4271
+ "/lib64",
4272
+ "/usr",
4273
+ "/var",
4274
+ "/opt",
4275
+ "/root",
4276
+ "/run",
4277
+ "/tmp",
4278
+ "/home",
4279
+ "/mnt",
4280
+ "/media"
4281
+ ];
4282
+ var BLOCKED_VIRTUAL_FS_PREFIXES = ["/proc/", "/sys/", "/dev/", "/boot/", "/etc/", "/lost+found/"];
4283
+ function getRootDirBlockReason(path, exactPaths = BLOCKED_EXACT_PATHS, virtualFsPrefixes = BLOCKED_VIRTUAL_FS_PREFIXES) {
4284
+ if (!path) return null;
4285
+ const normalized = path.replace(/\/+$/, "") || "/";
4286
+ if (exactPaths.includes(normalized)) return "exact";
4287
+ for (const prefix of virtualFsPrefixes) {
4288
+ if (normalized.startsWith(prefix)) return "virtual_fs";
4289
+ }
4290
+ return null;
4291
+ }
4292
+
4293
+ // src/server/routes/workspace-config.ts
4294
+ async function isWritable(path) {
4295
+ try {
4296
+ await access3(path, constants3.W_OK);
4297
+ return true;
4298
+ } catch {
4299
+ return false;
4300
+ }
4301
+ }
4302
+ function resolveRootDir(rootDir, workdir) {
4303
+ return isAbsolute3(rootDir) ? rootDir : resolve4(workdir, rootDir);
4304
+ }
4305
+ async function checkDirExists(path) {
4306
+ try {
4307
+ const st = await stat2(path);
4308
+ return st.isDirectory();
4309
+ } catch {
4310
+ return false;
4311
+ }
4312
+ }
4313
+ async function validatePathWritable(path) {
4314
+ if (await isWritable(path)) return null;
4315
+ return "Workspace root directory exists but is not writable";
4316
+ }
4317
+ async function findOrphanedWorkspaces(dir) {
4318
+ const results = [];
4319
+ try {
4320
+ const entries = await readdir(dir, { withFileTypes: true });
4321
+ for (const entry of entries) {
4322
+ if (entry.isDirectory()) {
4323
+ try {
4324
+ const gitStat = await stat2(join6(dir, entry.name, ".git"));
4325
+ if (gitStat.isDirectory()) {
4326
+ results.push({ name: entry.name });
4327
+ }
4328
+ } catch {
4329
+ }
4330
+ }
4331
+ }
4332
+ } catch (err) {
4333
+ if (err?.code !== "ENOENT") {
4334
+ logger.error("Error scanning for orphaned workspaces", { dir, error: String(err) });
4335
+ }
4336
+ }
4337
+ return results;
4338
+ }
4117
4339
  function createWorkspaceConfigRoutes() {
4118
4340
  const router = Router5();
4119
4341
  router.get("/config", async (req, res) => {
@@ -4125,18 +4347,93 @@ function createWorkspaceConfigRoutes() {
4125
4347
  router.post("/config", async (req, res) => {
4126
4348
  const workdir = req.query["workdir"];
4127
4349
  if (!workdir) return res.status(400).json({ error: "workdir required" });
4128
- const { setup } = req.body;
4129
- if (!Array.isArray(setup)) {
4350
+ const { setup, rootDir } = req.body;
4351
+ if (!Array.isArray(setup) && typeof rootDir !== "string") {
4352
+ return res.status(400).json({ error: "At least one of setup or rootDir must be provided" });
4353
+ }
4354
+ if (setup !== void 0 && !Array.isArray(setup)) {
4130
4355
  return res.status(400).json({ error: "setup must be an array of strings" });
4131
4356
  }
4357
+ const config3 = {};
4358
+ if (Array.isArray(setup)) {
4359
+ config3.setup = setup;
4360
+ }
4361
+ if (typeof rootDir === "string") {
4362
+ const trimmed = rootDir.trim();
4363
+ if (trimmed) {
4364
+ const resolvedPath = resolveRootDir(trimmed, workdir);
4365
+ const displayPath = resolvedPath.replace(/\/+$/, "") || "/";
4366
+ const blockReason = getRootDirBlockReason(resolvedPath);
4367
+ if (blockReason === "exact") {
4368
+ return res.status(400).json({ error: `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory instead.` });
4369
+ }
4370
+ if (blockReason === "virtual_fs") {
4371
+ return res.status(400).json({ error: `Cannot use paths under "${displayPath}" for workspaces.` });
4372
+ }
4373
+ const dirExists = await checkDirExists(resolvedPath);
4374
+ if (dirExists) {
4375
+ const writableErr = await validatePathWritable(resolvedPath);
4376
+ if (writableErr) return res.status(400).json({ error: writableErr });
4377
+ }
4378
+ config3.rootDir = trimmed;
4379
+ }
4380
+ }
4132
4381
  try {
4133
- const config3 = { setup };
4134
4382
  await saveWorkspaceConfig(workdir, config3);
4135
4383
  res.json({ config: config3 });
4136
4384
  } catch (err) {
4137
4385
  res.status(500).json({ error: err instanceof Error ? err.message : "Failed to save config" });
4138
4386
  }
4139
4387
  });
4388
+ router.post("/config/validate", async (req, res) => {
4389
+ const { rootDir, workdir, projectName, createIfMissing } = req.body;
4390
+ if (!rootDir || typeof rootDir !== "string") {
4391
+ return res.status(400).json({ error: "rootDir is required" });
4392
+ }
4393
+ if (!workdir || typeof workdir !== "string") {
4394
+ return res.status(400).json({ error: "workdir is required" });
4395
+ }
4396
+ const resolvedPath = resolveRootDir(rootDir, workdir);
4397
+ const displayPath = resolvedPath.replace(/\/+$/, "") || "/";
4398
+ const blockReason = getRootDirBlockReason(resolvedPath);
4399
+ if (blockReason === "exact") {
4400
+ const suggestion = typeof projectName === "string" ? `${displayPath}/${projectName}` : void 0;
4401
+ return res.status(400).json({
4402
+ error: suggestion ? `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory like "${suggestion}" instead.` : `Cannot use "${displayPath}" directly as workspace root. Use a subdirectory instead.`
4403
+ });
4404
+ }
4405
+ if (blockReason === "virtual_fs") {
4406
+ return res.status(400).json({ error: `Cannot use paths under "${displayPath}" for workspaces.` });
4407
+ }
4408
+ let dirExists = await checkDirExists(resolvedPath);
4409
+ if (dirExists) {
4410
+ const writableErr = await validatePathWritable(resolvedPath);
4411
+ if (writableErr) return res.status(400).json({ error: writableErr });
4412
+ }
4413
+ let created = false;
4414
+ if (!dirExists && createIfMissing) {
4415
+ await mkdir4(resolvedPath, { recursive: true });
4416
+ dirExists = true;
4417
+ created = true;
4418
+ }
4419
+ const workspaces = [];
4420
+ try {
4421
+ const currentConfig = await loadWorkspaceConfig(workdir);
4422
+ const previousRootDir = currentConfig?.rootDir ? resolveRootDir(currentConfig.rootDir, workdir) : null;
4423
+ if (previousRootDir && previousRootDir !== resolvedPath) {
4424
+ const orphans = await findOrphanedWorkspaces(previousRootDir);
4425
+ workspaces.push(...orphans);
4426
+ } else if (!previousRootDir && projectName && typeof projectName === "string") {
4427
+ const defaultDir = join6(getGlobalDataDir(), "workspaces", projectName);
4428
+ if (defaultDir !== resolvedPath) {
4429
+ const orphans = await findOrphanedWorkspaces(defaultDir);
4430
+ workspaces.push(...orphans);
4431
+ }
4432
+ }
4433
+ } catch {
4434
+ }
4435
+ res.json({ exists: dirExists, resolvedPath, created, workspaces });
4436
+ });
4140
4437
  return router;
4141
4438
  }
4142
4439
 
@@ -4183,19 +4480,19 @@ function createTerminalRoutes() {
4183
4480
 
4184
4481
  // src/server/routes/directories.ts
4185
4482
  import { Router as Router7 } from "express";
4186
- import { readdir } from "fs/promises";
4187
- import { resolve as resolve4, join as join6, dirname as dirname5, basename as basename2 } from "path";
4483
+ import { readdir as readdir2 } from "fs/promises";
4484
+ import { resolve as resolve5, join as join7, dirname as dirname5, basename as basename2 } from "path";
4188
4485
  function createDirectoryRoutes() {
4189
4486
  const router = Router7();
4190
4487
  const DEFAULT_BASE_PATH = process.cwd();
4191
4488
  router.get("/", async (req, res) => {
4192
4489
  const path = req.query["path"] || DEFAULT_BASE_PATH;
4193
4490
  try {
4194
- const resolvedPath = resolve4(path);
4195
- const entries = await readdir(resolvedPath, { withFileTypes: true });
4491
+ const resolvedPath = resolve5(path);
4492
+ const entries = await readdir2(resolvedPath, { withFileTypes: true });
4196
4493
  const directories = entries.filter((entry) => entry.isDirectory()).map((entry) => ({
4197
4494
  name: entry.name,
4198
- path: join6(resolvedPath, entry.name)
4495
+ path: join7(resolvedPath, entry.name)
4199
4496
  })).sort((a, b) => a.name.localeCompare(b.name));
4200
4497
  const parent = dirname5(resolvedPath);
4201
4498
  const hasParent = parent !== resolvedPath;
@@ -4349,7 +4646,7 @@ function isDevMode() {
4349
4646
  return process.env["NODE_ENV"] === "development";
4350
4647
  }
4351
4648
  async function getRemote() {
4352
- return new Promise((resolve8) => {
4649
+ return new Promise((resolve9) => {
4353
4650
  const child = spawn2("git", ["remote"], {
4354
4651
  stdio: ["ignore", "pipe", "pipe"],
4355
4652
  windowsHide: true
@@ -4360,23 +4657,23 @@ async function getRemote() {
4360
4657
  });
4361
4658
  child.on("close", () => {
4362
4659
  const remotes = output.trim().split("\n").map((r) => r.trim()).filter(Boolean);
4363
- if (remotes.includes("upstream")) resolve8("upstream");
4364
- else if (remotes.includes("origin")) resolve8("origin");
4365
- else resolve8("origin");
4660
+ if (remotes.includes("upstream")) resolve9("upstream");
4661
+ else if (remotes.includes("origin")) resolve9("origin");
4662
+ else resolve9("origin");
4366
4663
  });
4367
- child.on("error", () => resolve8("origin"));
4664
+ child.on("error", () => resolve9("origin"));
4368
4665
  });
4369
4666
  }
4370
4667
  async function getLatestDevelopVersion() {
4371
4668
  const remote = await getRemote();
4372
- return new Promise((resolve8) => {
4669
+ return new Promise((resolve9) => {
4373
4670
  const fetchChild = spawn2("git", ["fetch", remote, "develop", "--no-tags"], {
4374
4671
  stdio: ["ignore", "pipe", "pipe"],
4375
4672
  windowsHide: true
4376
4673
  });
4377
4674
  fetchChild.on("error", (err) => {
4378
4675
  logger.error("[auto-update] Git fetch error", { error: err.message });
4379
- resolve8("unknown");
4676
+ resolve9("unknown");
4380
4677
  });
4381
4678
  fetchChild.on("close", (code) => {
4382
4679
  if (code !== 0) {
@@ -4392,7 +4689,7 @@ async function getLatestDevelopVersion() {
4392
4689
  });
4393
4690
  tagChild.on("close", (tagCode) => {
4394
4691
  if (tagCode === 0 && tagOutput.trim()) {
4395
- resolve8(tagOutput.trim());
4692
+ resolve9(tagOutput.trim());
4396
4693
  } else {
4397
4694
  const fallbackChild = spawn2("git", ["describe", "--tags", "--abbrev=0", "develop"], {
4398
4695
  stdio: ["ignore", "pipe", "pipe"],
@@ -4404,18 +4701,18 @@ async function getLatestDevelopVersion() {
4404
4701
  });
4405
4702
  fallbackChild.on("close", (fallbackCode) => {
4406
4703
  if (fallbackCode === 0 && fallbackOutput.trim()) {
4407
- resolve8(fallbackOutput.trim());
4704
+ resolve9(fallbackOutput.trim());
4408
4705
  } else {
4409
4706
  logger.warn("[auto-update] No tags found on develop branch");
4410
- resolve8("unknown");
4707
+ resolve9("unknown");
4411
4708
  }
4412
4709
  });
4413
- fallbackChild.on("error", () => resolve8("unknown"));
4710
+ fallbackChild.on("error", () => resolve9("unknown"));
4414
4711
  }
4415
4712
  });
4416
4713
  tagChild.on("error", (err) => {
4417
4714
  logger.error("[auto-update] Git tag error", { error: err.message });
4418
- resolve8("unknown");
4715
+ resolve9("unknown");
4419
4716
  });
4420
4717
  });
4421
4718
  });
@@ -4432,7 +4729,7 @@ function createAutoUpdateRoutes(options = {}) {
4432
4729
  return;
4433
4730
  }
4434
4731
  try {
4435
- const latest = isDev ? await getLatestDevelopVersion() : await new Promise((resolve8, reject) => {
4732
+ const latest = isDev ? await getLatestDevelopVersion() : await new Promise((resolve9, reject) => {
4436
4733
  const win = process.platform === "win32";
4437
4734
  const child = spawn2(win ? "npm view openfox version" : "npm", win ? [] : ["view", "openfox", "version"], {
4438
4735
  stdio: ["ignore", "pipe", "pipe"],
@@ -4445,7 +4742,7 @@ function createAutoUpdateRoutes(options = {}) {
4445
4742
  });
4446
4743
  child.on("close", (code) => {
4447
4744
  if (code === 0) {
4448
- resolve8(stdout.trim());
4745
+ resolve9(stdout.trim());
4449
4746
  } else {
4450
4747
  reject(new Error(`npm view exited with code ${code}`));
4451
4748
  }
@@ -4460,12 +4757,13 @@ function createAutoUpdateRoutes(options = {}) {
4460
4757
  const latestVersion = latest.replace(/^v/, "");
4461
4758
  const isUpdateAvailable = currentVersion !== latestVersion;
4462
4759
  const now = Date.now();
4463
- versionCache.data = { current, latest, isUpdateAvailable, isService };
4760
+ versionCache.data = { current, latest: latestVersion, isUpdateAvailable, isService };
4464
4761
  versionCache.timestamp = now;
4465
- res.json({ current, latest, isUpdateAvailable, isService });
4762
+ res.json({ current, latest: latestVersion, isUpdateAvailable, isService });
4466
4763
  } catch (err) {
4467
4764
  logger.error("[auto-update] Error in version check", { error: err instanceof Error ? err.message : String(err) });
4468
- res.json({ current, latest: current, isUpdateAvailable: false, isService });
4765
+ const currentVersion = isDev ? current.replace(/-dev$/, "") : current;
4766
+ res.json({ current, latest: currentVersion, isUpdateAvailable: false, isService });
4469
4767
  }
4470
4768
  });
4471
4769
  router.post("/", async (req, res) => {
@@ -4496,9 +4794,9 @@ function createAutoUpdateRoutes(options = {}) {
4496
4794
  const timeout = setTimeout(() => {
4497
4795
  child.kill();
4498
4796
  }, UPDATE_TIMEOUT);
4499
- const exitCode = await new Promise((resolve8) => {
4500
- child.on("close", resolve8);
4501
- child.on("error", () => resolve8(1));
4797
+ const exitCode = await new Promise((resolve9) => {
4798
+ child.on("close", resolve9);
4799
+ child.on("error", () => resolve9(1));
4502
4800
  });
4503
4801
  clearTimeout(timeout);
4504
4802
  if (exitCode === 0) {
@@ -4559,7 +4857,7 @@ function createProviderAuthRoutes(config3, providerManager, registry) {
4559
4857
  try {
4560
4858
  const { challenge, completion } = await adapter.beginLogin({ providerId: provider.id });
4561
4859
  void completion.then(async ({ credentialRef }) => {
4562
- const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-EUMVEFHU.js");
4860
+ const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-MD74ETY5.js");
4563
4861
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4564
4862
  const updatedConfig = updateProvider(globalConfig, provider.id, { credentialRef, authAdapter: adapter.id });
4565
4863
  await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
@@ -4593,7 +4891,7 @@ function createProviderAuthRoutes(config3, providerManager, registry) {
4593
4891
  if (!context) return;
4594
4892
  const { provider, adapter } = context;
4595
4893
  if (provider.credentialRef && adapter) await adapter.logout(provider.credentialRef);
4596
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
4894
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
4597
4895
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
4598
4896
  const updatedConfig = {
4599
4897
  ...globalConfig,
@@ -4670,26 +4968,26 @@ var ProviderRegistry = class {
4670
4968
  };
4671
4969
 
4672
4970
  // src/server/providers/plugins/loader.ts
4673
- import { readdir as readdir2, readFile as readFile2, stat as stat2 } from "fs/promises";
4674
- import { dirname as dirname6, join as join7, resolve as resolve5 } from "path";
4971
+ import { readdir as readdir3, readFile as readFile2, stat as stat3 } from "fs/promises";
4972
+ import { dirname as dirname6, join as join8, resolve as resolve6 } from "path";
4675
4973
  import { fileURLToPath as fileURLToPath5, pathToFileURL } from "url";
4676
4974
  async function packageDirectories(root) {
4677
4975
  let entries;
4678
4976
  try {
4679
- entries = await readdir2(root, { withFileTypes: true });
4977
+ entries = await readdir3(root, { withFileTypes: true });
4680
4978
  } catch {
4681
4979
  return [];
4682
4980
  }
4683
4981
  const directories = [];
4684
4982
  for (const entry of entries) {
4685
- const entryPath = join7(root, entry.name);
4686
- const isDirectory = entry.isDirectory() || entry.isSymbolicLink() && (await stat2(entryPath).catch(() => void 0))?.isDirectory();
4983
+ const entryPath = join8(root, entry.name);
4984
+ const isDirectory = entry.isDirectory() || entry.isSymbolicLink() && (await stat3(entryPath).catch(() => void 0))?.isDirectory();
4687
4985
  if (!isDirectory) continue;
4688
4986
  if (entry.name.startsWith("@")) {
4689
- const scoped = await readdir2(entryPath, { withFileTypes: true }).catch(() => []);
4987
+ const scoped = await readdir3(entryPath, { withFileTypes: true }).catch(() => []);
4690
4988
  for (const child of scoped) {
4691
- const childPath = join7(entryPath, child.name);
4692
- const childIsDirectory = child.isDirectory() || child.isSymbolicLink() && (await stat2(childPath).catch(() => void 0))?.isDirectory();
4989
+ const childPath = join8(entryPath, child.name);
4990
+ const childIsDirectory = child.isDirectory() || child.isSymbolicLink() && (await stat3(childPath).catch(() => void 0))?.isDirectory();
4693
4991
  if (childIsDirectory) directories.push(childPath);
4694
4992
  }
4695
4993
  } else {
@@ -4701,16 +4999,16 @@ async function packageDirectories(root) {
4701
4999
  async function loadProviderPlugins(options) {
4702
5000
  const moduleDirectory = dirname6(fileURLToPath5(import.meta.url));
4703
5001
  const roots = [
4704
- join7(options.configDirectory, "plugins"),
4705
- join7(options.cwd ?? process.cwd(), "node_modules"),
4706
- resolve5(moduleDirectory, "node_modules"),
4707
- resolve5(moduleDirectory, "../../../../node_modules")
5002
+ join8(options.configDirectory, "plugins"),
5003
+ join8(options.cwd ?? process.cwd(), "node_modules"),
5004
+ resolve6(moduleDirectory, "node_modules"),
5005
+ resolve6(moduleDirectory, "../../../../node_modules")
4708
5006
  ];
4709
5007
  const seen = /* @__PURE__ */ new Set();
4710
5008
  const diagnostics = [];
4711
5009
  for (const root of roots) {
4712
5010
  for (const packageDir of await packageDirectories(root)) {
4713
- const manifestPath = join7(packageDir, "package.json");
5011
+ const manifestPath = join8(packageDir, "package.json");
4714
5012
  let manifest;
4715
5013
  try {
4716
5014
  manifest = JSON.parse(await readFile2(manifestPath, "utf8"));
@@ -4751,7 +5049,7 @@ async function loadProviderPlugins(options) {
4751
5049
  }
4752
5050
  };
4753
5051
  try {
4754
- const module = await import(pathToFileURL(join7(packageDir, plugin)).href);
5052
+ const module = await import(pathToFileURL(join8(packageDir, plugin)).href);
4755
5053
  if (typeof module.register !== "function") throw new Error("Plugin does not export register(registry)");
4756
5054
  await module.register(trackingRegistry);
4757
5055
  diagnostic.loaded = true;
@@ -4766,9 +5064,9 @@ async function loadProviderPlugins(options) {
4766
5064
  // src/server/routes/plugins.ts
4767
5065
  import { Router as Router11 } from "express";
4768
5066
  import { fileURLToPath as fileURLToPath6, pathToFileURL as pathToFileURL2 } from "url";
4769
- import { dirname as dirname7, resolve as resolve6, join as join8 } from "path";
5067
+ import { dirname as dirname7, resolve as resolve7, join as join9 } from "path";
4770
5068
  import { existsSync } from "fs";
4771
- import { readFile as readFile3, readdir as readdir3, rm as rm2, rename as rename2 } from "fs/promises";
5069
+ import { readFile as readFile3, readdir as readdir4, rm as rm2, rename as rename2 } from "fs/promises";
4772
5070
  import { platform } from "os";
4773
5071
  import { execFile } from "child_process";
4774
5072
  import { promisify } from "util";
@@ -4794,9 +5092,9 @@ function createPluginRoutes(options) {
4794
5092
  return res.json({ plugins: registryCache.data });
4795
5093
  }
4796
5094
  const moduleDir = dirname7(fileURLToPath6(import.meta.url));
4797
- let registryPath = resolve6(moduleDir, "../plugins-registry.json");
5095
+ let registryPath = resolve7(moduleDir, "../plugins-registry.json");
4798
5096
  if (!existsSync(registryPath)) {
4799
- registryPath = resolve6(moduleDir, "../../../plugins-registry.json");
5097
+ registryPath = resolve7(moduleDir, "../../../plugins-registry.json");
4800
5098
  }
4801
5099
  const data = JSON.parse(await readFile3(registryPath, "utf8"));
4802
5100
  registryCache = { data, ts: now };
@@ -4819,9 +5117,9 @@ function createPluginRoutes(options) {
4819
5117
  if (!/^[a-zA-Z0-9_-]+$/.test(repoName)) {
4820
5118
  return res.status(400).json({ error: "Invalid repository name" });
4821
5119
  }
4822
- const pluginsDir = join8(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4823
- const targetDir = join8(pluginsDir, repoName);
4824
- const tmpDir = join8(pluginsDir, `.${repoName}-tmp-${Date.now()}`);
5120
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
5121
+ const targetDir = join9(pluginsDir, repoName);
5122
+ const tmpDir = join9(pluginsDir, `.${repoName}-tmp-${Date.now()}`);
4825
5123
  try {
4826
5124
  await execFileP("mkdir", ["-p", pluginsDir], { timeout: 5e3 });
4827
5125
  } catch {
@@ -4852,7 +5150,7 @@ function createPluginRoutes(options) {
4852
5150
  }
4853
5151
  if (!loadError) {
4854
5152
  try {
4855
- const manifest = JSON.parse(await readFile3(join8(targetDir, "package.json"), "utf8"));
5153
+ const manifest = JSON.parse(await readFile3(join9(targetDir, "package.json"), "utf8"));
4856
5154
  const pluginEntry = manifest.openfox?.plugin;
4857
5155
  const apiVersion = manifest.openfox?.apiVersion;
4858
5156
  if (!pluginEntry || !manifest.name) {
@@ -4860,7 +5158,7 @@ function createPluginRoutes(options) {
4860
5158
  } else if (apiVersion !== 1) {
4861
5159
  loadError = `Unsupported plugin API version: ${String(apiVersion)}`;
4862
5160
  } else {
4863
- const mod = await import(pathToFileURL2(join8(targetDir, pluginEntry)).href);
5161
+ const mod = await import(pathToFileURL2(join9(targetDir, pluginEntry)).href);
4864
5162
  if (typeof mod.register !== "function") {
4865
5163
  loadError = "Plugin does not export register(registry)";
4866
5164
  } else {
@@ -4909,13 +5207,13 @@ function createPluginRoutes(options) {
4909
5207
  }
4910
5208
  });
4911
5209
  router.get("/installed", async (_req, res) => {
4912
- const pluginsDir = join8(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
5210
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4913
5211
  try {
4914
- const entries = await readdir3(pluginsDir, { withFileTypes: true });
5212
+ const entries = await readdir4(pluginsDir, { withFileTypes: true });
4915
5213
  const installed = [];
4916
5214
  for (const entry of entries) {
4917
5215
  if (!entry.isDirectory()) continue;
4918
- const pkgPath = join8(pluginsDir, entry.name, "package.json");
5216
+ const pkgPath = join9(pluginsDir, entry.name, "package.json");
4919
5217
  let version = null;
4920
5218
  try {
4921
5219
  const pkg = JSON.parse(await readFile3(pkgPath, "utf8"));
@@ -4930,13 +5228,13 @@ function createPluginRoutes(options) {
4930
5228
  }
4931
5229
  });
4932
5230
  router.get("/open-folder", async (_req, res) => {
4933
- const pluginsDir = join8(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
5231
+ const pluginsDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins");
4934
5232
  await openFolder(pluginsDir, res);
4935
5233
  });
4936
5234
  router.get("/:name/open-folder", async (req, res) => {
4937
5235
  const name = req.params.name;
4938
5236
  if (!/^[a-zA-Z0-9_-]+$/.test(name)) return res.status(400).json({ error: "Invalid plugin name" });
4939
- const targetDir = join8(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
5237
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4940
5238
  await openFolder(targetDir, res);
4941
5239
  });
4942
5240
  router.delete("/:name", async (req, res) => {
@@ -4944,7 +5242,7 @@ function createPluginRoutes(options) {
4944
5242
  if (!/^[a-zA-Z0-9_-]+$/.test(name)) {
4945
5243
  return res.status(400).json({ error: "Invalid plugin name" });
4946
5244
  }
4947
- const targetDir = join8(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
5245
+ const targetDir = join9(getGlobalConfigDir(config3.mode ?? "production"), "plugins", name);
4948
5246
  try {
4949
5247
  await rm2(targetDir, { recursive: true, force: true });
4950
5248
  for (let i = pluginDiagnostics.length - 1; i >= 0; i--) {
@@ -5073,7 +5371,7 @@ async function createServerHandle(config3) {
5073
5371
  const mcpManager = new McpManager({
5074
5372
  onToolsDiscovered: async (name, tools) => {
5075
5373
  try {
5076
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
5374
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
5077
5375
  const mode = config3.mode ?? "production";
5078
5376
  const globalConfig = await loadGlobalConfig(mode, config3.globalConfigPath);
5079
5377
  const mcpServers2 = {
@@ -5104,12 +5402,11 @@ async function createServerHandle(config3) {
5104
5402
  setMcpTools(mcpTools);
5105
5403
  logger.info("MCP tools registered", { count: mcpTools.length });
5106
5404
  }
5107
- const { signalMcpReady } = await import("./server-SPYFMFQE.js");
5405
+ const { signalMcpReady } = await import("./server-3XUD2NB7.js");
5108
5406
  signalMcpReady();
5109
5407
  });
5110
5408
  const app = express();
5111
5409
  app.use(cors());
5112
- app.use(express.json({ limit: "10mb" }));
5113
5410
  const authMiddleware = async (req, res, next) => {
5114
5411
  const path = req.path;
5115
5412
  const publicPaths = ["/health", "/auth", "/auth/login", "/auto-update/check"];
@@ -5127,6 +5424,7 @@ async function createServerHandle(config3) {
5127
5424
  next();
5128
5425
  };
5129
5426
  app.use("/api", authMiddleware);
5427
+ app.use(express.json({ limit: "75mb" }));
5130
5428
  app.get("/api/health", (_req, res) => {
5131
5429
  res.json({ status: "ok", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
5132
5430
  });
@@ -5166,7 +5464,7 @@ async function createServerHandle(config3) {
5166
5464
  res.json({ tools });
5167
5465
  });
5168
5466
  app.get("/api/projects", async (_req, res) => {
5169
- const { listProjects } = await import("./projects-WSVINK2C.js");
5467
+ const { listProjects } = await import("./projects-ZGJGICGT.js");
5170
5468
  const projects = listProjects();
5171
5469
  res.json({ projects });
5172
5470
  });
@@ -5175,7 +5473,7 @@ async function createServerHandle(config3) {
5175
5473
  if (!name || !workdir) {
5176
5474
  return res.status(400).json({ error: "name and workdir are required" });
5177
5475
  }
5178
- const { createDirectoryWithGit } = await import("./project-creator-C22J43BC.js");
5476
+ const { createDirectoryWithGit } = await import("./project-creator-NNUITNUI.js");
5179
5477
  try {
5180
5478
  const project = await createDirectoryWithGit(name, workdir);
5181
5479
  res.status(201).json({ project });
@@ -5220,7 +5518,7 @@ async function createServerHandle(config3) {
5220
5518
  }
5221
5519
  });
5222
5520
  app.get("/api/projects/:id", async (req, res) => {
5223
- const { getProject: getProject2 } = await import("./projects-WSVINK2C.js");
5521
+ const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
5224
5522
  const project = getProject2(req.params.id);
5225
5523
  if (!project) {
5226
5524
  return res.status(404).json({ error: "Project not found" });
@@ -5228,7 +5526,7 @@ async function createServerHandle(config3) {
5228
5526
  res.json({ project });
5229
5527
  });
5230
5528
  app.put("/api/projects/:id", async (req, res) => {
5231
- const { updateProject } = await import("./projects-WSVINK2C.js");
5529
+ const { updateProject } = await import("./projects-ZGJGICGT.js");
5232
5530
  const { name, customInstructions, dangerLevel } = req.body;
5233
5531
  const updates = {};
5234
5532
  if (name !== void 0) updates.name = name;
@@ -5241,7 +5539,7 @@ async function createServerHandle(config3) {
5241
5539
  res.json({ project: updated });
5242
5540
  });
5243
5541
  app.delete("/api/projects/:id", async (req, res) => {
5244
- const { getProject: getProject2, deleteProject } = await import("./projects-WSVINK2C.js");
5542
+ const { getProject: getProject2, deleteProject } = await import("./projects-ZGJGICGT.js");
5245
5543
  const project = getProject2(req.params.id);
5246
5544
  if (!project) {
5247
5545
  return res.status(404).json({ error: "Project not found" });
@@ -5250,7 +5548,7 @@ async function createServerHandle(config3) {
5250
5548
  res.json({ success: true });
5251
5549
  });
5252
5550
  app.put("/api/projects/:id/star", async (req, res) => {
5253
- const { toggleStar } = await import("./projects-WSVINK2C.js");
5551
+ const { toggleStar } = await import("./projects-ZGJGICGT.js");
5254
5552
  const { isStarred } = req.body;
5255
5553
  if (typeof isStarred !== "boolean") {
5256
5554
  return res.status(400).json({ error: "isStarred is required and must be a boolean" });
@@ -5262,37 +5560,60 @@ async function createServerHandle(config3) {
5262
5560
  res.json({ project });
5263
5561
  });
5264
5562
  app.get("/api/projects/:id/branches", async (req, res) => {
5265
- const { getProject: getProject2 } = await import("./projects-WSVINK2C.js");
5563
+ const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
5266
5564
  const project = getProject2(req.params.id);
5267
5565
  if (!project) return res.status(404).json({ error: "Project not found" });
5268
- const { listBranches } = await import("./workspace-6QTTUMMW.js");
5566
+ const { listBranches } = await import("./workspace-J74ZMSOJ.js");
5269
5567
  const branches = await listBranches(project.workdir);
5270
5568
  res.json({ branches });
5271
5569
  });
5272
5570
  app.post("/api/projects/:id/checkout", async (req, res) => {
5273
- const { getProject: getProject2 } = await import("./projects-WSVINK2C.js");
5571
+ const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
5274
5572
  const project = getProject2(req.params.id);
5275
5573
  if (!project) return res.status(404).json({ error: "Project not found" });
5276
5574
  const { branch } = req.body;
5277
5575
  if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
5278
- const { checkoutBranch } = await import("./workspace-6QTTUMMW.js");
5576
+ const { checkoutBranch } = await import("./workspace-J74ZMSOJ.js");
5279
5577
  try {
5280
5578
  await checkoutBranch(project.workdir, branch);
5579
+ const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
5580
+ const allSessions = sessionManager.listSessions();
5581
+ for (const s of allSessions) {
5582
+ const effectiveWorkdir = s.workspace ?? s.workdir;
5583
+ if (effectiveWorkdir === project.workdir) {
5584
+ updateSessionBranch2(s.id, branch);
5585
+ sessionManager.emitBranchChange(s.id);
5586
+ }
5587
+ }
5281
5588
  res.json({ branch });
5282
5589
  } catch (err) {
5283
5590
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to checkout branch" });
5284
5591
  }
5285
5592
  });
5286
5593
  app.post("/api/projects/:id/checkout-new", async (req, res) => {
5287
- const { getProject: getProject2 } = await import("./projects-WSVINK2C.js");
5594
+ const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
5288
5595
  const project = getProject2(req.params.id);
5289
5596
  if (!project) return res.status(404).json({ error: "Project not found" });
5290
- const { name } = req.body;
5597
+ const { name, sourceBranch } = req.body;
5291
5598
  if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
5292
- const { createBranch } = await import("./workspace-6QTTUMMW.js");
5599
+ const { createBranch, resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
5293
5600
  try {
5294
- await createBranch(project.workdir, name);
5295
- res.json({ branch: name });
5601
+ await validateRef2(project.workdir, name);
5602
+ let sb;
5603
+ if (sourceBranch) {
5604
+ sb = await resolveAndValidateSourceBranch2(project.workdir, sourceBranch, project.workdir);
5605
+ }
5606
+ await createBranch(project.workdir, name, sb);
5607
+ const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
5608
+ const allSessions = sessionManager.listSessions();
5609
+ for (const s of allSessions) {
5610
+ const effectiveWorkdir = s.workspace ?? s.workdir;
5611
+ if (effectiveWorkdir === project.workdir) {
5612
+ updateSessionBranch2(s.id, name);
5613
+ sessionManager.emitBranchChange(s.id);
5614
+ }
5615
+ }
5616
+ res.json({ branch: name, sourceBranch: sourceBranch ?? null });
5296
5617
  } catch (err) {
5297
5618
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
5298
5619
  }
@@ -5301,9 +5622,13 @@ async function createServerHandle(config3) {
5301
5622
  const session = sessionManager.getSession(req.params.id);
5302
5623
  if (!session) return res.status(404).json({ error: "Session not found" });
5303
5624
  const effectiveWorkdir = session.workspace ?? session.workdir;
5304
- const { listBranches } = await import("./workspace-6QTTUMMW.js");
5305
- const branches = await listBranches(effectiveWorkdir);
5306
- res.json({ branches });
5625
+ const { listBranches, getDefaultBranch: getDefaultBranch2 } = await import("./workspace-J74ZMSOJ.js");
5626
+ const [branches, defaultBranch] = await Promise.all([
5627
+ listBranches(effectiveWorkdir),
5628
+ getDefaultBranch2(session.workdir)
5629
+ // project root, not workspace, for real upstream origin/HEAD
5630
+ ]);
5631
+ res.json({ branches, defaultBranch });
5307
5632
  });
5308
5633
  app.post("/api/sessions/:id/checkout", async (req, res) => {
5309
5634
  const session = sessionManager.getSession(req.params.id);
@@ -5311,10 +5636,21 @@ async function createServerHandle(config3) {
5311
5636
  const { branch } = req.body;
5312
5637
  if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
5313
5638
  const effectiveWorkdir = session.workspace ?? session.workdir;
5314
- const { checkoutBranch, validateRef } = await import("./workspace-6QTTUMMW.js");
5639
+ const { checkoutBranch, validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
5640
+ const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
5315
5641
  try {
5316
- await validateRef(effectiveWorkdir, branch);
5642
+ await validateRef2(effectiveWorkdir, branch);
5317
5643
  await checkoutBranch(effectiveWorkdir, branch);
5644
+ updateSessionBranch2(req.params.id, branch);
5645
+ if (session.workspace) {
5646
+ const all = sessionManager.listSessions();
5647
+ for (const s of all) {
5648
+ if (s.id !== req.params.id && s.workspace === session.workspace) {
5649
+ updateSessionBranch2(s.id, branch);
5650
+ sessionManager.emitBranchChange(s.id);
5651
+ }
5652
+ }
5653
+ }
5318
5654
  res.json({ branch });
5319
5655
  } catch (err) {
5320
5656
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to checkout branch" });
@@ -5323,30 +5659,48 @@ async function createServerHandle(config3) {
5323
5659
  app.post("/api/sessions/:id/checkout-new", async (req, res) => {
5324
5660
  const session = sessionManager.getSession(req.params.id);
5325
5661
  if (!session) return res.status(404).json({ error: "Session not found" });
5326
- const { name } = req.body;
5662
+ const { name, sourceBranch } = req.body;
5327
5663
  if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
5328
5664
  const effectiveWorkdir = session.workspace ?? session.workdir;
5329
- const { createBranch, validateRef } = await import("./workspace-6QTTUMMW.js");
5665
+ const { validateRef: validateRef2 } = await import("./workspace-J74ZMSOJ.js");
5666
+ const { updateSessionBranch: updateSessionBranch2 } = await import("./sessions-SFG42M4W.js");
5330
5667
  try {
5331
- await validateRef(effectiveWorkdir, name);
5332
- await createBranch(effectiveWorkdir, name);
5333
- res.json({ branch: name });
5668
+ await validateRef2(effectiveWorkdir, name);
5669
+ if (sourceBranch) {
5670
+ const { resolveAndValidateSourceBranch: resolveAndValidateSourceBranch2, createBranch } = await import("./workspace-J74ZMSOJ.js");
5671
+ const sb = await resolveAndValidateSourceBranch2(effectiveWorkdir, sourceBranch, session.workdir);
5672
+ await createBranch(effectiveWorkdir, name, sb);
5673
+ } else {
5674
+ const { createBranch } = await import("./workspace-J74ZMSOJ.js");
5675
+ await createBranch(effectiveWorkdir, name);
5676
+ }
5677
+ updateSessionBranch2(req.params.id, name);
5678
+ if (session.workspace) {
5679
+ const all = sessionManager.listSessions();
5680
+ for (const s of all) {
5681
+ if (s.id !== req.params.id && s.workspace === session.workspace) {
5682
+ updateSessionBranch2(s.id, name);
5683
+ sessionManager.emitBranchChange(s.id);
5684
+ }
5685
+ }
5686
+ }
5687
+ res.json({ branch: name, sourceBranch: sourceBranch ?? null });
5334
5688
  } catch (err) {
5335
5689
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
5336
5690
  }
5337
5691
  });
5338
5692
  app.get("/api/projects/:id/workspaces", async (req, res) => {
5339
- const { getProject: getProject2 } = await import("./projects-WSVINK2C.js");
5693
+ const { getProject: getProject2 } = await import("./projects-ZGJGICGT.js");
5340
5694
  const project = getProject2(req.params.id);
5341
5695
  if (!project) return res.status(404).json({ error: "Project not found" });
5342
- const { listWorkspaces } = await import("./workspace-6QTTUMMW.js");
5343
- const all = await listWorkspaces(project.name);
5696
+ const { listWorkspaces } = await import("./workspace-J74ZMSOJ.js");
5697
+ const all = await listWorkspaces(project.name, project.workdir);
5344
5698
  const workspacesList = all.filter((ws) => ws.path !== project.workdir);
5345
5699
  res.json({ workspaces: workspacesList });
5346
5700
  });
5347
5701
  app.get("/api/sessions", async (req, res) => {
5348
- const { getRecentUserPromptsForSession } = await import("./events-FLWRLM6V.js");
5349
- const { getPendingConfirmationsBySession } = await import("./path-security-ZROWNL22.js");
5702
+ const { getRecentUserPromptsForSession } = await import("./events-KNOZX7YG.js");
5703
+ const { getPendingConfirmationsBySession } = await import("./path-security-HUXQHEQ5.js");
5350
5704
  const projectId = req.query["projectId"];
5351
5705
  const limit = Math.min(parseInt(req.query["limit"]) || 20, 100);
5352
5706
  const offset = parseInt(req.query["offset"]) || 0;
@@ -5384,7 +5738,7 @@ async function createServerHandle(config3) {
5384
5738
  }
5385
5739
  const { providerId, model } = parseDefaultModelSelection(config3.defaultModelSelection);
5386
5740
  const session = sessionManager.createSession(projectId, title, providerId ?? null, model ?? null);
5387
- wssExports.broadcastAll({
5741
+ wssExports.broadcastForProject(projectId, session.id, {
5388
5742
  type: "session.created",
5389
5743
  sessionId: session.id,
5390
5744
  payload: {
@@ -5412,10 +5766,10 @@ async function createServerHandle(config3) {
5412
5766
  app.post("/api/sessions/:id/switch-workspace", async (req, res) => {
5413
5767
  const session = sessionManager.getSession(req.params.id);
5414
5768
  if (!session) return res.status(404).json({ error: "Session not found" });
5415
- const { target, branch } = req.body;
5769
+ const { target, branch, sourceBranch } = req.body;
5416
5770
  if (!target || typeof target !== "string") return res.status(400).json({ error: "target is required" });
5417
5771
  try {
5418
- const updated = await sessionManager.switchWorkspace(req.params.id, target, branch);
5772
+ const updated = await sessionManager.switchWorkspace(req.params.id, target, branch, sourceBranch);
5419
5773
  res.json({ session: updated });
5420
5774
  } catch (err) {
5421
5775
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to switch workspace" });
@@ -5434,21 +5788,32 @@ async function createServerHandle(config3) {
5434
5788
  }
5435
5789
  });
5436
5790
  app.get("/api/sessions/:id", async (req, res) => {
5437
- const { getEventStore: getEventStore2 } = await import("./events-FLWRLM6V.js");
5438
- const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-ONH3D4OK.js");
5439
- const { getPendingQuestionsForSession } = await import("./tools-JRCUWPJO.js");
5791
+ const { getEventStore: getEventStore2, combineEventsWithSnapshot } = await import("./events-KNOZX7YG.js");
5792
+ const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-OPP2BVUB.js");
5793
+ const { getPendingQuestionsForSession } = await import("./tools-HGKUD327.js");
5794
+ const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
5440
5795
  const session = sessionManager.getSession(req.params.id);
5441
5796
  if (!session) {
5442
5797
  return res.status(404).json({ error: "Session not found" });
5443
5798
  }
5444
5799
  const eventStore = getEventStore2();
5445
- const events = eventStore.getEvents(req.params.id);
5446
- const messages = buildMessagesFromStoredEvents(events);
5800
+ const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(req.params.id);
5801
+ const events = combineEventsWithSnapshot(req.params.id, snapshot, eventsSinceSnapshot);
5802
+ const maxVisibleItems = req.query["full"] === "true" ? void 0 : getMaxVisibleItems() || void 0;
5803
+ const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems);
5447
5804
  const contextState = sessionManager.getContextState(req.params.id);
5448
5805
  const queueState = sessionManager.getQueueState(req.params.id);
5449
5806
  const pendingQuestions = getPendingQuestionsForSession(req.params.id);
5450
5807
  const pendingConfirmations = foldPendingConfirmations(events);
5451
- res.json({ session, messages, contextState, queueState, pendingQuestions, pendingConfirmations });
5808
+ res.json({
5809
+ session,
5810
+ messages,
5811
+ hiddenCount,
5812
+ contextState,
5813
+ queueState,
5814
+ pendingQuestions,
5815
+ pendingConfirmations
5816
+ });
5452
5817
  });
5453
5818
  app.delete("/api/sessions/:id", async (req, res) => {
5454
5819
  const sessionId = req.params["id"];
@@ -5456,8 +5821,8 @@ async function createServerHandle(config3) {
5456
5821
  if (!session) {
5457
5822
  return res.status(404).json({ error: "Session not found" });
5458
5823
  }
5459
- const { stopSessionExecution } = await import("./chat-handler-3BZ4PDL2.js");
5460
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-JRCUWPJO.js");
5824
+ const { stopSessionExecution } = await import("./chat-handler-5ZHPYU24.js");
5825
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-HGKUD327.js");
5461
5826
  sessionManager.clearMessageQueue(sessionId);
5462
5827
  stopSessionExecution(sessionId, sessionManager);
5463
5828
  abortSession(sessionId);
@@ -5486,8 +5851,9 @@ async function createServerHandle(config3) {
5486
5851
  res.json({ success: true });
5487
5852
  });
5488
5853
  app.post("/api/sessions/:id/provider", async (req, res) => {
5489
- const { getEventStore: getEventStore2 } = await import("./events-FLWRLM6V.js");
5490
- const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5854
+ const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-KNOZX7YG.js");
5855
+ const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
5856
+ const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
5491
5857
  const sessionId = req.params.id;
5492
5858
  const session = sessionManager.getSession(sessionId);
5493
5859
  if (!session) {
@@ -5502,10 +5868,12 @@ async function createServerHandle(config3) {
5502
5868
  sessionManager.setSessionProvider(sessionId, providerId, resolvedModel);
5503
5869
  const contextState = sessionManager.getContextState(sessionId);
5504
5870
  const eventStore = getEventStore2();
5505
- const events = eventStore.getEvents(sessionId);
5506
- const messages = buildMessagesFromStoredEvents(events);
5871
+ const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
5872
+ const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
5873
+ const maxVisibleItems = getMaxVisibleItems();
5874
+ const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
5507
5875
  const updatedSession = sessionManager.getSession(sessionId);
5508
- res.json({ session: updatedSession, messages, contextState });
5876
+ res.json({ session: updatedSession, messages, hiddenCount, contextState });
5509
5877
  });
5510
5878
  app.post("/api/default-model", async (req, res) => {
5511
5879
  const { providerId, model } = req.body;
@@ -5520,7 +5888,7 @@ async function createServerHandle(config3) {
5520
5888
  if (!result.success) {
5521
5889
  return res.status(500).json({ error: result.error ?? "Failed to set default model" });
5522
5890
  }
5523
- const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-EUMVEFHU.js");
5891
+ const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-MD74ETY5.js");
5524
5892
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5525
5893
  const updatedConfig = setDefaultModelSelection(globalConfig, providerId, model);
5526
5894
  await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
@@ -5598,8 +5966,9 @@ async function createServerHandle(config3) {
5598
5966
  res.json({ success: true });
5599
5967
  });
5600
5968
  app.put("/api/sessions/:id/mode", async (req, res) => {
5601
- const { getEventStore: getEventStore2 } = await import("./events-FLWRLM6V.js");
5602
- const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5969
+ const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEv } = await import("./events-KNOZX7YG.js");
5970
+ const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
5971
+ const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
5603
5972
  const sessionId = req.params.id;
5604
5973
  const session = sessionManager.getSession(sessionId);
5605
5974
  if (!session) {
@@ -5616,10 +5985,12 @@ async function createServerHandle(config3) {
5616
5985
  }
5617
5986
  sessionManager.setMode(sessionId, mode);
5618
5987
  const eventStore = getEventStore2();
5619
- const events = eventStore.getEvents(sessionId);
5620
- const messages = buildMessagesFromStoredEvents(events);
5988
+ const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
5989
+ const events = combineEv(sessionId, snapshot, eventsSinceSnapshot);
5990
+ const maxVisibleItems = getMaxVisibleItems();
5991
+ const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
5621
5992
  const updatedSession = sessionManager.getSession(sessionId);
5622
- res.json({ session: updatedSession, messages });
5993
+ res.json({ session: updatedSession, messages, hiddenCount });
5623
5994
  });
5624
5995
  app.put("/api/sessions/:id/danger-level", async (req, res) => {
5625
5996
  const sessionId = req.params.id;
@@ -5652,29 +6023,50 @@ async function createServerHandle(config3) {
5652
6023
  app.post("/api/sessions/:id/confirm-path", async (req, res) => {
5653
6024
  const sessionId = req.params.id;
5654
6025
  const { callId, approved, alwaysAllow } = req.body;
5655
- if (!callId || approved === void 0) {
5656
- return res.status(400).json({ error: "callId and approved are required" });
6026
+ if (!callId || typeof approved !== "boolean") {
6027
+ return res.status(400).json({ error: "callId and approved (boolean) are required" });
6028
+ }
6029
+ if (alwaysAllow !== void 0 && typeof alwaysAllow !== "boolean") {
6030
+ return res.status(400).json({ error: "alwaysAllow must be a boolean if provided" });
6031
+ }
6032
+ const { providePathConfirmation, getConfirmationSessionId } = await import("./tools-HGKUD327.js");
6033
+ const pendingSessionId = getConfirmationSessionId(callId);
6034
+ if (!pendingSessionId) {
6035
+ return res.status(404).json({ error: "No pending path confirmation with that ID" });
6036
+ }
6037
+ if (pendingSessionId !== sessionId) {
6038
+ return res.status(403).json({ error: "Confirmation does not belong to this session" });
5657
6039
  }
5658
- const { providePathConfirmation } = await import("./tools-JRCUWPJO.js");
5659
6040
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5660
6041
  if (!result.found) {
5661
6042
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
5662
6043
  }
5663
- const { getEventStore: getEventStore2 } = await import("./events-FLWRLM6V.js");
5664
- const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-ONH3D4OK.js");
5665
- const { createSessionStateMessage } = await import("./protocol-EWEVW65B.js");
5666
- const { getPendingQuestionsForSession } = await import("./tools-JRCUWPJO.js");
6044
+ const { getEventStore: getEventStore2, combineEventsWithSnapshot: combineEvents } = await import("./events-KNOZX7YG.js");
6045
+ const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-OPP2BVUB.js");
6046
+ const { createSessionStateMessage } = await import("./protocol-Q7X4NEME.js");
6047
+ const { getPendingQuestionsForSession } = await import("./tools-HGKUD327.js");
6048
+ const { getMaxVisibleItems } = await import("./settings-6M55N4Q2.js");
5667
6049
  const eventStore = getEventStore2();
5668
- const events = eventStore.getEvents(sessionId);
5669
- const messages = buildMessagesFromStoredEvents(events);
6050
+ const { snapshot, events: eventsSinceSnapshot } = eventStore.getEventsSinceSnapshot(sessionId);
6051
+ const events = combineEvents(sessionId, snapshot, eventsSinceSnapshot);
6052
+ const maxVisibleItems = getMaxVisibleItems();
6053
+ const { messages, hiddenCount } = buildMessagesFromStoredEvents(events, maxVisibleItems || void 0);
5670
6054
  const pendingConfirmations = foldPendingConfirmations(events);
5671
6055
  const pendingQuestions = getPendingQuestionsForSession(sessionId);
5672
6056
  const session = sessionManager.getSession(sessionId);
5673
6057
  if (session) {
5674
- const stateMsg = createSessionStateMessage(session, messages, pendingConfirmations, pendingQuestions);
6058
+ const stateMsg = createSessionStateMessage(
6059
+ session,
6060
+ messages,
6061
+ pendingConfirmations,
6062
+ pendingQuestions,
6063
+ void 0,
6064
+ void 0,
6065
+ hiddenCount
6066
+ );
5675
6067
  wssExports.broadcastForSession(sessionId, { ...stateMsg, sessionId });
5676
6068
  }
5677
- wssExports.broadcastAll({
6069
+ wssExports.broadcastForSession(sessionId, {
5678
6070
  type: "session.confirmation_resolved",
5679
6071
  sessionId,
5680
6072
  payload: { sessionId, callId }
@@ -5689,7 +6081,7 @@ async function createServerHandle(config3) {
5689
6081
  if (!skip && typeof answer !== "string") {
5690
6082
  return res.status(400).json({ error: "answer is required when not skipping" });
5691
6083
  }
5692
- const { provideAnswer } = await import("./tools-JRCUWPJO.js");
6084
+ const { provideAnswer } = await import("./tools-HGKUD327.js");
5693
6085
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
5694
6086
  if (!found) {
5695
6087
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5717,7 +6109,7 @@ async function createServerHandle(config3) {
5717
6109
  if (!session) {
5718
6110
  return res.status(404).json({ error: "Session not found" });
5719
6111
  }
5720
- const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-7XDMSPIC.js");
6112
+ const { getSetting: getSetting2, SETTINGS_KEYS } = await import("./settings-6M55N4Q2.js");
5721
6113
  if (getSetting2(SETTINGS_KEYS.CACHE_WARMING) !== "true") {
5722
6114
  return res.json({ success: false, reason: "disabled" });
5723
6115
  }
@@ -5749,7 +6141,7 @@ async function createServerHandle(config3) {
5749
6141
  backend: activeProvider?.backend ?? llmClient.getBackend(),
5750
6142
  model: llmClient.getModel()
5751
6143
  };
5752
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-5IMCWX5P.js");
6144
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-L6LX5BV7.js");
5753
6145
  runAgentTurn(
5754
6146
  {
5755
6147
  sessionManager,
@@ -5787,15 +6179,15 @@ async function createServerHandle(config3) {
5787
6179
  if (!session) {
5788
6180
  return res.status(404).json({ error: "Session not found" });
5789
6181
  }
5790
- const { stopSessionExecution } = await import("./chat-handler-3BZ4PDL2.js");
5791
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-JRCUWPJO.js");
6182
+ const { stopSessionExecution } = await import("./chat-handler-5ZHPYU24.js");
6183
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-HGKUD327.js");
5792
6184
  const queuedMessages = sessionManager.getQueueState(sessionId);
5793
6185
  sessionManager.clearMessageQueue(sessionId);
5794
6186
  stopSessionExecution(sessionId, sessionManager);
5795
6187
  abortSession(sessionId);
5796
6188
  cancelQuestionsForSession(sessionId, "Session stopped by user");
5797
6189
  cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
5798
- const eventStore = (await import("./events-FLWRLM6V.js")).getEventStore();
6190
+ const eventStore = (await import("./events-KNOZX7YG.js")).getEventStore();
5799
6191
  eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
5800
6192
  res.json({ success: true, queuedMessages });
5801
6193
  });
@@ -5809,7 +6201,7 @@ async function createServerHandle(config3) {
5809
6201
  if (typeof messageIndex !== "number" || messageIndex < 0) {
5810
6202
  return res.status(400).json({ error: "messageIndex must be a non-negative number" });
5811
6203
  }
5812
- const { truncateSessionMessages } = await import("./events-FLWRLM6V.js");
6204
+ const { truncateSessionMessages } = await import("./events-KNOZX7YG.js");
5813
6205
  truncateSessionMessages(sessionId, messageIndex);
5814
6206
  res.json({ success: true });
5815
6207
  });
@@ -5826,11 +6218,11 @@ async function createServerHandle(config3) {
5826
6218
  if (content !== void 0 && (typeof content !== "string" || !content.trim())) {
5827
6219
  return res.status(400).json({ error: "content must be a non-empty string if provided" });
5828
6220
  }
5829
- const { getEventStore: getEventStore2 } = await import("./events-FLWRLM6V.js");
5830
- const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
6221
+ const { getEventStore: getEventStore2 } = await import("./events-KNOZX7YG.js");
6222
+ const { buildMessagesFromStoredEvents } = await import("./folding-OPP2BVUB.js");
5831
6223
  const eventStore = getEventStore2();
5832
6224
  const events = eventStore.getEvents(sessionId);
5833
- const messages = buildMessagesFromStoredEvents(events);
6225
+ const { messages } = buildMessagesFromStoredEvents(events);
5834
6226
  const msgIndex = messages.findIndex((m) => m.id === messageId);
5835
6227
  if (msgIndex === -1) {
5836
6228
  return res.status(400).json({ error: "Message not found" });
@@ -5839,7 +6231,7 @@ async function createServerHandle(config3) {
5839
6231
  if (msg.role !== "user" || msg.isSystemGenerated) {
5840
6232
  return res.status(400).json({ error: "Can only replay user messages" });
5841
6233
  }
5842
- const { truncateSessionMessages } = await import("./events-FLWRLM6V.js");
6234
+ const { truncateSessionMessages } = await import("./events-KNOZX7YG.js");
5843
6235
  truncateSessionMessages(sessionId, msgIndex - 1);
5844
6236
  sessionManager.queueMessage(sessionId, "asap", content ?? msg.content, msg.attachments, msg.messageKind);
5845
6237
  res.json({ success: true });
@@ -5871,7 +6263,7 @@ async function createServerHandle(config3) {
5871
6263
  res.json({ accepted: true });
5872
6264
  });
5873
6265
  app.get("/api/settings", async (req, res) => {
5874
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-7XDMSPIC.js");
6266
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
5875
6267
  const keysParam = req.query["keys"];
5876
6268
  if (!keysParam) {
5877
6269
  return res.status(400).json({ error: "keys query parameter is required" });
@@ -5884,19 +6276,19 @@ async function createServerHandle(config3) {
5884
6276
  res.json(result);
5885
6277
  });
5886
6278
  app.get("/api/settings/:key", async (req, res) => {
5887
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-7XDMSPIC.js");
6279
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
5888
6280
  const key = req.params.key;
5889
6281
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
5890
6282
  res.json({ key, value });
5891
6283
  });
5892
6284
  app.get("/api/settings/:key", async (req, res) => {
5893
- const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-7XDMSPIC.js");
6285
+ const { getSetting: getSetting2, SETTINGS_DEFAULTS } = await import("./settings-6M55N4Q2.js");
5894
6286
  const key = req.params.key;
5895
6287
  const value = getSetting2(key) ?? SETTINGS_DEFAULTS[key] ?? null;
5896
6288
  res.json({ key, value });
5897
6289
  });
5898
6290
  app.put("/api/settings/:key", async (req, res) => {
5899
- const { setSetting: setSetting2 } = await import("./settings-7XDMSPIC.js");
6291
+ const { setSetting: setSetting2 } = await import("./settings-6M55N4Q2.js");
5900
6292
  const key = req.params.key;
5901
6293
  const { value } = req.body;
5902
6294
  if (value === void 0) {
@@ -5908,14 +6300,14 @@ async function createServerHandle(config3) {
5908
6300
  app.get("/api/tools/rtk-check", async (_req, res) => {
5909
6301
  const { spawn: spawn3 } = await import("child_process");
5910
6302
  try {
5911
- const available = await new Promise((resolve8) => {
6303
+ const available = await new Promise((resolve9) => {
5912
6304
  const proc = spawn3("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
5913
6305
  let out = "";
5914
6306
  proc.stdout?.on("data", (d) => {
5915
6307
  out += d.toString();
5916
6308
  });
5917
- proc.on("error", () => resolve8(false));
5918
- proc.on("close", (code) => resolve8(code === 0 && out.startsWith("rtk ")));
6309
+ proc.on("error", () => resolve9(false));
6310
+ proc.on("close", (code) => resolve9(code === 0 && out.startsWith("rtk ")));
5919
6311
  });
5920
6312
  res.json({ available });
5921
6313
  } catch {
@@ -5923,7 +6315,7 @@ async function createServerHandle(config3) {
5923
6315
  }
5924
6316
  });
5925
6317
  app.get("/api/tools/shells", async (_req, res) => {
5926
- const { listAvailableShells } = await import("./platform-QGXO3CNR.js");
6318
+ const { listAvailableShells } = await import("./platform-VBNNMTR5.js");
5927
6319
  res.json({ shells: listAvailableShells() });
5928
6320
  });
5929
6321
  app.get("/api/config", async (_req, res) => {
@@ -5932,7 +6324,7 @@ async function createServerHandle(config3) {
5932
6324
  let visionFallback;
5933
6325
  let globalWorkdir;
5934
6326
  try {
5935
- const { loadGlobalConfig, getVisionFallback } = await import("./config-EUMVEFHU.js");
6327
+ const { loadGlobalConfig, getVisionFallback } = await import("./config-MD74ETY5.js");
5936
6328
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
5937
6329
  const fallback = getVisionFallback(globalConfig);
5938
6330
  if (fallback) {
@@ -6086,7 +6478,7 @@ async function createServerHandle(config3) {
6086
6478
  const backend = req.query["backend"];
6087
6479
  if (!url) return res.status(400).json({ error: "url is required" });
6088
6480
  try {
6089
- const { fetchModelsWithContext } = await import("./provider-manager-SQIELYLK.js");
6481
+ const { fetchModelsWithContext } = await import("./provider-manager-PD3BXUHY.js");
6090
6482
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
6091
6483
  const models = await fetchModelsWithContext(
6092
6484
  url,
@@ -6156,7 +6548,7 @@ async function createServerHandle(config3) {
6156
6548
  }
6157
6549
  const { getModelProfile: getModelProfile2 } = await import("./profiles-5J7F4BML.js");
6158
6550
  const { getBackendCapabilities } = await import("./backend-P3GHHLHJ.js");
6159
- const { buildNonStreamingCreateParams } = await import("./client-pure-NMLDKRDN.js");
6551
+ const { buildNonStreamingCreateParams } = await import("./client-pure-5YE5WFJP.js");
6160
6552
  const { OpenAIHttpClient } = await import("./http-client-PIWB7BXK.js");
6161
6553
  const { ensureVersionPrefix } = await import("./url-utils-SAJP7AEX.js");
6162
6554
  const profile = getModelProfile2(model);
@@ -6226,7 +6618,7 @@ async function createServerHandle(config3) {
6226
6618
  return res.status(400).json({ error: "name, url, and backend are required" });
6227
6619
  }
6228
6620
  try {
6229
- const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-EUMVEFHU.js");
6621
+ const { loadGlobalConfig, saveGlobalConfig, addProvider, setDefaultModelSelection } = await import("./config-MD74ETY5.js");
6230
6622
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6231
6623
  const providerBackend = backend;
6232
6624
  const providerModels = modelConfigs?.length ? buildModelConfigs(modelConfigs) : model ? [{ id: model, contextWindow: 2e5, source: "user" }] : [];
@@ -6266,7 +6658,7 @@ async function createServerHandle(config3) {
6266
6658
  app.post("/api/init/config", async (req, res) => {
6267
6659
  const { workdir, visionFallback } = req.body;
6268
6660
  try {
6269
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6661
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
6270
6662
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6271
6663
  const updatedConfig = {
6272
6664
  ...globalConfig,
@@ -6305,7 +6697,7 @@ async function createServerHandle(config3) {
6305
6697
  });
6306
6698
  app.delete("/api/providers/:id", async (req, res) => {
6307
6699
  const { id } = req.params;
6308
- const { loadGlobalConfig, saveGlobalConfig, removeProvider } = await import("./config-EUMVEFHU.js");
6700
+ const { loadGlobalConfig, saveGlobalConfig, removeProvider } = await import("./config-MD74ETY5.js");
6309
6701
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6310
6702
  const updatedConfig = removeProvider(globalConfig, id);
6311
6703
  await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
@@ -6317,7 +6709,7 @@ async function createServerHandle(config3) {
6317
6709
  const { id } = req.params;
6318
6710
  const { isLocal } = req.body;
6319
6711
  try {
6320
- const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-EUMVEFHU.js");
6712
+ const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-MD74ETY5.js");
6321
6713
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6322
6714
  const provider = globalConfig.providers.find((p) => p.id === id);
6323
6715
  if (!provider) {
@@ -6348,7 +6740,7 @@ async function createServerHandle(config3) {
6348
6740
  transportAdapter
6349
6741
  } = req.body;
6350
6742
  try {
6351
- const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-EUMVEFHU.js");
6743
+ const { loadGlobalConfig, saveGlobalConfig, updateProvider } = await import("./config-MD74ETY5.js");
6352
6744
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6353
6745
  const provider = globalConfig.providers.find((p) => p.id === id);
6354
6746
  if (!provider) {
@@ -6388,7 +6780,7 @@ async function createServerHandle(config3) {
6388
6780
  return res.status(400).json({ error: result.error });
6389
6781
  }
6390
6782
  const llmClient = getLLMClient();
6391
- const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-EUMVEFHU.js");
6783
+ const { loadGlobalConfig, saveGlobalConfig, setDefaultModelSelection } = await import("./config-MD74ETY5.js");
6392
6784
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6393
6785
  const updatedConfig = setDefaultModelSelection(globalConfig, id, llmClient.getModel());
6394
6786
  await saveGlobalConfig(config3.mode ?? "production", updatedConfig, config3.globalConfigPath);
@@ -6415,7 +6807,7 @@ async function createServerHandle(config3) {
6415
6807
  });
6416
6808
  async function rebuildMcpTools() {
6417
6809
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
6418
- const { setMcpTools: setMcpTools2 } = await import("./tools-JRCUWPJO.js");
6810
+ const { setMcpTools: setMcpTools2 } = await import("./tools-HGKUD327.js");
6419
6811
  const mcpTools = createMcpTools2(mcpManager);
6420
6812
  setMcpTools2(mcpTools);
6421
6813
  }
@@ -6424,7 +6816,7 @@ async function createServerHandle(config3) {
6424
6816
  res.json({ servers });
6425
6817
  });
6426
6818
  app.post("/api/mcp/servers/test", async (req, res) => {
6427
- const { name, transport, command, args, env, url, headers } = req.body;
6819
+ const { name, transport, command, args, env, url, headers, timeout } = req.body;
6428
6820
  if (transport !== void 0 && transport !== "stdio" && transport !== "http") {
6429
6821
  return res.status(400).json({ error: `Invalid transport '${transport}'. Must be 'stdio' or 'http'.` });
6430
6822
  }
@@ -6434,6 +6826,9 @@ async function createServerHandle(config3) {
6434
6826
  if (transport === "http" && !url) {
6435
6827
  return res.status(400).json({ error: "url is required for http transport" });
6436
6828
  }
6829
+ if (timeout !== void 0 && (typeof timeout !== "number" || timeout <= 0)) {
6830
+ return res.status(400).json({ error: "timeout must be a positive number" });
6831
+ }
6437
6832
  try {
6438
6833
  const testManager = new McpManager();
6439
6834
  const resolvedTransport = transport === "http" ? "http" : "stdio";
@@ -6443,7 +6838,8 @@ async function createServerHandle(config3) {
6443
6838
  ...args && args.length > 0 ? { args } : {},
6444
6839
  ...env && Object.keys(env).length > 0 ? { env } : {},
6445
6840
  ...url ? { url } : {},
6446
- ...headers && Object.keys(headers).length > 0 ? { headers } : {}
6841
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {},
6842
+ ...timeout !== void 0 ? { timeout } : {}
6447
6843
  };
6448
6844
  await testManager.addServer(name ?? "test", testConfig);
6449
6845
  const server = testManager.getServer(name ?? "test");
@@ -6458,13 +6854,16 @@ async function createServerHandle(config3) {
6458
6854
  }
6459
6855
  });
6460
6856
  app.post("/api/mcp/servers", async (req, res) => {
6461
- const { name, transport, command, args, env, url, headers } = req.body;
6857
+ const { name, transport, command, args, env, url, headers, timeout } = req.body;
6462
6858
  if (!name) {
6463
6859
  return res.status(400).json({ error: "name is required" });
6464
6860
  }
6465
6861
  if (transport !== void 0 && transport !== "stdio" && transport !== "http") {
6466
6862
  return res.status(400).json({ error: `Invalid transport '${transport}'. Must be 'stdio' or 'http'.` });
6467
6863
  }
6864
+ if (timeout !== void 0 && (typeof timeout !== "number" || timeout <= 0)) {
6865
+ return res.status(400).json({ error: "timeout must be a positive number" });
6866
+ }
6468
6867
  try {
6469
6868
  const resolvedTransport = transport === "http" ? "http" : "stdio";
6470
6869
  const serverCfg = {
@@ -6473,11 +6872,12 @@ async function createServerHandle(config3) {
6473
6872
  ...args && args.length > 0 ? { args } : {},
6474
6873
  ...env && Object.keys(env).length > 0 ? { env } : {},
6475
6874
  ...url ? { url } : {},
6476
- ...headers && Object.keys(headers).length > 0 ? { headers } : {}
6875
+ ...headers && Object.keys(headers).length > 0 ? { headers } : {},
6876
+ ...timeout !== void 0 ? { timeout } : {}
6477
6877
  };
6478
6878
  await mcpManager.addServer(name, serverCfg);
6479
6879
  const server = mcpManager.getServer(name);
6480
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6880
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
6481
6881
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6482
6882
  const updatedMcpServers = { ...globalConfig.mcpServers ?? {}, [name]: serverCfg };
6483
6883
  await saveGlobalConfig(
@@ -6507,7 +6907,7 @@ async function createServerHandle(config3) {
6507
6907
  return res.status(404).json({ error: `MCP server '${name}' not found` });
6508
6908
  }
6509
6909
  const body = req.body;
6510
- const { transport: rawTransport, command, args, env, url, headers } = body;
6910
+ const { transport: rawTransport, command, args, env, url, headers, timeout } = body;
6511
6911
  if (rawTransport !== void 0 && rawTransport !== "stdio" && rawTransport !== "http") {
6512
6912
  return res.status(400).json({ error: `Invalid transport '${String(rawTransport)}'. Must be 'stdio' or 'http'.` });
6513
6913
  }
@@ -6526,9 +6926,12 @@ async function createServerHandle(config3) {
6526
6926
  if (headers !== void 0 && (typeof headers !== "object" || headers === null || Array.isArray(headers) || Object.values(headers).some((v) => typeof v !== "string"))) {
6527
6927
  return res.status(400).json({ error: "headers must be a string/string object" });
6528
6928
  }
6929
+ if (timeout !== void 0 && (typeof timeout !== "number" || timeout <= 0)) {
6930
+ return res.status(400).json({ error: "timeout must be a positive number" });
6931
+ }
6529
6932
  try {
6530
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6531
- const { applyMcpServerUpdate } = await import("./update-server-4MTM7M5P.js");
6933
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
6934
+ const { applyMcpServerUpdate } = await import("./update-server-V6GJ43WH.js");
6532
6935
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6533
6936
  const mcpServers2 = { ...globalConfig.mcpServers ?? {} };
6534
6937
  const patch = {
@@ -6537,7 +6940,8 @@ async function createServerHandle(config3) {
6537
6940
  ...args !== void 0 ? { args } : {},
6538
6941
  ...env !== void 0 ? { env } : {},
6539
6942
  ...url !== void 0 ? { url } : {},
6540
- ...headers !== void 0 ? { headers } : {}
6943
+ ...headers !== void 0 ? { headers } : {},
6944
+ ...timeout !== void 0 ? { timeout } : {}
6541
6945
  };
6542
6946
  const { error: updateError } = await applyMcpServerUpdate({
6543
6947
  name,
@@ -6570,7 +6974,7 @@ async function createServerHandle(config3) {
6570
6974
  return res.status(404).json({ error: `MCP server '${name}' not found` });
6571
6975
  }
6572
6976
  mcpManager.removeServer(name);
6573
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
6977
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
6574
6978
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6575
6979
  const updatedMcpServers = { ...globalConfig.mcpServers ?? {} };
6576
6980
  delete updatedMcpServers[name];
@@ -6599,7 +7003,7 @@ async function createServerHandle(config3) {
6599
7003
  await mcpManager.setToolEnabled(name, toolName, enabled);
6600
7004
  const server = mcpManager.getServer(name);
6601
7005
  if (server) {
6602
- const { loadGlobalConfig, saveGlobalConfig } = await import("./config-EUMVEFHU.js");
7006
+ const { loadGlobalConfig, saveGlobalConfig } = await import("./config-MD74ETY5.js");
6603
7007
  const globalConfig = await loadGlobalConfig(config3.mode ?? "production", config3.globalConfigPath);
6604
7008
  const mcpServers2 = { ...globalConfig.mcpServers ?? {} };
6605
7009
  const serverCfg = mcpServers2[name];
@@ -6650,13 +7054,13 @@ async function createServerHandle(config3) {
6650
7054
  })
6651
7055
  );
6652
7056
  app.get("/api/sessions/:id/background-processes", async (req, res) => {
6653
- const { getSessionProcesses } = await import("./manager-OPMCSYN2.js");
7057
+ const { getSessionProcesses } = await import("./manager-ATWZUL3O.js");
6654
7058
  const sessionId = req.params.id;
6655
7059
  const processes = getSessionProcesses(sessionId);
6656
7060
  res.json({ processes });
6657
7061
  });
6658
7062
  app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
6659
- const { stopProcess } = await import("./manager-OPMCSYN2.js");
7063
+ const { stopProcess } = await import("./manager-ATWZUL3O.js");
6660
7064
  const sessionId = req.params.id;
6661
7065
  const processId = req.params.processId;
6662
7066
  const session = sessionManager.getSession(sessionId);
@@ -6676,14 +7080,14 @@ async function createServerHandle(config3) {
6676
7080
  });
6677
7081
  app.use("/api/directories", createDirectoryRoutes());
6678
7082
  app.use("/api/files", createFileSearchRoutes());
6679
- const webDir = resolve7(__dirname3, "../../web");
7083
+ const webDir = resolve8(__dirname3, "../../web");
6680
7084
  const isDev = config3.mode === "development";
6681
7085
  let viteServer;
6682
7086
  if (isDev) {
6683
7087
  logger.info("Dev mode: using Vite middleware");
6684
7088
  viteServer = await createViteServer({
6685
7089
  root: webDir,
6686
- configFile: resolve7(__dirname3, "../../web/vite.config.ts"),
7090
+ configFile: resolve8(__dirname3, "../../web/vite.config.ts"),
6687
7091
  server: { middlewareMode: true },
6688
7092
  appType: "spa",
6689
7093
  logLevel: "warn"
@@ -6703,7 +7107,7 @@ async function createServerHandle(config3) {
6703
7107
  }
6704
7108
  });
6705
7109
  app.get("/fox.svg", (_req, res) => {
6706
- readFile4(join9(webDir, "fox.svg")).then((content) => {
7110
+ readFile4(join10(webDir, "fox.svg")).then((content) => {
6707
7111
  res.set("Content-Type", "image/svg+xml");
6708
7112
  res.send(content);
6709
7113
  }).catch(() => {
@@ -6712,7 +7116,7 @@ async function createServerHandle(config3) {
6712
7116
  });
6713
7117
  app.use(
6714
7118
  "/sounds",
6715
- express.static(join9(webDir, "public", "sounds"), {
7119
+ express.static(join10(webDir, "public", "sounds"), {
6716
7120
  setHeaders: (res) => {
6717
7121
  res.set("Content-Type", "audio/mpeg");
6718
7122
  }
@@ -6721,13 +7125,13 @@ async function createServerHandle(config3) {
6721
7125
  app.use("/__inspect__.js", (_req, res) => {
6722
7126
  res.set("Access-Control-Allow-Origin", "*");
6723
7127
  res.set("Content-Type", "application/javascript");
6724
- readFile4(join9(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
7128
+ readFile4(join10(webDir, "public", "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6725
7129
  });
6726
7130
  app.get("/*path", (req, res) => {
6727
7131
  if (req.path.startsWith("/api/")) {
6728
7132
  return;
6729
7133
  }
6730
- readFile4(join9(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
7134
+ readFile4(join10(webDir, "index.html"), "utf-8").then((indexHtml) => viteServer.transformIndexHtml(req.originalUrl, indexHtml)).then((transformed) => res.send(transformed)).catch((err) => {
6731
7135
  logger.error("Error serving index.html", { error: err });
6732
7136
  res.status(500).send("Server error");
6733
7137
  });
@@ -6735,10 +7139,10 @@ async function createServerHandle(config3) {
6735
7139
  logger.info("Vite middleware ready", { port: config3.server.port });
6736
7140
  }
6737
7141
  if (!isDev) {
6738
- const distWebDir = resolve7(__dirname3, "web");
7142
+ const distWebDir = resolve8(__dirname3, "web");
6739
7143
  app.use(
6740
7144
  "/assets",
6741
- express.static(join9(distWebDir, "assets"), {
7145
+ express.static(join10(distWebDir, "assets"), {
6742
7146
  setHeaders: (res, filepath) => {
6743
7147
  if (filepath.endsWith(".css")) {
6744
7148
  res.set("Content-Type", "text/css");
@@ -6746,11 +7150,11 @@ async function createServerHandle(config3) {
6746
7150
  }
6747
7151
  })
6748
7152
  );
6749
- app.use("/manifest.webmanifest", express.static(join9(distWebDir, "manifest.webmanifest")));
6750
- app.use("/registerSW.js", express.static(join9(distWebDir, "registerSW.js")));
6751
- app.use("/sw.js", express.static(join9(distWebDir, "sw.js")));
7153
+ app.use("/manifest.webmanifest", express.static(join10(distWebDir, "manifest.webmanifest")));
7154
+ app.use("/registerSW.js", express.static(join10(distWebDir, "registerSW.js")));
7155
+ app.use("/sw.js", express.static(join10(distWebDir, "sw.js")));
6752
7156
  app.get("/fox.svg", (_req, res) => {
6753
- readFile4(join9(distWebDir, "fox.svg")).then((content) => {
7157
+ readFile4(join10(distWebDir, "fox.svg")).then((content) => {
6754
7158
  res.set("Content-Type", "image/svg+xml");
6755
7159
  res.send(content);
6756
7160
  }).catch(() => {
@@ -6759,7 +7163,7 @@ async function createServerHandle(config3) {
6759
7163
  });
6760
7164
  app.use(
6761
7165
  "/sounds",
6762
- express.static(join9(distWebDir, "sounds"), {
7166
+ express.static(join10(distWebDir, "sounds"), {
6763
7167
  setHeaders: (res) => {
6764
7168
  res.set("Content-Type", "audio/mpeg");
6765
7169
  }
@@ -6768,10 +7172,10 @@ async function createServerHandle(config3) {
6768
7172
  app.use("/__inspect__.js", (_req, res) => {
6769
7173
  res.set("Access-Control-Allow-Origin", "*");
6770
7174
  res.set("Content-Type", "application/javascript");
6771
- readFile4(join9(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
7175
+ readFile4(join10(distWebDir, "__inspect__.js")).then((content) => res.send(content)).catch(() => res.status(404).send("Not found"));
6772
7176
  });
6773
7177
  app.get("/", (_req, res) => {
6774
- readFile4(join9(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
7178
+ readFile4(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6775
7179
  res.status(404).send("Web UI not built. Run `npm run build:web`");
6776
7180
  });
6777
7181
  });
@@ -6779,7 +7183,7 @@ async function createServerHandle(config3) {
6779
7183
  if (req.path.startsWith("/api/") || req.path.startsWith("/assets/") || req.path.startsWith("/sounds/") || req.path.startsWith("/manifest.webmanifest") || req.path.startsWith("/registerSW.js") || req.path.startsWith("/sw.js") || req.path === "/fox.svg") {
6780
7184
  return;
6781
7185
  }
6782
- readFile4(join9(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
7186
+ readFile4(join10(distWebDir, "index.html"), "utf-8").then((content) => res.send(content)).catch(() => {
6783
7187
  res.status(404).send("Web UI not built");
6784
7188
  });
6785
7189
  });
@@ -6799,7 +7203,7 @@ async function createServerHandle(config3) {
6799
7203
  const state = sessionManager.getContextState(sessionId);
6800
7204
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
6801
7205
  });
6802
- const { QueueProcessor } = await import("./processor-3R4GS6VB.js");
7206
+ const { QueueProcessor } = await import("./processor-UQX5KL56.js");
6803
7207
  const queueProcessor = new QueueProcessor({
6804
7208
  sessionManager,
6805
7209
  providerManager,
@@ -6822,7 +7226,7 @@ async function createServerHandle(config3) {
6822
7226
  return {
6823
7227
  httpServer,
6824
7228
  ctx: { config: config3, sessionManager, llmClient: getLLMClient(), toolRegistry, providerManager },
6825
- start: (port) => new Promise((resolve8, reject) => {
7229
+ start: (port) => new Promise((resolve9, reject) => {
6826
7230
  const listenPort = port ?? config3.server.port;
6827
7231
  const host = config3.server.host;
6828
7232
  httpServer.listen(listenPort, host, () => {
@@ -6832,16 +7236,16 @@ async function createServerHandle(config3) {
6832
7236
  logger.info(`OpenFox server running at http://${host}:${actualPort}`);
6833
7237
  logger.info(`WebSocket available at ws://${host}:${actualPort}/ws`);
6834
7238
  logger.info(`LLM backend: ${client.getBackend()}, model: ${client.getModel()}, url: ${config3.llm.baseUrl}`);
6835
- resolve8({ port: actualPort });
7239
+ resolve9({ port: actualPort });
6836
7240
  });
6837
7241
  httpServer.on("error", reject);
6838
7242
  }),
6839
- close: () => new Promise((resolve8) => {
7243
+ close: () => new Promise((resolve9) => {
6840
7244
  logger.info("Shutting down...");
6841
7245
  void (async () => {
6842
7246
  await devServerManager.stopAll();
6843
7247
  await mcpManager.disconnectAll();
6844
- const { stopAllInspectProxies } = await import("./inspect-proxy-JHZV34QR.js");
7248
+ const { stopAllInspectProxies } = await import("./inspect-proxy-DRYIIH4O.js");
6845
7249
  stopAllInspectProxies();
6846
7250
  const { cleanupAllProcesses } = await import("./store-SARB6V2N.js");
6847
7251
  cleanupAllProcesses();
@@ -6855,7 +7259,7 @@ async function createServerHandle(config3) {
6855
7259
  client.terminate();
6856
7260
  }
6857
7261
  wss.close();
6858
- httpServer.close(() => resolve8());
7262
+ httpServer.close(() => resolve9());
6859
7263
  })();
6860
7264
  })
6861
7265
  };
@@ -6879,4 +7283,4 @@ export {
6879
7283
  createServerHandle,
6880
7284
  createServer
6881
7285
  };
6882
- //# sourceMappingURL=chunk-J7VCI5EX.js.map
7286
+ //# sourceMappingURL=chunk-UMX7MMNW.js.map