@lovelybunch/api 1.0.73 → 1.0.74

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 (92) hide show
  1. package/dist/routes/api/v1/ai/route.js +79 -18
  2. package/dist/routes/api/v1/ai/tools.js +76 -16
  3. package/dist/routes/api/v1/mcp/index.js +129 -573
  4. package/dist/routes/api/v1/proposals/[id]/route.d.ts +12 -0
  5. package/dist/routes/api/v1/proposals/route.d.ts +12 -0
  6. package/package.json +6 -5
  7. package/static/assets/ActivityPage-AWTVFLmK.js +1 -0
  8. package/static/assets/{AgentDetailPage-WLAnnZep.js → AgentDetailPage-DtGFrHzZ.js} +1 -1
  9. package/static/assets/{AgentEditPage-DOemUkvg.js → AgentEditPage-1026aJox.js} +1 -1
  10. package/static/assets/{AgentsPage-Bage8eYW.js → AgentsPage-9Hre8AD1.js} +2 -2
  11. package/static/assets/{AgentsSettingsPage-Cw2MTnHU.js → AgentsSettingsPage-CILCHaO9.js} +1 -1
  12. package/static/assets/{ApiKeysSettingsPage-DCKd4LXE.js → ApiKeysSettingsPage-D-l8q30N.js} +1 -1
  13. package/static/assets/{ArchitectureEditPage-WY9k_1tR.js → ArchitectureEditPage-DiRR28Rx.js} +1 -1
  14. package/static/assets/{ArchitecturePage-Cj4dVDWO.js → ArchitecturePage-FcRJGw8O.js} +1 -1
  15. package/static/assets/{AuthSettingsPage-Bs5wL5Yj.js → AuthSettingsPage-D528tGhc.js} +1 -1
  16. package/static/assets/{CallbackPage-CtydJ4j3.js → CallbackPage-BMZOjCy_.js} +1 -1
  17. package/static/assets/CodePage-CCNmmkv_.js +2 -0
  18. package/static/assets/{CollapsibleSection-5qSoX47l.js → CollapsibleSection-CG5gAVWB.js} +1 -1
  19. package/static/assets/DashboardPage-D5C34QbO.js +41 -0
  20. package/static/assets/{GitPage-BG-ZSGfu.js → GitPage-ofZrdSSl.js} +1 -1
  21. package/static/assets/{GitSettingsPage-Cn-MciXq.js → GitSettingsPage-Cld_sN5t.js} +1 -1
  22. package/static/assets/{IdentityPage-DCpoDF2j.js → IdentityPage-Dj-Do8q7.js} +1 -1
  23. package/static/assets/{ImplementationStepsEditor-PM47REBn.js → ImplementationStepsEditor-BZQKPJ1C.js} +1 -1
  24. package/static/assets/{IntegrationsSettingsPage-DJpf2gZn.js → IntegrationsSettingsPage-DTT6nX3v.js} +1 -1
  25. package/static/assets/JobDetailPage-CWexORzH.js +1 -0
  26. package/static/assets/{KnowledgeDetailPage-CLizVIxC.js → KnowledgeDetailPage-C-a0njhy.js} +1 -1
  27. package/static/assets/{KnowledgeEditPage-B-sdY_DS.js → KnowledgeEditPage-B93ffsgz.js} +1 -1
  28. package/static/assets/{KnowledgePage-DIlUufGq.js → KnowledgePage-DS7xi1qJ.js} +1 -1
  29. package/static/assets/{LoginPage-DcTU__Dc.js → LoginPage-FUflG9B8.js} +1 -1
  30. package/static/assets/{McpSettingsPage-BPDLne4q.js → McpSettingsPage-A6uws8gQ.js} +1 -1
  31. package/static/assets/{NewAgentPage-8-abvhkI.js → NewAgentPage-CqBlVqdy.js} +1 -1
  32. package/static/assets/{NewKnowledgePage-Rvj741x1.js → NewKnowledgePage-ue0ZRgKf.js} +1 -1
  33. package/static/assets/{NewProposalPage-BLIqmuks.js → NewProposalPage-BDbStKts.js} +1 -1
  34. package/static/assets/{ProjectEditPage-DC5-v_2a.js → ProjectEditPage-_a9hJTgi.js} +1 -1
  35. package/static/assets/{ProjectPage-_L4wqwrB.js → ProjectPage-DAlEu9Af.js} +1 -1
  36. package/static/assets/{PromptsSettingsPage-K-qqpF2S.js → PromptsSettingsPage-4_C5zvq6.js} +1 -1
  37. package/static/assets/{ProposalDetailPage-BVXTjXw2.js → ProposalDetailPage-mZ9qj0wJ.js} +1 -1
  38. package/static/assets/{ProposalEditPage-Bs3ak0PG.js → ProposalEditPage-DqJ3Po23.js} +1 -1
  39. package/static/assets/{ProposalsPage-BvKlKvuo.js → ProposalsPage-DaR_KuZx.js} +1 -1
  40. package/static/assets/{ResourcesPage-BSyX_kHV.js → ResourcesPage-WMPlFn0S.js} +1 -1
  41. package/static/assets/{RoleEditPage-CnWierul.js → RoleEditPage-DIoamJfW.js} +1 -1
  42. package/static/assets/{RolePage-D8xB0I-F.js → RolePage-4aL0vR7F.js} +1 -1
  43. package/static/assets/{RulesSettingsPage-CZBQ0u-x.js → RulesSettingsPage-CerRq_kE.js} +1 -1
  44. package/static/assets/SchedulePage-DF9TqOA6.js +4 -0
  45. package/static/assets/{SourceInput-BoRGYtye.js → SourceInput-CXv23vkE.js} +1 -1
  46. package/static/assets/{TagInput-CdXzv6hj.js → TagInput-DL_48L0p.js} +1 -1
  47. package/static/assets/{TerminalPage-CqPXFOIN.js → TerminalPage-BL8pa_9k.js} +1 -1
  48. package/static/assets/{TerminalSessionPage-DR2cApWv.js → TerminalSessionPage-DcC_o_sA.js} +1 -1
  49. package/static/assets/{UserPreferencesPage-CPkulDiM.js → UserPreferencesPage-DoMyvwD4.js} +1 -1
  50. package/static/assets/{UserSettingsPage-BuMnU394.js → UserSettingsPage-CqibuEiq.js} +1 -1
  51. package/static/assets/{UtilitiesPage-BGiBB5PT.js → UtilitiesPage-6BGeolw9.js} +1 -1
  52. package/static/assets/{alert-CHpdnk6F.js → alert-DQ9shGSz.js} +1 -1
  53. package/static/assets/{arrow-down-BjGtxyJ7.js → arrow-down-D5oSUxtq.js} +1 -1
  54. package/static/assets/{arrow-left-DR0foZvi.js → arrow-left-BJ9vzpff.js} +1 -1
  55. package/static/assets/{arrow-up-DCGMV1HG.js → arrow-up-DWNinaN_.js} +1 -1
  56. package/static/assets/{badge-Cds9UgAB.js → badge-BLbqPEov.js} +1 -1
  57. package/static/assets/{browser-modal-Ck4-s_jh.js → browser-modal-Dh2dy_2x.js} +1 -1
  58. package/static/assets/{calendar-CWhxfYG2.js → calendar-CaBty4QE.js} +1 -1
  59. package/static/assets/{card-BdOrf6VU.js → card-BTg0Fecj.js} +1 -1
  60. package/static/assets/{chevron-left-Wnwkk8g7.js → chevron-left-V0REBjky.js} +1 -1
  61. package/static/assets/{chevrons-up-DbnuGl5g.js → chevrons-up-BvR9KFeO.js} +1 -1
  62. package/static/assets/{circle-alert-BPvQggmh.js → circle-alert-DR1DiLh5.js} +1 -1
  63. package/static/assets/{circle-check-CTlDV9K4.js → circle-check-DED-Ne-m.js} +1 -1
  64. package/static/assets/{circle-check-big-KMhw8TDM.js → circle-check-big-CFsUUo2_.js} +1 -1
  65. package/static/assets/{circle-play-tZLgOcAc.js → circle-play-DQ32zg60.js} +1 -1
  66. package/static/assets/{circle-x-D3tazxxl.js → circle-x-CDTWutj4.js} +1 -1
  67. package/static/assets/{clipboard-C86rsVqL.js → clipboard-CTFAlaYo.js} +1 -1
  68. package/static/assets/{clock-Qm5u6CAm.js → clock-pVWUoJDo.js} +1 -1
  69. package/static/assets/{download-Bo3vdlNo.js → download-Bt7lWWYL.js} +1 -1
  70. package/static/assets/{eye-Cr9Hfzxo.js → eye-o7gPa7E6.js} +1 -1
  71. package/static/assets/{folder-git-2-Em4ZglGs.js → folder-git-2-CHwnYB8a.js} +1 -1
  72. package/static/assets/{index-CQpPrvm_.js → index-DaqYJNAM.js} +30 -30
  73. package/static/assets/{label-nG86oxuW.js → label-BtnLfquf.js} +1 -1
  74. package/static/assets/{markdown-editor-CEQMlLWe.js → markdown-editor-Cmsc5nNu.js} +1 -1
  75. package/static/assets/{pause-DuR7ql7H.js → pause-EIDnvByV.js} +1 -1
  76. package/static/assets/{play-p4m8WHP3.js → play-D_fLXyLR.js} +1 -1
  77. package/static/assets/{plus-CD075YlZ.js → plus-ZCd709aN.js} +1 -1
  78. package/static/assets/{radio-group-DCKkxIgI.js → radio-group-STvlCDl-.js} +1 -1
  79. package/static/assets/{refresh-cw-D-IgYQ7y.js → refresh-cw-BF0hXtxu.js} +1 -1
  80. package/static/assets/{search-W3H-E0eW.js → search-kmqzieAc.js} +1 -1
  81. package/static/assets/{switch-E0GrYmgh.js → switch-CjDKvzd0.js} +1 -1
  82. package/static/assets/{tabs-DjZiD9WD.js → tabs-DPohYGac.js} +1 -1
  83. package/static/assets/{tag-D0iVh1-U.js → tag-Dg9notds.js} +1 -1
  84. package/static/assets/{terminal-preview-BQBOEop2.js → terminal-preview-CnytxbzD.js} +1 -1
  85. package/static/assets/{use-terminal-0TezQnxO.js → use-terminal-LpFJK0k7.js} +1 -1
  86. package/static/assets/{zap-C_5I7lLi.js → zap-DBKgrFQ_.js} +1 -1
  87. package/static/index.html +1 -1
  88. package/static/assets/ActivityPage-C_HqpJt2.js +0 -1
  89. package/static/assets/CodePage-BiRf5q_q.js +0 -2
  90. package/static/assets/DashboardPage-e9hNRsi2.js +0 -41
  91. package/static/assets/JobDetailPage-9shaUPlO.js +0 -1
  92. package/static/assets/SchedulePage-HBFJT_19.js +0 -4
