openfox 2.0.66 → 2.0.68

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 (49) hide show
  1. package/dist/{chat-handler-ZRDNSLOG.js → chat-handler-QFEHJBWN.js} +13 -12
  2. package/dist/{chunk-R7EED6ZU.js → chunk-32KLLK4N.js} +3 -3
  3. package/dist/{chunk-WNA5VPZG.js → chunk-4QBDV6LX.js} +16 -13
  4. package/dist/{chunk-TMHWQYWE.js → chunk-64GN6OPR.js} +2 -2
  5. package/dist/{chunk-3ACDQKK6.js → chunk-6LK47TNE.js} +37 -138
  6. package/dist/{chunk-5EO6SEGV.js → chunk-7OW6X764.js} +2 -2
  7. package/dist/{chunk-ZUM2ZJOX.js → chunk-COWJRBV4.js} +54 -16
  8. package/dist/{chunk-OE5B44P6.js → chunk-DWZEGLHJ.js} +409 -143
  9. package/dist/{chunk-B7WGOK7G.js → chunk-E4POUD2M.js} +2 -2
  10. package/dist/{chunk-UBIHP5Z7.js → chunk-FCLCEIY4.js} +7 -33
  11. package/dist/{chunk-BL5LOLVA.js → chunk-JHGKXQBO.js} +161 -28
  12. package/dist/{chunk-R4UTFE63.js → chunk-QFSDJIUS.js} +8 -5
  13. package/dist/{chunk-BGTEIA2S.js → chunk-TFCHWMOJ.js} +23 -30
  14. package/dist/{chunk-OBK3I4KD.js → chunk-VLV3XWZS.js} +57 -3
  15. package/dist/chunk-X5DOUA3Z.js +66 -0
  16. package/dist/{chunk-PVRSNXD4.js → chunk-XUPKS5FL.js} +5 -3
  17. package/dist/{chunk-5V5DM5E4.js → chunk-YQ3MEZJL.js} +2 -2
  18. package/dist/cli/dev.js +1 -1
  19. package/dist/cli/index.js +1 -1
  20. package/dist/{client-BUX62J2K.js → client-63U5CTPE.js} +3 -3
  21. package/dist/{client-pure-U6ZYUPJQ.js → client-pure-XHH4RRLB.js} +2 -2
  22. package/dist/{compactor-V43WJ3WX.js → compactor-IPMMFFHM.js} +6 -4
  23. package/dist/{inspect-proxy-PKMDR47R.js → inspect-proxy-OVEFBJ26.js} +2 -2
  24. package/dist/{manager-344C5JLZ.js → manager-JXYRGR6B.js} +6 -2
  25. package/dist/{orchestrator-LZQTDOWM.js → orchestrator-GNXKJPX4.js} +12 -13
  26. package/dist/package.json +4 -2
  27. package/dist/platform-AB3GIEGO.js +21 -0
  28. package/dist/{processor-7KAXESY5.js → processor-TIQEQDP7.js} +13 -12
  29. package/dist/{protocol-42f_Png6.d.ts → protocol-DIhV-tAc.d.ts} +1 -1
  30. package/dist/provider/index.d.ts +4 -4
  31. package/dist/{provider-BDH6JGZB.js → provider-IBIWR4AI.js} +4 -4
  32. package/dist/{provider-manager-D3KIOD5E.js → provider-manager-7C3JVOLW.js} +4 -4
  33. package/dist/{serve-HGHFN3D2.js → serve-QVVZ5SJE.js} +17 -16
  34. package/dist/server/index.d.ts +3 -3
  35. package/dist/server/index.js +16 -15
  36. package/dist/{server-W42ENYFX.js → server-ECKS2Y3R.js} +14 -13
  37. package/dist/{settings-ZZ6AKFPD.js → settings-6ZPWOBWL.js} +2 -2
  38. package/dist/shared/index.d.ts +3 -3
  39. package/dist/{tools-2EB7C6H2.js → tools-QAEN5KHA.js} +11 -10
  40. package/dist/{types-vezAEt_y.d.ts → types-BsfYpEai.d.ts} +2 -1
  41. package/dist/{types-apiMd7Df.d.ts → types-CBC6lLqc.d.ts} +1 -1
  42. package/dist/web/assets/{index-B0Q5uK2h.css → index-BA_65WJ5.css} +1 -1
  43. package/dist/web/assets/index-CsZQdTAd.js +303 -0
  44. package/dist/web/index.html +2 -2
  45. package/dist/web/sw.js +1 -1
  46. package/dist/{worktree-EIF75WGF.js → worktree-6U45P63P.js} +6 -2
  47. package/package.json +4 -2
  48. package/plugins-registry.json +14 -0
  49. package/dist/web/assets/index-BhQN93bI.js +0 -303
