openfox 2.0.147 → 2.0.149

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 (45) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/dist/CHANGELOG.md +21 -0
  3. package/dist/{auto-continue-FGQ63HHR.js → auto-continue-AZRNVDV5.js} +7 -7
  4. package/dist/{chat-handler-VCED3JYF.js → chat-handler-NJGT2L65.js} +10 -10
  5. package/dist/{chunk-AJMBQMJH.js → chunk-2GIY77DT.js} +191 -14
  6. package/dist/chunk-2IK5KDVV.js +7 -0
  7. package/dist/{chunk-UOBJP4GN.js → chunk-7HNACZXD.js} +10 -10
  8. package/dist/{chunk-2DBWCXAQ.js → chunk-A6YTJZAO.js} +2 -2
  9. package/dist/{chunk-FAN4P45J.js → chunk-BAN2F7TG.js} +18 -2
  10. package/dist/{chunk-P7KUU5VB.js → chunk-E4RD42PU.js} +2 -2
  11. package/dist/{chunk-L2RESXUF.js → chunk-EBMHUSMZ.js} +2 -2
  12. package/dist/{chunk-QHFJRUJV.js → chunk-I46WU3NP.js} +4 -4
  13. package/dist/{chunk-QMU3ZY2C.js → chunk-JRWG4KWV.js} +3 -1
  14. package/dist/{chunk-ESHP42KO.js → chunk-MA4NG3F7.js} +2 -2
  15. package/dist/{chunk-AWBV2VXO.js → chunk-MICSOCCE.js} +3 -3
  16. package/dist/{chunk-ABXERHIY.js → chunk-N342GU3N.js} +19 -8
  17. package/dist/{chunk-YQJ6HEFS.js → chunk-RXKZVMKL.js} +75 -54
  18. package/dist/{chunk-ORIPUMOQ.js → chunk-XWKX5IAU.js} +2 -2
  19. package/dist/{chunk-2V6QL6IW.js → chunk-ZWI4T4WF.js} +5 -5
  20. package/dist/cli/dev.js +1 -1
  21. package/dist/cli/index.js +1 -1
  22. package/dist/{client-L4PANCG4.js → client-WQFM3PDL.js} +3 -3
  23. package/dist/{client-pure-YZ4IGCU4.js → client-pure-EM26AITO.js} +2 -2
  24. package/dist/{compactor-YY7PO2XE.js → compactor-JDR6AGEW.js} +4 -4
  25. package/dist/{dynamic-context-YLRA34WJ.js → dynamic-context-SS3AVCVM.js} +2 -2
  26. package/dist/{export-import-R4E5BUQS.js → export-import-QT7CV7ZU.js} +2 -2
  27. package/dist/{launch-QP2BI3PB.js → launch-2DPUISQR.js} +10 -10
  28. package/dist/{orchestrator-7T62RLDL.js → orchestrator-2BBZC4VH.js} +11 -9
  29. package/dist/package.json +1 -1
  30. package/dist/{processor-LDLRELLS.js → processor-N3EIGT4S.js} +9 -9
  31. package/dist/{provider-QE7XSGYA.js → provider-52J4WIIQ.js} +4 -4
  32. package/dist/{provider-manager-WW2EF76W.js → provider-manager-6EACWIDV.js} +4 -4
  33. package/dist/{serve-A4AJAH2N.js → serve-O44ZIZ7N.js} +15 -15
  34. package/dist/server/index.d.ts +3 -0
  35. package/dist/server/index.js +14 -14
  36. package/dist/{server-A3EGJ6MD.js → server-Q5PTCS4G.js} +13 -13
  37. package/dist/{service-KMOSB6SU.js → service-ALXVEWCE.js} +5 -5
  38. package/dist/{tasks-X5DM553T.js → tasks-LD3VJYFH.js} +5 -5
  39. package/dist/{tools-MRC27SZG.js → tools-SK4EU3D5.js} +7 -7
  40. package/dist/{update-MLDVVG3D.js → update-32UNL44B.js} +2 -2
  41. package/dist/web/assets/{index-CkVmYTBd.js → index-Cith3wH6.js} +55 -55
  42. package/dist/web/index.html +1 -1
  43. package/dist/web/sw.js +1 -1
  44. package/package.json +1 -1
  45. package/dist/chunk-65KDNVRA.js +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.149 - 2026-09-15
4
+
5
+ ### Features
6
+
7
+ - **Task attachments now readable by agents** — a new project_tasks get_attachment action returns image, text, and PDF attachments, with attachment metadata listed on task output.
8
+
9
+ ### Enhancements
10
+
11
+ - **Retry pattern editor validates inline** — invalid regexes are flagged live as you type, and empty or invalid patterns are no longer saved.
12
+
13
+ ### Bug Fixes
14
+
15
+ - **Empty retry patterns no longer loop every turn** — blank or invalid patterns are skipped server-side, so a stray empty pattern can't trigger infinite auto-retries.
16
+ - **MCP tools honor their per-tool timeout** — calls no longer cap at the 60s default, and timed-out requests are aborted in flight.
17
+
18
+ ## 2.0.148 - 2026-09-14
19
+
20
+ ### Bug Fixes
21
+
22
+ - **Sub-agent responses no longer truncate in long sessions** — sub-agent output is now budgeted against its own context instead of the parent session's, so plans and reviews run to completion even when the orchestrator session is huge.
23
+
3
24
  ## 2.0.147 - 2026-09-14
