@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/cli/index.js +970 -77
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +130 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
11817
|
-
|
|
11818
|
-
|
|
11819
|
-
|
|
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
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
21017
|
+
return DANGEROUS_PATTERNS.some((pattern) => pattern.test(sql2));
|
|
20929
21018
|
}
|
|
20930
21019
|
var sqlQueryTool = defineTool({
|
|
20931
21020
|
name: "sql_query",
|