openfox 2.0.148 → 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 (44) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/CHANGELOG.md +15 -0
  3. package/dist/{auto-continue-NODN37EK.js → auto-continue-AZRNVDV5.js} +7 -7
  4. package/dist/{chat-handler-PETIVNGQ.js → chat-handler-NJGT2L65.js} +10 -10
  5. package/dist/{chunk-EJKAN7HK.js → chunk-2GIY77DT.js} +184 -10
  6. package/dist/chunk-2IK5KDVV.js +7 -0
  7. package/dist/{chunk-V4BEYGBJ.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-6UU3W3XO.js → chunk-I46WU3NP.js} +4 -4
  13. package/dist/{chunk-QMU3ZY2C.js → chunk-JRWG4KWV.js} +3 -1
  14. package/dist/{chunk-LNJOQAXT.js → chunk-MA4NG3F7.js} +2 -2
  15. package/dist/{chunk-AWBV2VXO.js → chunk-MICSOCCE.js} +3 -3
  16. package/dist/{chunk-F6QMFCHQ.js → chunk-N342GU3N.js} +19 -8
  17. package/dist/{chunk-772W5PF7.js → chunk-RXKZVMKL.js} +67 -54
  18. package/dist/{chunk-ORIPUMOQ.js → chunk-XWKX5IAU.js} +2 -2
  19. package/dist/{chunk-NELDQCPC.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-CM3M2EJC.js → dynamic-context-SS3AVCVM.js} +2 -2
  26. package/dist/{export-import-UITAIBU5.js → export-import-QT7CV7ZU.js} +2 -2
  27. package/dist/{launch-E6SDDTHE.js → launch-2DPUISQR.js} +10 -10
  28. package/dist/{orchestrator-MVQIPITF.js → orchestrator-2BBZC4VH.js} +11 -9
  29. package/dist/package.json +1 -1
  30. package/dist/{processor-UTBTJVV5.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-XGIHFEC6.js → serve-O44ZIZ7N.js} +15 -15
  34. package/dist/server/index.js +14 -14
  35. package/dist/{server-F66GXJUS.js → server-Q5PTCS4G.js} +13 -13
  36. package/dist/{service-KMOSB6SU.js → service-ALXVEWCE.js} +5 -5
  37. package/dist/{tasks-X5DM553T.js → tasks-LD3VJYFH.js} +5 -5
  38. package/dist/{tools-NIOESJYT.js → tools-SK4EU3D5.js} +7 -7
  39. package/dist/{update-X6BS6MUF.js → update-32UNL44B.js} +2 -2
  40. package/dist/web/assets/{index-CkVmYTBd.js → index-Cith3wH6.js} +55 -55
  41. package/dist/web/index.html +1 -1
  42. package/dist/web/sw.js +1 -1
  43. package/package.json +1 -1
  44. package/dist/chunk-DKHY5UNR.js +0 -7
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
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
+
3
18
  ## 2.0.148 - 2026-09-14
4
19
 
5
20
  ### Bug Fixes
package/dist/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
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
+
3
18
  ## 2.0.148 - 2026-09-14
4
19
 
5
20
  ### Bug Fixes
