openfox 2.0.73 → 2.0.75

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 (42) hide show
  1. package/dist/{chat-handler-LK5RUAGO.js → chat-handler-QLMBYRDY.js} +15 -14
  2. package/dist/{chunk-YD6NDTKF.js → chunk-5GOOSZX7.js} +1 -1
  3. package/dist/chunk-6COWK53A.js +7 -0
  4. package/dist/{chunk-GMF36EZ3.js → chunk-6ZMGWDOD.js} +87 -506
  5. package/dist/{chunk-2DCJERYX.js → chunk-CJ2AJ7HU.js} +3 -3
  6. package/dist/{chunk-O2BE7KVQ.js → chunk-J3VQBVGO.js} +8 -6
  7. package/dist/chunk-KEVRZWL3.js +495 -0
  8. package/dist/{chunk-TK4BZIXA.js → chunk-LKUAKQZY.js} +42 -20
  9. package/dist/{chunk-PYB2UEC3.js → chunk-LTFB2WWE.js} +4 -4
  10. package/dist/{chunk-CHPRGEY5.js → chunk-LUG5QTFC.js} +2 -2
  11. package/dist/{chunk-X2Y3AJPP.js → chunk-MF3UW36X.js} +10 -2
  12. package/dist/{chunk-EYXDM2FY.js → chunk-MIJX54CG.js} +395 -116
  13. package/dist/{chunk-NOU2QIL5.js → chunk-O3NUNU4H.js} +5 -2
  14. package/dist/{chunk-KKRLQH6F.js → chunk-PJQW6TYI.js} +17 -7
  15. package/dist/{chunk-6XKM25OT.js → chunk-YFFP4DR4.js} +4 -4
  16. package/dist/cli/dev.js +1 -1
  17. package/dist/cli/index.js +1 -1
  18. package/dist/{compactor-4UKYWC5F.js → compactor-AERNYEMP.js} +4 -4
  19. package/dist/{events-AEQ6GQXI.js → events-X353XCPN.js} +3 -3
  20. package/dist/install-CPOPMCSE.js +120 -0
  21. package/dist/{manager-24C2AXVF.js → manager-5GILDCVD.js} +3 -3
  22. package/dist/{orchestrator-RTZVMWQV.js → orchestrator-BIFTALBG.js} +13 -12
  23. package/dist/package.json +1 -1
  24. package/dist/path-security-3FAIPJSV.js +50 -0
  25. package/dist/{processor-3O26I7DO.js → processor-MQ47QSDR.js} +15 -14
  26. package/dist/{protocol-4QFU7WAI.js → protocol-EWEVW65B.js} +3 -3
  27. package/dist/{protocol-DGQWVFcP.d.ts → protocol-Sr6Jr4ZS.d.ts} +5 -2
  28. package/dist/{serve-I6K7A7RL.js → serve-UD27KOL2.js} +18 -17
  29. package/dist/server/index.d.ts +5 -1
  30. package/dist/server/index.js +16 -15
  31. package/dist/{server-G2J4RAXL.js → server-TJ3PP5EW.js} +15 -14
  32. package/dist/shared/index.d.ts +1 -1
  33. package/dist/shared/index.js +1 -1
  34. package/dist/{tools-X52DFUS6.js → tools-RZ464GUJ.js} +17 -15
  35. package/dist/{update-RJB4GPHE.js → update-MWIZH4P2.js} +12 -3
  36. package/dist/web/assets/{index-CmechlaK.css → index-BNVBxKnB.css} +1 -1
  37. package/dist/web/assets/{index-C7SW16F7.js → index-Bn4hwZeP.js} +74 -74
  38. package/dist/web/index.html +2 -2
  39. package/dist/web/sw.js +1 -1
  40. package/dist/{workspace-ET3FHZYN.js → workspace-6QTTUMMW.js} +4 -2
  41. package/package.json +1 -1
  42. package/dist/chunk-NE7OWIIO.js +0 -7
@@ -21,7 +21,7 @@ import {
21
21
  tokenFromPassword,
22
22
  verifyPassword,
23
23
  workflowExists
24
- } from "./chunk-TK4BZIXA.js";
24
+ } from "./chunk-LKUAKQZY.js";
25
25
  import {
26
26
  agentExists,
27
27
  createToolRegistry,
@@ -66,7 +66,10 @@ import {
66
66
  setSkillEnabled,
67
67
  skillExists,
68
68
  updateOwnedSkill
69
- } from "./chunk-GMF36EZ3.js";
69
+ } from "./chunk-6ZMGWDOD.js";
70
+ import {
71
+ createMcpTools
72
+ } from "./chunk-NWO6GRYE.js";
70
73
  import {
71
74
  deleteWorkspace,
72
75
  ensureWorkspace,
@@ -76,8 +79,9 @@ import {
76
79
  loadWorkspaceConfig,
77
80
  runGit,
78
81
  saveWorkspaceConfig,
82
+ validateWorkspaceName,
79
83
  workspaceExists
80
- } from "./chunk-X2Y3AJPP.js";
84
+ } from "./chunk-MF3UW36X.js";
81
85
  import {
82
86
  EventEmitter
83
87
  } from "./chunk-EU3WWTFH.js";
@@ -90,11 +94,17 @@ import {
90
94
  getProject
91
95
  } from "./chunk-UV3EMWL4.js";
92
96
  import {
93
- createMcpTools
94
- } from "./chunk-NWO6GRYE.js";
97
+ getPathSeparator,
98
+ isAbsolutePath
99
+ } from "./chunk-S7O77CVO.js";
100
+ import {
101
+ deleteSetting,
102
+ getSetting,
103
+ setSetting
104
+ } from "./chunk-QNKXZH3Z.js";
95
105
  import {
96
106
  VERSION
97
- } from "./chunk-NE7OWIIO.js";
107
+ } from "./chunk-6COWK53A.js";
98
108
  import {
99
109
  createSession,
100
110
  deleteSession,
@@ -122,22 +132,13 @@ import {
122
132
  updateSessionProvider,
123
133
  updateSessionRunning,
124
134
  updateSessionWorkdir
125
- } from "./chunk-6XKM25OT.js";
126
- import {
127
- createContextStateMessage
128
- } from "./chunk-NOU2QIL5.js";
129
- import {
130
- getPathSeparator,
131
- isAbsolutePath
132
- } from "./chunk-S7O77CVO.js";
133
- import {
134
- deleteSetting,
135
- getSetting,
136
- setSetting
137
- } from "./chunk-QNKXZH3Z.js";
135
+ } from "./chunk-YFFP4DR4.js";
138
136
  import {
139
137
  initDatabase
140
138
  } from "./chunk-I4RWK2L2.js";