@@ -3,8 +3,9 @@ import { join, resolve as pathResolve, basename } from 'path';
3
3
  import { existsSync, readFileSync, promises as fs, createReadStream } from 'fs';
4
4
  import { fileURLToPath } from 'url';
5
5
  import readline from 'readline';
6
- import { getLogsDir } from '@lovelybunch/core';
7
- import { proposalsReadOnlyTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool } from '@lovelybunch/mcp';
6
+ import { ZodError } from 'zod';
7
+ import { getLogsDir, listProposals, getProposal, createProposal, updateProposal, deleteProposal, } from '@lovelybunch/core';
8
+ import { proposalsFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool } from '@lovelybunch/mcp';
8
9
  import matter from 'gray-matter';
9
10
  import Fuse from 'fuse.js';
10
11
  import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
@@ -53,11 +54,11 @@ export async function POST(c) {
53
54
  ? `${baseSystem}\n\nThe following persona is authoritative and overrides general guidance above. You must strictly follow it.\n\n${agentPersona}`
54
55
  : baseSystem;
55
56
  // Prepare tools for function calling
56
- // Note: proposals is read-only, knowledge/project/architecture are read+write, events is read-only
57
+ // Note: proposals/knowledge/project/architecture are read+write, events is read-only
57
58
  const tools = enableTools ? [
58
59
  {
59
60
  type: "function",
60
- function: proposalsReadOnlyTool
61
+ function: proposalsFullTool
61
62
  },
62
63
  {
63
64
  type: "function",
@@ -239,17 +240,26 @@ async function executeToolCalls(toolCalls) {
239
240
  let functionArgs;
240
241
  if (toolCall.function?.arguments) {
241
242
  try {
242
- functionArgs = JSON.parse(toolCall.function.arguments);
243
+ // Try to sanitize common JSON issues before parsing
244
+ let argsStr = toolCall.function.arguments;
245
+ // Remove trailing commas before ] or }
246
+ argsStr = argsStr.replace(/,(\s*[}\]])/g, '$1');
247
+ // Fix common LLM JSON issues:
248
+ // Remove control characters (except \n, \r, \t which are valid in JSON strings)
249
+ argsStr = argsStr.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
250
+ functionArgs = JSON.parse(argsStr);
243
251
  }
244
252
  catch (parseError) {
245
253
  // LLM may generate invalid JSON with unescaped characters in long content
246
- // Return a helpful error message
247
254
  const errorMsg = parseError instanceof Error ? parseError.message : 'Unknown parse error';
255
+ const hint = errorMsg.includes('position')
256
+ ? 'The content may contain unescaped quotes or special characters. Try updating with shorter/simpler content, or update one field at a time.'
257
+ : 'Please retry with simpler content.';
248
258
  return {
249
259
  tool_call_id: toolCall.id,
250
260
  content: JSON.stringify({
251
261
  success: false,
252
- error: `Invalid tool arguments: ${errorMsg}. Please retry with simpler content.`
262
+ error: `Invalid tool arguments: ${errorMsg}. ${hint}`
253
263
  })
254
264
  };
255
265
  }
@@ -299,38 +309,89 @@ async function executeToolCalls(toolCalls) {
299
309
  });
300
310
  return Promise.all(resultPromises);
301
311
  }
302
- // Proposals tool is READ-ONLY - only list and get operations are supported
303
- async function executeProposalsToolDirect(args, storage) {
304
- const { operation, id, filters } = args;
312
+ // Proposals tool - full CRUD operations using @lovelybunch/core
313
+ async function executeProposalsToolDirect(args, _storage) {
314
+ const { operation, id, filters, proposal, updates } = args;
305
315
  try {
306
316
  switch (operation) {
307
317
  case 'list': {
308
- const proposals = await storage.listCPs(filters || {});
318
+ const proposals = await listProposals(filters || {});
309
319
  return {
310
320
  success: true,
311
321
  data: proposals,
312
- message: `Found ${proposals.length} proposals`
322
+ count: proposals.length,
323
+ message: `Found ${proposals.length} tasks`
313
324
  };
314
325
  }
315
326
  case 'get': {
316
327
  if (!id) {
317
- return { success: false, error: 'Proposal ID is required for get operation' };
328
+ return { success: false, error: 'Task ID is required for get operation' };
318
329
  }
319
- const proposal = await storage.getCP(id);
330
+ const result = await getProposal(id);
331
+ if (!result) {
332
+ return { success: false, error: 'Task not found' };
333
+ }
334
+ return {
335
+ success: true,
336
+ data: result,
337
+ message: `Retrieved task ${id}`
338
+ };
339
+ }
340
+ case 'create': {
320
341
  if (!proposal) {
321
- return { success: false, error: 'Proposal not found' };
342
+ return { success: false, error: 'Task data is required for create operation' };
322
343
  }
344
+ const created = await createProposal(proposal);
323
345
  return {
324
346
  success: true,
325
- data: proposal,
326
- message: `Retrieved proposal ${id}`
347
+ data: created,
348
+ message: `Created task ${created.id}`
349
+ };
350
+ }
351
+ case 'update': {
352
+ if (!id) {
353
+ return { success: false, error: 'Task ID is required for update operation' };
354
+ }
355
+ const updateData = updates || proposal;
356
+ if (!updateData) {
357
+ return { success: false, error: 'Update data is required for update operation' };
358
+ }
359
+ const updated = await updateProposal(id, updateData);
360
+ return {
361
+ success: true,
362
+ data: updated,
363
+ message: `Updated task ${id}`
364
+ };
365
+ }
366
+ case 'delete': {
367
+ if (!id) {
368
+ return { success: false, error: 'Task ID is required for delete operation' };
369
+ }
370
+ const deleted = await deleteProposal(id);
371
+ if (!deleted) {
372
+ return { success: false, error: 'Task not found' };
373
+ }
374
+ return {
375
+ success: true,
376
+ message: `Deleted task ${id}`
327
377
  };
328
378
  }
329
379
  default:
330
- return { success: false, error: `Proposals are read-only. Only 'list' and 'get' operations are supported. To create proposals, use a coding agent (Claude Code, Cursor, etc.) or the Proposals UI.` };
380
+ return { success: false, error: `Unknown operation: ${operation}. Supported operations: list, get, create, update, delete` };
331
381
  }
332
382
  }
333
383
  catch (error) {
384
+ // Handle Zod validation errors specially
385
+ if (error instanceof ZodError) {
386
+ return {
387
+ success: false,
388
+ error: 'Validation failed',
389
+ details: error.issues.map(e => ({
390
+ path: e.path.join('.'),
391
+ message: e.message
392
+ }))
393
+ };
394
+ }
334
395
  console.error('Error executing proposals tool:', error);
335
396
  return { success: false, error: error.message || 'Tool execution failed' };
336
397
  }
@@ -3,8 +3,9 @@ import { homedir } from 'os';
3
3
  import { join, resolve as pathResolve, basename } from 'path';
4
4
  import { existsSync, readFileSync, promises as fs, createReadStream } from 'fs';
5
5
  import readline from 'readline';
6
- import { getLogsDir } from '@lovelybunch/core';
7
- import { proposalsReadOnlyTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool } from '@lovelybunch/mcp';
6
+ import { ZodError } from 'zod';
7
+ import { getLogsDir, listProposals, getProposal, createProposal, updateProposal, deleteProposal, } from '@lovelybunch/core';
8
+ import { proposalsFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool } from '@lovelybunch/mcp';
8
9
  import matter from 'gray-matter';
9
10
  import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
10
11
  // Function to get global config API key as fallback
@@ -93,11 +94,11 @@ export async function POST(c) {
93
94
  ...toolResultMessages
94
95
  ];
95
96
  // Prepare tools for potential additional tool calls
96
- // Note: proposals is read-only, knowledge/project/architecture are read+write, events is read-only
97
+ // Note: proposals/knowledge/project/architecture are read+write, events is read-only
97
98
  const tools = [
98
99
  {
99
100
  type: "function",
100
- function: proposalsReadOnlyTool
101
+ function: proposalsFullTool
101
102
  },
102
103
  {
103
104
  type: "function",
@@ -242,6 +243,9 @@ async function executeToolCalls(toolCalls) {
242
243
  let argsStr = toolCall.function.arguments;
243
244
  // Remove trailing commas before ] or }
244
245
  argsStr = argsStr.replace(/,(\s*[}\]])/g, '$1');
246
+ // Fix common LLM JSON issues:
247
+ // 1. Remove control characters (except \n, \r, \t which are valid in JSON strings)
248
+ argsStr = argsStr.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
245
249
  // Try parsing the sanitized string
246
250
  functionArgs = JSON.parse(argsStr);
247
251
  }
@@ -255,11 +259,16 @@ async function executeToolCalls(toolCalls) {
255
259
  arguments: toolCall.function?.arguments?.substring(0, 500),
256
260
  error: parseError instanceof Error ? parseError.message : 'Unknown parse error'
257
261
  });
262
+ // Provide a more helpful error message
263
+ const errorMsg = parseError instanceof Error ? parseError.message : 'JSON parse error';
264
+ const hint = errorMsg.includes('position')
265
+ ? 'The content may contain unescaped quotes or special characters. Try updating with shorter/simpler content, or update one field at a time.'
266
+ : 'Please retry with simpler content.';
258
267
  return {
259
268
  tool_call_id: toolCall.id,
260
269
  content: JSON.stringify({
261
270
  success: false,
262
- error: `Invalid tool arguments: ${parseError instanceof Error ? parseError.message : 'JSON parse error'}. Please retry with simpler content.`
271
+ error: `Invalid tool arguments: ${errorMsg}. ${hint}`
263
272
  })
264
273
  };
265
274
  }
@@ -304,38 +313,89 @@ async function executeToolCalls(toolCalls) {
304
313
  });
