openfox 2.0.70 → 2.0.72

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 (68) hide show
  1. package/dist/{chat-handler-K3DWIGHG.js → chat-handler-LK5RUAGO.js} +19 -19
  2. package/dist/{chunk-64GN6OPR.js → chunk-2DCJERYX.js} +8 -8
  3. package/dist/chunk-2M6JM7OR.js +7 -0
  4. package/dist/{chunk-XDWV5CYV.js → chunk-4LBSC4R3.js} +15 -20
  5. package/dist/{chunk-TFDK6VSG.js → chunk-6XKM25OT.js} +13 -13
  6. package/dist/{chunk-E4POUD2M.js → chunk-6XOA2WPU.js} +2 -2
  7. package/dist/{chunk-YQ3MEZJL.js → chunk-CHPRGEY5.js} +3 -3
  8. package/dist/{chunk-C3VLGVSQ.js → chunk-DJASQ2ZB.js} +11 -2
  9. package/dist/{chunk-4QBDV6LX.js → chunk-E5LLEO3C.js} +2 -2
  10. package/dist/{chunk-JHGKXQBO.js → chunk-EZK7CGJ7.js} +87 -30
  11. package/dist/{chunk-ZZ5JPVBT.js → chunk-FILRB5QX.js} +259 -300
  12. package/dist/{chunk-PVWAE43V.js → chunk-GMF36EZ3.js} +123 -106
  13. package/dist/{chunk-GMXKJGJ2.js → chunk-I4RWK2L2.js} +8 -4
  14. package/dist/{chunk-PK2RMVMB.js → chunk-I72ACQ6R.js} +19 -18
  15. package/dist/{chunk-F4PMNP7S.js → chunk-NOU2QIL5.js} +3 -2
  16. package/dist/{chunk-UTSSFPYA.js → chunk-O2BE7KVQ.js} +8 -8
  17. package/dist/{chunk-7OW6X764.js → chunk-OHGQILRF.js} +15 -4
  18. package/dist/{chunk-TFCHWMOJ.js → chunk-PYB2UEC3.js} +11 -5
  19. package/dist/{chunk-XUPKS5FL.js → chunk-QNKXZH3Z.js} +2 -2
  20. package/dist/{chunk-X5DOUA3Z.js → chunk-S7O77CVO.js} +2 -2
  21. package/dist/{chunk-46OR4W3L.js → chunk-TK4BZIXA.js} +45 -31
  22. package/dist/{chunk-KXVFHMSM.js → chunk-UV3EMWL4.js} +2 -2
  23. package/dist/chunk-X2Y3AJPP.js +216 -0
  24. package/dist/{chunk-QFSDJIUS.js → chunk-XWBNYU2L.js} +3 -3
  25. package/dist/cli/dev.js +1 -1
  26. package/dist/cli/index.js +1 -1
  27. package/dist/{client-63U5CTPE.js → client-SC6X65GY.js} +3 -3
  28. package/dist/{client-pure-XHH4RRLB.js → client-pure-NMLDKRDN.js} +6 -4
  29. package/dist/{compactor-IPMMFFHM.js → compactor-4UKYWC5F.js} +9 -9
  30. package/dist/{events-CDQ4HASP.js → events-AEQ6GQXI.js} +4 -4
  31. package/dist/{folding-V2K34EGN.js → folding-ONH3D4OK.js} +2 -2
  32. package/dist/{inspect-proxy-OVEFBJ26.js → inspect-proxy-PHVZBZUO.js} +4 -4
  33. package/dist/{manager-JXYRGR6B.js → manager-24C2AXVF.js} +6 -6
  34. package/dist/{orchestrator-PUV4WG77.js → orchestrator-RTZVMWQV.js} +18 -18
  35. package/dist/package.json +2 -2
  36. package/dist/{platform-AB3GIEGO.js → platform-XQ7BS3KQ.js} +4 -4
  37. package/dist/{processor-O2QW27BD.js → processor-3O26I7DO.js} +19 -19
  38. package/dist/{project-creator-EMGRSPYS.js → project-creator-FQRBN3RG.js} +3 -3
  39. package/dist/{projects-7HSBZY2D.js → projects-LHRNCUGW.js} +3 -3
  40. package/dist/{protocol-CTNE3ANP.js → protocol-4QFU7WAI.js} +2 -2
  41. package/dist/{protocol-DIhV-tAc.d.ts → protocol-DGQWVFcP.d.ts} +1 -1
  42. package/dist/provider/index.d.ts +4 -4
  43. package/dist/{provider-IBIWR4AI.js → provider-OJUCZ5RN.js} +4 -4
  44. package/dist/{provider-manager-7C3JVOLW.js → provider-manager-MAKXSL6L.js} +4 -4
  45. package/dist/{pwa-T6JRAYJB.js → pwa-SEWCVKZX.js} +7 -8
  46. package/dist/{serve-NOHD22CT.js → serve-6PSAQ2YH.js} +26 -24
  47. package/dist/server/index.d.ts +12 -22
  48. package/dist/server/index.js +23 -22
  49. package/dist/server-G2J4RAXL.js +38 -0
  50. package/dist/{service-6DL3MJZD.js → service-2PW2V25M.js} +6 -1
  51. package/dist/{settings-6ZPWOBWL.js → settings-PFXRYXZU.js} +3 -3
  52. package/dist/shared/index.d.ts +3 -3
  53. package/dist/{store-FXTCHBI4.js → store-SARB6V2N.js} +2 -2
  54. package/dist/{tools-CALGWIV2.js → tools-X52DFUS6.js} +17 -17
  55. package/dist/{types-BsfYpEai.d.ts → types-C4cG9CtC.d.ts} +3 -2
  56. package/dist/{types-CBC6lLqc.d.ts → types-DSZhFA8O.d.ts} +1 -1
  57. package/dist/update-I2M2RG4Q.js +40 -0
  58. package/dist/web/assets/index-C7SW16F7.js +305 -0
  59. package/dist/web/assets/{index-BVTG7SRG.css → index-CmechlaK.css} +1 -1
  60. package/dist/web/index.html +2 -2
  61. package/dist/web/sw.js +1 -1
  62. package/dist/workspace-ET3FHZYN.js +32 -0
  63. package/package.json +2 -2
  64. package/dist/chunk-VLV3XWZS.js +0 -191
  65. package/dist/cli/update.sh +0 -16
  66. package/dist/server-RLNPHLY2.js +0 -38
  67. package/dist/web/assets/index-CtWMMQ_X.js +0 -305
  68. package/dist/worktree-6U45P63P.js +0 -30