4
25
 
5
26
  ### Features
package/dist/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.149 - 2026-09-15
4
+
5
+ ### Features
6
+
7
+ - **Task attachments now readable by agents** — a new project_tasks get_attachment action returns image, text, and PDF attachments, with attachment metadata listed on task output.
8
+
9
+ ### Enhancements
10
+
11
+ - **Retry pattern editor validates inline** — invalid regexes are flagged live as you type, and empty or invalid patterns are no longer saved.
12
+
13
+ ### Bug Fixes
14
+
15
+ - **Empty retry patterns no longer loop every turn** — blank or invalid patterns are skipped server-side, so a stray empty pattern can't trigger infinite auto-retries.
16
+ - **MCP tools honor their per-tool timeout** — calls no longer cap at the 60s default, and timed-out requests are aborted in flight.
17
+
18
+ ## 2.0.148 - 2026-09-14
19
+
20
+ ### Bug Fixes
21
+
22
+ - **Sub-agent responses no longer truncate in long sessions** — sub-agent output is now budgeted against its own context instead of the parent session's, so plans and reviews run to completion even when the orchestrator session is huge.
23
+
3
24
  ## 2.0.147 - 2026-09-14
4
25
 
5
26
  ### Features
@@ -1,16 +1,16 @@
1
- import "./chunk-AJMBQMJH.js";
1
+ import "./chunk-2GIY77DT.js";
2
2
  import "./chunk-OAN4BXEW.js";
3
3
  import "./chunk-DJH6LMBT.js";
4
4
  import "./chunk-PHR64A6U.js";
5
5
  import "./chunk-J7GAZGZT.js";
6
- import "./chunk-2DBWCXAQ.js";
6
+ import "./chunk-A6YTJZAO.js";
7
7
  import "./chunk-UO4X2ENE.js";
8
8
  import "./chunk-KJEOKIH2.js";
9
9
  import "./chunk-X7ZAP6YW.js";
10
10
  import "./chunk-CVQYCAMY.js";
11
11
  import {
12
12
  createMessageDoneEvent
13
- } from "./chunk-FAN4P45J.js";
13
+ } from "./chunk-BAN2F7TG.js";
14
14
  import {
15
15
  CONTINUE_AFTER_STREAM_ERROR_PROMPT,
16
16
  CONTINUE_PROMPT
@@ -29,15 +29,15 @@ import "./chunk-V5AC2TV7.js";
29
29
  import "./chunk-2PGRX4RH.js";
30
30
  import "./chunk-LCLH6ZUL.js";
31
31
  import "./chunk-YEFZ5HBD.js";
32
- import "./chunk-AWBV2VXO.js";
32
+ import "./chunk-MICSOCCE.js";
33
33
  import "./chunk-OA2FURC3.js";
34
34
  import "./chunk-J2GP3J3X.js";
35
35
  import "./chunk-JYYBSODN.js";
36
- import "./chunk-L2RESXUF.js";
36
+ import "./chunk-EBMHUSMZ.js";
37
37
  import "./chunk-TS5WKTFB.js";
38
38
  import "./chunk-FTPQI5TA.js";
39
39
  import "./chunk-HNCM3D7Y.js";
40
- import "./chunk-QMU3ZY2C.js";
40
+ import "./chunk-JRWG4KWV.js";
41
41
  import "./chunk-FSSUPWYO.js";
42
42
  import "./chunk-XG3AUMPI.js";
43
43
  import "./chunk-GZ4EK4LZ.js";
@@ -108,4 +108,4 @@ export {
108
108
  planBootContinuation,
109
109
  runBootAutoContinuations
110
110
  };
111
- //# sourceMappingURL=auto-continue-FGQ63HHR.js.map
111
+ //# sourceMappingURL=auto-continue-AZRNVDV5.js.map
@@ -2,22 +2,22 @@ import {
2
2
  buildRunChatTurnParams,
3
3
  finalizeTurnCompletion,
4
4
  generateSessionNameForSession
5
- } from "./chunk-ORIPUMOQ.js";
5
+ } from "./chunk-XWKX5IAU.js";
6
6
  import {
7
7
  runChatTurn
8
- } from "./chunk-ABXERHIY.js";
9
- import "./chunk-ESHP42KO.js";
10
- import "./chunk-AJMBQMJH.js";
8
+ } from "./chunk-N342GU3N.js";
9
+ import "./chunk-MA4NG3F7.js";
10
+ import "./chunk-2GIY77DT.js";
11
11
  import "./chunk-OAN4BXEW.js";
12
12
  import "./chunk-DJH6LMBT.js";
13
13
  import "./chunk-PHR64A6U.js";
14
14
  import "./chunk-J7GAZGZT.js";
