@corbat-tech/coco 2.5.1 → 2.5.3

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.
package/dist/index.js CHANGED
@@ -17,8 +17,8 @@ import { promisify } from 'util';
17
17
  import { z } from 'zod';
18
18
  import { Logger } from 'tslog';
19
19
  import Anthropic from '@anthropic-ai/sdk';
20
- import OpenAI from 'openai';
21
20
  import { jsonrepair } from 'jsonrepair';
21
+ import OpenAI from 'openai';
22
22
  import 'http';
23
23
  import { GoogleGenerativeAI, FunctionCallingMode } from '@google/generative-ai';
24
24
  import JSON5 from 'json5';
@@ -8401,6 +8401,12 @@ var ToolRegistry = class {
8401
8401
  return `${field} (${issue.message.toLowerCase()})`;
8402
8402
  });
8403
8403
  errorMessage = `Invalid tool input \u2014 ${fields.join(", ")}`;
8404
+ const allUndefined = error.issues.every(
8405
+ (i) => i.message.toLowerCase().includes("received undefined")
8406
+ );
8407
+ if (allUndefined && error.issues.length > 1) {
8408
+ errorMessage += ". All parameters are missing \u2014 this is likely a JSON serialization error on our side. Please retry with the same arguments.";
8409
+ }
8404
8410
  } else {
8405
8411
  const rawMessage = error instanceof Error ? error.message : String(error);
8406
8412
  errorMessage = humanizeError(rawMessage, name);
@@ -11784,6 +11790,20 @@ var AnthropicProvider = class {
11784
11790
  if (event.type === "content_block_start") {
11785
11791
  const contentBlock = event.content_block;
11786
11792
  if (contentBlock.type === "tool_use") {
11793
+ if (currentToolCall) {
11794
+ getLogger().warn(
11795
+ `[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
11796
+ );
11797
+ try {
11798
+ currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11799
+ } catch {
11800
+ currentToolCall.input = {};
11801
+ }
11802
+ yield {
11803
+ type: "tool_use_end",
11804
+ toolCall: { ...currentToolCall }
11805
+ };
11806
+ }
11787
11807
  currentToolCall = {
11788
11808
  id: contentBlock.id,
11789
11809
  name: contentBlock.name
@@ -11813,10 +11833,21 @@ var AnthropicProvider = class {
11813
11833
  try {
11814
11834
  currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
11815
11835
  } catch {
11816
- getLogger().warn(
11817
- `Failed to parse tool call arguments: ${currentToolInputJson?.slice(0, 100)}`
11818
- );
11819
- currentToolCall.input = {};
11836
+ let repaired = false;
11837
+ if (currentToolInputJson) {
11838
+ try {
11839
+ currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
11840
+ repaired = true;
11841
+ getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
11842
+ } catch {
11843
+ }
11844
+ }
11845
+ if (!repaired) {
11846
+ getLogger().warn(
11847
+ `Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
11848
+ );
11849
+ currentToolCall.input = {};
11850
+ }
11820
11851
  }
11821
11852
  yield {
11822
11853
  type: "tool_use_end",
@@ -12366,6 +12397,30 @@ var OpenAIProvider = class {
12366
12397
  }
12367
12398
  };
12368
12399
  const timeoutInterval = setInterval(checkTimeout, 5e3);
12400
+ const providerName = this.name;
12401
+ const parseArguments = (builder) => {
12402
+ let input = {};
12403
+ try {
12404
+ input = builder.arguments ? JSON.parse(builder.arguments) : {};
12405
+ } catch (error) {
12406
+ console.warn(
12407
+ `[${providerName}] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 300)}`
12408
+ );
12409
+ try {
12410
+ if (builder.arguments) {
12411
+ const repaired = jsonrepair(builder.arguments);
12412
+ input = JSON.parse(repaired);
12413
+ console.log(`[${providerName}] \u2713 Successfully repaired JSON for ${builder.name}`);
12414
+ }
12415
+ } catch {
12416
+ console.error(
12417
+ `[${providerName}] Cannot repair JSON for ${builder.name}, using empty object`
12418
+ );
12419
+ console.error(`[${providerName}] Original error:`, error);
12420
+ }
12421
+ }
12422
+ return input;
12423
+ };
12369
12424
  try {
12370
12425
  for await (const chunk of stream) {
12371
12426
  const delta = chunk.choices[0]?.delta;
@@ -12377,7 +12432,7 @@ var OpenAIProvider = class {
12377
12432
  }
12378
12433
  if (delta?.tool_calls) {
12379
12434
  for (const toolCallDelta of delta.tool_calls) {
12380
- const index = toolCallDelta.index;
12435
+ const index = toolCallDelta.index ?? toolCallBuilders.size;
12381
12436
  if (!toolCallBuilders.has(index)) {
12382
12437
  toolCallBuilders.set(index, {
12383
12438
  id: toolCallDelta.id ?? "",
@@ -12412,34 +12467,28 @@ var OpenAIProvider = class {
12412
12467
  }
12413
12468
  }
12414
12469
  }
12415
- }
12416
- for (const [, builder] of toolCallBuilders) {
12417
- let input = {};
12418
- try {
12419
- input = builder.arguments ? JSON.parse(builder.arguments) : {};
12420
- } catch (error) {
12421
- console.warn(
12422
- `[${this.name}] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 100)}`
12423
- );
12424
- try {
12425
- if (builder.arguments) {
12426
- const repaired = jsonrepair(builder.arguments);
12427
- input = JSON.parse(repaired);
12428
- console.log(`[${this.name}] \u2713 Successfully repaired JSON for ${builder.name}`);
12429
- }
12430
- } catch {
12431
- console.error(
12432
- `[${this.name}] Cannot repair JSON for ${builder.name}, using empty object`
12433
- );
12434
- console.error(`[${this.name}] Original error:`, error);
12470
+ const finishReason = chunk.choices[0]?.finish_reason;
12471
+ if (finishReason && toolCallBuilders.size > 0) {
12472
+ for (const [, builder] of toolCallBuilders) {
12473
+ yield {
12474
+ type: "tool_use_end",
12475
+ toolCall: {
12476
+ id: builder.id,
12477
+ name: builder.name,
12478
+ input: parseArguments(builder)
12479
+ }
12480
+ };
12435
12481
  }
12482
+ toolCallBuilders.clear();
12436
12483
  }
12484
+ }
12485
+ for (const [, builder] of toolCallBuilders) {
12437
12486
  yield {
12438
12487
  type: "tool_use_end",
12439
12488
  toolCall: {
12440
12489
  id: builder.id,
12441
12490
  name: builder.name,
12442
- input
12491
+ input: parseArguments(builder)
12443
12492
  }
12444
12493
  };
12445
12494
  }
@@ -12608,7 +12657,8 @@ var OpenAIProvider = class {
12608
12657
  if (msg.role === "system") {
12609
12658
  result.push({ role: "system", content: this.contentToString(msg.content) });
12610
12659
  } else if (msg.role === "user") {
12611
- if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
12660
+ if (Array.isArray(msg.content) && msg.content.some((b) => b.type === "tool_result")) {
12661
+ const textParts = [];
12612
12662
  for (const block of msg.content) {
12613
12663
  if (block.type === "tool_result") {
12614
12664
  const toolResult = block;
@@ -12617,8 +12667,16 @@ var OpenAIProvider = class {
12617
12667
  tool_call_id: toolResult.tool_use_id,
12618
12668
  content: toolResult.content
12619
12669
  });
12670
+ } else if (block.type === "text") {
12671
+ textParts.push(block.text);
12620
12672
  }
12621
12673
  }
12674
+ if (textParts.length > 0) {
12675
+ console.warn(
12676
+ `[${this.name}] User message has mixed tool_result and text blocks \u2014 text emitted as a separate user message.`
12677
+ );
12678
+ result.push({ role: "user", content: textParts.join("") });
12679
+ }
12622
12680
  } else if (Array.isArray(msg.content) && msg.content.some((b) => b.type === "image")) {
12623
12681
  const parts = [];
12624
12682
  for (const block of msg.content) {
@@ -12663,6 +12721,10 @@ var OpenAIProvider = class {
12663
12721
  arguments: JSON.stringify(block.input)
12664
12722
  }
12665
12723
  });
12724
+ } else {
12725
+ console.warn(
12726
+ `[${this.name}] Unexpected block type '${block.type}' in assistant message \u2014 dropping. This may indicate a message history corruption.`
12727
+ );
12666
12728
  }
12667
12729
  }
12668
12730
  if (textParts.length > 0) {
@@ -15139,7 +15201,7 @@ function escapeRegex(str) {
15139
15201
  }
15140
15202
  var DEFAULT_TIMEOUT_MS = 12e4;
15141
15203
  var MAX_OUTPUT_SIZE = 1024 * 1024;
15142
- var DANGEROUS_PATTERNS = [
15204
+ var DANGEROUS_PATTERNS_FULL = [
15143
15205
  /\brm\s+-rf\s+\/(?!\w)/,
15144
15206
  // rm -rf / (root)
15145
15207
  /\bsudo\s+rm\s+-rf/,
@@ -15152,14 +15214,6 @@ var DANGEROUS_PATTERNS = [
15152
15214
  // Format filesystem
15153
15215
  /\bformat\s+/,
15154
15216
  // Windows format
15155
- /`[^`]+`/,
15156
- // Backtick command substitution
15157
- /\$\([^)]+\)/,
15158
- // $() command substitution
15159
- /\beval\s+/,
15160
- // eval command
15161
- /\bsource\s+/,
15162
- // source command (can execute arbitrary scripts)
15163
15217
  />\s*\/etc\//,
15164
15218
  // Write to /etc
15165
15219
  />\s*\/root\//,
@@ -15173,6 +15227,25 @@ var DANGEROUS_PATTERNS = [
15173
15227
  /\bwget\s+.*\|\s*(ba)?sh/
15174
15228
  // wget | sh pattern
15175
15229
  ];
15230
+ var DANGEROUS_PATTERNS_SHELL_ONLY = [
15231
+ /`[^`]+`/,
15232
+ // Backtick command substitution
15233
+ /\$\([^)]+\)/,
15234
+ // $() command substitution
15235
+ /\beval\s+/,
15236
+ // eval command (shell eval, not JS eval())
15237
+ /\bsource\s+/
15238
+ // source command (can execute arbitrary scripts)
15239
+ ];
15240
+ function getShellCommandPart(command) {
15241
+ const firstNewline = command.indexOf("\n");
15242
+ if (firstNewline === -1) return command;
15243
+ const firstLine = command.slice(0, firstNewline);
15244
+ if (/<<-?\s*['"]?\w/.test(firstLine)) {
15245
+ return firstLine;
15246
+ }
15247
+ return command;
15248
+ }
15176
15249
  var SAFE_ENV_VARS = /* @__PURE__ */ new Set([
15177
15250
  // System info (non-sensitive)
15178
15251
  "PATH",
@@ -15239,13 +15312,21 @@ Examples:
15239
15312
  env: z.record(z.string(), z.string()).optional().describe("Environment variables")
15240
15313
  }),
15241
15314
  async execute({ command, cwd, timeout, env: env2 }) {
15242
- for (const pattern of DANGEROUS_PATTERNS) {
15315
+ const shellPart = getShellCommandPart(command);
15316
+ for (const pattern of DANGEROUS_PATTERNS_FULL) {
15243
15317
  if (pattern.test(command)) {
15244
15318
  throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15245
15319
  tool: "bash_exec"
15246
15320
  });
15247
15321
  }
15248
15322
  }
15323
+ for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
15324
+ if (pattern.test(shellPart)) {
15325
+ throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15326
+ tool: "bash_exec"
15327
+ });
15328
+ }
15329
+ }
15249
15330
  const startTime = performance.now();
15250
15331
  const timeoutMs = timeout ?? DEFAULT_TIMEOUT_MS;
15251
15332
  const { CommandHeartbeat: CommandHeartbeat2 } = await Promise.resolve().then(() => (init_heartbeat(), heartbeat_exports));
@@ -15327,13 +15408,21 @@ Examples:
15327
15408
  env: z.record(z.string(), z.string()).optional().describe("Environment variables")
15328
15409
  }),
15329
15410
  async execute({ command, cwd, env: env2 }) {
15330
- for (const pattern of DANGEROUS_PATTERNS) {
15411
+ const shellPart = getShellCommandPart(command);
15412
+ for (const pattern of DANGEROUS_PATTERNS_FULL) {
15331
15413
  if (pattern.test(command)) {
15332
15414
  throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15333
15415
  tool: "bash_background"
15334
15416
  });
15335
15417
  }
15336
15418
  }
15419
+ for (const pattern of DANGEROUS_PATTERNS_SHELL_ONLY) {
15420
+ if (pattern.test(shellPart)) {
15421
+ throw new ToolError(`Potentially dangerous command blocked: ${command.slice(0, 100)}`, {
15422
+ tool: "bash_background"
15423
+ });
15424
+ }
15425
+ }
15337
15426
  try {
15338
15427
  const filteredEnv = {};
15339
15428
  for (const [key, value] of Object.entries(process.env)) {
@@ -20915,7 +21004,7 @@ Examples:
20915
21004
  });
20916
21005
  var imageTools = [readImageTool];
20917
21006
  var path31 = await import('path');
20918
- var DANGEROUS_PATTERNS2 = [
21007
+ var DANGEROUS_PATTERNS = [
20919
21008
  /\bDROP\s+(?:TABLE|DATABASE|INDEX|VIEW)\b/i,
20920
21009
  /\bTRUNCATE\b/i,
20921
21010
  /\bALTER\s+TABLE\b/i,
@@ -20925,7 +21014,7 @@ var DANGEROUS_PATTERNS2 = [
20925
21014
  /\bCREATE\s+(?:TABLE|DATABASE|INDEX)\b/i
20926
21015
  ];
20927
21016
  function isDangerousSql(sql2) {
20928
- return DANGEROUS_PATTERNS2.some((pattern) => pattern.test(sql2));
21017
+ return DANGEROUS_PATTERNS.some((pattern) => pattern.test(sql2));
20929
21018
  }
20930
21019
  var sqlQueryTool = defineTool({
20931
21020
  name: "sql_query",