@@ -1,16 +1,16 @@
1
- import "./chunk-EJKAN7HK.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-NODN37EK.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-F6QMFCHQ.js";
9
- import "./chunk-LNJOQAXT.js";
10
- import "./chunk-EJKAN7HK.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-PETIVNGQ.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,
@@ -1769,7 +1774,7 @@ ${CONTINUE_PROMPT}` : CONTINUE_PROMPT;
1769
1774
  currentMaxTokensOverride = void 0;
1770
1775
  if (!compacting) {
1771
1776
  const contextState = sessionManager.getContextState(sessionId);
1772
- const { shouldCompact, appendCompactionPrompt } = await import("./compactor-YY7PO2XE.js");
1777
+ const { shouldCompact, appendCompactionPrompt } = await import("./compactor-JDR6AGEW.js");
1773
1778
  const compactionTokens = config.subAgentMetadata ? sessionManager.getSubAgentContextTokens?.(config.subAgentMetadata.subAgentId) ?? 0 : contextState.currentTokens;
1774
1779
  const compactionWindow = config.subAgentMetadata ? sessionManager.getCurrentModelContext(sessionId, config.mode) : contextState.maxTokens;
1775
1780
  if (shouldCompact(
@@ -2481,7 +2486,7 @@ var callSubAgentTool = {
2481
2486
  };
2482
2487
  }
2483
2488
  try {
2484
- const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-NIOESJYT.js");
2489
+ const { getToolRegistryForAgent: getToolRegistryForAgent2 } = await import("./tools-SK4EU3D5.js");
2485
2490
  const toolRegistry = getToolRegistryForAgent2(agentDef);
2486
2491
  const turnMetrics = new TurnMetrics();
2487
2492
  const result = await executeSubAgent({
@@ -3585,11 +3590,11 @@ var mcpConfigTool = createTool(
3585
3590
  async function notifyContextChanged(sessionId) {
3586
3591
  context.sessionManager.setDynamicContextChanged(sessionId, true);
3587
3592
  mcpNotifyChanged?.(sessionId);
3588
- const { injectContextDriftReminders } = await import("./dynamic-context-CM3M2EJC.js");
3593
+ const { injectContextDriftReminders } = await import("./dynamic-context-SS3AVCVM.js");
3589
3594
  await injectContextDriftReminders(context.sessionManager, sessionId);
3590
3595
  }
3591
3596
  async function rebuildTools() {
3592
- const { setMcpTools: setMcpTools2 } = await import("./tools-NIOESJYT.js");
3597
+ const { setMcpTools: setMcpTools2 } = await import("./tools-SK4EU3D5.js");
3593
3598
  const mcpTools = createMcpTools(mcpManagerForTools);
3594
3599
  setMcpTools2(mcpTools);
3595
3600
  }
@@ -4153,7 +4158,7 @@ function getTasksService() {
4153
4158
  }
4154
4159
  return tasksService;
4155
4160
  }
4156
- 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"];
4157
4162
  var LIST_STATUSES = ["todo", "in_progress", "done", "all"];
4158
4163
  var LIST_DEFAULT_LIMIT = 10;
4159
4164
  var LIST_MAX_LIMIT = 25;
@@ -4163,12 +4168,19 @@ var projectTasksTool = createTool(
4163
4168
  type: "function",
4164
4169
  function: {
4165
4170
  name: "project_tasks",
4166
- 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)",
4167
4172
  parameters: {
4168
4173
  type: "object",
4169
4174
  properties: {
4170
4175
  action: { type: "string", enum: VALID_ACTIONS, description: "The action to perform" },
4171
- 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
+ },
4172
4184
  prompt: { type: "string", description: "The prompt/instruction executed when the task launches" },
4173
4185
  attachments: {
4174
4186
  type: "array",
@@ -4351,6 +4363,48 @@ var projectTasksTool = createTool(
4351
4363
  );
4352
4364
  return helpers.success(JSON.stringify(taskForAgent(result.task), null, 2));
4353
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
+ }
4354
4408
  case "delete": {
4355
4409
  if (!args.taskId)
4356
4410
  return helpers.error(
@@ -4428,6 +4482,14 @@ function taskForAgent(task) {
4428
4482
  ...task.model ? { model: task.model } : {},
4429
4483
  version: task.version,
4430
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
+ } : {},
4431
4493
  gateValues: task.gateValues.map((v) => ({ [v.gateId]: v.value, actor: v.actor, timestamp: v.timestamp })),
4432
4494
  auditTrail: task.auditTrail.map((a) => ({
4433
4495
  action: a.action,
@@ -4443,6 +4505,118 @@ function sanitizeAttachments(raw) {
4443
4505
  (a) => typeof a === "object" && a !== null && typeof a.id === "string" && typeof a.filename === "string"
4444
4506
  );
4445
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
+ }
4446
4620
 
4447
4621
  // src/server/tools/index.ts
4448
4622
  var _builtInTools;
@@ -4732,4 +4906,4 @@ export {
4732
4906
  getToolRegistryForAgent,
4733
4907
  createToolRegistry
4734
4908
  };
4735
- //# sourceMappingURL=chunk-EJKAN7HK.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-NELDQCPC.js";
8
+ } from "./chunk-ZWI4T4WF.js";
9
9
  import {
10
10
  runChatTurn
11
- } from "./chunk-F6QMFCHQ.js";
11
+ } from "./chunk-N342GU3N.js";
12
12
  import {
13
13
  applyDynamicContext,
14
14
  computeSessionHash,
15
15
  computeUnifiedDiff
16
- } from "./chunk-LNJOQAXT.js";
16
+ } from "./chunk-MA4NG3F7.js";
17
17
  import {
18
18
  devServerManager
19
- } from "./chunk-EJKAN7HK.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-CM3M2EJC.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-NIOESJYT.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-V4BEYGBJ.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-X6BS6MUF.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-XGIHFEC6.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-6UU3W3XO.js.map
280
+ //# sourceMappingURL=chunk-I46WU3NP.js.map
@@ -434,6 +434,8 @@ function mapFinishReason(reason) {
434
434
  }
435
435
 
436
436
  export {
437
+ TEXT_MIME_PREFIXES,
438
+ TEXT_MIME_EXACT,
437
439
  resolveAttachmentsInMessages,
438
440
  parseToolArguments,
439
441
  buildModelParams,
@@ -444,4 +446,4 @@ export {
444
446
  buildStreamingCreateParams,
445
447
  mapFinishReason
446
448
  };
447
- //# sourceMappingURL=chunk-QMU3ZY2C.js.map
449
+ //# sourceMappingURL=chunk-JRWG4KWV.js.map
@@ -329,7 +329,7 @@ function resolveAgentDef(sessionManager, sessionId) {
329
329
  });
330
330
  }
331
331
  async function getEffectiveToolDefinitions(agentDef, sessionId, modelName) {
332
- const { getToolRegistryForAgent } = await import("./tools-NIOESJYT.js");
332
+ const { getToolRegistryForAgent } = await import("./tools-SK4EU3D5.js");
333
333
  const { isDescribeImageEligible } = await import("./describe-image-Z5WS6YU3.js");
334
334
  const all = getToolRegistryForAgent(agentDef, sessionId).definitions;
335
335
  const eligible = await isDescribeImageEligible(modelName);
@@ -398,4 +398,4 @@ export {
398
398
  computeSessionHash,
399
399
  applyDynamicContext
400
400
  };
401
- //# sourceMappingURL=chunk-LNJOQAXT.js.map
401
+ //# sourceMappingURL=chunk-MA4NG3F7.js.map