15
- import "./chunk-2DBWCXAQ.js";
15
+ import "./chunk-A6YTJZAO.js";
16
16
  import "./chunk-UO4X2ENE.js";
17
17
  import "./chunk-KJEOKIH2.js";
18
18
  import "./chunk-X7ZAP6YW.js";
19
19
  import "./chunk-CVQYCAMY.js";
20
- import "./chunk-FAN4P45J.js";
20
+ import "./chunk-BAN2F7TG.js";
21
21
  import "./chunk-ZONLEX2F.js";
22
22
  import "./chunk-VGE6UYND.js";
23
23
  import "./chunk-ODETQHMI.js";
@@ -39,15 +39,15 @@ import {
39
39
  } from "./chunk-2PGRX4RH.js";
40
40
  import "./chunk-LCLH6ZUL.js";
41
41
  import "./chunk-YEFZ5HBD.js";
42
- import "./chunk-AWBV2VXO.js";
42
+ import "./chunk-MICSOCCE.js";
43
43
  import "./chunk-OA2FURC3.js";
44
44
  import "./chunk-J2GP3J3X.js";
45
45
  import "./chunk-JYYBSODN.js";
46
- import "./chunk-L2RESXUF.js";
46
+ import "./chunk-EBMHUSMZ.js";
47
47
  import "./chunk-TS5WKTFB.js";
48
48
  import "./chunk-FTPQI5TA.js";
49
49
  import "./chunk-HNCM3D7Y.js";
50
- import "./chunk-QMU3ZY2C.js";
50
+ import "./chunk-JRWG4KWV.js";
51
51
  import "./chunk-FSSUPWYO.js";
52
52
  import "./chunk-XG3AUMPI.js";
53
53
  import "./chunk-GZ4EK4LZ.js";
@@ -181,4 +181,4 @@ export {
181
181
  startChatSession,
182
182
  stopSessionExecution
183
183
  };
184
- //# sourceMappingURL=chat-handler-VCED3JYF.js.map
184
+ //# sourceMappingURL=chat-handler-NJGT2L65.js.map
@@ -17,7 +17,7 @@ import {
17
17
  getGateConfig,
18
18
  isTaskConflictError,
19
19
  isTaskGateError
20
- } from "./chunk-2DBWCXAQ.js";
20
+ } from "./chunk-A6YTJZAO.js";
21
21
  import {
22
22
  getSessionDisabledServers
23
23
  } from "./chunk-UO4X2ENE.js";
@@ -59,7 +59,7 @@ import {
59
59
  recordLLMFailure,
60
60
  sleepThroughRetryBackoff,
61
61
  streamLLMPure
62
- } from "./chunk-FAN4P45J.js";
62
+ } from "./chunk-BAN2F7TG.js";
63
63
  import {
64
64
  COMPACTION_PROMPT,
65
65
  CONTINUE_AFTER_STREAM_ERROR_PROMPT,
@@ -132,8 +132,13 @@ import {
132
132
  getModelProfile,
133
133
  modelSupportsVision
134
134
  } from "./chunk-TS5WKTFB.js";
135
+ import {
136
+ TEXT_MIME_EXACT,
137
+ TEXT_MIME_PREFIXES
138
+ } from "./chunk-JRWG4KWV.js";
135
139
  import {
136
140
  OUTPUT_LIMITS,
141
+ decodeDataUrl,
137
142
  extractPdfText,
138
143
  formatPdfErrorMessage,
139
144
  isPdfBuffer,
@@ -1616,11 +1621,12 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1616
1621
  );
1617
1622
  };
1618
1623
  const contextState = sessionManager.getContextState(sessionId);
1619
- previousContextTokens = contextState.currentTokens;
1624
+ const subAgentContextTokens = config.subAgentMetadata ? sessionManager.getSubAgentContextTokens?.(config.subAgentMetadata.subAgentId) ?? 0 : void 0;
1625
+ previousContextTokens = subAgentContextTokens ?? contextState.currentTokens;
1620
1626
  const contextWindow = sessionManager.getCurrentModelContext(sessionId, config.mode);
1621
1627
  const availableForOutput = Math.max(
1622
1628
  256,
1623
- contextWindow - contextState.currentTokens - pendingToolResultTokens - OUTPUT_RESERVE_TOKENS
1629
+ contextWindow - previousContextTokens - pendingToolResultTokens - OUTPUT_RESERVE_TOKENS
1624
1630
  );
1625
1631
  let modelSettings = config.modelSettings ?? sessionManager.getCurrentModelSettings(sessionId, config.mode);