@@ -1,3 +1,11 @@
1
+ import {
2
+ getGitBranch,
3
+ listWorkspaces
4
+ } from "./chunk-X2Y3AJPP.js";
5
+ import {
6
+ AskUserInterrupt,
7
+ askUserTool
8
+ } from "./chunk-EU3WWTFH.js";
1
9
  import {
2
10
  COMPACTION_PROMPT,
3
11
  TurnMetrics,
@@ -10,13 +18,13 @@ import {
10
18
  createToolCallEvent,
11
19
  createToolResultEvent,
12
20
  streamLLMPure
13
- } from "./chunk-QFSDJIUS.js";
21
+ } from "./chunk-XWBNYU2L.js";
14
22
  import {
15
23
  startInspectProxy
16
- } from "./chunk-4QBDV6LX.js";
24
+ } from "./chunk-E5LLEO3C.js";
17
25
  import {
18
26
  getProject
19
- } from "./chunk-KXVFHMSM.js";
27
+ } from "./chunk-UV3EMWL4.js";
20
28
  import {
21
29
  checkAborted,
22
30
  createProcess,
@@ -27,27 +35,24 @@ import {
27
35
  spawnShellProcess,
28
36
  startProcessCommand,
29
37
  stopProcess
30
- } from "./chunk-TFCHWMOJ.js";
38
+ } from "./chunk-PYB2UEC3.js";
31
39
  import {
32
40
  getMaxPerSession,
33
41
  getSessionProcessCount,
34
42
  terminateProcessTree
35
- } from "./chunk-PK2RMVMB.js";
43
+ } from "./chunk-I72ACQ6R.js";
36
44
  import {
37
45
  createMcpTools
38
46
  } from "./chunk-NWO6GRYE.js";
39
47
  import {
40
48
  applyMcpServerUpdate
41
49
  } from "./chunk-UN26GIQG.js";
42
- import {
43
- listBranches
44
- } from "./chunk-VLV3XWZS.js";
45
50
  import {
46
51
  getCurrentContextWindowId,
47
52
  getCurrentWindowMessageOptions,
48
53
  getEventStore,
49
54
  getRuntimeConfig
50
- } from "./chunk-TFDK6VSG.js";
55
+ } from "./chunk-6XKM25OT.js";
51
56
  import {
52
57
  buildContextMessagesFromEventHistory,
53
58
  foldContextState,
@@ -57,24 +62,20 @@ import {
57
62
  handleToolResult,
58
63
  reorderToolMessages,
59
64
  stripOrphanedToolCalls
60
- } from "./chunk-C3VLGVSQ.js";
65
+ } from "./chunk-DJASQ2ZB.js";
61
66
  import {
62
67
  createChatDoneMessage,
63
68
  createChatMessageMessage,
64
69
  createChatMessageUpdatedMessage,
65
70
  createChatPathConfirmationMessage,
66
71
  createQueueStateMessage
67
- } from "./chunk-F4PMNP7S.js";
72
+ } from "./chunk-NOU2QIL5.js";
68
73
  import {
69
74
  SETTINGS_KEYS,
70
75
  deleteSetting,
71
76
  getSetting,
72
77
  setSetting
73
- } from "./chunk-XUPKS5FL.js";
74
- import {
75
- AskUserInterrupt,
76
- askUserTool
77
- } from "./chunk-EU3WWTFH.js";
78
+ } from "./chunk-QNKXZH3Z.js";
78
79
  import {
79
80
  modelSupportsVision
80
81
  } from "./chunk-V4IE7HJY.js";