305
314
  return Promise.all(resultPromises);
306
315
  }
307
- // Proposals tool is READ-ONLY - only list and get operations are supported
308
- async function executeProposalsToolDirect(args, storage) {
309
- const { operation, id, filters } = args;
316
+ // Proposals tool - full CRUD operations using @lovelybunch/core
317
+ async function executeProposalsToolDirect(args, _storage) {
318
+ const { operation, id, filters, proposal, updates } = args;
310
319
  try {
311
320
  switch (operation) {
312
321
  case 'list': {
313
- const proposals = await storage.listCPs(filters || {});
322
+ const proposals = await listProposals(filters || {});
314
323
  return {
315
324
  success: true,
316
325
  data: proposals,
317
- message: `Found ${proposals.length} proposals`
326
+ count: proposals.length,
327
+ message: `Found ${proposals.length} tasks`
318
328
  };
319
329
  }
320
330
  case 'get': {
321
331
  if (!id) {
322
- return { success: false, error: 'Proposal ID is required for get operation' };
332
+ return { success: false, error: 'Task ID is required for get operation' };
323
333
  }
324
- const proposal = await storage.getCP(id);
334
+ const result = await getProposal(id);
335
+ if (!result) {
336
+ return { success: false, error: 'Task not found' };
337
+ }
338
+ return {
339
+ success: true,
340
+ data: result,
341
+ message: `Retrieved task ${id}`
342
+ };
343
+ }
344
+ case 'create': {
325
345
  if (!proposal) {
326
- return { success: false, error: 'Proposal not found' };
346
+ return { success: false, error: 'Task data is required for create operation' };
347
+ }
348
+ const created = await createProposal(proposal);
349
+ return {
350
+ success: true,
351
+ data: created,
352
+ message: `Created task ${created.id}`
353
+ };
354
+ }
355
+ case 'update': {
356
+ if (!id) {
357
+ return { success: false, error: 'Task ID is required for update operation' };
358
+ }
359
+ const updateData = updates || proposal;
360
+ if (!updateData) {
361
+ return { success: false, error: 'Update data is required for update operation' };
362
+ }
363
+ const updated = await updateProposal(id, updateData);
364
+ return {
365
+ success: true,
366
+ data: updated,
367
+ message: `Updated task ${id}`
368
+ };
369
+ }
370
+ case 'delete': {
371
+ if (!id) {
372
+ return { success: false, error: 'Task ID is required for delete operation' };
373
+ }
374
+ const deleted = await deleteProposal(id);
375
+ if (!deleted) {
376
+ return { success: false, error: 'Task not found' };
327
377
  }
328
378
  return {
329
379
  success: true,
330
- data: proposal,
331
- message: `Retrieved proposal ${id}`
380
+ message: `Deleted task ${id}`
332
381
  };
333
382
  }
334
383
  default:
335
- return { success: false, error: `Proposals are read-only. Only 'list' and 'get' operations are supported. To create proposals, use a coding agent (Claude Code, Cursor, etc.) or the Proposals UI.` };
384
+ return { success: false, error: `Unknown operation: ${operation}. Supported operations: list, get, create, update, delete` };
336
385
  }
337
386
  }
338
387
  catch (error) {
388
+ // Handle Zod validation errors specially
389
+ if (error instanceof ZodError) {
390
+ return {
391
+ success: false,
392
+ error: 'Validation failed',
393
+ details: error.issues.map(e => ({
394
+ path: e.path.join('.'),
395
+ message: e.message
396
+ }))
397
+ };
398
+ }
339
399
  console.error('Error executing proposals tool:', error);
340
400
  return { success: false, error: error.message || 'Tool execution failed' };
341
401
  }