1626
1632
  if (modelSettings && currentMaxTokensOverride !== void 0) {
@@ -1768,10 +1774,12 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1768
1774
  currentMaxTokensOverride = void 0;
1769
1775
  if (!compacting) {
1770
1776
  const contextState = sessionManager.getContextState(sessionId);
1771
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-YY7PO2XE.js");
1777
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-JDR6AGEW.js");
1778
+ const compactionTokens = config.subAgentMetadata ? sessionManager.getSubAgentContextTokens?.(config.subAgentMetadata.subAgentId) ?? 0 : contextState.currentTokens;
1779
+ const compactionWindow = config.subAgentMetadata ? sessionManager.getCurrentModelContext(sessionId, config.mode) : contextState.maxTokens;
1772
1780
  if (shouldCompact(
1773
- contextState.currentTokens,
1774
- contextState.maxTokens,
1781
+ compactionTokens,
1782
+ compactionWindow,
1775
1783
  sessionManager.getModelCompactionThreshold(sessionId, config.mode) ?? runtimeConfig.context.compactionThreshold
1776
1784
  )) {
1777
1785
  appendCompactionPrompt(sessionId, append);
@@ -2478,7 +2486,7 @@ var callSubAgentTool = {
2478
2486
  };
2479
2487
  }
2480
2488
  try {
2481
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-MRC27SZG.js");
2489
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-SK4EU3D5.js");
2482
2490
  const toolRegistry = getToolRegistryForAgent2(agentDef);
2483
2491
  const turnMetrics = new TurnMetrics();
2484
2492
  const result = await executeSubAgent({
@@ -3582,11 +3590,11 @@ var mcpConfigTool = createTool(
3582
3590
  async function notifyContextChanged(sessionId) {
3583
3591
  context.sessionManager.setDynamicContextChanged(sessionId, true);
3584
3592
  mcpNotifyChanged?.(sessionId);
3585
- const { injectContextDriftReminders } = await import("./dynamic-context-YLRA34WJ.js");
3593
+ const { injectContextDriftReminders } = await import("./dynamic-context-SS3AVCVM.js");
3586
3594
  await injectContextDriftReminders(context.sessionManager, sessionId);
3587
3595
  }
3588
3596
  async function rebuildTools() {
3589
- const { setMcpTools: setMcpTools2 } = await import("./tools-MRC27SZG.js");
3597
+ const { setMcpTools: setMcpTools2 } = await import("./tools-SK4EU3D5.js");
3590
3598
  const mcpTools = createMcpTools(mcpManagerForTools);
3591
3599
  setMcpTools2(mcpTools);
3592
3600
  }
@@ -4150,7 +4158,7 @@ function getTasksService() {
4150
4158
  }
4151
4159
  return tasksService;
4152
4160
  }
4153
- var VALID_ACTIONS = ["list", "create", "edit", "move", "set_gate_value", "delete"];
4161
+ var VALID_ACTIONS = ["list", "create", "edit", "move", "set_gate_value", "delete", "get_attachment"];
4154
4162
  var LIST_STATUSES = ["todo", "in_progress", "done", "all"];
4155
4163
  var LIST_DEFAULT_LIMIT = 10;
4156
4164
  var LIST_MAX_LIMIT = 25;
@@ -4160,12 +4168,19 @@ var projectTasksTool = createTool(
4160
4168
  type: "function",
4161
4169
  function: {
4162
4170
  name: "project_tasks",
4163
- description: "Kanban task board for this project. Core loop: list \u2192 move \u2192 set_gate_value.\n\nRules:\n- Moving to in_progress binds the task to YOUR current session.\n- Do not move tasks or fill gate values without explicit user approval or a system instruction \u2014 complete the work, then let the user review before the task advances.\n- Moving to done is blocked by unmet gates: the error names the missing fields \u2014 fill them only with user approval, then retry the move.\n- Stale writes fail with CONFLICT \u2014 re-list and retry.\n\nActions:\n- list: tasks (status, gate values, queue position, bound session, audit trail); defaults to open tasks, filter via status (todo | in_progress | done | all); paginated \u2014 limit 10 max 25, page with offset\n- create: add a task to To Do (prompt required)\n- edit: update prompt/attachments/agent/model (taskId + fields)\n- move: change column (to: todo | in_progress | done; optional reason)\n- set_gate_value: fill a gate field (taskId, gateId, value)\n- delete: remove a task (taskId)",
4171
+ description: "Kanban task board for this project. Core loop: list \u2192 move \u2192 set_gate_value.\n\nRules:\n- Moving to in_progress binds the task to YOUR current session.\n- Do not move tasks or fill gate values without explicit user approval or a system instruction \u2014 complete the work, then let the user review before the task advances.\n- Moving to done is blocked by unmet gates: the error names the missing fields \u2014 fill them only with user approval, then retry the move.\n- Stale writes fail with CONFLICT \u2014 re-list and retry.\n\nActions:\n- list: tasks (status, gate values, queue position, bound session, audit trail); defaults to open tasks, filter via status (todo | in_progress | done | all); paginated \u2014 limit 10 max 25, page with offset\n- create: add a task to To Do (prompt required)\n- edit: update prompt/attachments/agent/model (taskId + fields)\n- move: change column (to: todo | in_progress | done; optional reason)\n- set_gate_value: fill a gate field (taskId, gateId, value)\n- get_attachment: read a task attachment (taskId, attachmentId from attachmentList) \u2014 text inline, images as image data, PDFs as extracted text\n- delete: remove a task (taskId)",
4164
4172
  parameters: {
4165
4173
  type: "object",
4166
4174
  properties: {
4167
4175
  action: { type: "string", enum: VALID_ACTIONS, description: "The action to perform" },
4168
- taskId: { type: "string", description: "Target task id (edit, move, set_gate_value, delete)" },
4176
+ taskId: {
4177
+ type: "string",
4178
+ description: "Target task id (edit, move, set_gate_value, get_attachment, delete)"
4179
+ },
4180
+ attachmentId: {
4181
+ type: "string",
4182
+ description: "Attachment id for action=get_attachment (see attachmentList in list output)"
4183
+ },
4169
4184
  prompt: { type: "string", description: "The prompt/instruction executed when the task launches" },
4170
4185
  attachments: {
4171
4186
  type: "array",
@@ -4348,6 +4363,48 @@ var projectTasksTool = createTool(
4348
4363
  );
4349
4364
  return helpers.success(JSON.stringify(taskForAgent(result.task), null, 2));
4350
4365
  }
4366
+ case "get_attachment": {
4367
+ if (!args.taskId)
4368
+ return helpers.error(
4369
+ serverT({
4370
+ en: 'Parameter "taskId" is required for action=get_attachment',
4371
+ fr: "Le param\xE8tre \xAB taskId \xBB est requis pour action=get_attachment"
4372
+ })
4373
+ );
4374
+ if (!args.attachmentId)
4375
+ return helpers.error(
4376
+ serverT({
4377
+ en: 'Parameter "attachmentId" is required for action=get_attachment',
4378
+ fr: "Le param\xE8tre \xAB attachmentId \xBB est requis pour action=get_attachment"
4379
+ })
4380
+ );
4381
+ const task = svc.get(projectId, args.taskId);
4382
+ if (!task)
4383
+ return helpers.error(
4384
+ serverT({ en: "Task not found: {{id}}", fr: "T\xE2che introuvable : {{id}}" }, { id: args.taskId })
4385
+ );
4386
+ if (task.attachments.length === 0)
4387
+ return helpers.error(
4388
+ serverT(
4389
+ { en: "Task {{id}} has no attachments", fr: "La t\xE2che {{id}} n\u2019a aucune pi\xE8ce jointe" },
4390
+ { id: task.id }
4391
+ )
4392
+ );
4393
+ const attachment = task.attachments.find((a) => a.id === args.attachmentId);
4394
+ if (!attachment) {
4395
+ const available = task.attachments.map((a) => `${a.id} (${a.filename})`).join(", ");
4396
+ return helpers.error(
4397
+ serverT(
4398
+ {
4399
+ en: "Attachment {{id}} not found on task {{taskId}}. Available: {{available}}",
4400
+ fr: "Pi\xE8ce jointe {{id}} introuvable sur la t\xE2che {{taskId}}. Disponibles : {{available}}"
4401
+ },
4402
+ { id: args.attachmentId, taskId: task.id, available }
4403
+ )
4404
+ );
4405
+ }
4406
+ return await attachmentContent(attachment, helpers);
4407
+ }
4351
4408
  case "delete": {
4352
4409
  if (!args.taskId)
4353
4410
  return helpers.error(
@@ -4425,6 +4482,14 @@ function taskForAgent(task) {
4425
4482
  ...task.model ? { model: task.model } : {},
4426
4483
  version: task.version,
4427
4484
  attachments: task.attachments.length,
4485
+ ...task.attachments.length > 0 ? {
4486
+ attachmentList: task.attachments.map((a) => ({
4487
+ id: a.id,
4488
+ filename: a.filename,
4489
+ mimeType: a.mimeType,
4490
+ size: a.size
4491
+ }))
4492
+ } : {},
4428
4493
  gateValues: task.gateValues.map((v) => ({ [v.gateId]: v.value, actor: v.actor, timestamp: v.timestamp })),
4429
4494
  auditTrail: task.auditTrail.map((a) => ({
4430
4495
  action: a.action,
@@ -4440,6 +4505,118 @@ function sanitizeAttachments(raw) {
4440
4505
  (a) => typeof a === "object" && a !== null && typeof a.id === "string" && typeof a.filename === "string"
4441
4506
  );
4442
4507
  }
4508
+ async function attachmentContent(attachment, helpers) {
4509
+ const mime = attachment.mimeType;
4510
+ const filename = attachment.filename;
4511
+ if (mime.startsWith("image/")) {
4512
+ const decoded = decodeDataUrl(attachment.data);
4513
+ if (!decoded)
4514
+ return helpers.error(
4515
+ serverT(
4516
+ {
4517
+ en: "Image attachment {{filename}} has corrupt or undecodable data",
4518
+ fr: "La pi\xE8ce jointe image {{filename}} est corrompue ou illisible"
4519
+ },
4520
+ { filename }
4521
+ )
4522
+ );
4523
+ const size = attachment.size > 0 ? attachment.size : decoded.length;
4524
+ if (size > OUTPUT_LIMITS.read_file.maxImageBytes || decoded.length > OUTPUT_LIMITS.read_file.maxImageBytes) {
4525
+ return helpers.error(
4526
+ serverT(
4527
+ {
4528
+ en: "Image attachment {{filename}} ({{size}} bytes) exceeds the image size limit ({{limit}} bytes, same as read_file). Stored attachments cannot be modified in place \u2014 ask the user to compress or downsize the image and re-upload it.",
4529
+ fr: "La pi\xE8ce jointe image {{filename}} ({{size}} octets) d\xE9passe la limite pour les images ({{limit}} octets, comme read_file). Les pi\xE8ces jointes stock\xE9es ne peuvent pas \xEAtre modifi\xE9es en place \u2014 demandez \xE0 l\u2019utilisateur de compacter ou r\xE9duire l\u2019image et de la r\xE9envoyer."
4530
+ },
4531
+ { filename, size, limit: OUTPUT_LIMITS.read_file.maxImageBytes }
4532
+ )
4533
+ );
4534
+ }
4535
+ const base64Data = decoded.toString("base64");
4536
+ return helpers.success(
4537
+ serverT(
4538
+ {
4539
+ en: "[Attachment: {{filename}} ({{mime}}, {{size}} bytes)]",
4540
+ fr: "[Pi\xE8ce jointe : {{filename}} ({{mime}}, {{size}} octets)]"
4541
+ },
4542
+ { filename, mime, size }
4543
+ ),
4544
+ false,
4545
+ {
4546
+ metadata: {
4547
+ mimeType: mime,
4548
+ size,
4549
+ base64Data,
4550
+ dataUrl: `data:${mime};base64,${base64Data}`,
4551
+ path: filename,
4552
+ ...attachment.description ? { description: attachment.description } : {}
4553
+ }
4554
+ }
4555
+ );
4556
+ }
4557
+ if (TEXT_MIME_EXACT.includes(mime) || TEXT_MIME_PREFIXES.some((p) => mime.startsWith(p))) {
4558
+ const decoded = decodeDataUrl(attachment.data);
4559
+ const text = decoded ? decoded.toString("utf-8") : attachment.data;
4560
+ if (text.length > OUTPUT_LIMITS.read_file.maxBytes) {
4561
+ return helpers.success(
4562
+ text.slice(0, OUTPUT_LIMITS.read_file.maxBytes) + "\n\n[Output truncated due to size limit]",
4563
+ true
4564
+ );
4565
+ }
4566
+ return helpers.success(text, false);
4567
+ }
4568
+ if (mime === "application/pdf") {
4569
+ if (attachment.pdfContent) {
4570
+ const text = attachment.pdfContent;
4571
+ const truncated = text.length > OUTPUT_LIMITS.read_file.maxBytes;
4572
+ return helpers.success(
4573
+ truncated ? text.slice(0, OUTPUT_LIMITS.read_file.maxBytes) + "\n\n[Output truncated due to size limit]" : text,
4574
+ truncated
4575
+ );
4576
+ }
4577
+ const buffer = decodeDataUrl(attachment.data);
4578
+ if (!buffer || !isPdfBuffer(buffer))
4579
+ return helpers.error(
4580
+ serverT(
4581
+ {
4582
+ en: "PDF attachment {{filename}} has corrupt or undecodable data",
4583
+ fr: "La pi\xE8ce jointe PDF {{filename}} est corrompue ou illisible"
4584
+ },
4585
+ { filename }
4586
+ )
4587
+ );
4588
+ try {
4589
+ const { text, pageCount, title, author } = await extractPdfText(buffer);
4590
+ const { output, truncated, isScanned } = processPdfContent(text, OUTPUT_LIMITS.read_file.maxBytes);
4591
+ const metadata = { format: "pdf", pageCount, title, author, path: filename };
4592
+ if (isScanned) {
4593
+ return helpers.success(
4594
+ serverT(
4595
+ {
4596
+ en: "[PDF: {{filename}} \u2014 This PDF has no text layer (scanned or image-only), so its text cannot be extracted. Ask the user to re-upload a version with a text layer, or to describe its content.]",
4597
+ fr: "[PDF : {{filename}} \u2014 Ce PDF n\u2019a pas de couche texte (scann\xE9 ou image uniquement), son texte ne peut donc pas \xEAtre extrait. Demandez \xE0 l\u2019utilisateur de renvoyer une version avec une couche texte, ou de d\xE9crire son contenu.]"
4598
+ },
4599
+ { filename }
4600
+ ),
4601
+ false,
4602
+ { metadata }
4603
+ );
4604
+ }
4605
+ return helpers.success(output, truncated, { metadata });
4606
+ } catch (err) {
4607
+ return helpers.error(formatPdfErrorMessage(err));
4608
+ }
4609
+ }
4610
+ return helpers.error(
4611
+ serverT(
4612
+ {
4613
+ en: "Unsupported attachment type: {{mime}} ({{filename}}). Supported: image/*, text/* and common text types (JSON, XML, YAML, \u2026), application/pdf.",
4614
+ fr: "Type de pi\xE8ce jointe non pris en charge : {{mime}} ({{filename}}). Pris en charge : image/*, text/* et les types texte courants (JSON, XML, YAML, \u2026), application/pdf."
4615
+ },
4616
+ { mime, filename }
4617
+ )
4618
+ );
4619
+ }
4443
4620
 
4444
4621
  // src/server/tools/index.ts
4445
4622
  var _builtInTools;
@@ -4729,4 +4906,4 @@ export {
4729
4906
  getToolRegistryForAgent,
4730
4907
  createToolRegistry
4731
4908
  };
4732
- //# sourceMappingURL=chunk-AJMBQMJH.js.map
4909
+ //# sourceMappingURL=chunk-2GIY77DT.js.map
@@ -0,0 +1,7 @@
1
+ // src/constants.ts
2
+ var VERSION = "2.0.149";
3
+
4
+ export {
5
+ VERSION
6
+ };
7
+ //# sourceMappingURL=chunk-2IK5KDVV.js.map
@@ -2,21 +2,21 @@ import {
2
2
  generateSessionNameForSession,
3
3
  getSessionMessageCount,
4
4
  needsNameGeneration
5
- } from "./chunk-ORIPUMOQ.js";
5
+ } from "./chunk-XWKX5IAU.js";
6
6
  import {
7
7
  launchWorkflowRun
8
- } from "./chunk-2V6QL6IW.js";
8
+ } from "./chunk-ZWI4T4WF.js";
9
9
  import {
10
10
  runChatTurn
11
- } from "./chunk-ABXERHIY.js";
11
+ } from "./chunk-N342GU3N.js";
12
12
  import {
13
13
  applyDynamicContext,
14
14
  computeSessionHash,
15
15
  computeUnifiedDiff
16
- } from "./chunk-ESHP42KO.js";
16
+ } from "./chunk-MA4NG3F7.js";
17
17
  import {
18
18
  devServerManager
19
- } from "./chunk-AJMBQMJH.js";
19
+ } from "./chunk-2GIY77DT.js";
20
20
  import {
21
21
  gitSpawnEnv
22
22
  } from "./chunk-J7GAZGZT.js";
@@ -25,11 +25,11 @@ import {
25
25
  } from "./chunk-X7ZAP6YW.js";
26
26
  import {
27
27
  appendCompactionPrompt
28
- } from "./chunk-P7KUU5VB.js";
28
+ } from "./chunk-E4RD42PU.js";
29
29
  import {
30
30
  hasRecentLLMFailure,
31
31
  interruptLLMRetryWait
32
- } from "./chunk-FAN4P45J.js";
32
+ } from "./chunk-BAN2F7TG.js";
33
33
  import {
34
34
  onProcessEvent
35
35
  } from "./chunk-ODETQHMI.js";
@@ -1358,7 +1358,7 @@ async function handleClientMessage(ws, client, message, _getLLMClient, _getActiv
1358
1358
  }
1359
1359
  const session = sessionManager.requireSession(sessionId);
1360
1360
  try {
1361
- const { buildCachedPrompt, computePreviewToolDiff } = await import("./dynamic-context-YLRA34WJ.js");
1361
+ const { buildCachedPrompt, computePreviewToolDiff } = await import("./dynamic-context-SS3AVCVM.js");
1362
1362
  const allAgents = await import("./registry-24U5BZAJ.js");
1363
1363
  const agentDef = allAgents.findAgentById(session.mode, await allAgents.loadAllAgentsDefault()) ?? allAgents.findAgentById("planner", await allAgents.loadAllAgentsDefault());
1364
1364
  const modelName = session.providerModel ?? _providerManager?.getCurrentModel();
@@ -1370,7 +1370,7 @@ async function handleClientMessage(ws, client, message, _getLLMClient, _getActiv
1370
1370
  const oldCached = sessionManager.getCachedPrompt(sessionId);
1371
1371
  const oldPrompt = oldCached?.systemPrompt;
1372
1372
  const diff = oldPrompt ? computeUnifiedDiff(oldPrompt, newPrompt) : [];
1373
- const { getToolRegistryForAgent } = await import("./tools-MRC27SZG.js");
1373
+ const { getToolRegistryForAgent } = await import("./tools-SK4EU3D5.js");
1374
1374
  const unfilteredTools = getToolRegistryForAgent(agentDef).definitions;
1375
1375
  const toolDiff = computePreviewToolDiff(oldCached?.tools, unfilteredTools, newTools);
1376
1376
  send({
@@ -1743,4 +1743,4 @@ export {
1743
1743
  signalMcpReady,
1744
1744
  createWebSocketServer
1745
1745
  };
1746
- //# sourceMappingURL=chunk-UOBJP4GN.js.map
1746
+ //# sourceMappingURL=chunk-7HNACZXD.js.map
@@ -11,7 +11,7 @@ import {
11
11
  } from "./chunk-OSXPQSLW.js";
12
12
  import {
13
13
  parseDefaultModelSelection
14
- } from "./chunk-AWBV2VXO.js";
14
+ } from "./chunk-MICSOCCE.js";
15
15
  import {
16
16
  getProject
17
17
  } from "./chunk-JYYBSODN.js";
@@ -1462,4 +1462,4 @@ export {
1462
1462
  isTaskGateError,
1463
1463
  createTasksService
1464
1464
  };
1465
- //# sourceMappingURL=chunk-2DBWCXAQ.js.map
1465
+ //# sourceMappingURL=chunk-A6YTJZAO.js.map
@@ -9,16 +9,18 @@ import {
9
9
  } from "./chunk-FTPQI5TA.js";
10
10
  import {
11
11
  buildModelParams
12
- } from "./chunk-QMU3ZY2C.js";
12
+ } from "./chunk-JRWG4KWV.js";
13
13
  import {
14
14
  logger
15
15
  } from "./chunk-K44MW7JJ.js";
16
16
 
17
17
  // src/server/chat/auto-patterns.ts
18
+ var VALID_FIELDS = ["thinking", "content", "both"];
18
19
  function matchRetryPatterns(content, thinking, patterns) {
19
20
  const matches = [];
20
21
  for (const config of patterns) {
21
22
  if (!config.active) continue;
23
+ if (!config.pattern || config.pattern.trim() === "") continue;
22
24
  let regex;
23
25
  try {
24
26
  regex = new RegExp(config.pattern);
@@ -36,6 +38,19 @@ function matchRetryPatterns(content, thinking, patterns) {
36
38
  }
37
39
  return matches;
38
40
  }
41
+ function sanitizeRetryPatterns(patterns) {
42
+ return patterns.filter(
43
+ (p) => p.pattern && p.pattern.trim() !== "" && isValidPattern(p.pattern) && VALID_FIELDS.includes(p.field)
44
+ );
45
+ }
46
+ function isValidPattern(pattern) {
47
+ try {
48
+ new RegExp(pattern);
49
+ return true;
50
+ } catch {
51
+ return false;
52
+ }
53
+ }
39
54
 
40
55
  // src/server/chat/stream-utils.ts
41
56
  function buildStreamRequestObject(params) {
@@ -505,6 +520,7 @@ async function consumeStreamGenerator(gen, onEvent) {
505
520
  }
506
521
 
507
522
  export {
523
+ sanitizeRetryPatterns,
508
524
  streamLLMPure,
509
525
  evaluateLLMRetry,
510
526
  hasRecentLLMFailure,
@@ -520,4 +536,4 @@ export {
520
536
  createChatDoneEvent,
521
537
  consumeStreamGenerator
522
538
  };
523
- //# sourceMappingURL=chunk-FAN4P45J.js.map
539
+ //# sourceMappingURL=chunk-BAN2F7TG.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createMessageStartEvent
3
- } from "./chunk-FAN4P45J.js";
3
+ } from "./chunk-BAN2F7TG.js";
4
4
  import {
5
5
  COMPACTION_PROMPT
6
6
  } from "./chunk-ZONLEX2F.js";
@@ -36,4 +36,4 @@ export {
36
36
  COMPACTION_MAX_RATIO,
37
37
  shouldCompact
38
38
  };
39
- //# sourceMappingURL=chunk-P7KUU5VB.js.map
39
+ //# sourceMappingURL=chunk-E4RD42PU.js.map
@@ -14,7 +14,7 @@ import {
14
14
  getThinking,
15
15
  mapFinishReason,
16
16
  parseToolArguments
17
- } from "./chunk-QMU3ZY2C.js";
17
+ } from "./chunk-JRWG4KWV.js";
18
18
  import {
19
19
  ChatHttpClient,
20
20
  DONE,
@@ -825,4 +825,4 @@ function createLLMClient(config, initialBackend = config.llm.backend ?? "unknown
825
825
  export {
826
826
  createLLMClient
827
827
  };
828
- //# sourceMappingURL=chunk-L2RESXUF.js.map
828
+ //# sourceMappingURL=chunk-EBMHUSMZ.js.map
@@ -207,7 +207,7 @@ async function runCli(options) {
207
207
  break;
208
208
  }
209
209
  case "provider": {
210
- const { runProviderCommand } = await import("./provider-QE7XSGYA.js");
210
+ const { runProviderCommand } = await import("./provider-52J4WIIQ.js");
211
211
  const [, subcommand] = positionals;
212
212
  await runProviderCommand(mode, subcommand);
213
213
  break;
@@ -252,7 +252,7 @@ async function runCli(options) {
252
252
  break;
253
253
  }
254
254
  case "update": {
255
- const { runUpdate } = await import("./update-MLDVVG3D.js");
255
+ const { runUpdate } = await import("./update-32UNL44B.js");
256
256
  const code = await runUpdate();
257
257
  if (code !== 0) {
258
258
  process.exit(code);
@@ -265,7 +265,7 @@ async function runCli(options) {
265
265
  if (!configExists) {
266
266
  await runNetworkSetup(mode);
267
267
  }
268
- const { runServe } = await import("./serve-A4AJAH2N.js");
268
+ const { runServe } = await import("./serve-O44ZIZ7N.js");
269
269
  const serveOptions = { mode };
270
270
  if (values.port) serveOptions.port = parseInt(values.port);
271
271
  if (values["no-browser"] === true) serveOptions.openBrowser = false;
@@ -277,4 +277,4 @@ async function runCli(options) {
277
277
  export {
278
278
  runCli
279
279
  };
280
- //# sourceMappingURL=chunk-QHFJRUJV.js.map
280
+ //# sourceMappingURL=chunk-I46WU3NP.js.map