@@ -84,7 +85,7 @@ import {
84
85
  formatPdfErrorMessage,
85
86
  isPdfBuffer,
86
87
  processPdfContent
87
- } from "./chunk-JHGKXQBO.js";
88
+ } from "./chunk-EZK7CGJ7.js";
88
89
  import {
89
90
  logger
90
91
  } from "./chunk-K44MW7JJ.js";
@@ -105,7 +106,7 @@ import { resolve as resolve2, isAbsolute } from "path";
105
106
 
106
107
  // src/server/tools/path-security.ts
107
108
  import { realpath } from "fs/promises";
108
- import { resolve, normalize, join, basename, sep } from "path";
109
+ import { resolve, normalize, join, basename, sep, posix, win32 } from "path";
109
110
  import { homedir, tmpdir } from "os";
110
111
  var SAFE_PATHS = /* @__PURE__ */ new Set([
111
112
  "/dev/null",
@@ -218,6 +219,9 @@ var isWindows = () => process.platform === "win32";
218
219
  function isWindowsAbsolutePath(str) {
219
220
  return /^[A-Za-z]:[\\/]/.test(str);
220
221
  }
222
+ function normalizeExtracted(path) {
223
+ return isWindowsAbsolutePath(path) ? win32.normalize(path) : posix.normalize(path);
224
+ }
221
225
  function isPlaceholderToken(str) {
222
226
  return str.includes("__URL__") || str.includes("__FILEURL__") || str.includes("__SED__") || str.includes("__COMMIT_MSG__");
223
227
  }
@@ -239,7 +243,7 @@ function extractAbsolutePathsFromCommand(command) {
239
243
  for (const match2 of fileUrlMatches) {
240
244
  const filePath = match2[1];
241
245
  if (filePath && !isSafePath(filePath)) {
242
- paths.push(normalize(filePath));
246
+ paths.push(normalizeExtracted(filePath));
243
247
  }
244
248
  }
245
249
  sanitized = sanitized.replace(/file:\/\/[^\s'"]+/g, " __FILEURL__ ");
@@ -263,12 +267,12 @@ function extractAbsolutePathsFromCommand(command) {
263
267
  continue;
264
268
  }
265
269
  if (isWindowsAbsolutePath(content)) {
266
- const resolved = normalize(content);
270
+ const resolved = normalizeExtracted(content);
267
271
  if (!isSafePath(resolved)) {
268
272
  paths.push(resolved);
269
273
  }
270
274
  } else if (!isWindows() && content.startsWith("/")) {
271
- const resolved = normalize(content);
275
+ const resolved = normalizeExtracted(content);
272
276
  if (!isSafePath(resolved)) {
273
277
  paths.push(resolved);
274
278
  }
@@ -286,7 +290,7 @@ function extractAbsolutePathsFromCommand(command) {
286
290
  while ((match = winAbsolutePattern.exec(sanitized)) !== null) {
287
291
  const candidate = match[1];
288
292
  if (isPlaceholderToken(candidate)) continue;
289
- const resolved = normalize(candidate);
293
+ const resolved = normalizeExtracted(candidate);
290
294
  if (!isSafePath(resolved)) {
291
295
  paths.push(resolved);
292
296
  }
@@ -297,7 +301,7 @@ function extractAbsolutePathsFromCommand(command) {
297
301
  const candidate = match[1];
298
302
  if (isPlaceholderToken(candidate)) continue;
299
303
  if (looksLikeRegex(candidate)) continue;
300
- const resolved = normalize(candidate);
304
+ const resolved = normalizeExtracted(candidate);
301
305
  if (resolved === "/" || resolved === "") continue;
302
306
  if (!isSafePath(resolved)) {
303
307
  paths.push(resolved);
@@ -331,7 +335,7 @@ function extractSensitivePathsFromCommand(command) {
331
335
  return [...new Set(paths)];
332
336
  }
333
337
  function isSafePath(path) {
334
- const normalized = normalize(path);
338
+ const normalized = posix.normalize(path);
335
339
  return SAFE_PATHS.has(normalized);
336
340
  }
337
341
  async function checkPathsAccess(paths, workdir, sessionId) {
@@ -1444,13 +1448,14 @@ async function checkRtkAvailability() {
1444
1448
  return rtkAvailable;
1445
1449
  }
1446
1450
  function hasBackgroundAmpersand(command) {
1447
- const trimmed = command.replace(/[;\s]+$/, "");
1448
- if (trimmed.endsWith("&")) {
1449
- const before = trimmed[trimmed.length - 2];
1450
- if (before === "&" || before === "|") return false;
1451
- return true;
1452
- }
1453
- return false;
1451
+ let processed = command.replace(/'[^']*'/g, " ").replace(/"[^"]*"/g, " ");
1452
+ processed = processed.replace(/\\./g, " ");
1453
+ processed = processed.replace(/&&/g, " ");
1454
+ processed = processed.replace(/\|&/g, " ");
1455
+ processed = processed.replace(/&>/g, " ");
1456
+ processed = processed.replace(/>&\d/g, " ");
1457
+ processed = processed.replace(/>&/g, " ");
1458
+ return processed.includes("&");
1454
1459
  }
1455
1460
  var runCommandTool = createTool(
1456
1461
  "run_command",
@@ -1598,6 +1603,32 @@ function executeCommand(command, cwd, timeout, signal, onProgress) {
1598
1603
  stderr += chunk;
1599
1604
  onProgress?.(`[stderr] ${chunk}`);
1600
1605
  });
1606
+ proc.on("exit", () => {
1607
+ if (aborted) {
1608
+ clearTimeout(timer);
1609
+ signal?.removeEventListener("abort", onAbort);
1610
+ let output = stdout.trim();
1611
+ if (output) output += "\n\n";
1612
+ output += "[interrupted by user]";
1613
+ resolve6({
1614
+ stdout: output,
1615
+ stderr: stderr.trim(),
1616
+ exitCode: 130
1617
+ });
1618
+ } else if (timedOut) {
1619
+ clearTimeout(timer);
1620
+ signal?.removeEventListener("abort", onAbort);
1621
+ let output = stdout.trim();
1622
+ if (output) output += "\n\n";
1623
+ output += `[Exit code: 124]
1624
+ [Process timed out after ${timeout}ms]`;
1625
+ resolve6({
1626
+ stdout: output,
1627
+ stderr: stderr.trim(),
1628
+ exitCode: 124
1629
+ });
1630
+ }
1631
+ });
1601
1632
  proc.on("close", (code) => {
1602
1633
  exited = true;
1603
1634
  clearTimeout(timer);
@@ -2523,6 +2554,7 @@ async function executeTools(assistantMsgId, toolCalls, ctx, append) {
2523
2554
  type: "chat.ask_user",
2524
2555
  data: { callId: error.callId, question: error.question, type: error.type, options: error.options }
2525
2556
  });
2557
+ append(createChatDoneEvent(assistantMsgId, "waiting_for_user"));
2526
2558
  const { awaitAnswer } = await import("./ask-TOBEA6KD.js");
2527
2559
  const answerPromise = awaitAnswer(error.callId);
2528
2560
  if (!answerPromise) {
@@ -2874,10 +2906,10 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2874
2906
  previousContextTokens,
2875
2907
  result.modelParams
2876
2908
  );
2877
- sessionManager.setCurrentContextSize(sessionId, result.usage.promptTokens);
2909
+ sessionManager.setCurrentContextSize(sessionId, result.usage.promptTokens, config.subAgentMetadata?.subAgentId);
2878
2910
  if (!compacting) {
2879
2911
  const contextState2 = sessionManager.getContextState(sessionId);
2880
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-IPMMFFHM.js");
2912
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-4UKYWC5F.js");
2881
2913
  if (shouldCompact(contextState2.currentTokens, contextState2.maxTokens, runtimeConfig.context.compactionThreshold)) {
2882
2914
  appendCompactionPrompt(sessionId, append);
2883
2915
  compacting = true;
@@ -2958,7 +2990,7 @@ ${COMPACTION_PROMPT}`,
2958
2990
  toolRegistry,
2959
2991
  sessionManager,
2960
2992
  sessionId,
2961
- workdir: session.worktree ?? session.workdir,
2993
+ workdir: session.workspace ?? session.workdir,
2962
2994
  turnMetrics,
2963
2995
  signal,
2964
2996
  onMessage,
@@ -3667,7 +3699,7 @@ async function executeSubAgent(options) {
3667
3699
  })
3668
3700
  );
3669
3701
  }
3670
- const effectiveWorkdir = session.worktree ?? session.workdir;
3702
+ const effectiveWorkdir = session.workspace ?? session.workdir;
3671
3703
  const { content: instructionContent } = await getAllInstructions(effectiveWorkdir, session.projectId);
3672
3704
  const config = getRuntimeConfig();
3673
3705
  const configDir = getGlobalConfigDir(config.mode ?? "production");
@@ -3782,7 +3814,7 @@ var callSubAgentTool = {
3782
3814
  };
3783
3815
  }
3784
3816
  try {
3785
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
3817
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-X52DFUS6.js");
3786
3818
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3787
3819
  const turnMetrics = new TurnMetrics();
3788
3820
  const result = await executeSubAgent({
@@ -4190,7 +4222,7 @@ var DevServerManager = class {
4190
4222
  }
4191
4223
  /**
4192
4224
  * Load dev server config from workdir.
4193
- * If not found and the path looks like a git worktree (contains /worktrees/),
4225
+ * If not found and the path looks like a workspace (contains /workspaces/),
4194
4226
  * falls back to the parent project root.
4195
4227
  */
4196
4228
  async loadConfig(workdir) {
@@ -4212,9 +4244,9 @@ var DevServerManager = class {
4212
4244
  };
4213
4245
  const config = await tryLoad(workdir);
4214
4246
  if (config) return config;
4215
- const wtIdx = workdir.indexOf("/worktrees/");
4216
- if (wtIdx !== -1) {
4217
- const projectRoot = workdir.slice(0, wtIdx);
4247
+ const wsIdx = workdir.indexOf("/workspaces/");
4248
+ if (wsIdx !== -1) {
4249
+ const projectRoot = workdir.slice(0, wsIdx);
4218
4250
  if (projectRoot !== workdir) {
4219
4251
  return tryLoad(projectRoot);
4220
4252
  }
@@ -4390,7 +4422,7 @@ var devServerTool = createTool(
4390
4422
  type: "function",
4391
4423
  function: {
4392
4424
  name: "dev_server",
4393
- description: "Control the project dev server. Start, stop, restart, check status, or fetch logs with optional pagination. Each workdir (project root or git worktree) gets its own independent dev server instance with auto-assigned ports. The dev server is configured via .openfox/dev.json \u2014 searched in the current workdir first, falling back to the project root. Fields: command (string, required), url (string, required), hotReload (boolean, optional, default false), disableInspect (boolean, optional, default false). You can use ${PORT} in command and url \u2014 it will be replaced with an available port at runtime (auto-assigned if the configured port is taken).",
4425
+ description: "Control the project dev server. Start, stop, restart, check status, or fetch logs with optional pagination. Each workdir (project root or workspace) gets its own independent dev server instance with auto-assigned ports. The dev server is configured via .openfox/dev.json \u2014 searched in the current workdir first, falling back to the project root. Fields: command (string, required), url (string, required), hotReload (boolean, optional, default false), disableInspect (boolean, optional, default false). You can use ${PORT} in command and url \u2014 it will be replaced with an available port at runtime (auto-assigned if the configured port is taken).",
4394
4426
  parameters: {
4395
4427
  type: "object",
4396
4428
  properties: {
@@ -4781,7 +4813,7 @@ var mcpConfigTool = createTool(
4781
4813
  mcpNotifyChanged?.(sessionId);
4782
4814
  }
4783
4815
  async function rebuildTools() {
4784
- const { setMcpTools: setMcpTools2 } = await import("./tools-CALGWIV2.js");
4816
+ const { setMcpTools: setMcpTools2 } = await import("./tools-X52DFUS6.js");
4785
4817
  const mcpTools = createMcpTools(mcpManagerForTools);
4786
4818
  setMcpTools2(mcpTools);
4787
4819
  }
@@ -5046,29 +5078,29 @@ var webSearchTool = createTool(
5046
5078
  }
5047
5079
  );
5048
5080
 
5049
- // src/server/tools/worktree.ts
5050
- var worktreeTool = createTool(
5051
- "worktree",
5081
+ // src/server/tools/workspace.ts
5082
+ var workspaceTool = createTool(
5083
+ "workspace",
5052
5084
  {
5053
5085
  type: "function",
5054
5086
  function: {
5055
- name: "worktree",
5056
- description: 'Manage git worktrees for the current session. Use this to work on isolated branches without affecting the main working tree.\n\nAsset strategy for .gitignored files (e.g. node_modules) is configured via .openfox/worktree.json in the project root with fields: ignoredAssets ("symlink"|"copy"|"skip", default "skip"), overrides (optional, per-path exceptions). The server reads this file automatically on create. You can manage it via read_file/write_file, or the user can configure it in Project Settings \u2192 Worktree Asset Strategy.\n\nActions:\n- list_branches: List local git branches for the current project\n- create: Create a new branch + git worktree and attach this session to it (provide: name)\n- attach: Attach this session to an existing git worktree by path (provide: path)\n- close: Close the current worktree and return the session to the project root\n- status: Show current worktree state (active branch, worktree path)',
5087
+ name: "workspace",
5088
+ description: 'Manage workspaces for the current session. A workspace is a cloned copy of the project.\n\nUse "switch" to move between workspaces. Target "original" for the project root, or a workspace name.\nIf the workspace does not exist yet, it is created automatically.\n\nSetup commands (e.g. npm install) are configured via .openfox/workspace.json.\n\nActions:\n- switch: Switch to a workspace (target: "original" or a name, optional branch for new workspaces)\n- list: List all workspaces with their current branch and active status\n- delete: Delete a workspace by name (cannot delete "original")',
5057
5089
  parameters: {
5058
5090
  type: "object",
5059
5091
  properties: {
5060
5092
  action: {
5061
5093
  type: "string",
5062
- enum: ["list_branches", "create", "attach", "close", "status"],
5094
+ enum: ["switch", "list", "delete"],
5063
5095
  description: "The action to perform"
5064
5096
  },
5065
- name: {
5097
+ target: {
5066
5098
  type: "string",
5067
- description: "Branch name for creating a new worktree (required for action=create)"
5099
+ description: 'For switch: "original" or a workspace name. For delete: the workspace name to delete.'
5068
5100
  },
5069
- path: {
5101
+ branch: {
5070
5102
  type: "string",
5071
- description: "Path to an existing worktree directory (required for action=attach)"
5103
+ description: "Optional git branch to check out when creating a new workspace"
5072
5104
  }
5073
5105
  },
5074
5106
  required: ["action"]
@@ -5077,77 +5109,62 @@ var worktreeTool = createTool(
5077
5109
  },
5078
5110
  async (args, context, helpers) => {
5079
5111
  const { sessionId, sessionManager } = context;
5080
- const validActions = ["list_branches", "create", "attach", "close", "status"];
5112
+ const validActions = ["switch", "list", "delete"];
5081
5113
  if (!validActions.includes(args.action)) {
5082
5114
  return helpers.error(`Invalid action: ${args.action}. Must be one of: ${validActions.join(", ")}`);
5083
5115
  }
5084
5116
  switch (args.action) {
5085
- case "list_branches": {
5086
- const session = sessionManager.getSession(sessionId);
5087
- if (!session) return helpers.error("Session not found");
5088
- const project = sessionManager.getProject(session.projectId);
5089
- if (!project) return helpers.error("Project not found");
5090
- const branches = await listBranches(project.workdir);
5091
- return helpers.success(JSON.stringify({ branches }, null, 2));
5092
- }
5093
- case "create": {
5094
- if (!args.name || typeof args.name !== "string") {
5095
- return helpers.error('Parameter "name" is required for action=create (the branch name)');
5096
- }
5097
- const updated = await sessionManager.createSessionWorktree(sessionId, args.name);
5098
- return helpers.success(
5099
- JSON.stringify(
5100
- {
5101
- worktree: updated.worktree,
5102
- branch: args.name,
5103
- message: `Worktree created and session attached to branch "${args.name}"`
5104
- },
5105
- null,
5106
- 2
5107
- )
5108
- );
5109
- }
5110
- case "attach": {
5111
- if (!args.path || typeof args.path !== "string") {
5112
- return helpers.error('Parameter "path" is required for action=attach (path to existing worktree)');
5117
+ case "switch": {
5118
+ if (!args.target || typeof args.target !== "string") {
5119
+ return helpers.error('Parameter "target" is required for action=switch ("original" or a workspace name)');
5113
5120
  }
5114
- const updated = await sessionManager.attachSessionWorktree(sessionId, args.path);
5121
+ const updated = await sessionManager.switchWorkspace(sessionId, args.target, args.branch);
5122
+ const wsName = args.target === "original" ? "original" : updated.workspace?.split("/").pop() ?? args.target;
5123
+ const branch = await getGitBranch(updated.workspace ?? updated.workdir);
5115
5124
  return helpers.success(
5116
5125
  JSON.stringify(
5117
5126
  {
5118
- worktree: updated.worktree,
5119
- message: `Session attached to existing worktree at ${args.path}`
5127
+ workspace: wsName,
5128
+ path: updated.workspace ?? updated.workdir,
5129
+ branch,
5130
+ message: args.target === "original" ? "Switched to original project" : `Switched to workspace "${wsName}" on branch "${branch ?? "unknown"}"`
5120
5131
  },
5121
5132
  null,
5122
5133
  2
5123
5134
  )
5124
5135
  );
5125
5136
  }
5126
- case "close": {
5127
- const updated = await sessionManager.closeSessionWorktree(sessionId);
5128
- return helpers.success(
5129
- JSON.stringify(
5130
- {
5131
- worktree: updated.worktree,
5132
- workdir: updated.workdir,
5133
- message: "Worktree closed. Session returned to project root."
5134
- },
5135
- null,
5136
- 2
5137
- )
5138
- );
5139
- }
5140
- case "status": {
5137
+ case "list": {
5141
5138
  const session = sessionManager.getSession(sessionId);
5142
5139
  if (!session) return helpers.error("Session not found");
5143
- const active = !!session.worktree;
5140
+ const project = sessionManager.getProject(session.projectId);
5141
+ if (!project) return helpers.error("Project not found");
5142
+ const currentBranch = await getGitBranch(session.workspace ?? session.workdir);
5143
+ const named = await listWorkspaces(project.name);
5144
+ const activeWsName = session.workspace?.split("/").pop() ?? null;
5145
+ const workspaces = [
5146
+ { name: "original", branch: currentBranch, active: !session.workspace },
5147
+ ...named.map((ws) => ({
5148
+ name: ws.name,
5149
+ branch: ws.branch,
5150
+ active: ws.name === activeWsName
5151
+ }))
5152
+ ];
5153
+ return helpers.success(JSON.stringify({ workspaces }, null, 2));
5154
+ }
5155
+ case "delete": {
5156
+ if (!args.target || typeof args.target !== "string") {
5157
+ return helpers.error('Parameter "target" is required for action=delete (the workspace name)');
5158
+ }
5159
+ if (args.target === "original") {
5160
+ return helpers.error("Cannot delete the original workspace");
5161
+ }
5162
+ await sessionManager.deleteWorkspace(sessionId, args.target);
5144
5163
  return helpers.success(
5145
5164
  JSON.stringify(
5146
5165
  {
5147
- active,
5148
- worktree: session.worktree ?? null,
5149
- workdir: session.workdir,
5150
- ...active ? { branch: session.worktree?.split("/worktrees/")[1] ?? session.worktree } : {}
5166
+ workspace: args.target,
5167
+ message: `Workspace "${args.target}" has been deleted`
5151
5168
  },
5152
5169
  null,
5153
5170
  2
@@ -5180,7 +5197,7 @@ function getBuiltInTools() {
5180
5197
  stepDoneTool,
5181
5198
  backgroundProcessTool,
5182
5199
  mcpConfigTool,
5183
- worktreeTool
5200
+ workspaceTool
5184
5201
  ];
5185
5202
  }
5186
5203
  return _builtInTools;
@@ -5476,4 +5493,4 @@ export {
5476
5493
  getToolRegistryForAgent,
5477
5494
  createToolRegistry
5478
5495
  };
5479
- //# sourceMappingURL=chunk-PVWAE43V.js.map
5496
+ //# sourceMappingURL=chunk-GMF36EZ3.js.map
@@ -173,9 +173,13 @@ function runMigrations(db2) {
173
173
  logger.info("Migrating sessions table: adding cached_hash column");
174
174
  db2.exec(`ALTER TABLE sessions ADD COLUMN cached_hash TEXT`);
175
175
  }
176
- if (!columnNames.includes("worktree")) {
177
- logger.info("Migrating sessions table: adding worktree column");
178
- db2.exec(`ALTER TABLE sessions ADD COLUMN worktree TEXT`);
176
+ if (!columnNames.includes("workspace") && columnNames.includes("worktree")) {
177
+ logger.info("Migrating sessions table: renaming worktree to workspace");
178
+ db2.exec(`ALTER TABLE sessions RENAME COLUMN worktree TO workspace`);
179
+ }
180
+ if (!columnNames.includes("workspace")) {
181
+ logger.info("Migrating sessions table: adding workspace column");
182
+ db2.exec(`ALTER TABLE sessions ADD COLUMN workspace TEXT`);
179
183
  }
180
184
  logger.info("Database migrations completed");
181
185
  }
@@ -184,4 +188,4 @@ export {
184
188
  initDatabase,
185
189
  getDatabase
186
190
  };
187
- //# sourceMappingURL=chunk-GMXKJGJ2.js.map
191
+ //# sourceMappingURL=chunk-I4RWK2L2.js.map
@@ -43,7 +43,7 @@ function killProcess(pid, signal) {
43
43
  } catch {
44
44
  }
45
45
  }
46
- async function killProcessTree(pid) {
46
+ async function killProcessTree(pid, immediate = false) {
47
47
  if (process.platform === "win32") {
48
48
  await new Promise((resolve) => {
49
49
  const killer = spawn("taskkill", ["/pid", String(pid), "/f", "/t"], {
@@ -55,14 +55,23 @@ async function killProcessTree(pid) {
55
55
  });
56
56
  return;
57
57
  }
58
- const allPids = await getDescendantPids(pid);
59
- allPids.push(pid);
60
- for (const p of allPids) {
61
- killProcess(p, "SIGTERM");
58
+ if (!immediate) {
59
+ try {
60
+ process.kill(-pid, "SIGTERM");
61
+ } catch {
62
+ }
63
+ await sleep(SIGKILL_TIMEOUT_MS);
62
64
  }
63
- await sleep(SIGKILL_TIMEOUT_MS);
64
- for (const p of allPids) {
65
- killProcess(p, "SIGKILL");
65
+ try {
66
+ process.kill(-pid, "SIGKILL");
67
+ } catch {
68
+ }
69
+ const survivors = await getDescendantPids(pid);
70
+ if (survivors.length > 0) {
71
+ survivors.push(pid);
72
+ for (const p of survivors) {
73
+ killProcess(p, "SIGKILL");
74
+ }
66
75
  }
67
76
  }
68
77
  async function terminateProcessTree(proc, options) {
@@ -70,15 +79,7 @@ async function terminateProcessTree(proc, options) {
70
79
  if (!pid || options?.exited?.()) {
71
80
  return;
72
81
  }
73
- if (options?.immediate) {
74
- const allPids = await getDescendantPids(pid);
75
- allPids.push(pid);
76
- for (const p of allPids) {
77
- killProcess(p, "SIGKILL");
78
- }
79
- return;
80
- }
81
- await killProcessTree(pid);
82
+ await killProcessTree(pid, options?.immediate ?? false);
82
83
  }
83
84
 
84
85
  // src/server/tools/background-process/store.ts
@@ -224,4 +225,4 @@ export {
224
225
  getProcessById,
225
226
  cleanupAllProcesses
226
227
  };
227
- //# sourceMappingURL=chunk-PK2RMVMB.js.map
228
+ //# sourceMappingURL=chunk-I72ACQ6R.js.map
@@ -263,7 +263,8 @@ function storedEventToServerMessage(event) {
263
263
  }
264
264
  case "context.state": {
265
265
  const data = event.data;
266
- return createContextStateMessage(data);
266
+ const { subAgentId, ...context } = data;
267
+ return createContextStateMessage(context, subAgentId);
267
268
  }
268
269
  case "session.name_generated": {
269
270
  const data = event.data;
@@ -339,4 +340,4 @@ export {
339
340
  createQueueStateMessage,
340
341
  storedEventToServerMessage
341
342
  };
342
- //# sourceMappingURL=chunk-F4PMNP7S.js.map
343
+ //# sourceMappingURL=chunk-NOU2QIL5.js.map
@@ -10,7 +10,7 @@ import {
10
10
  loadAllAgentsDefault,
11
11
  processEventsForConversation,
12
12
  runTopLevelAgentLoop
13
- } from "./chunk-PVWAE43V.js";
13
+ } from "./chunk-GMF36EZ3.js";
14
14
  import {
15
15
  TurnMetrics,
16
16
  buildAgentReminder,
@@ -18,16 +18,16 @@ import {
18
18
  buildTopLevelSystemPrompt,
19
19
  createChatDoneEvent,
20
20
  createMessageStartEvent
21
- } from "./chunk-QFSDJIUS.js";
21
+ } from "./chunk-XWBNYU2L.js";
22
22
  import {
23
23
  getCurrentContextWindowId,
24
24
  getCurrentWindowMessageOptions,
25
25
  getEventStore,
26
26
  getRuntimeConfig
27
- } from "./chunk-TFDK6VSG.js";
27
+ } from "./chunk-6XKM25OT.js";
28
28
  import {
29
29
  buildSnapshotFromSessionState
30
- } from "./chunk-C3VLGVSQ.js";
30
+ } from "./chunk-DJASQ2ZB.js";
31
31
  import {
32
32
  logger
33
33
  } from "./chunk-K44MW7JJ.js";
@@ -64,7 +64,7 @@ function resolveAgentDef(sessionManager, sessionId) {
64
64
  }
65
65
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
66
66
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
67
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
67
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-X52DFUS6.js");
68
68
  const tools = getToolRegistryForAgent2(agentDef).definitions;
69
69
  const toolFingerprint = getToolFingerprint(tools);
70
70
  const allAgents = await loadAllAgentsDefault();
@@ -77,7 +77,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
77
77
  async function computeSessionHash(sessionManager, sessionId) {
78
78
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
79
79
  const agentDef = await resolveAgentDef(sessionManager, sessionId);
80
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-CALGWIV2.js");
80
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-X52DFUS6.js");
81
81
  const tools = getToolRegistryForAgent2(agentDef).definitions;
82
82
  const toolFingerprint = getToolFingerprint(tools);
83
83
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -95,7 +95,7 @@ async function applyDynamicContext(sessionManager, sessionId) {
95
95
 
96
96
  // src/server/chat/orchestrator.ts
97
97
  async function buildRetryPatterns() {
98
- const { getSetting, SETTINGS_KEYS } = await import("./settings-6ZPWOBWL.js");
98
+ const { getSetting, SETTINGS_KEYS } = await import("./settings-PFXRYXZU.js");
99
99
  const raw = getSetting(SETTINGS_KEYS.RETRY_PATTERNS);
100
100
  if (!raw) {
101
101
  const oldXmlProtection = getSetting("llm.disableXmlProtection");
@@ -357,4 +357,4 @@ export {
357
357
  runChatTurn,
358
358
  runAgentTurn
359
359
  };
360
- //# sourceMappingURL=chunk-UTSSFPYA.js.map
360
+ //# sourceMappingURL=chunk-O2BE7KVQ.js.map