139
+ import {
140
+ createContextStateMessage
141
+ } from "./chunk-O3NUNU4H.js";
141
142
  import {
142
143
  createProviderManager,
143
144
  detectModel,
@@ -162,7 +163,7 @@ import {
162
163
  } from "./chunk-K44MW7JJ.js";
163
164
  import {
164
165
  createServerMessage
165
- } from "./chunk-YD6NDTKF.js";
166
+ } from "./chunk-5GOOSZX7.js";
166
167
  import {
167
168
  getGlobalConfigDir
168
169
  } from "./chunk-CQGTEGKL.js";
@@ -2557,6 +2558,8 @@ var SessionManager = class {
2557
2558
  dynamicContextChangedStore = /* @__PURE__ */ new Map();
2558
2559
  debugDumpStore = /* @__PURE__ */ new Map();
2559
2560
  warmedUpSessions = /* @__PURE__ */ new Set();
2561
+ switchLocks = /* @__PURE__ */ new Map();
2562
+ workspaceCreationLocks = /* @__PURE__ */ new Map();
2560
2563
  constructor(providerManager) {
2561
2564
  this.providerManager = providerManager;
2562
2565
  }
@@ -3213,89 +3216,135 @@ var SessionManager = class {
3213
3216
  * Switch to a workspace. Target can be "original" (project root) or a workspace name.
3214
3217
  * If the workspace doesn't exist yet, it's created first.
3215
3218
  * Emits a single type of event — switching is always "opening" a workspace.
3219
+ * Switches are serialized per-session to prevent race conditions.
3216
3220
  */
3217
3221
  async switchWorkspace(sessionId, target, branch) {
3218
- const session = this.requireSession(sessionId);
3219
- const project = getProject(session.projectId);
3220
- if (!project) throw new Error(`Project not found: ${session.projectId}`);
3221
- if (target === "original" && !session.workspace) return session;
3222
- if (target !== "original" && session.workspace?.endsWith(`/${target}`)) return session;
3223
- const previousPath = session.workspace;
3224
- if (previousPath) {
3222
+ if (target !== "original") {
3223
+ validateWorkspaceName(target);
3224
+ }
3225
+ const existingLock = this.switchLocks.get(sessionId);
3226
+ if (existingLock) {
3227
+ await existingLock;
3228
+ }
3229
+ const lockPromise = (async () => {
3230
+ const session = this.requireSession(sessionId);
3231
+ const project = getProject(session.projectId);
3232
+ 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;
3235
+ 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
3244
+ });
3245
+ }
3246
+ }
3247
+ if (target === "original") {
3248
+ updateSessionWorkdir(sessionId, project.workdir, null);
3249
+ } else {
3250
+ const createLockKey = `${project.name}:${target}`;
3251
+ const existingCreateLock = this.workspaceCreationLocks.get(createLockKey);
3252
+ if (existingCreateLock) {
3253
+ await existingCreateLock;
3254
+ }
3255
+ const createLockPromise = (async () => {
3256
+ const exists = await workspaceExists(project.name, target);
3257
+ if (!exists) {
3258
+ await ensureWorkspace(project.workdir, target, project.name, branch);
3259
+ }
3260
+ })();
3261
+ this.workspaceCreationLocks.set(createLockKey, createLockPromise);
3262
+ try {
3263
+ await createLockPromise;
3264
+ } finally {
3265
+ this.workspaceCreationLocks.delete(createLockKey);
3266
+ }
3267
+ const wsPath = resolve2(getWorkspacesDir(project.name), target);
3268
+ updateSessionWorkdir(sessionId, project.workdir, wsPath);
3269
+ }
3225
3270
  try {
3226
- await devServerManager.stop(previousPath);
3271
+ await shutdownLspManager(sessionId);
3227
3272
  } catch (err) {
3228
- logger.error("Error stopping dev server for workspace switch", {
3229
- sessionId,
3230
- workspace: previousPath,
3231
- error: err
3232
- });
3233
- }
3234
- }
3235
- if (target === "original") {
3236
- updateSessionWorkdir(sessionId, project.workdir, null);
3237
- } else {
3238
- const exists = await workspaceExists(project.name, target);
3239
- if (!exists) {
3240
- await ensureWorkspace(project.workdir, target, project.name, branch);
3273
+ logger.error("Error shutting down LSP for workspace switch", { sessionId, error: err });
3241
3274
  }
3242
- const wsPath = resolve2(getWorkspacesDir(project.name), target);
3243
- updateSessionWorkdir(sessionId, project.workdir, wsPath);
3244
- }
3245
- try {
3246
- await shutdownLspManager(sessionId);
3247
- } catch (err) {
3248
- logger.error("Error shutting down LSP for workspace switch", { sessionId, error: err });
3249
- }
3250
- const effectiveWorkdir = target === "original" ? project.workdir : resolve2(getWorkspacesDir(project.name), target);
3251
- const actualBranch = await getGitBranch(effectiveWorkdir);
3252
- let stalenessHint = "";
3253
- if (target !== "original" && actualBranch) {
3254
- await runGit(effectiveWorkdir, ["fetch", "origin", "--no-tags", "--quiet"]).catch(() => {
3255
- });
3256
- const behind = await getCommitsBehind(effectiveWorkdir, actualBranch);
3257
- if (behind !== null && behind > 0) {
3258
- const plural = behind === 1 ? "" : "s";
3259
- stalenessHint = `
3275
+ const effectiveWorkdir = target === "original" ? project.workdir : resolve2(getWorkspacesDir(project.name), target);
3276
+ const actualBranch = await getGitBranch(effectiveWorkdir);
3277
+ let stalenessHint = "";
3278
+ if (target !== "original" && actualBranch) {
3279
+ await runGit(effectiveWorkdir, ["fetch", "origin", "--no-tags", "--quiet"]).catch(() => {
3280
+ });
3281
+ const behind = await getCommitsBehind(effectiveWorkdir, actualBranch);
3282
+ if (behind !== null && behind > 0) {
3283
+ const plural = behind === 1 ? "" : "s";
3284
+ stalenessHint = `
3260
3285
  (${behind} commit${plural} behind ${actualBranch} on main workspace \u2014 run \`git pull\` to sync)`;
3286
+ }
3261
3287
  }
3262
- }
3263
- const wsLabel = target === "original" ? "original" : target;
3264
- const reminderContent = `<system-reminder>
3288
+ const wsLabel = target === "original" ? "original" : target;
3289
+ const reminderContent = `<system-reminder>
3265
3290
  This session is now operating in workspace "${wsLabel}" on branch "${actualBranch ?? "unknown"}" at ${effectiveWorkdir}.${stalenessHint}
3266
3291
  All file and git operations should use this directory.
3267
3292
  </system-reminder>`;
3268
- this.addMessage(sessionId, {
3269
- role: "user",
3270
- content: reminderContent,
3271
- isSystemGenerated: true,
3272
- messageKind: "auto-prompt",
3273
- metadata: {
3274
- type: "workspace",
3275
- name: "Workspace",
3276
- color: "#22c55e",
3277
- kind: "definition",
3278
- workspaceName: wsLabel,
3279
- ...actualBranch ? { branchName: actualBranch } : {}
3293
+ this.addMessage(sessionId, {
3294
+ role: "user",
3295
+ content: reminderContent,
3296
+ isSystemGenerated: true,
3297
+ messageKind: "auto-prompt",
3298
+ metadata: {
3299
+ type: "workspace",
3300
+ name: "Workspace",
3301
+ color: "#22c55e",
3302
+ kind: "definition",
3303
+ workspaceName: wsLabel,
3304
+ ...actualBranch ? { branchName: actualBranch } : {}
3305
+ }
3306
+ });
3307
+ const updated = this.requireSession(sessionId);
3308
+ this.emit({ type: "session_updated", session: updated });
3309
+ return updated;
3310
+ })();
3311
+ this.switchLocks.set(sessionId, lockPromise);
3312
+ lockPromise.finally(() => {
3313
+ if (this.switchLocks.get(sessionId) === lockPromise) {
3314
+ this.switchLocks.delete(sessionId);
3280
3315
  }
3281
3316
  });
3282
- const updated = this.requireSession(sessionId);
3283
- this.emit({ type: "session_updated", session: updated });
3284
- return updated;
3317
+ return lockPromise;
3285
3318
  }
3286
3319
  /**
3287
3320
  * Delete a workspace by name. If the session is currently in that workspace,
3288
3321
  * switches to original first. Throws if target is "original".
3322
+ * Refuses deletion if other sessions reference this workspace.
3289
3323
  */
3290
3324
  async deleteWorkspace(sessionId, target) {
3291
3325
  if (target === "original") throw new Error("Cannot delete the original workspace");
3326
+ validateWorkspaceName(target);
3292
3327
  const session = this.requireSession(sessionId);
3293
3328
  const project = getProject(session.projectId);
3294
3329
  if (!project) throw new Error(`Project not found: ${session.projectId}`);
3330
+ const otherSessionsUsingIt = this.listSessions().filter(
3331
+ (s) => s.id !== sessionId && s.workspace?.split("/").pop() === target
3332
+ );
3333
+ if (otherSessionsUsingIt.length > 0) {
3334
+ const otherIds = otherSessionsUsingIt.map((s) => s.id).join(", ");
3335
+ throw new Error(
3336
+ `Workspace "${target}" is in use by other session(s): ${otherIds}. Switch them to original first.`
3337
+ );
3338
+ }
3295
3339
  const currentWsName = session.workspace?.split("/").pop();
3296
3340
  if (currentWsName === target) {
3297
3341
  await this.switchWorkspace(sessionId, "original");
3298
3342
  }
3343
+ const effectivePath = resolve2(getWorkspacesDir(project.name), target);
3344
+ try {
3345
+ await devServerManager.stop(effectivePath);
3346
+ } catch {
3347
+ }
3299
3348
  await deleteWorkspace(project.name, target);
3300
3349
  const updated = this.requireSession(sessionId);
3301
3350
  this.emit({ type: "session_updated", session: updated });
@@ -4252,6 +4301,11 @@ function fuzzyScore(filePath, query) {
4252
4301
  import { Router as Router9 } from "express";
4253
4302
  import { spawn as spawn2 } from "child_process";
4254
4303
  var updateInProgress = false;
4304
+ var CACHE_TTL_MS = 60 * 60 * 1e3;
4305
+ var versionCache = {
4306
+ data: null,
4307
+ timestamp: 0
4308
+ };
4255
4309
  function isRunningAsService() {
4256
4310
  return process.env["OPENFOX_SERVICE"] === "true";
4257
4311
  }
@@ -4265,13 +4319,94 @@ async function checkAuth(req, opts) {
4265
4319
  }
4266
4320
  return true;
4267
4321
  }
4322
+ function isDevMode() {
4323
+ return process.env["NODE_ENV"] === "development";
4324
+ }
4325
+ async function getRemote() {
4326
+ return new Promise((resolve8) => {
4327
+ const child = spawn2("git", ["remote"], {
4328
+ stdio: ["ignore", "pipe", "pipe"],
4329
+ windowsHide: true
4330
+ });
4331
+ let output = "";
4332
+ child.stdout?.on("data", (data) => {
4333
+ output += data.toString();
4334
+ });
4335
+ child.on("close", () => {
4336
+ const remotes = output.trim().split("\n").map((r) => r.trim()).filter(Boolean);
4337
+ if (remotes.includes("upstream")) resolve8("upstream");
4338
+ else if (remotes.includes("origin")) resolve8("origin");
4339
+ else resolve8("origin");
4340
+ });
4341
+ child.on("error", () => resolve8("origin"));
4342
+ });
4343
+ }
4344
+ async function getLatestDevelopVersion() {
4345
+ const remote = await getRemote();
4346
+ return new Promise((resolve8) => {
4347
+ const fetchChild = spawn2("git", ["fetch", remote, "develop", "--no-tags"], {
4348
+ stdio: ["ignore", "pipe", "pipe"],
4349
+ windowsHide: true
4350
+ });
4351
+ fetchChild.on("error", (err) => {
4352
+ logger.error("[auto-update] Git fetch error", { error: err.message });
4353
+ resolve8("unknown");
4354
+ });
4355
+ fetchChild.on("close", (code) => {
4356
+ if (code !== 0) {
4357
+ logger.warn("[auto-update] Git fetch failed, trying local tags...");
4358
+ }
4359
+ const tagChild = spawn2("git", ["describe", "--tags", "--abbrev=0", `${remote}/develop`], {
4360
+ stdio: ["ignore", "pipe", "pipe"],
4361
+ windowsHide: true
4362
+ });
4363
+ let tagOutput = "";
4364
+ tagChild.stdout?.on("data", (data) => {
4365
+ tagOutput += data.toString();
4366
+ });
4367
+ tagChild.on("close", (tagCode) => {
4368
+ if (tagCode === 0 && tagOutput.trim()) {
4369
+ resolve8(tagOutput.trim());
4370
+ } else {
4371
+ const fallbackChild = spawn2("git", ["describe", "--tags", "--abbrev=0", "develop"], {
4372
+ stdio: ["ignore", "pipe", "pipe"],
4373
+ windowsHide: true
4374
+ });
4375
+ let fallbackOutput = "";
4376
+ fallbackChild.stdout?.on("data", (data) => {
4377
+ fallbackOutput += data.toString();
4378
+ });
4379
+ fallbackChild.on("close", (fallbackCode) => {
4380
+ if (fallbackCode === 0 && fallbackOutput.trim()) {
4381
+ resolve8(fallbackOutput.trim());
4382
+ } else {
4383
+ logger.warn("[auto-update] No tags found on develop branch");
4384
+ resolve8("unknown");
4385
+ }
4386
+ });
4387
+ fallbackChild.on("error", () => resolve8("unknown"));
4388
+ }
4389
+ });
4390
+ tagChild.on("error", (err) => {
4391
+ logger.error("[auto-update] Git tag error", { error: err.message });
4392
+ resolve8("unknown");
4393
+ });
4394
+ });
4395
+ });
4396
+ }
4268
4397
  function createAutoUpdateRoutes(options = {}) {
4269
4398
  const router = Router9();
4270
- router.get("/check", async (_req, res) => {
4399
+ router.get("/check", async (req, res) => {
4271
4400
  const isService = isRunningAsService();
4401
+ const isDev = isDevMode();
4272
4402
  const current = VERSION;
4403
+ const forceRefresh = req.query["force"] === "true";
4404
+ if (!forceRefresh && versionCache.data && Date.now() - versionCache.timestamp < CACHE_TTL_MS) {
4405
+ res.json({ ...versionCache.data, isService });
4406
+ return;
4407
+ }
4273
4408
  try {
4274
- const latest = await new Promise((resolve8, reject) => {
4409
+ const latest = isDev ? await getLatestDevelopVersion() : await new Promise((resolve8, reject) => {
4275
4410
  const win = process.platform === "win32";
4276
4411
  const child = spawn2(win ? "npm view openfox version" : "npm", win ? [] : ["view", "openfox", "version"], {
4277
4412
  stdio: ["ignore", "pipe", "pipe"],
@@ -4295,9 +4430,15 @@ function createAutoUpdateRoutes(options = {}) {
4295
4430
  reject(new Error("npm view timed out"));
4296
4431
  }, 1e4);
4297
4432
  });
4298
- const isUpdateAvailable = current !== latest;
4433
+ const currentVersion = isDev ? current.replace(/-dev$/, "") : current;
4434
+ const latestVersion = latest.replace(/^v/, "");
4435
+ const isUpdateAvailable = currentVersion !== latestVersion;
4436
+ const now = Date.now();
4437
+ versionCache.data = { current, latest, isUpdateAvailable, isService };
4438
+ versionCache.timestamp = now;
4299
4439
  res.json({ current, latest, isUpdateAvailable, isService });
4300
- } catch {
4440
+ } catch (err) {
4441
+ logger.error("[auto-update] Error in version check", { error: err instanceof Error ? err.message : String(err) });
4301
4442
  res.json({ current, latest: current, isUpdateAvailable: false, isService });
4302
4443
  }
4303
4444
  });
@@ -4791,6 +4932,53 @@ function createPluginRoutes(options) {
4791
4932
  return router;
4792
4933
  }
4793
4934
 
4935
+ // src/server/utils/wsl.ts
4936
+ import { readFileSync as readFileSync3 } from "fs";
4937
+ var cached = null;
4938
+ function readEtcOsRelease() {
4939
+ try {
4940
+ const content = readFileSync3("/etc/os-release", "utf-8");
4941
+ const result = {};
4942
+ for (const line of content.split("\n")) {
4943
+ const m = line.match(/^([A-Za-z_][A-Za-z_0-9]*)=(.*)$/);
4944
+ if (m) {
4945
+ let val = m[2] ?? "";
4946
+ if (val.startsWith('"') && val.endsWith('"') || val.startsWith("'") && val.endsWith("'")) {
4947
+ val = val.slice(1, -1);
4948
+ }
4949
+ result[m[1]] = val;
4950
+ }
4951
+ }
4952
+ return result;
4953
+ } catch {
4954
+ return null;
4955
+ }
4956
+ }
4957
+ function detectWsl() {
4958
+ if (cached) return cached;
4959
+ if (process.platform === "win32") {
4960
+ cached = { isWSL: false, wslDistro: "" };
4961
+ return cached;
4962
+ }
4963
+ const distro = process.env["WSL_DISTRO_NAME"];
4964
+ if (distro) {
4965
+ cached = { isWSL: true, wslDistro: distro };
4966
+ return cached;
4967
+ }
4968
+ try {
4969
+ const osrelease = readFileSync3("/proc/sys/kernel/osrelease", "utf-8");
4970
+ if (osrelease.toLowerCase().includes("wsl") || osrelease.toLowerCase().includes("microsoft")) {
4971
+ const osInfo = readEtcOsRelease();
4972
+ const name = osInfo?.["NAME"] ?? "Ubuntu";
4973
+ cached = { isWSL: true, wslDistro: name };
4974
+ return cached;
4975
+ }
4976
+ } catch {
4977
+ }
4978
+ cached = { isWSL: false, wslDistro: "" };
4979
+ return cached;
4980
+ }
4981
+
4794
4982
  // src/server/index.ts
4795
4983
  var __dirname3 = dirname8(fileURLToPath7(import.meta.url));
4796
4984
  async function createServerHandle(config3) {
@@ -4819,6 +5007,7 @@ async function createServerHandle(config3) {
4819
5007
  if (useMock) {
4820
5008
  logger.info("Using MOCK LLM client - deterministic responses for testing");
4821
5009
  }
5010
+ const platformInfo = detectWsl();
4822
5011
  async function initLLM() {
4823
5012
  const llmClient = getLLMClient();
4824
5013
  const backend = config3.llm.backend;
@@ -4889,7 +5078,7 @@ async function createServerHandle(config3) {
4889
5078
  setMcpTools(mcpTools);
4890
5079
  logger.info("MCP tools registered", { count: mcpTools.length });
4891
5080
  }
4892
- const { signalMcpReady } = await import("./server-G2J4RAXL.js");
5081
+ const { signalMcpReady } = await import("./server-TJ3PP5EW.js");
4893
5082
  signalMcpReady();
4894
5083
  });
4895
5084
  const app = express();
@@ -5050,7 +5239,7 @@ async function createServerHandle(config3) {
5050
5239
  const { getProject: getProject2 } = await import("./projects-LHRNCUGW.js");
5051
5240
  const project = getProject2(req.params.id);
5052
5241
  if (!project) return res.status(404).json({ error: "Project not found" });
5053
- const { listBranches } = await import("./workspace-ET3FHZYN.js");
5242
+ const { listBranches } = await import("./workspace-6QTTUMMW.js");
5054
5243
  const branches = await listBranches(project.workdir);
5055
5244
  res.json({ branches });
5056
5245
  });
@@ -5060,7 +5249,7 @@ async function createServerHandle(config3) {
5060
5249
  if (!project) return res.status(404).json({ error: "Project not found" });
5061
5250
  const { branch } = req.body;
5062
5251
  if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
5063
- const { checkoutBranch } = await import("./workspace-ET3FHZYN.js");
5252
+ const { checkoutBranch } = await import("./workspace-6QTTUMMW.js");
5064
5253
  try {
5065
5254
  await checkoutBranch(project.workdir, branch);
5066
5255
  res.json({ branch });
@@ -5074,7 +5263,7 @@ async function createServerHandle(config3) {
5074
5263
  if (!project) return res.status(404).json({ error: "Project not found" });
5075
5264
  const { name } = req.body;
5076
5265
  if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
5077
- const { createBranch } = await import("./workspace-ET3FHZYN.js");
5266
+ const { createBranch } = await import("./workspace-6QTTUMMW.js");
5078
5267
  try {
5079
5268
  await createBranch(project.workdir, name);
5080
5269
  res.json({ branch: name });
@@ -5082,17 +5271,56 @@ async function createServerHandle(config3) {
5082
5271
  res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
5083
5272
  }
5084
5273
  });
5274
+ app.get("/api/sessions/:id/branches", async (req, res) => {
5275
+ const session = sessionManager.getSession(req.params.id);
5276
+ if (!session) return res.status(404).json({ error: "Session not found" });
5277
+ const effectiveWorkdir = session.workspace ?? session.workdir;
5278
+ const { listBranches } = await import("./workspace-6QTTUMMW.js");
5279
+ const branches = await listBranches(effectiveWorkdir);
5280
+ res.json({ branches });
5281
+ });
5282
+ app.post("/api/sessions/:id/checkout", async (req, res) => {
5283
+ const session = sessionManager.getSession(req.params.id);
5284
+ if (!session) return res.status(404).json({ error: "Session not found" });
5285
+ const { branch } = req.body;
5286
+ if (!branch || typeof branch !== "string") return res.status(400).json({ error: "branch is required" });
5287
+ const effectiveWorkdir = session.workspace ?? session.workdir;
5288
+ const { checkoutBranch, validateRef } = await import("./workspace-6QTTUMMW.js");
5289
+ try {
5290
+ await validateRef(effectiveWorkdir, branch);
5291
+ await checkoutBranch(effectiveWorkdir, branch);
5292
+ res.json({ branch });
5293
+ } catch (err) {
5294
+ res.status(400).json({ error: err instanceof Error ? err.message : "Failed to checkout branch" });
5295
+ }
5296
+ });
5297
+ app.post("/api/sessions/:id/checkout-new", async (req, res) => {
5298
+ const session = sessionManager.getSession(req.params.id);
5299
+ if (!session) return res.status(404).json({ error: "Session not found" });
5300
+ const { name } = req.body;
5301
+ if (!name || typeof name !== "string") return res.status(400).json({ error: "name is required" });
5302
+ const effectiveWorkdir = session.workspace ?? session.workdir;
5303
+ const { createBranch, validateRef } = await import("./workspace-6QTTUMMW.js");
5304
+ try {
5305
+ await validateRef(effectiveWorkdir, name);
5306
+ await createBranch(effectiveWorkdir, name);
5307
+ res.json({ branch: name });
5308
+ } catch (err) {
5309
+ res.status(400).json({ error: err instanceof Error ? err.message : "Failed to create branch" });
5310
+ }
5311
+ });
5085
5312
  app.get("/api/projects/:id/workspaces", async (req, res) => {
5086
5313
  const { getProject: getProject2 } = await import("./projects-LHRNCUGW.js");
5087
5314
  const project = getProject2(req.params.id);
5088
5315
  if (!project) return res.status(404).json({ error: "Project not found" });
5089
- const { listWorkspaces } = await import("./workspace-ET3FHZYN.js");
5316
+ const { listWorkspaces } = await import("./workspace-6QTTUMMW.js");
5090
5317
  const all = await listWorkspaces(project.name);
5091
5318
  const workspacesList = all.filter((ws) => ws.path !== project.workdir);
5092
5319
  res.json({ workspaces: workspacesList });
5093
5320
  });
5094
5321
  app.get("/api/sessions", async (req, res) => {
5095
- const { getRecentUserPromptsForSession } = await import("./events-AEQ6GQXI.js");
5322
+ const { getRecentUserPromptsForSession } = await import("./events-X353XCPN.js");
5323
+ const { getPendingConfirmationsBySession } = await import("./path-security-3FAIPJSV.js");
5096
5324
  const projectId = req.query["projectId"];
5097
5325
  const limit = Math.min(parseInt(req.query["limit"]) || 20, 100);
5098
5326
  const offset = parseInt(req.query["offset"]) || 0;
@@ -5109,7 +5337,15 @@ async function createServerHandle(config3) {
5109
5337
  ...session,
5110
5338
  recentUserPrompts: getRecentUserPromptsForSession(session.id, 10)
5111
5339
  }));
5112
- res.json({ sessions: sessionsWithPrompts, hasMore });
5340
+ const allPending = getPendingConfirmationsBySession();
5341
+ const pendingConfirmationsBySession = {};
5342
+ for (const s of sessions) {
5343
+ const confs = allPending[s.id];
5344
+ if (confs) {
5345
+ pendingConfirmationsBySession[s.id] = confs;
5346
+ }
5347
+ }
5348
+ res.json({ sessions: sessionsWithPrompts, hasMore, pendingConfirmationsBySession });
5113
5349
  });
5114
5350
  app.post("/api/sessions", async (req, res) => {
5115
5351
  const { projectId, title } = req.body;
@@ -5122,6 +5358,29 @@ async function createServerHandle(config3) {
5122
5358
  }
5123
5359
  const { providerId, model } = parseDefaultModelSelection(config3.defaultModelSelection);
5124
5360
  const session = sessionManager.createSession(projectId, title, providerId ?? null, model ?? null);
5361
+ wssExports.broadcastAll({
5362
+ type: "session.created",
5363
+ sessionId: session.id,
5364
+ payload: {
5365
+ session: {
5366
+ id: session.id,
5367
+ projectId: session.projectId,
5368
+ title: session.metadata.title,
5369
+ workdir: session.workdir,
5370
+ workspace: session.workspace,
5371
+ mode: session.mode,
5372
+ phase: session.phase,
5373
+ isRunning: session.isRunning,
5374
+ providerId: session.providerId,
5375
+ providerModel: session.providerModel,
5376
+ createdAt: session.createdAt,
5377
+ updatedAt: session.updatedAt,
5378
+ criteriaCount: session.criteria.length,
5379
+ criteriaCompleted: session.criteria.filter((c) => c.status.type === "passed").length,
5380
+ messageCount: session.messageCount ?? session.messages.length
5381
+ }
5382
+ }
5383
+ });
5125
5384
  res.status(201).json({ session });
5126
5385
  });
5127
5386
  app.post("/api/sessions/:id/switch-workspace", async (req, res) => {
@@ -5149,9 +5408,9 @@ async function createServerHandle(config3) {
5149
5408
  }
5150
5409
  });
5151
5410
  app.get("/api/sessions/:id", async (req, res) => {
5152
- const { getEventStore: getEventStore2 } = await import("./events-AEQ6GQXI.js");
5153
- const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5154
- const { getPendingQuestionsForSession } = await import("./tools-X52DFUS6.js");
5411
+ const { getEventStore: getEventStore2 } = await import("./events-X353XCPN.js");
5412
+ const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-ONH3D4OK.js");
5413
+ const { getPendingQuestionsForSession } = await import("./tools-RZ464GUJ.js");
5155
5414
  const session = sessionManager.getSession(req.params.id);
5156
5415
  if (!session) {
5157
5416
  return res.status(404).json({ error: "Session not found" });
@@ -5162,7 +5421,8 @@ async function createServerHandle(config3) {
5162
5421
  const contextState = sessionManager.getContextState(req.params.id);
5163
5422
  const queueState = sessionManager.getQueueState(req.params.id);
5164
5423
  const pendingQuestions = getPendingQuestionsForSession(req.params.id);
5165
- res.json({ session, messages, contextState, queueState, pendingQuestions });
5424
+ const pendingConfirmations = foldPendingConfirmations(events);
5425
+ res.json({ session, messages, contextState, queueState, pendingQuestions, pendingConfirmations });
5166
5426
  });
5167
5427
  app.delete("/api/sessions/:id", async (req, res) => {
5168
5428
  const sessionId = req.params["id"];
@@ -5170,14 +5430,19 @@ async function createServerHandle(config3) {
5170
5430
  if (!session) {
5171
5431
  return res.status(404).json({ error: "Session not found" });
5172
5432
  }
5173
- const { stopSessionExecution } = await import("./chat-handler-LK5RUAGO.js");
5174
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-X52DFUS6.js");
5433
+ const { stopSessionExecution } = await import("./chat-handler-QLMBYRDY.js");
5434
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-RZ464GUJ.js");
5175
5435
  sessionManager.clearMessageQueue(sessionId);
5176
5436
  stopSessionExecution(sessionId, sessionManager);
5177
5437
  abortSession(sessionId);
5178
5438
  cancelQuestionsForSession(sessionId, "Session deleted");
5179
5439
  cancelPathConfirmationsForSession(sessionId, "Session deleted");
5180
5440
  sessionManager.deleteSession(sessionId);
5441
+ wssExports.broadcastAll({
5442
+ type: "session.deleted",
5443
+ sessionId,
5444
+ payload: { sessionId }
5445
+ });
5181
5446
  res.json({ success: true });
5182
5447
  });
5183
5448
  app.delete("/api/projects/:projectId/sessions", (req, res) => {
@@ -5187,10 +5452,15 @@ async function createServerHandle(config3) {
5187
5452
  return res.status(404).json({ error: "Project not found" });
5188
5453
  }
5189
5454
  sessionManager.deleteAllSessions(projectId, project.workdir);
5455
+ wssExports.broadcastAll({
5456
+ type: "session.deletedAll",
5457
+ sessionId: projectId,
5458
+ payload: {}
5459
+ });
5190
5460
  res.json({ success: true });
5191
5461
  });
5192
5462
  app.post("/api/sessions/:id/provider", async (req, res) => {
5193
- const { getEventStore: getEventStore2 } = await import("./events-AEQ6GQXI.js");
5463
+ const { getEventStore: getEventStore2 } = await import("./events-X353XCPN.js");
5194
5464
  const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5195
5465
  const sessionId = req.params.id;
5196
5466
  const session = sessionManager.getSession(sessionId);
@@ -5302,7 +5572,7 @@ async function createServerHandle(config3) {
5302
5572
  res.json({ success: true });
5303
5573
  });
5304
5574
  app.put("/api/sessions/:id/mode", async (req, res) => {
5305
- const { getEventStore: getEventStore2 } = await import("./events-AEQ6GQXI.js");
5575
+ const { getEventStore: getEventStore2 } = await import("./events-X353XCPN.js");
5306
5576
  const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5307
5577
  const sessionId = req.params.id;
5308
5578
  const session = sessionManager.getSession(sessionId);
@@ -5359,15 +5629,15 @@ async function createServerHandle(config3) {
5359
5629
  if (!callId || approved === void 0) {
5360
5630
  return res.status(400).json({ error: "callId and approved are required" });
5361
5631
  }
5362
- const { providePathConfirmation } = await import("./tools-X52DFUS6.js");
5632
+ const { providePathConfirmation } = await import("./tools-RZ464GUJ.js");
5363
5633
  const result = providePathConfirmation(callId, approved, alwaysAllow);
5364
5634
  if (!result.found) {
5365
5635
  return res.status(404).json({ error: "No pending path confirmation with that ID" });
5366
5636
  }
5367
- const { getEventStore: getEventStore2 } = await import("./events-AEQ6GQXI.js");
5637
+ const { getEventStore: getEventStore2 } = await import("./events-X353XCPN.js");
5368
5638
  const { buildMessagesFromStoredEvents, foldPendingConfirmations } = await import("./folding-ONH3D4OK.js");
5369
- const { createSessionStateMessage } = await import("./protocol-4QFU7WAI.js");
5370
- const { getPendingQuestionsForSession } = await import("./tools-X52DFUS6.js");
5639
+ const { createSessionStateMessage } = await import("./protocol-EWEVW65B.js");
5640
+ const { getPendingQuestionsForSession } = await import("./tools-RZ464GUJ.js");
5371
5641
  const eventStore = getEventStore2();
5372
5642
  const events = eventStore.getEvents(sessionId);
5373
5643
  const messages = buildMessagesFromStoredEvents(events);
@@ -5378,6 +5648,11 @@ async function createServerHandle(config3) {
5378
5648
  const stateMsg = createSessionStateMessage(session, messages, pendingConfirmations, pendingQuestions);
5379
5649
  wssExports.broadcastForSession(sessionId, { ...stateMsg, sessionId });
5380
5650
  }
5651
+ wssExports.broadcastAll({
5652
+ type: "session.confirmation_resolved",
5653
+ sessionId,
5654
+ payload: { sessionId, callId }
5655
+ });
5381
5656
  res.json({ success: true });
5382
5657
  });
5383
5658
  app.post("/api/sessions/:id/answer", async (req, res) => {
@@ -5388,7 +5663,7 @@ async function createServerHandle(config3) {
5388
5663
  if (!skip && typeof answer !== "string") {
5389
5664
  return res.status(400).json({ error: "answer is required when not skipping" });
5390
5665
  }
5391
- const { provideAnswer } = await import("./tools-X52DFUS6.js");
5666
+ const { provideAnswer } = await import("./tools-RZ464GUJ.js");
5392
5667
  const found = provideAnswer(callId, answer ?? "", skip ?? false);
5393
5668
  if (!found) {
5394
5669
  return res.status(404).json({ error: "No pending question with that ID" });
@@ -5448,7 +5723,7 @@ async function createServerHandle(config3) {
5448
5723
  backend: activeProvider?.backend ?? llmClient.getBackend(),
5449
5724
  model: llmClient.getModel()
5450
5725
  };
5451
- const { runAgentTurn, TurnMetrics } = await import("./orchestrator-RTZVMWQV.js");
5726
+ const { runAgentTurn, TurnMetrics } = await import("./orchestrator-BIFTALBG.js");
5452
5727
  runAgentTurn(
5453
5728
  {
5454
5729
  sessionManager,
@@ -5486,15 +5761,15 @@ async function createServerHandle(config3) {
5486
5761
  if (!session) {
5487
5762
  return res.status(404).json({ error: "Session not found" });
5488
5763
  }
5489
- const { stopSessionExecution } = await import("./chat-handler-LK5RUAGO.js");
5490
- const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-X52DFUS6.js");
5764
+ const { stopSessionExecution } = await import("./chat-handler-QLMBYRDY.js");
5765
+ const { cancelQuestionsForSession, cancelPathConfirmationsForSession } = await import("./tools-RZ464GUJ.js");
5491
5766
  const queuedMessages = sessionManager.getQueueState(sessionId);
5492
5767
  sessionManager.clearMessageQueue(sessionId);
5493
5768
  stopSessionExecution(sessionId, sessionManager);
5494
5769
  abortSession(sessionId);
5495
5770
  cancelQuestionsForSession(sessionId, "Session stopped by user");
5496
5771
  cancelPathConfirmationsForSession(sessionId, "Session stopped by user");
5497
- const eventStore = (await import("./events-AEQ6GQXI.js")).getEventStore();
5772
+ const eventStore = (await import("./events-X353XCPN.js")).getEventStore();
5498
5773
  eventStore.append(sessionId, { type: "running.changed", data: { isRunning: false } });
5499
5774
  res.json({ success: true, queuedMessages });
5500
5775
  });
@@ -5508,7 +5783,7 @@ async function createServerHandle(config3) {
5508
5783
  if (typeof messageIndex !== "number" || messageIndex < 0) {
5509
5784
  return res.status(400).json({ error: "messageIndex must be a non-negative number" });
5510
5785
  }
5511
- const { truncateSessionMessages } = await import("./events-AEQ6GQXI.js");
5786
+ const { truncateSessionMessages } = await import("./events-X353XCPN.js");
5512
5787
  truncateSessionMessages(sessionId, messageIndex);
5513
5788
  res.json({ success: true });
5514
5789
  });
@@ -5518,11 +5793,14 @@ async function createServerHandle(config3) {
5518
5793
  if (!session) {
5519
5794
  return res.status(404).json({ error: "Session not found" });
5520
5795
  }
5521
- const { messageId } = req.body;
5796
+ const { messageId, content } = req.body;
5522
5797
  if (typeof messageId !== "string" || !messageId) {
5523
5798
  return res.status(400).json({ error: "messageId is required" });
5524
5799
  }
5525
- const { getEventStore: getEventStore2 } = await import("./events-AEQ6GQXI.js");
5800
+ if (content !== void 0 && (typeof content !== "string" || !content.trim())) {
5801
+ return res.status(400).json({ error: "content must be a non-empty string if provided" });
5802
+ }
5803
+ const { getEventStore: getEventStore2 } = await import("./events-X353XCPN.js");
5526
5804
  const { buildMessagesFromStoredEvents } = await import("./folding-ONH3D4OK.js");
5527
5805
  const eventStore = getEventStore2();
5528
5806
  const events = eventStore.getEvents(sessionId);
@@ -5535,9 +5813,9 @@ async function createServerHandle(config3) {
5535
5813
  if (msg.role !== "user" || msg.isSystemGenerated) {
5536
5814
  return res.status(400).json({ error: "Can only replay user messages" });
5537
5815
  }
5538
- const { truncateSessionMessages } = await import("./events-AEQ6GQXI.js");
5816
+ const { truncateSessionMessages } = await import("./events-X353XCPN.js");
5539
5817
  truncateSessionMessages(sessionId, msgIndex - 1);
5540
- sessionManager.queueMessage(sessionId, "asap", msg.content, msg.attachments, msg.messageKind);
5818
+ sessionManager.queueMessage(sessionId, "asap", content ?? msg.content, msg.attachments, msg.messageKind);
5541
5819
  res.json({ success: true });
5542
5820
  });
5543
5821
  app.post("/api/sessions/:id/chat", async (req, res) => {
@@ -5654,7 +5932,8 @@ async function createServerHandle(config3) {
5654
5932
  providers: providerManager.getProviders(),
5655
5933
  activeProviderId: providerManager.getActiveProviderId(),
5656
5934
  defaultModelSelection: config3.defaultModelSelection,
5657
- visionFallback
5935
+ visionFallback,
5936
+ platform: platformInfo
5658
5937
  });
5659
5938
  });
5660
5939
  app.post("/api/model/refresh", async (_req, res) => {
@@ -6110,7 +6389,7 @@ async function createServerHandle(config3) {
6110
6389
  });
6111
6390
  async function rebuildMcpTools() {
6112
6391
  const { createMcpTools: createMcpTools2 } = await import("./tool-adapter-MOGEI6TP.js");
6113
- const { setMcpTools: setMcpTools2 } = await import("./tools-X52DFUS6.js");
6392
+ const { setMcpTools: setMcpTools2 } = await import("./tools-RZ464GUJ.js");
6114
6393
  const mcpTools = createMcpTools2(mcpManager);
6115
6394
  setMcpTools2(mcpTools);
6116
6395
  }
@@ -6345,13 +6624,13 @@ async function createServerHandle(config3) {
6345
6624
  })
6346
6625
  );
6347
6626
  app.get("/api/sessions/:id/background-processes", async (req, res) => {
6348
- const { getSessionProcesses } = await import("./manager-24C2AXVF.js");
6627
+ const { getSessionProcesses } = await import("./manager-5GILDCVD.js");
6349
6628
  const sessionId = req.params.id;
6350
6629
  const processes = getSessionProcesses(sessionId);
6351
6630
  res.json({ processes });
6352
6631
  });
6353
6632
  app.post("/api/sessions/:id/background-process/:processId/stop", async (req, res) => {
6354
- const { stopProcess } = await import("./manager-24C2AXVF.js");
6633
+ const { stopProcess } = await import("./manager-5GILDCVD.js");
6355
6634
  const sessionId = req.params.id;
6356
6635
  const processId = req.params.processId;
6357
6636
  const session = sessionManager.getSession(sessionId);
@@ -6494,7 +6773,7 @@ async function createServerHandle(config3) {
6494
6773
  const state = sessionManager.getContextState(sessionId);
6495
6774
  wssExports.broadcastForSession(sessionId, createContextStateMessage(state));
6496
6775
  });
6497
- const { QueueProcessor } = await import("./processor-3O26I7DO.js");
6776
+ const { QueueProcessor } = await import("./processor-MQ47QSDR.js");
6498
6777
  const queueProcessor = new QueueProcessor({
6499
6778
  sessionManager,
6500
6779
  providerManager,
@@ -6574,4 +6853,4 @@ export {
6574
6853
  createServerHandle,
6575
6854
  createServer
6576
6855
  };
6577
- //# sourceMappingURL=chunk-EYXDM2FY.js.map
6856
+ //# sourceMappingURL=chunk-MIJX54CG.js.map