@@ -2,17 +2,18 @@ import {
2
2
  buildRunChatTurnParams,
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
- } from "./chunk-TMHWQYWE.js";
5
+ } from "./chunk-64GN6OPR.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-UBIHP5Z7.js";
9
- import "./chunk-3ACDQKK6.js";
10
- import "./chunk-R4UTFE63.js";
11
- import "./chunk-WNA5VPZG.js";
12
- import "./chunk-BGTEIA2S.js";
8
+ } from "./chunk-FCLCEIY4.js";
9
+ import "./chunk-6LK47TNE.js";
10
+ import "./chunk-QFSDJIUS.js";
11
+ import "./chunk-4QBDV6LX.js";
12
+ import "./chunk-KXVFHMSM.js";
13
+ import "./chunk-TFCHWMOJ.js";
13
14
  import "./chunk-PK2RMVMB.js";
14
15
  import "./chunk-NWO6GRYE.js";
15
- import "./chunk-OBK3I4KD.js";
16
+ import "./chunk-VLV3XWZS.js";
16
17
  import "./chunk-WGMJCFCE.js";
17
18
  import {
18
19
  getEventStore
@@ -23,14 +24,14 @@ import {
23
24
  createPhaseChangedMessage,
24
25
  createSessionRunningMessage
25
26
  } from "./chunk-F4PMNP7S.js";
26
- import "./chunk-PVRSNXD4.js";
27
- import "./chunk-EU3WWTFH.js";
28
- import "./chunk-KXVFHMSM.js";
27
+ import "./chunk-X5DOUA3Z.js";
28
+ import "./chunk-XUPKS5FL.js";
29
29
  import "./chunk-GMXKJGJ2.js";
30
+ import "./chunk-EU3WWTFH.js";
30
31
  import "./chunk-J2GP3J3X.js";
31
32
  import "./chunk-V4IE7HJY.js";
32
33
  import "./chunk-YHEQTVFV.js";
33
- import "./chunk-BL5LOLVA.js";
34
+ import "./chunk-JHGKXQBO.js";
34
35
  import "./chunk-K44MW7JJ.js";
35
36
  import "./chunk-YD6NDTKF.js";
36
37
  import "./chunk-WGAAQASA.js";
@@ -156,4 +157,4 @@ export {
156
157
  startChatSession,
157
158
  stopSessionExecution
158
159
  };
159
- //# sourceMappingURL=chat-handler-ZRDNSLOG.js.map
160
+ //# sourceMappingURL=chat-handler-QFEHJBWN.js.map
@@ -164,7 +164,7 @@ async function runCli(options) {
164
164
  break;
165
165
  }
166
166
  case "provider": {
167
- const { runProviderCommand } = await import("./provider-BDH6JGZB.js");
167
+ const { runProviderCommand } = await import("./provider-IBIWR4AI.js");
168
168
  const [, subcommand] = positionals;
169
169
  await runProviderCommand(mode, subcommand);
170
170
  break;
@@ -208,7 +208,7 @@ async function runCli(options) {
208
208
  if (!configExists) {
209
209
  await runNetworkSetup(mode);
210
210
  }
211
- const { runServe } = await import("./serve-HGHFN3D2.js");
211
+ const { runServe } = await import("./serve-QVVZ5SJE.js");
212
212
  const serveOptions = { mode };
213
213
  if (values.port) serveOptions.port = parseInt(values.port);
214
214
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -220,4 +220,4 @@ async function runCli(options) {
220
220
  export {
221
221
  runCli
222
222
  };
223
- //# sourceMappingURL=chunk-R7EED6ZU.js.map
223
+ //# sourceMappingURL=chunk-32KLLK4N.js.map
@@ -22,17 +22,20 @@ function buildHttpHeaders(resHeaders, status, contentLengthOverride, contentType
22
22
  }
23
23
  var proxyPool = /* @__PURE__ */ new Map();
24
24
  var nextOffset = 0;
25
- function getAvailablePort() {
26
- const base = Number(process.env["OPENFOX_PORT"] ?? 10369);
27
- const used = /* @__PURE__ */ new Set();
28
- for (const instance of proxyPool.values()) {
29
- const addr = instance.server.address();
30
- if (addr && typeof addr === "object") used.add(addr.port);
31
- }
32
- for (let port = base + 1; port < base + 200; port++) {
33
- if (!used.has(port)) return port;
25
+ async function getAvailablePort(proxyBase) {
26
+ for (let port = proxyBase; port < proxyBase + 200; port++) {
27
+ if (await tryBind(port)) return port;
34
28
  }
35
- return base + (nextOffset++ % 200 + 1);
29
+ return proxyBase + nextOffset++ % 200;
30
+ }
31
+ function tryBind(port) {
32
+ return new Promise((resolve) => {
33
+ const server = net.createServer();
34
+ server.on("error", () => resolve(false));
35
+ server.listen(port, "127.0.0.1", () => {
36
+ server.close(() => resolve(true));
37
+ });
38
+ });
36
39
  }
37
40
  function parseReqHeaders(str) {
38
41
  const lines = str.split("\r\n");
@@ -85,8 +88,8 @@ function dechunk(buf) {
85
88
  }
86
89
  return Buffer.concat(parts);
87
90
  }
88
- function startInspectProxy(target, sessionManager, workdir) {
89
- const port = getAvailablePort();
91
+ async function startInspectProxy(target, sessionManager, devServerPort, workdir) {
92
+ const port = await getAvailablePort(devServerPort + 1e3);
90
93
  const server = net.createServer((client) => {
91
94
  let clientHead = "";
92
95
  let clientParsed = false;
@@ -340,4 +343,4 @@ export {
340
343
  startInspectProxy,
341
344
  stopAllInspectProxies
342
345
  };
343
- //# sourceMappingURL=chunk-WNA5VPZG.js.map
346
+ //# sourceMappingURL=chunk-4QBDV6LX.js.map
@@ -135,7 +135,7 @@ async function generateSessionNameForSession(sessionId, userMessage, deps, signa
135
135
  client = deps.getLLMClient();
136
136
  client.setModel(providerConfig.model);
137
137
  } else if (providerConfig) {
138
- const { createLLMClient } = await import("./client-BUX62J2K.js");
138
+ const { createLLMClient } = await import("./client-63U5CTPE.js");
139
139
  client = createLLMClient({
140
140
  llm: {
141
141
  baseUrl: providerConfig.baseUrl,
@@ -184,4 +184,4 @@ export {
184
184
  buildRunChatTurnParams,
185
185
  generateSessionNameForSession
186
186
  };
187
- //# sourceMappingURL=chunk-TMHWQYWE.js.map
187
+ //# sourceMappingURL=chunk-64GN6OPR.js.map
@@ -10,19 +10,24 @@ import {
10
10
  createToolCallEvent,
11
11
  createToolResultEvent,
12
12
  streamLLMPure
13
- } from "./chunk-R4UTFE63.js";
13
+ } from "./chunk-QFSDJIUS.js";
14
14
  import {
15
15
  startInspectProxy
16
- } from "./chunk-WNA5VPZG.js";
16
+ } from "./chunk-4QBDV6LX.js";
17
17
  import {
18
+ getProject
19
+ } from "./chunk-KXVFHMSM.js";
20
+ import {
21
+ checkAborted,
18
22
  createProcess,
19
- getPlatformShell,
20
23
  getProcessLogs,
21
24
  getProcessStatus,
22
25
  getSessionProcesses,
26
+ spawnShell,
27
+ spawnShellProcess,
23
28
  startProcessCommand,
24
29
  stopProcess
25
- } from "./chunk-BGTEIA2S.js";
30
+ } from "./chunk-TFCHWMOJ.js";
26
31
  import {
27
32
  getMaxPerSession,
28
33
  getSessionProcessCount,
@@ -33,7 +38,7 @@ import {
33
38
  } from "./chunk-NWO6GRYE.js";
34
39
  import {
35
40
  listBranches
36
- } from "./chunk-OBK3I4KD.js";
41
+ } from "./chunk-VLV3XWZS.js";
37
42
  import {
38
43
  getCurrentContextWindowId,
39
44
  getCurrentWindowMessageOptions,
@@ -62,17 +67,21 @@ import {
62
67
  deleteSetting,
63
68
  getSetting,
64
69
  setSetting
65
- } from "./chunk-PVRSNXD4.js";
70
+ } from "./chunk-XUPKS5FL.js";
66
71
  import {
67
72
  AskUserInterrupt,
68
73
  askUserTool
69
74
  } from "./chunk-EU3WWTFH.js";
70
- import {
71
- getProject
72
- } from "./chunk-KXVFHMSM.js";
73
75
  import {
74
76
  modelSupportsVision
75
77
  } from "./chunk-V4IE7HJY.js";
78
+ import {
79
+ OUTPUT_LIMITS,
80
+ extractPdfText,
81
+ formatPdfErrorMessage,
82
+ isPdfBuffer,
83
+ processPdfContent
84
+ } from "./chunk-JHGKXQBO.js";
76
85
  import {
77
86
  logger
78
87
  } from "./chunk-K44MW7JJ.js";
@@ -88,32 +97,6 @@ import {
88
97
  import { readFile as readFile2, stat, readdir } from "fs/promises";
89
98
  import { extname, join as join2 } from "path";
90
99
 
91
- // src/server/tools/types.ts
92
- var OUTPUT_LIMITS = {
93
- read_file: {
94
- maxLines: 2e3,
95
- maxBytes: 1e5,
96
- maxImageBytes: 2097152,
97
- // 2MB for images
98
- maxPdfPages: 50,
99
- maxFileBytes: 20971520
100
- // 20MB general file safety limit
101
- },
102
- run_command: {
103
- maxLines: 2e3,
104
- maxBytes: 5e4
105
- },
106
- glob: {
107
- maxResults: 500
108
- },
109
- grep: {
110
- maxMatches: 200
111
- },
112
- web_fetch: {
113
- maxBytes: 1e5
114
- }
115
- };
116
-
117
100
  // src/server/tools/tool-helpers.ts
118
101
  import { resolve as resolve2, isAbsolute } from "path";
119
102
 
@@ -242,9 +225,9 @@ function extractAbsolutePathsFromCommand(command) {
242
225
  const paths = [];
243
226
  const home = homedir();
244
227
  let sanitized = command.replace(/https?:\/\/[^\s'"]+/g, " __URL__ ").replace(/ftp:\/\/[^\s'"]+/g, " __URL__ ");
245
- sanitized = sanitized.replace(/s\/[^/]*\/[^/]*\/[gip]*/g, " __SED__ ");
246
- sanitized = sanitized.replace(/s\|[^|]*\|[^|]*\|[gip]*/g, " __SED__ ");
247
- sanitized = sanitized.replace(/s:[^:]*:[^:]*:[gip]*/g, " __SED__ ");
228
+ sanitized = sanitized.replace(/(?<!\w)s\/[^/]*\/[^/]*\/[gip]*/g, " __SED__ ");
229
+ sanitized = sanitized.replace(/(?<!\w)s\|[^|]*\|[^|]*\|[gip]*/g, " __SED__ ");
230
+ sanitized = sanitized.replace(/(?<!\w)s:[^:]*:[^:]*:[gip]*/g, " __SED__ ");
248
231
  sanitized = sanitized.replace(
249
232
  /git\s+commit\b.*?(?:-(?:[a-zA-Z]*m)(?=\s)|--message)\s+(["'])(?:(?!\1).)*\1/g,
250
233
  (match2) => match2.replace(/\/[^\s"'|&;<>`()]+/g, " __COMMIT_MSG__ ")
@@ -861,67 +844,6 @@ function stringToUtf32(str, littleEndian) {
861
844
 
862
845
  // src/server/tools/read.ts
863
846
  import { fileTypeFromBuffer } from "file-type";
864
-
865
- // src/server/tools/pdf-utils.ts
866
- import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
867
- var PDF_HEADER = Buffer.from("%PDF");
868
- function isPdfBuffer(buffer) {
869
- return buffer.length > 4 && buffer.subarray(0, 4).equals(PDF_HEADER);
870
- }
871
- function isPasswordError(err) {
872
- const message = err instanceof Error ? err.message : String(err);
873
- return message.toLowerCase().includes("password") || message.toLowerCase().includes("encrypt");
874
- }
875
- function formatPdfErrorMessage(err) {
876
- if (isPasswordError(err)) {
877
- return "This PDF is password-protected. Unlock it with an external tool first.";
878
- }
879
- return `Failed to read PDF: ${err instanceof Error ? err.message : String(err)}`;
880
- }
881
- function processPdfContent(text, maxBytes) {
882
- let output = text;
883
- let truncated = false;
884
- if (output.length > maxBytes) {
885
- output = output.slice(0, maxBytes) + "\n\n[Output truncated due to size limit]";
886
- truncated = true;
887
- }
888
- const isScanned = output.replace(/\[Page \d+\/\d+\]\n/g, "").trim().length === 0;
889
- return { output, truncated, isScanned };
890
- }
891
- async function extractPdfText(buffer) {
892
- const doc = await getDocument({ data: Uint8Array.from(buffer) }).promise;
893
- const pageCount = doc.numPages;
894
- const rawMeta = await doc.getMetadata();
895
- const info = rawMeta.info;
896
- const title = info?.["Title"] || null;
897
- const author = info?.["Author"] || null;
898
- const pages = [];
899
- const limitedPageCount = Math.min(pageCount, OUTPUT_LIMITS.read_file.maxPdfPages);
900
- try {
901
- for (let i = 1; i <= limitedPageCount; i++) {
902
- const page = await doc.getPage(i);
903
- try {
904
- const content = await page.getTextContent();
905
- const pageText = content.items.map((item) => "str" in item ? item.str : "").join(" ");
906
- pages.push(`[Page ${i}/${pageCount}]
907
- ${pageText}`);
908
- } finally {
909
- page.cleanup();
910
- }
911
- }
912
- } finally {
913
- doc.cleanup();
914
- }
915
- let text = pages.join("\n\n");
916
- if (pageCount > limitedPageCount) {
917
- text += `
918
-
919
- [PDF has ${pageCount} pages, showing first ${limitedPageCount}. Use a shell command to process more.]`;
920
- }
921
- return { text, pageCount, title, author };
922
- }
923
-
924
- // src/server/tools/read.ts
925
847
  async function detectImageType(buffer, filePath) {
926
848
  const fileType = await fileTypeFromBuffer(buffer);
927
849
  const imageMimeTypes = ["image/png", "image/jpeg", "image/gif", "image/webp", "image/bmp", "image/svg+xml"];
@@ -1472,27 +1394,11 @@ Make sure whitespace and indentation match exactly.`
1472
1394
  );
1473
1395
 
1474
1396
  // src/server/tools/shell.ts
1475
- import { spawn as spawn2 } from "child_process";
1397
+ import { spawn } from "child_process";
1476
1398
  import { resolve as resolve4, isAbsolute as isAbsolute2 } from "path";
1477
1399
  import { access } from "fs/promises";
1478
1400
  import stripAnsi from "strip-ansi";
1479
1401
 
1480
- // src/server/utils/shell.ts
1481
- import { spawn } from "child_process";
1482
- function checkAborted(signal) {
1483
- return !!signal?.aborted;
1484
- }
1485
- function spawnShellProcess(command, cwd, _signal, detached = false) {
1486
- const shell = getPlatformShell();
1487
- return spawn(shell.command, [...shell.args, command], {
1488
- cwd,
1489
- env: { ...process.env, FORCE_COLOR: "0" },
1490
- stdio: ["ignore", "pipe", "pipe"],
1491
- windowsHide: true,
1492
- ...detached ? { detached: true } : {}
1493
- });
1494
- }
1495
-
1496
1402
  // src/server/tools/shell-tail.ts
1497
1403
  var TAIL_END_RE = /^(.*?)\s*\|\s*tail\s+(?:-n\s+)?-?(\d+)\s*$/;
1498
1404
  function stripTailPipe(command) {
@@ -1516,7 +1422,7 @@ async function checkRtkAvailability() {
1516
1422
  } catch {
1517
1423
  try {
1518
1424
  const out = await new Promise((resolve6, reject) => {
1519
- const proc = spawn2("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
1425
+ const proc = spawn("rtk", ["--version"], { stdio: ["ignore", "pipe", "pipe"] });
1520
1426
  let output = "";
1521
1427
  proc.stdout?.on("data", (d) => {
1522
1428
  output += d.toString();
@@ -1637,7 +1543,7 @@ async function tryRtkRewrite(command) {
1637
1543
  if (!await checkRtkAvailability()) return command;
1638
1544
  try {
1639
1545
  const result = await new Promise((resolve6, reject) => {
1640
- const proc = spawn2("rtk", ["rewrite", command], {
1546
+ const proc = spawn("rtk", ["rewrite", command], {
1641
1547
  stdio: ["ignore", "pipe", "pipe"],
1642
1548
  timeout: 2e3
1643
1549
  });
@@ -2968,7 +2874,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
2968
2874
  sessionManager.setCurrentContextSize(sessionId, result.usage.promptTokens);
2969
2875
  if (!compacting) {
2970
2876
  const contextState2 = sessionManager.getContextState(sessionId);
2971
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-V43WJ3WX.js");
2877
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-IPMMFFHM.js");
2972
2878
  if (shouldCompact(contextState2.currentTokens, contextState2.maxTokens, runtimeConfig.context.compactionThreshold)) {
2973
2879
  appendCompactionPrompt(sessionId, append);
2974
2880
  compacting = true;
@@ -3873,7 +3779,7 @@ var callSubAgentTool = {
3873
3779
  };
3874
3780
  }
3875
3781
  try {
3876
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-2EB7C6H2.js");
3782
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
3877
3783
  const toolRegistry = getToolRegistryForAgent2(agentDef);
3878
3784
  const turnMetrics = new TurnMetrics();
3879
3785
  const result = await executeSubAgent({
@@ -4157,18 +4063,17 @@ var webFetchTool = createTool(
4157
4063
  );
4158
4064
 
4159
4065
  // src/server/dev-server/manager.ts
4160
- import { spawn as spawn3 } from "child_process";
4066
+ import "child_process";
4161
4067
  import { readFile as readFile9, writeFile as writeFile6, mkdir as mkdir5 } from "fs/promises";
4162
4068
  import { resolve as resolve5, join as join8 } from "path";
4163
4069
  import net from "net";
4164
4070
  var MAX_LOG_LINES = 2e3;
4165
4071
  var MAX_LOG_BYTES = 1e5;
4166
- var DEFAULT_PROBE_PORT = 3e3;
4072
+ var DEFAULT_PROBE_PORT = 10469;
4167
4073
  var MAX_PORT_SCAN = 200;
4168
4074
  var PROBE_TIMEOUT_MS = 300;
4169
4075
  var getDevServerConfigPath = (workdir) => {
4170
- const mode = getRuntimeConfig().mode ?? "production";
4171
- return join8(resolve5(workdir), ".openfox", `${mode === "development" ? "dev-dev" : "dev"}.json`);
4076
+ return join8(resolve5(workdir), ".openfox", "dev.json");
4172
4077
  };
4173
4078
  var ERROR_PATTERNS = [
4174
4079
  /EADDRINUSE/,
@@ -4343,7 +4248,7 @@ var DevServerManager = class {
4343
4248
  instance.exited = false;
4344
4249
  if (!config.disableInspect && resolvedUrl && this._sessionManager) {
4345
4250
  try {
4346
- const { port, cleanup } = startInspectProxy(resolvedUrl, this._sessionManager, workdir);
4251
+ const { port, cleanup } = await startInspectProxy(resolvedUrl, this._sessionManager, assignedPort, workdir);
4347
4252
  instance.inspectProxyPort = port;
4348
4253
  instance.proxyCleanup = cleanup;
4349
4254
  logger.debug("Inspect proxy started", { workdir, port, target: resolvedUrl });
@@ -4352,13 +4257,9 @@ var DevServerManager = class {
4352
4257
  }
4353
4258
  }
4354
4259
  const resolved = this.resolveWorkdir(workdir);
4355
- const shell = getPlatformShell();
4356
- const proc = spawn3(shell.command, [...shell.args, resolvedCommand], {
4260
+ const proc = spawnShell(resolvedCommand, {
4357
4261
  cwd: resolved,
4358
- env: { ...process.env, FORCE_COLOR: "1" },
4359
- stdio: ["ignore", "pipe", "pipe"],
4360
- detached: true,
4361
- windowsHide: true
4262
+ detached: true
4362
4263
  });
4363
4264
  instance.process = proc;
4364
4265
  const appendLog = (stream, content) => {
@@ -4486,7 +4387,7 @@ var devServerTool = createTool(
4486
4387
  type: "function",
4487
4388
  function: {
4488
4389
  name: "dev_server",
4489
- description: "Control the project dev server. Start, stop, restart, check status, or fetch logs with optional pagination. The dev server is configured via .openfox/dev.json in the project root with 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).",
4390
+ 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).",
4490
4391
  parameters: {
4491
4392
  type: "object",
4492
4393
  properties: {
@@ -4554,7 +4455,7 @@ var devServerTool = createTool(
4554
4455
  }
4555
4456
  if (!status.config) {
4556
4457
  return helpers.error(
4557
- 'No .openfox/dev.json config found in the project root. Create one with:\n\n{\n "command": "npm run dev",\n "url": "http://localhost:3000",\n "hotReload": true,\n "disableInspect": false\n}'
4458
+ 'No .openfox/dev.json config found. Create one in the project root:\n\n{\n "command": "npm run dev",\n "url": "http://localhost:3000",\n "hotReload": true,\n "disableInspect": false\n}\n\n(Worktrees inherit the project root config automatically.)'
4558
4459
  );
4559
4460
  }
4560
4461
  return helpers.success(
@@ -4877,7 +4778,7 @@ var mcpConfigTool = createTool(
4877
4778
  mcpNotifyChanged?.(sessionId);
4878
4779
  }
4879
4780
  async function rebuildTools() {
4880
- const { setMcpTools: setMcpTools2 } = await import("./tools-2EB7C6H2.js");
4781
+ const { setMcpTools: setMcpTools2 } = await import("./tools-QAEN5KHA.js");
4881
4782
  const mcpTools = createMcpTools(mcpManagerForTools);
4882
4783
  setMcpTools2(mcpTools);
4883
4784
  }
@@ -5117,7 +5018,7 @@ var worktreeTool = createTool(
5117
5018
  type: "function",
5118
5019
  function: {
5119
5020
  name: "worktree",
5120
- description: "Manage git worktrees for the current session. Use this to work on isolated branches without affecting the main working tree.\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)",
5021
+ 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)',
5121
5022
  parameters: {
5122
5023
  type: "object",
5123
5024
  properties: {
@@ -5480,8 +5381,6 @@ export {
5480
5381
  PathAccessDeniedError,
5481
5382
  providePathConfirmation,
5482
5383
  cancelPathConfirmationsForSession,
5483
- checkAborted,
5484
- spawnShellProcess,
5485
5384
  pathExists,
5486
5385
  getDefaultIds,
5487
5386
  loadItemsFromDir,
@@ -5542,4 +5441,4 @@ export {
5542
5441
  getToolRegistryForAgent,
5543
5442
  createToolRegistry
5544
5443
  };
5545
- //# sourceMappingURL=chunk-3ACDQKK6.js.map
5444
+ //# sourceMappingURL=chunk-6LK47TNE.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createLLMClient
3
- } from "./chunk-B7WGOK7G.js";
3
+ } from "./chunk-E4POUD2M.js";
4
4
  import {
5
5
  getModelProfile
6
6
  } from "./chunk-V4IE7HJY.js";
@@ -796,4 +796,4 @@ export {
796
796
  parseDefaultModelSelection,
797
797
  createProviderManager
798
798
  };
799
- //# sourceMappingURL=chunk-5EO6SEGV.js.map
799
+ //# sourceMappingURL=chunk-7OW6X764.js.map
@@ -1,15 +1,13 @@
1
- import {
2
- appendCompactionPrompt
3
- } from "./chunk-5V5DM5E4.js";
4
1
  import {
5
2
  applyDynamicContext,
6
3
  computeSessionHash,
7
- injectWorkflowKickoffIfNeeded,
8
4
  runAgentTurn,
9
5
  runChatTurn
10
- } from "./chunk-UBIHP5Z7.js";
6
+ } from "./chunk-FCLCEIY4.js";
7
+ import {
8
+ appendCompactionPrompt
9
+ } from "./chunk-YQ3MEZJL.js";
11
10
  import {
12
- checkAborted,
13
11
  deleteItemFromDir,
14
12
  devServerManager,
15
13
  executeSubAgent,
@@ -17,17 +15,17 @@ import {
17
15
  getToolRegistryForAgent,
18
16
  jsonSerializer,
19
17
  loadAllAgentsDefault,
20
- saveItemToDir,
21
- spawnShellProcess
22
- } from "./chunk-3ACDQKK6.js";
18
+ saveItemToDir
19
+ } from "./chunk-6LK47TNE.js";
23
20
  import {
24
21
  TurnMetrics,
25
22
  createMessageStartEvent
26
- } from "./chunk-R4UTFE63.js";
23
+ } from "./chunk-QFSDJIUS.js";
27
24
  import {
28
- getPlatformShell,
29
- onProcessEvent
30
- } from "./chunk-BGTEIA2S.js";
25
+ checkAborted,
26
+ onProcessEvent,
27
+ spawnShellProcess
28
+ } from "./chunk-TFCHWMOJ.js";
31
29
  import {
32
30
  gitSpawnEnv
33
31
  } from "./chunk-WGMJCFCE.js";
@@ -55,6 +53,9 @@ import {
55
53
  serializeServerMessage,
56
54
  storedEventToServerMessage
57
55
  } from "./chunk-F4PMNP7S.js";
56
+ import {
57
+ getPlatformShell
58
+ } from "./chunk-X5DOUA3Z.js";
58
59
  import {
59
60
  getPendingQuestionsForSession,
60
61
  provideAnswer
@@ -91,6 +92,9 @@ var TerminalManager = class {
91
92
  generateId() {
92
93
  return `term_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
93
94
  }
95
+ // Uses getPlatformShell() directly instead of spawnShell() because
96
+ // node-pty.spawn() has a different API than child_process.spawn().
97
+ // PTY requires the command and argv separately, not a shell -c invocation.
94
98
  getShell() {
95
99
  return getPlatformShell().command;
96
100
  }
@@ -678,6 +682,21 @@ function emitWorkflowMessage(eventStore, sessionId, content, windowOptions, onMe
678
682
  }
679
683
  return msgId;
680
684
  }
685
+ function injectGenericKickoff(sessionId) {
686
+ const eventStore = getEventStore();
687
+ const windowOpts = getCurrentWindowMessageOptions(sessionId);
688
+ const msgId = crypto.randomUUID();
689
+ eventStore.append(
690
+ sessionId,
691
+ createMessageStartEvent(msgId, "user", "Proceed with the current step.", {
692
+ ...windowOpts ?? {},
693
+ isSystemGenerated: true,
694
+ messageKind: "auto-prompt",
695
+ metadata: { type: "workflow", name: "Workflow", color: "#f59e0b" }
696
+ })
697
+ );
698
+ eventStore.append(sessionId, { type: "message.done", data: { messageId: msgId } });
699
+ }
681
700
  function getCurrentWindowMessageOptions(sessionId) {
682
701
  const contextWindowId = getCurrentContextWindowId(sessionId);
683
702
  return contextWindowId ? { contextWindowId } : void 0;
@@ -848,7 +867,7 @@ async function executeWorkflow(workflow, options, subGroup) {
848
867
  agentStep.agentId ?? "planner",
849
868
  append,
850
869
  {
851
- ...!firstEntryForStep.has(step.id) && !agentStep.prompt && options.injectWorkflowKickoff === true ? { injectKickoff: () => injectWorkflowKickoffIfNeeded(sessionManager, sessionId, es) } : {},
870
+ ...!firstEntryForStep.has(step.id) && !agentStep.prompt ? { injectKickoff: () => injectGenericKickoff(sessionId) } : {},
852
871
  onToolExecuted: (toolCall, toolResult) => {
853
872
  if (toolCall.name === "step_done" && toolResult.success) {
854
873
  stepDoneCalled = true;
@@ -1591,10 +1610,30 @@ function createWebSocketServer(httpServer, _config, getLLMClient, getActiveProvi
1591
1610
  const messages = buildMessagesFromStoredEvents(events);
1592
1611
  const pendingConfirmations = foldPendingConfirmations(events);
1593
1612
  const pendingQuestions = getPendingQuestionsForSession(updatedSession.id);
1613
+ const effectiveWorkdir = updatedSession.worktree ?? updatedSession.workdir;
1614
+ let worktreeChanged = false;
1615
+ for (const [, client] of clients) {
1616
+ if (client.activeSessionId === updatedSession.id && client.activeWorkdir !== effectiveWorkdir) {
1617
+ worktreeChanged = true;
1618
+ const prevWorkdir = client.activeWorkdir;
1619
+ client.activeWorkdir = effectiveWorkdir;
1620
+ if (prevWorkdir) moduleStopGitPolling(prevWorkdir);
1621
+ }
1622
+ }
1623
+ if (effectiveWorkdir) moduleStartGitPolling(effectiveWorkdir);
1594
1624
  broadcastForSession(
1595
1625
  updatedSession.id,
1596
1626
  createSessionStateMessage(updatedSession, messages, pendingConfirmations, pendingQuestions)
1597
1627
  );
1628
+ if (worktreeChanged && effectiveWorkdir) {
1629
+ ;
1630
+ (async () => {
1631
+ const branch = await moduleGitBranch(effectiveWorkdir);
1632
+ if (!branch) return;
1633
+ const { files } = await moduleGitDiff(effectiveWorkdir);
1634
+ broadcastForSession(updatedSession.id, createGitStatusMessage(branch, files));
1635
+ })();
1636
+ }
1598
1637
  }
1599
1638
  });
1600
1639
  onProcessEvent((_processId, msg) => {
@@ -2023,7 +2062,6 @@ ${content}` : workflowInfo;
2023
2062
  sessionId,
2024
2063
  llmClient: llmForSession(sessionId),
2025
2064
  statsIdentity: statsForSession(sessionId),
2026
- injectWorkflowKickoff: !hasUserMessage,
2027
2065
  ...launchPayload?.workflowId ? { workflowId: launchPayload.workflowId } : {},
2028
2066
  ...launchPayload?.subGroup ? { subGroup: launchPayload.subGroup } : {},
2029
2067
  ...hasUserMessage ? {
@@ -2120,4 +2158,4 @@ export {
2120
2158
  signalMcpReady,
2121
2159
  createWebSocketServer
2122
2160
  };
2123
- //# sourceMappingURL=chunk-ZUM2ZJOX.js.map
2161
+ //# sourceMappingURL=chunk-COWJRBV4.js.map