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
@@ -13,7 +13,7 @@ import {
13
13
  getThinking,
14
14
  mapFinishReason,
15
15
  parseToolArguments
16
- } from "./chunk-BL5LOLVA.js";
16
+ } from "./chunk-JHGKXQBO.js";
17
17
  import {
18
18
  LLMError,
19
19
  OpenAIHttpClient
@@ -265,4 +265,4 @@ function createLLMClient(config, initialBackend = "unknown") {
265
265
  export {
266
266
  createLLMClient
267
267
  };
268
- //# sourceMappingURL=chunk-B7WGOK7G.js.map
268
+ //# sourceMappingURL=chunk-E4POUD2M.js.map
@@ -10,16 +10,15 @@ import {
10
10
  loadAllAgentsDefault,
11
11
  processEventsForConversation,
12
12
  runTopLevelAgentLoop
13
- } from "./chunk-3ACDQKK6.js";
13
+ } from "./chunk-6LK47TNE.js";
14
14
  import {
15
15
  TurnMetrics,
16
- WORKFLOW_KICKOFF_PROMPT,
17
16
  buildAgentReminder,
18
17
  buildAgentSmallReminder,
19
18
  buildTopLevelSystemPrompt,
20
19
  createChatDoneEvent,
21
20
  createMessageStartEvent
22
- } from "./chunk-R4UTFE63.js";
21
+ } from "./chunk-QFSDJIUS.js";
23
22
  import {
24
23
  getCurrentContextWindowId,
25
24
  getCurrentWindowMessageOptions,
@@ -65,7 +64,7 @@ function resolveAgentDef(sessionManager, sessionId) {
65
64
  }
66
65
  async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
67
66
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
68
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-2EB7C6H2.js");
67
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
69
68
  const tools = getToolRegistryForAgent2(agentDef).definitions;
70
69
  const toolFingerprint = getToolFingerprint(tools);
71
70
  const allAgents = await loadAllAgentsDefault();
@@ -78,7 +77,7 @@ async function buildCachedPrompt(sessionManager, sessionId, agentDef) {
78
77
  async function computeSessionHash(sessionManager, sessionId) {
79
78
  const { instructionContent, skills } = await loadSessionContext(sessionManager, sessionId);
80
79
  const agentDef = await resolveAgentDef(sessionManager, sessionId);
81
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-2EB7C6H2.js");
80
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-QAEN5KHA.js");
82
81
  const tools = getToolRegistryForAgent2(agentDef).definitions;
83
82
  const toolFingerprint = getToolFingerprint(tools);
84
83
  return computeDynamicContextHash(instructionContent, skills, toolFingerprint);
@@ -96,7 +95,7 @@ async function applyDynamicContext(sessionManager, sessionId) {
96
95
 
97
96
  // src/server/chat/orchestrator.ts
98
97
  async function buildRetryPatterns() {
99
- const { getSetting, SETTINGS_KEYS } = await import("./settings-ZZ6AKFPD.js");
98
+ const { getSetting, SETTINGS_KEYS } = await import("./settings-6ZPWOBWL.js");
100
99
  const raw = getSetting(SETTINGS_KEYS.RETRY_PATTERNS);
101
100
  if (!raw) {
102
101
  const oldXmlProtection = getSetting("llm.disableXmlProtection");
@@ -338,30 +337,6 @@ async function runAgentTurn(options, turnMetrics, agentId, append, callbacks) {
338
337
  turnMetrics
339
338
  );
340
339
  }
341
- function injectWorkflowKickoffIfNeeded(sessionManager, sessionId, eventStore) {
342
- const session = sessionManager.requireSession(sessionId);
343
- const currentWindowMessageOptions = getCurrentContextWindowId(sessionId) ? { contextWindowId: getCurrentContextWindowId(sessionId) } : void 0;
344
- const events = eventStore.getEvents(sessionId);
345
- const hasKickoff = events.some((e) => {
346
- if (e.type !== "message.start") return false;
347
- const data = e.data;
348
- return data.messageKind === "auto-prompt" && data.content?.includes("fulfil the");
349
- });
350
- if (!hasKickoff) {
351
- const kickoffMsgId = crypto.randomUUID();
352
- const kickoffContent = WORKFLOW_KICKOFF_PROMPT(session.criteria.length);
353
- eventStore.append(
354
- sessionId,
355
- createMessageStartEvent(kickoffMsgId, "user", kickoffContent, {
356
- ...currentWindowMessageOptions ?? {},
357
- isSystemGenerated: true,
358
- messageKind: "auto-prompt",
359
- metadata: { type: "workflow", name: "Workflow", color: "#f59e0b" }
360
- })
361
- );
362
- eventStore.append(sessionId, { type: "message.done", data: { messageId: kickoffMsgId } });
363
- }
364
- }
365
340
  function buildSnapshot(sessionManager, sessionId, _lastStats) {
366
341
  const eventStore = getEventStore();
367
342
  const session = sessionManager.requireSession(sessionId);
@@ -380,7 +355,6 @@ export {
380
355
  computeSessionHash,
381
356
  applyDynamicContext,
382
357
  runChatTurn,
383
- runAgentTurn,
384
- injectWorkflowKickoffIfNeeded
358
+ runAgentTurn
385
359
  };
386
- //# sourceMappingURL=chunk-UBIHP5Z7.js.map
360
+ //# sourceMappingURL=chunk-FCLCEIY4.js.map
@@ -1,3 +1,101 @@
1
+ // src/server/tools/types.ts
2
+ var OUTPUT_LIMITS = {
3
+ read_file: {
4
+ maxLines: 2e3,
5
+ maxBytes: 1e5,
6
+ maxImageBytes: 2097152,
7
+ // 2MB for images
8
+ maxPdfPages: 50,
9
+ maxFileBytes: 20971520
10
+ // 20MB general file safety limit
11
+ },
12
+ run_command: {
13
+ maxLines: 2e3,
14
+ maxBytes: 5e4
15
+ },
16
+ glob: {
17
+ maxResults: 500
18
+ },
19
+ grep: {
20
+ maxMatches: 200
21
+ },
22
+ web_fetch: {
23
+ maxBytes: 1e5
24
+ }
25
+ };
26
+
27
+ // src/server/tools/pdf-utils.ts
28
+ import { getDocument } from "pdfjs-dist/legacy/build/pdf.mjs";
29
+ var PDF_HEADER = Buffer.from("%PDF");
30
+ function isPdfBuffer(buffer) {
31
+ return buffer.length > 4 && buffer.subarray(0, 4).equals(PDF_HEADER);
32
+ }
33
+ function isPasswordError(err) {
34
+ const message = err instanceof Error ? err.message : String(err);
35
+ return message.toLowerCase().includes("password") || message.toLowerCase().includes("encrypt");
36
+ }
37
+ function formatPdfErrorMessage(err) {
38
+ if (isPasswordError(err)) {
39
+ return "This PDF is password-protected. Unlock it with an external tool first.";
40
+ }
41
+ return `Failed to read PDF: ${err instanceof Error ? err.message : String(err)}`;
42
+ }
43
+ function processPdfContent(text, maxBytes) {
44
+ let output = text;
45
+ let truncated = false;
46
+ if (output.length > maxBytes) {
47
+ output = output.slice(0, maxBytes) + "\n\n[Output truncated due to size limit]";
48
+ truncated = true;
49
+ }
50
+ const isScanned = output.replace(/\[Page \d+\/\d+\]\n/g, "").trim().length === 0;
51
+ return { output, truncated, isScanned };
52
+ }
53
+ async function extractPdfText(buffer) {
54
+ const doc = await getDocument({ data: Uint8Array.from(buffer) }).promise;
55
+ const pageCount = doc.numPages;
56
+ const rawMeta = await doc.getMetadata();
57
+ const info = rawMeta.info;
58
+ const title = info?.["Title"] || null;
59
+ const author = info?.["Author"] || null;
60
+ const pages = [];
61
+ const limitedPageCount = Math.min(pageCount, OUTPUT_LIMITS.read_file.maxPdfPages);
62
+ try {
63
+ for (let i = 1; i <= limitedPageCount; i++) {
64
+ const page = await doc.getPage(i);
65
+ try {
66
+ const content = await page.getTextContent();
67
+ const pageText = content.items.map((item) => "str" in item ? item.str : "").join(" ");
68
+ pages.push(`[Page ${i}/${pageCount}]
69
+ ${pageText}`);
70
+ } finally {
71
+ page.cleanup();
72
+ }
73
+ }
74
+ } finally {
75
+ doc.cleanup();
76
+ }
77
+ let text = pages.join("\n\n");
78
+ if (pageCount > limitedPageCount) {
79
+ text += `
80
+
81
+ [PDF has ${pageCount} pages, showing first ${limitedPageCount}. Use a shell command to process more.]`;
82
+ }
83
+ return { text, pageCount, title, author };
84
+ }
85
+
86
+ // src/shared/constants.ts
87
+ var TEXT_MIME_PREFIXES = ["text/"];
88
+ var TEXT_MIME_EXACT = [
89
+ "application/json",
90
+ "application/xml",
91
+ "application/yaml",
92
+ "application/x-yaml",
93
+ "application/javascript",
94
+ "application/xhtml+xml",
95
+ "application/x-sh",
96
+ "image/svg+xml"
97
+ ];
98
+
1
99
  // src/server/llm/client-pure.ts
2
100
  function parseToolArguments(raw, _meta) {
3
101
  const input = raw?.trim() ? raw : "{}";
@@ -22,13 +120,14 @@ function buildModelParams(params) {
22
120
  ...params.maxTokens !== void 0 && { maxTokens: params.maxTokens }
23
121
  };
24
122
  }
25
- function buildAttachmentContent(msgContent, attachments, modelSupportsVision) {
123
+ async function buildAttachmentContent(msgContent, attachments, modelSupportsVision) {
26
124
  const content = [];
27
125
  if (msgContent?.trim()) {
28
126
  content.push({ type: "text", text: msgContent });
29
127
  }
30
128
  for (const attachment of attachments) {
31
- content.push(convertAttachmentSync(attachment, modelSupportsVision));
129
+ const converted = await convertAttachment(attachment, modelSupportsVision);
130
+ content.push(converted);
32
131
  }
33
132
  return content;
34
133
  }
@@ -62,7 +161,34 @@ function buildAssistantMessage(msg, thinkingField) {
62
161
  }
63
162
  return result;
64
163
  }
65
- function convertAttachmentSync(attachment, modelSupportsVision) {
164
+ async function convertAttachment(attachment, modelSupportsVision) {
165
+ const mimeType = attachment.mimeType;
166
+ if (TEXT_MIME_EXACT.includes(mimeType) || TEXT_MIME_PREFIXES.some((p) => mimeType.startsWith(p))) {
167
+ return {
168
+ type: "text",
169
+ text: `[File: ${attachment.filename || "file"}]
170
+ ${attachment.data}`
171
+ };
172
+ }
173
+ if (mimeType === "application/pdf") {
174
+ try {
175
+ const base64Match = attachment.data.match(/^data:.*?;base64,(.+)$/);
176
+ if (base64Match?.[1]) {
177
+ const buffer = Buffer.from(base64Match[1], "base64");
178
+ const result = await extractPdfText(buffer);
179
+ return {
180
+ type: "text",
181
+ text: `[PDF: ${attachment.filename || "document.pdf"}]
182
+ ${result.text}`
183
+ };
184
+ }
185
+ } catch {
186
+ }
187
+ return {
188
+ type: "text",
189
+ text: `[PDF: ${attachment.filename || "document.pdf"}] (could not extract text)`
190
+ };
191
+ }
66
192
  if (modelSupportsVision) {
67
193
  return {
68
194
  type: "image_url",
@@ -80,41 +206,43 @@ function convertAttachmentSync(attachment, modelSupportsVision) {
80
206
  text: `[Image: ${attachment.filename || "image"}] (vision not supported, cannot describe)`
81
207
  };
82
208
  }
83
- function convertMessages(messages, modelSupportsVision, thinkingField) {
209
+ async function convertMessages(messages, modelSupportsVision, thinkingField) {
84
210
  const filtered = messages.filter((msg) => {
85
211
  return !(msg.role === "assistant" && !msg.content?.trim() && (!msg.toolCalls || msg.toolCalls.length === 0));
86
212
  });
87
- return filtered.map((msg) => {
213
+ const result = [];
214
+ for (const msg of filtered) {
88
215
  if (msg.role === "tool") {
89
216
  if (msg.attachments && msg.attachments.length > 0) {
90
- const content = buildAttachmentContent(msg.content, msg.attachments, modelSupportsVision);
91
- return {
217
+ const content = await buildAttachmentContent(msg.content, msg.attachments, modelSupportsVision);
218
+ result.push({
92
219
  role: "tool",
93
220
  content,
94
221
  tool_call_id: msg.toolCallId
95
- };
222
+ });
223
+ } else {
224
+ result.push({
225
+ role: "tool",
226
+ content: msg.content,
227
+ tool_call_id: msg.toolCallId
228
+ });
96
229
  }
97
- return {
98
- role: "tool",
99
- content: msg.content,
100
- tool_call_id: msg.toolCallId
101
- };
102
- }
103
- if (msg.role === "assistant") {
104
- return buildAssistantMessage(msg, thinkingField);
105
- }
106
- if (msg.role === "user" && msg.attachments && msg.attachments.length > 0) {
107
- const content = buildAttachmentContent(msg.content, msg.attachments, modelSupportsVision);
108
- return {
230
+ } else if (msg.role === "assistant") {
231
+ result.push(buildAssistantMessage(msg, thinkingField));
232
+ } else if (msg.role === "user" && msg.attachments && msg.attachments.length > 0) {
233
+ const content = await buildAttachmentContent(msg.content, msg.attachments, modelSupportsVision);
234
+ result.push({
109
235
  role: "user",
110
236
  content
111
- };
237
+ });
238
+ } else {
239
+ result.push({
240
+ role: msg.role,
241
+ content: msg.content
242
+ });
112
243
  }
113
- return {
114
- role: msg.role,
115
- content: msg.content
116
- };
117
- });
244
+ }
245
+ return result;
118
246
  }
119
247
  function convertTools(tools) {
120
248
  return tools.map((tool) => ({
@@ -129,7 +257,7 @@ function convertTools(tools) {
129
257
  async function buildChatCompletionCreateParams(model, request, profile, capabilities, reasoningEffort, isStreaming, thinkingField) {
130
258
  const userVisionOverride = request.modelSettings?.supportsVision;
131
259
  const modelSupportsVision = userVisionOverride ?? profile.supportsVision ?? false;
132
- const convertedMessages = convertMessages(request.messages, modelSupportsVision, thinkingField);
260
+ const convertedMessages = await convertMessages(request.messages, modelSupportsVision, thinkingField);
133
261
  const temperature = request.modelSettings?.temperature ?? request.temperature ?? profile.temperature;
134
262
  const maxTokens = request.modelSettings?.maxTokens ?? request.maxTokens ?? profile.defaultMaxTokens;
135
263
  const topP = request.modelSettings?.topP ?? profile.topP;
@@ -210,6 +338,11 @@ function mapFinishReason(reason) {
210
338
  }
211
339
 
212
340
  export {
341
+ OUTPUT_LIMITS,
342
+ isPdfBuffer,
343
+ formatPdfErrorMessage,
344
+ processPdfContent,
345
+ extractPdfText,
213
346
  parseToolArguments,
214
347
  buildModelParams,
215
348
  getThinking,
@@ -219,4 +352,4 @@ export {
219
352
  buildStreamingCreateParams,
220
353
  mapFinishReason
221
354
  };
222
- //# sourceMappingURL=chunk-BL5LOLVA.js.map
355
+ //# sourceMappingURL=chunk-JHGKXQBO.js.map
@@ -1,3 +1,6 @@
1
+ import {
2
+ getPlatformShell
3
+ } from "./chunk-X5DOUA3Z.js";
1
4
  import {
2
5
  streamWithSegments
3
6
  } from "./chunk-J2GP3J3X.js";
@@ -9,7 +12,7 @@ import {
9
12
  } from "./chunk-YHEQTVFV.js";
10
13
  import {
11
14
  buildModelParams
12
- } from "./chunk-BL5LOLVA.js";
15
+ } from "./chunk-JHGKXQBO.js";
13
16
  import {
14
17
  logger
15
18
  } from "./chunk-K44MW7JJ.js";
@@ -33,6 +36,7 @@ function computeEffectiveTools(allowedTools, type) {
33
36
  }
34
37
 
35
38
  // src/server/chat/prompts.ts
39
+ import { basename } from "path";
36
40
  function buildBasePrompt(workdir, customInstructions, skills, modelName) {
37
41
  const instructionsSection = customInstructions ? `
38
42
 
@@ -47,7 +51,8 @@ Today's date is ${(/* @__PURE__ */ new Date()).toISOString().split("T")[0].repla
47
51
 
48
52
  ## ENVIRONMENT
49
53
  Working directory: ${workdir}
50
- Platform: ${process.platform} (${process.arch})${modelLine}
54
+ Platform: ${process.platform} (${process.arch})
55
+ Shell (run_command): ${basename(getPlatformShell().command)}${modelLine}
51
56
 
52
57
  ## CORE BEHAVIOR
53
58
  Help user complete tasks safely and efficiently.
@@ -226,7 +231,6 @@ function buildAgentSmallReminder(name) {
226
231
  Reminder: you are in '${name}' mode.
227
232
  </system-reminder>`;
228
233
  }
229
- var WORKFLOW_KICKOFF_PROMPT = (criteriaCount) => `Implement the task and make sure you fulfil the ${criteriaCount} criteria.`;
230
234
  var COMPACTION_PROMPT = `You are a helpful AI assistant tasked with summarizing conversations for continuation.
231
235
 
232
236
  Summarize the conversation history concisely, preserving:
@@ -664,7 +668,6 @@ export {
664
668
  buildTopLevelSystemPrompt,
665
669
  buildAgentReminder,
666
670
  buildAgentSmallReminder,
667
- WORKFLOW_KICKOFF_PROMPT,
668
671
  COMPACTION_PROMPT,
669
672
  streamLLMPure,
670
673
  TurnMetrics,
@@ -675,4 +678,4 @@ export {
675
678
  createChatDoneEvent,
676
679
  consumeStreamGenerator
677
680
  };
678
- //# sourceMappingURL=chunk-R4UTFE63.js.map
681
+ //# sourceMappingURL=chunk-QFSDJIUS.js.map
@@ -9,30 +9,27 @@ import {
9
9
  startProcess,
10
10
  updateStatus
11
11
  } from "./chunk-PK2RMVMB.js";
12
+ import {
13
+ getPlatformShell
14
+ } from "./chunk-X5DOUA3Z.js";
12
15
 
13
- // src/server/tools/background-process/manager.ts
16
+ // src/server/utils/shell.ts
14
17
  import { spawn } from "child_process";
15
-
16
- // src/server/utils/platform.ts
17
- import os from "os";
18
- function getPlatformShell() {
19
- if (process.platform === "win32") {
20
- return { command: "cmd.exe", args: ["/c"] };
21
- }
22
- const userShell = os.userInfo().shell;
23
- if (userShell) {
24
- return { command: userShell, args: ["-c"] };
25
- }
26
- return { command: "sh", args: ["-c"] };
18
+ function checkAborted(signal) {
19
+ return !!signal?.aborted;
27
20
  }
28
- function getPathSeparator() {
29
- return process.platform === "win32" ? ";" : ":";
30
- }
31
- function isWindowsPath(path) {
32
- return /^[A-Za-z]:[/\\]/.test(path);
21
+ function spawnShell(command, options) {
22
+ const shell = getPlatformShell();
23
+ return spawn(shell.command, [...shell.args, command], {
24
+ cwd: options.cwd,
25
+ env: { ...process.env },
26
+ stdio: options.stdio ?? ["ignore", "pipe", "pipe"],
27
+ windowsHide: true,
28
+ ...options.detached ? { detached: true } : {}
29
+ });
33
30
  }
34
- function isAbsolutePath(path) {
35
- return path.startsWith("/") || isWindowsPath(path);
31
+ function spawnShellProcess(command, cwd, _signal, detached = false) {
32
+ return spawnShell(command, { cwd, detached });
36
33
  }
37
34
 
38
35
  // src/server/tools/background-process/manager.ts
@@ -64,13 +61,9 @@ function createProcess2(sessionId, name, command, cwd, timeout) {
64
61
  function startProcessCommand(processId, sessionId, command, cwd) {
65
62
  const proc = startProcess(processId, sessionId, 0);
66
63
  if (!proc) return null;
67
- const shell = getPlatformShell();
68
- const child = spawn(shell.command, [...shell.args, command], {
64
+ const child = spawnShell(command, {
69
65
  cwd,
70
- env: { ...process.env, FORCE_COLOR: "1" },
71
- stdio: ["ignore", "pipe", "pipe"],
72
- detached: true,
73
- windowsHide: true
66
+ detached: true
74
67
  });
75
68
  proc.pid = child.pid ?? null;
76
69
  updateStatus(processId, sessionId, "running");
@@ -143,9 +136,9 @@ function getProcessLogs(processId, since = 0, maxLines) {
143
136
  }
144
137
 
145
138
  export {
146
- getPlatformShell,
147
- getPathSeparator,
148
- isAbsolutePath,
139
+ checkAborted,
140
+ spawnShell,
141
+ spawnShellProcess,
149
142
  onProcessEvent,
150
143
  createProcess2 as createProcess,
151
144
  startProcessCommand,
@@ -154,4 +147,4 @@ export {
154
147
  getSessionProcesses2 as getSessionProcesses,
155
148
  getProcessLogs
156
149
  };
157
- //# sourceMappingURL=chunk-BGTEIA2S.js.map
150
+ //# sourceMappingURL=chunk-TFCHWMOJ.js.map
@@ -7,8 +7,9 @@ import {
7
7
 
8
8
  // src/server/git/worktree.ts
9
9
  import { spawn } from "child_process";
10
- import { mkdir, readFile, appendFile, stat } from "fs/promises";
10
+ import { mkdir, readFile, appendFile, stat, symlink } from "fs/promises";
11
11
  import { resolve } from "path";
12
+ import copy from "@danieldietrich/copy";
12
13
  function captureStdout(cwd, args) {
13
14
  return new Promise((resolvePromise) => {
14
15
  const proc = spawn("git", args, { cwd, env: gitSpawnEnv(), stdio: ["ignore", "pipe", "ignore"] });
@@ -122,6 +123,57 @@ async function ensureWorktree(projectDir, name, startBranch) {
122
123
  }
123
124
  return { path: wtPath, name };
124
125
  }
126
+ async function getIgnoredDirectories(projectDir) {
127
+ const out = await captureStdout(projectDir, [
128
+ "ls-files",
129
+ "--others",
130
+ "--ignored",
131
+ "--exclude-standard",
132
+ "--directory"
133
+ ]);
134
+ if (!out) return [];
135
+ return out.trim().split("\n").filter(Boolean);
136
+ }
137
+ function resolveStrategy(relPath, config) {
138
+ const normalized = relPath.replace(/\/$/, "");
139
+ const basename = normalized.split("/").pop() ?? normalized;
140
+ return config.overrides?.[normalized] ?? config.overrides?.[basename] ?? config.ignoredAssets;
141
+ }
142
+ async function syncIgnoredAssets(projectDir, worktreePath, config) {
143
+ const ignoredPaths = await getIgnoredDirectories(projectDir);
144
+ if (ignoredPaths.length === 0) return;
145
+ const results = [];
146
+ for (const relPath of ignoredPaths) {
147
+ const sourcePath = resolve(projectDir, relPath);
148
+ const targetPath = resolve(worktreePath, relPath);
149
+ try {
150
+ await stat(sourcePath);
151
+ } catch {
152
+ continue;
153
+ }
154
+ const strategy = resolveStrategy(relPath, config);
155
+ if (strategy === "skip") continue;
156
+ try {
157
+ if (strategy === "symlink") {
158
+ try {
159
+ await stat(targetPath);
160
+ continue;
161
+ } catch {
162
+ }
163
+ await symlink(sourcePath, targetPath);
164
+ results.push(`symlink ${relPath}`);
165
+ } else if (strategy === "copy") {
166
+ await copy(sourcePath, targetPath);
167
+ results.push(`copy ${relPath}`);
168
+ }
169
+ } catch (err) {
170
+ logger.warn("Failed to sync ignored asset to worktree", { relPath, strategy, error: String(err) });
171
+ }
172
+ }
173
+ if (results.length > 0) {
174
+ logger.info("Synced ignored assets to worktree", { count: results.length, actions: results });
175
+ }
176
+ }
125
177
 
126
178
  export {
127
179
  getGitBranch,
@@ -132,6 +184,8 @@ export {
132
184
  listBranches,
133
185
  checkoutBranch,
134
186
  createBranch,
135
- ensureWorktree
187
+ ensureWorktree,
188
+ getIgnoredDirectories,
189
+ syncIgnoredAssets
136
190
  };
137
- //# sourceMappingURL=chunk-OBK3I4KD.js.map
191
+ //# sourceMappingURL=chunk-VLV3XWZS.js.map
@@ -0,0 +1,66 @@
1
+ import {
2
+ SETTINGS_KEYS,
3
+ getSetting
4
+ } from "./chunk-XUPKS5FL.js";
5
+
6
+ // src/server/utils/platform.ts
7
+ import os from "os";
8
+ import { existsSync } from "fs";
9
+ import { join } from "path";
10
+ function findGitBash() {
11
+ const candidates = [
12
+ process.env["ProgramFiles"] && join(process.env["ProgramFiles"], "Git", "bin", "bash.exe"),
13
+ process.env["ProgramFiles(x86)"] && join(process.env["ProgramFiles(x86)"], "Git", "bin", "bash.exe"),
14
+ process.env["LocalAppData"] && join(process.env["LocalAppData"], "Programs", "Git", "bin", "bash.exe")
15
+ ].filter((p) => !!p);
16
+ return candidates.find((p) => existsSync(p)) ?? null;
17
+ }
18
+ var CMD_SHELL = { command: "cmd.exe", args: ["/c"] };
19
+ var WINDOWS_SHELLS = {
20
+ cmd: () => CMD_SHELL,
21
+ powershell: () => ({ command: "powershell.exe", args: ["-NoProfile", "-Command"] }),
22
+ gitbash: () => {
23
+ const bash = findGitBash();
24
+ return bash ? { command: bash, args: ["-c"] } : null;
25
+ }
26
+ };
27
+ function listAvailableShells() {
28
+ if (process.platform !== "win32") {
29
+ return [];
30
+ }
31
+ return [
32
+ { id: "cmd", label: "cmd.exe", available: true },
33
+ { id: "powershell", label: "PowerShell", available: true },
34
+ { id: "gitbash", label: "Git Bash", available: findGitBash() !== null }
35
+ ];
36
+ }
37
+ function getPlatformShell() {
38
+ if (process.platform === "win32") {
39
+ const choice = getSetting(SETTINGS_KEYS.TOOLS_SHELL) ?? "cmd";
40
+ return WINDOWS_SHELLS[choice]?.() ?? CMD_SHELL;
41
+ }
42
+ const userShell = os.userInfo().shell;
43
+ if (userShell) {
44
+ return { command: userShell, args: ["-c"] };
45
+ }
46
+ return { command: "sh", args: ["-c"] };
47
+ }
48
+ function getPathSeparator() {
49
+ return process.platform === "win32" ? ";" : ":";
50
+ }
51
+ function isWindowsPath(path) {
52
+ return /^[A-Za-z]:[/\\]/.test(path);
53
+ }
54
+ function isAbsolutePath(path) {
55
+ return path.startsWith("/") || isWindowsPath(path);
56
+ }
57
+
58
+ export {
59
+ findGitBash,
60
+ listAvailableShells,
61
+ getPlatformShell,
62
+ getPathSeparator,
63
+ isWindowsPath,
64
+ isAbsolutePath
65
+ };
66
+ //# sourceMappingURL=chunk-X5DOUA3Z.js.map
@@ -25,7 +25,8 @@ var SETTINGS_KEYS = {
25
25
  SEARCH_TAVILY_API_KEY: "search.tavilyApiKey",
26
26
  SEARCH_SEARXNG_URL: "search.searxngUrl",
27
27
  SEARCH_SEARXNG_API_KEY: "search.searxngApiKey",
28
- TOOLS_USE_RTK: "tools.useRtk"
28
+ TOOLS_USE_RTK: "tools.useRtk",
29
+ TOOLS_SHELL: "tools.shell"
29
30
  };
30
31
  var SETTINGS_DEFAULTS = {
31
32
  [SETTINGS_KEYS.DISPLAY_SHOW_THINKING]: "true",
@@ -51,7 +52,8 @@ var SETTINGS_DEFAULTS = {
51
52
  { type: "chord", key: "4", modifiers: ["ctrl"] }
52
53
  ]
53
54
  }),
54
- [SETTINGS_KEYS.TOOLS_USE_RTK]: "false"
55
+ [SETTINGS_KEYS.TOOLS_USE_RTK]: "false",
56
+ [SETTINGS_KEYS.TOOLS_SHELL]: "cmd"
55
57
  };
56
58
  function getSetting(key) {
57
59
  try {
@@ -99,4 +101,4 @@ export {
99
101
  deleteSetting,
100
102
  getAllSettings
101
103
  };
102
- //# sourceMappingURL=chunk-PVRSNXD4.js.map
104
+ //# sourceMappingURL=chunk-XUPKS5FL.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  COMPACTION_PROMPT,
3
3
  createMessageStartEvent
4
- } from "./chunk-R4UTFE63.js";
4
+ } from "./chunk-QFSDJIUS.js";
5
5
  import {
6
6
  getCurrentWindowMessageOptions
7
7
  } from "./chunk-TFDK6VSG.js";
@@ -31,4 +31,4 @@ export {
31
31
  shouldCompact,
32
32
  getCompactionTarget
33
33
  };
34
- //# sourceMappingURL=chunk-5V5DM5E4.js.map
34
+ //# sourceMappingURL=chunk-YQ3MEZJL.js.map
package/dist/cli/dev.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "../chunk-R7EED6ZU.js";
4
+ } from "../chunk-32KLLK4N.js";
5
5
  import "../chunk-XY3LESVZ.js";
6
6
  import {
7
7
  logger