@corbat-tech/coco 2.23.0 → 2.24.0
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/README.md +110 -372
- package/dist/cli/index.js +601 -217
- package/dist/cli/index.js.map +1 -1
- package/dist/index.js +604 -205
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/cli/index.js
CHANGED
|
@@ -1425,6 +1425,150 @@ var init_anthropic = __esm({
|
|
|
1425
1425
|
};
|
|
1426
1426
|
}
|
|
1427
1427
|
});
|
|
1428
|
+
function getSingleBuilderKey(builders) {
|
|
1429
|
+
return builders.size === 1 ? Array.from(builders.keys())[0] ?? null : null;
|
|
1430
|
+
}
|
|
1431
|
+
function parseToolCallArguments(args, providerName) {
|
|
1432
|
+
try {
|
|
1433
|
+
return args ? JSON.parse(args) : {};
|
|
1434
|
+
} catch {
|
|
1435
|
+
try {
|
|
1436
|
+
if (args) {
|
|
1437
|
+
const repaired = jsonrepair(args);
|
|
1438
|
+
return JSON.parse(repaired);
|
|
1439
|
+
}
|
|
1440
|
+
} catch {
|
|
1441
|
+
console.error(`[${providerName}] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
1442
|
+
}
|
|
1443
|
+
return {};
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
var ChatToolCallAssembler, ResponsesToolCallAssembler;
|
|
1447
|
+
var init_tool_call_normalizer = __esm({
|
|
1448
|
+
"src/providers/tool-call-normalizer.ts"() {
|
|
1449
|
+
ChatToolCallAssembler = class {
|
|
1450
|
+
builders = /* @__PURE__ */ new Map();
|
|
1451
|
+
lastBuilderKey = null;
|
|
1452
|
+
consume(delta) {
|
|
1453
|
+
const key = typeof delta.index === "number" ? `index:${delta.index}` : typeof delta.id === "string" && delta.id.length > 0 ? `id:${delta.id}` : getSingleBuilderKey(this.builders) ?? this.lastBuilderKey ?? `fallback:${this.builders.size}`;
|
|
1454
|
+
let started;
|
|
1455
|
+
if (!this.builders.has(key)) {
|
|
1456
|
+
const initialId = delta.id ?? "";
|
|
1457
|
+
const initialName = delta.function?.name ?? "";
|
|
1458
|
+
this.builders.set(key, { id: initialId, name: initialName, arguments: "" });
|
|
1459
|
+
started = {
|
|
1460
|
+
id: initialId || void 0,
|
|
1461
|
+
name: initialName || void 0
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
const builder = this.builders.get(key);
|
|
1465
|
+
this.lastBuilderKey = key;
|
|
1466
|
+
if (delta.id) {
|
|
1467
|
+
builder.id = delta.id;
|
|
1468
|
+
}
|
|
1469
|
+
if (delta.function?.name) {
|
|
1470
|
+
builder.name = delta.function.name;
|
|
1471
|
+
}
|
|
1472
|
+
const text13 = delta.function?.arguments ?? "";
|
|
1473
|
+
if (!text13) return { started };
|
|
1474
|
+
builder.arguments += text13;
|
|
1475
|
+
return {
|
|
1476
|
+
started,
|
|
1477
|
+
argumentDelta: {
|
|
1478
|
+
id: builder.id,
|
|
1479
|
+
name: builder.name,
|
|
1480
|
+
text: text13
|
|
1481
|
+
}
|
|
1482
|
+
};
|
|
1483
|
+
}
|
|
1484
|
+
finalizeAll(providerName) {
|
|
1485
|
+
const result = [];
|
|
1486
|
+
for (const builder of this.builders.values()) {
|
|
1487
|
+
result.push({
|
|
1488
|
+
id: builder.id,
|
|
1489
|
+
name: builder.name,
|
|
1490
|
+
input: parseToolCallArguments(builder.arguments, providerName)
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
this.builders.clear();
|
|
1494
|
+
this.lastBuilderKey = null;
|
|
1495
|
+
return result;
|
|
1496
|
+
}
|
|
1497
|
+
};
|
|
1498
|
+
ResponsesToolCallAssembler = class {
|
|
1499
|
+
builders = /* @__PURE__ */ new Map();
|
|
1500
|
+
outputIndexToBuilderKey = /* @__PURE__ */ new Map();
|
|
1501
|
+
onOutputItemAdded(event) {
|
|
1502
|
+
const item = event.item;
|
|
1503
|
+
if (!item || item.type !== "function_call") return null;
|
|
1504
|
+
const callId = item.call_id ?? "";
|
|
1505
|
+
const itemKey = item.id ?? callId;
|
|
1506
|
+
this.builders.set(itemKey, {
|
|
1507
|
+
callId,
|
|
1508
|
+
name: item.name ?? "",
|
|
1509
|
+
arguments: item.arguments ?? ""
|
|
1510
|
+
});
|
|
1511
|
+
if (typeof event.output_index === "number") {
|
|
1512
|
+
this.outputIndexToBuilderKey.set(event.output_index, itemKey);
|
|
1513
|
+
}
|
|
1514
|
+
return {
|
|
1515
|
+
id: callId,
|
|
1516
|
+
name: item.name ?? ""
|
|
1517
|
+
};
|
|
1518
|
+
}
|
|
1519
|
+
onArgumentsDelta(event) {
|
|
1520
|
+
const builderKey = this.resolveBuilderKey(event.item_id, event.output_index);
|
|
1521
|
+
if (!builderKey) return;
|
|
1522
|
+
const builder = this.builders.get(builderKey);
|
|
1523
|
+
if (!builder) return;
|
|
1524
|
+
builder.arguments += event.delta ?? "";
|
|
1525
|
+
}
|
|
1526
|
+
onArgumentsDone(event, providerName) {
|
|
1527
|
+
const builderKey = this.resolveBuilderKey(event.item_id, event.output_index);
|
|
1528
|
+
if (!builderKey) return null;
|
|
1529
|
+
const builder = this.builders.get(builderKey);
|
|
1530
|
+
if (!builder) return null;
|
|
1531
|
+
const toolCall = {
|
|
1532
|
+
id: builder.callId,
|
|
1533
|
+
name: builder.name,
|
|
1534
|
+
input: parseToolCallArguments(event.arguments ?? builder.arguments, providerName)
|
|
1535
|
+
};
|
|
1536
|
+
this.deleteBuilder(builderKey);
|
|
1537
|
+
return toolCall;
|
|
1538
|
+
}
|
|
1539
|
+
finalizeAll(providerName) {
|
|
1540
|
+
const calls = [];
|
|
1541
|
+
for (const builder of this.builders.values()) {
|
|
1542
|
+
calls.push({
|
|
1543
|
+
id: builder.callId,
|
|
1544
|
+
name: builder.name,
|
|
1545
|
+
input: parseToolCallArguments(builder.arguments, providerName)
|
|
1546
|
+
});
|
|
1547
|
+
}
|
|
1548
|
+
this.builders.clear();
|
|
1549
|
+
this.outputIndexToBuilderKey.clear();
|
|
1550
|
+
return calls;
|
|
1551
|
+
}
|
|
1552
|
+
resolveBuilderKey(itemId, outputIndex) {
|
|
1553
|
+
if (itemId && this.builders.has(itemId)) {
|
|
1554
|
+
return itemId;
|
|
1555
|
+
}
|
|
1556
|
+
if (typeof outputIndex === "number") {
|
|
1557
|
+
return this.outputIndexToBuilderKey.get(outputIndex) ?? null;
|
|
1558
|
+
}
|
|
1559
|
+
return getSingleBuilderKey(this.builders);
|
|
1560
|
+
}
|
|
1561
|
+
deleteBuilder(builderKey) {
|
|
1562
|
+
this.builders.delete(builderKey);
|
|
1563
|
+
for (const [idx, key] of this.outputIndexToBuilderKey.entries()) {
|
|
1564
|
+
if (key === builderKey) {
|
|
1565
|
+
this.outputIndexToBuilderKey.delete(idx);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
};
|
|
1570
|
+
}
|
|
1571
|
+
});
|
|
1428
1572
|
function needsResponsesApi(model) {
|
|
1429
1573
|
return model.includes("codex") || model.startsWith("gpt-5") || model.startsWith("o4-") || model === "o3";
|
|
1430
1574
|
}
|
|
@@ -1464,6 +1608,7 @@ var init_openai = __esm({
|
|
|
1464
1608
|
"src/providers/openai.ts"() {
|
|
1465
1609
|
init_errors();
|
|
1466
1610
|
init_retry();
|
|
1611
|
+
init_tool_call_normalizer();
|
|
1467
1612
|
DEFAULT_MODEL2 = "gpt-5.4-codex";
|
|
1468
1613
|
CONTEXT_WINDOWS2 = {
|
|
1469
1614
|
// OpenAI models
|
|
@@ -1791,7 +1936,7 @@ var init_openai = __esm({
|
|
|
1791
1936
|
const stream = await this.client.chat.completions.create(
|
|
1792
1937
|
requestParams
|
|
1793
1938
|
);
|
|
1794
|
-
const
|
|
1939
|
+
const toolCallAssembler = new ChatToolCallAssembler();
|
|
1795
1940
|
const streamTimeout = this.config.timeout ?? 12e4;
|
|
1796
1941
|
let lastActivityTime = Date.now();
|
|
1797
1942
|
const timeoutController = new AbortController();
|
|
@@ -1805,30 +1950,6 @@ var init_openai = __esm({
|
|
|
1805
1950
|
timeoutController.signal.addEventListener("abort", () => stream.controller.abort(), {
|
|
1806
1951
|
once: true
|
|
1807
1952
|
});
|
|
1808
|
-
const providerName = this.name;
|
|
1809
|
-
const parseArguments2 = (builder) => {
|
|
1810
|
-
let input = {};
|
|
1811
|
-
try {
|
|
1812
|
-
input = builder.arguments ? JSON.parse(builder.arguments) : {};
|
|
1813
|
-
} catch (error) {
|
|
1814
|
-
console.warn(
|
|
1815
|
-
`[${providerName}] Failed to parse tool call arguments for ${builder.name}: ${builder.arguments?.slice(0, 300)}`
|
|
1816
|
-
);
|
|
1817
|
-
try {
|
|
1818
|
-
if (builder.arguments) {
|
|
1819
|
-
const repaired = jsonrepair(builder.arguments);
|
|
1820
|
-
input = JSON.parse(repaired);
|
|
1821
|
-
console.log(`[${providerName}] \u2713 Successfully repaired JSON for ${builder.name}`);
|
|
1822
|
-
}
|
|
1823
|
-
} catch {
|
|
1824
|
-
console.error(
|
|
1825
|
-
`[${providerName}] Cannot repair JSON for ${builder.name}, using empty object`
|
|
1826
|
-
);
|
|
1827
|
-
console.error(`[${providerName}] Original error:`, error);
|
|
1828
|
-
}
|
|
1829
|
-
}
|
|
1830
|
-
return input;
|
|
1831
|
-
};
|
|
1832
1953
|
try {
|
|
1833
1954
|
let streamStopReason;
|
|
1834
1955
|
for await (const chunk of stream) {
|
|
@@ -1841,37 +1962,31 @@ var init_openai = __esm({
|
|
|
1841
1962
|
}
|
|
1842
1963
|
if (delta?.tool_calls) {
|
|
1843
1964
|
for (const toolCallDelta of delta.tool_calls) {
|
|
1844
|
-
const
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
name: toolCallDelta.function?.name ??
|
|
1849
|
-
arguments:
|
|
1850
|
-
}
|
|
1965
|
+
const consumed = toolCallAssembler.consume({
|
|
1966
|
+
index: toolCallDelta.index,
|
|
1967
|
+
id: toolCallDelta.id ?? void 0,
|
|
1968
|
+
function: {
|
|
1969
|
+
name: toolCallDelta.function?.name ?? void 0,
|
|
1970
|
+
arguments: toolCallDelta.function?.arguments ?? void 0
|
|
1971
|
+
}
|
|
1972
|
+
});
|
|
1973
|
+
if (consumed.started) {
|
|
1851
1974
|
yield {
|
|
1852
1975
|
type: "tool_use_start",
|
|
1853
1976
|
toolCall: {
|
|
1854
|
-
id:
|
|
1855
|
-
name:
|
|
1977
|
+
id: consumed.started.id,
|
|
1978
|
+
name: consumed.started.name
|
|
1856
1979
|
}
|
|
1857
1980
|
};
|
|
1858
1981
|
}
|
|
1859
|
-
|
|
1860
|
-
if (toolCallDelta.id) {
|
|
1861
|
-
builder.id = toolCallDelta.id;
|
|
1862
|
-
}
|
|
1863
|
-
if (toolCallDelta.function?.name) {
|
|
1864
|
-
builder.name = toolCallDelta.function.name;
|
|
1865
|
-
}
|
|
1866
|
-
if (toolCallDelta.function?.arguments) {
|
|
1867
|
-
builder.arguments += toolCallDelta.function.arguments;
|
|
1982
|
+
if (consumed.argumentDelta) {
|
|
1868
1983
|
yield {
|
|
1869
1984
|
type: "tool_use_delta",
|
|
1870
1985
|
toolCall: {
|
|
1871
|
-
id:
|
|
1872
|
-
name:
|
|
1986
|
+
id: consumed.argumentDelta.id,
|
|
1987
|
+
name: consumed.argumentDelta.name
|
|
1873
1988
|
},
|
|
1874
|
-
text:
|
|
1989
|
+
text: consumed.argumentDelta.text
|
|
1875
1990
|
};
|
|
1876
1991
|
}
|
|
1877
1992
|
}
|
|
@@ -1880,27 +1995,26 @@ var init_openai = __esm({
|
|
|
1880
1995
|
if (finishReason) {
|
|
1881
1996
|
streamStopReason = this.mapFinishReason(finishReason);
|
|
1882
1997
|
}
|
|
1883
|
-
if (finishReason
|
|
1884
|
-
for (const
|
|
1998
|
+
if (finishReason) {
|
|
1999
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
1885
2000
|
yield {
|
|
1886
2001
|
type: "tool_use_end",
|
|
1887
2002
|
toolCall: {
|
|
1888
|
-
id:
|
|
1889
|
-
name:
|
|
1890
|
-
input:
|
|
2003
|
+
id: toolCall.id,
|
|
2004
|
+
name: toolCall.name,
|
|
2005
|
+
input: toolCall.input
|
|
1891
2006
|
}
|
|
1892
2007
|
};
|
|
1893
2008
|
}
|
|
1894
|
-
toolCallBuilders.clear();
|
|
1895
2009
|
}
|
|
1896
2010
|
}
|
|
1897
|
-
for (const
|
|
2011
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
1898
2012
|
yield {
|
|
1899
2013
|
type: "tool_use_end",
|
|
1900
2014
|
toolCall: {
|
|
1901
|
-
id:
|
|
1902
|
-
name:
|
|
1903
|
-
input:
|
|
2015
|
+
id: toolCall.id,
|
|
2016
|
+
name: toolCall.name,
|
|
2017
|
+
input: toolCall.input
|
|
1904
2018
|
}
|
|
1905
2019
|
};
|
|
1906
2020
|
}
|
|
@@ -2337,7 +2451,7 @@ var init_openai = __esm({
|
|
|
2337
2451
|
toolCalls.push({
|
|
2338
2452
|
id: item.call_id,
|
|
2339
2453
|
name: item.name,
|
|
2340
|
-
input:
|
|
2454
|
+
input: parseToolCallArguments(item.arguments, this.name)
|
|
2341
2455
|
});
|
|
2342
2456
|
}
|
|
2343
2457
|
}
|
|
@@ -2443,7 +2557,7 @@ var init_openai = __esm({
|
|
|
2443
2557
|
const stream = await this.client.responses.create(
|
|
2444
2558
|
requestParams
|
|
2445
2559
|
);
|
|
2446
|
-
const
|
|
2560
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
2447
2561
|
const streamTimeout = this.config.timeout ?? 12e4;
|
|
2448
2562
|
let lastActivityTime = Date.now();
|
|
2449
2563
|
const timeoutController = new AbortController();
|
|
@@ -2467,57 +2581,66 @@ var init_openai = __esm({
|
|
|
2467
2581
|
yield { type: "text", text: event.delta };
|
|
2468
2582
|
break;
|
|
2469
2583
|
case "response.output_item.added":
|
|
2470
|
-
|
|
2471
|
-
const
|
|
2472
|
-
const
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2584
|
+
{
|
|
2585
|
+
const item = event.item;
|
|
2586
|
+
const start = toolCallAssembler.onOutputItemAdded({
|
|
2587
|
+
output_index: event.output_index,
|
|
2588
|
+
item: {
|
|
2589
|
+
type: item.type,
|
|
2590
|
+
id: item.id,
|
|
2591
|
+
call_id: item.call_id,
|
|
2592
|
+
name: item.name,
|
|
2593
|
+
arguments: item.arguments
|
|
2594
|
+
}
|
|
2477
2595
|
});
|
|
2596
|
+
if (!start) break;
|
|
2478
2597
|
yield {
|
|
2479
2598
|
type: "tool_use_start",
|
|
2480
|
-
toolCall: { id:
|
|
2599
|
+
toolCall: { id: start.id, name: start.name }
|
|
2481
2600
|
};
|
|
2482
2601
|
}
|
|
2483
2602
|
break;
|
|
2484
2603
|
case "response.function_call_arguments.delta":
|
|
2485
|
-
{
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
}
|
|
2604
|
+
toolCallAssembler.onArgumentsDelta({
|
|
2605
|
+
item_id: event.item_id,
|
|
2606
|
+
output_index: event.output_index,
|
|
2607
|
+
delta: event.delta
|
|
2608
|
+
});
|
|
2491
2609
|
break;
|
|
2492
2610
|
case "response.function_call_arguments.done":
|
|
2493
2611
|
{
|
|
2494
|
-
const
|
|
2495
|
-
|
|
2612
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
2613
|
+
{
|
|
2614
|
+
item_id: event.item_id,
|
|
2615
|
+
output_index: event.output_index,
|
|
2616
|
+
arguments: event.arguments
|
|
2617
|
+
},
|
|
2618
|
+
this.name
|
|
2619
|
+
);
|
|
2620
|
+
if (toolCall) {
|
|
2496
2621
|
yield {
|
|
2497
2622
|
type: "tool_use_end",
|
|
2498
2623
|
toolCall: {
|
|
2499
|
-
id:
|
|
2500
|
-
name:
|
|
2501
|
-
input:
|
|
2624
|
+
id: toolCall.id,
|
|
2625
|
+
name: toolCall.name,
|
|
2626
|
+
input: toolCall.input
|
|
2502
2627
|
}
|
|
2503
2628
|
};
|
|
2504
|
-
fnCallBuilders.delete(event.item_id);
|
|
2505
2629
|
}
|
|
2506
2630
|
}
|
|
2507
2631
|
break;
|
|
2508
2632
|
case "response.completed":
|
|
2509
2633
|
{
|
|
2510
|
-
for (const
|
|
2634
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
2511
2635
|
yield {
|
|
2512
2636
|
type: "tool_use_end",
|
|
2513
2637
|
toolCall: {
|
|
2514
|
-
id:
|
|
2515
|
-
name:
|
|
2516
|
-
input:
|
|
2638
|
+
id: toolCall.id,
|
|
2639
|
+
name: toolCall.name,
|
|
2640
|
+
input: toolCall.input
|
|
2517
2641
|
}
|
|
2518
2642
|
};
|
|
2519
2643
|
}
|
|
2520
|
-
fnCallBuilders.clear();
|
|
2521
2644
|
const hasToolCalls = event.response.output.some(
|
|
2522
2645
|
(i) => i.type === "function_call"
|
|
2523
2646
|
);
|
|
@@ -2637,24 +2760,6 @@ var init_openai = __esm({
|
|
|
2637
2760
|
strict: false
|
|
2638
2761
|
}));
|
|
2639
2762
|
}
|
|
2640
|
-
/**
|
|
2641
|
-
* Parse tool call arguments with jsonrepair fallback (Responses API)
|
|
2642
|
-
*/
|
|
2643
|
-
parseResponsesArguments(args) {
|
|
2644
|
-
try {
|
|
2645
|
-
return args ? JSON.parse(args) : {};
|
|
2646
|
-
} catch {
|
|
2647
|
-
try {
|
|
2648
|
-
if (args) {
|
|
2649
|
-
const repaired = jsonrepair(args);
|
|
2650
|
-
return JSON.parse(repaired);
|
|
2651
|
-
}
|
|
2652
|
-
} catch {
|
|
2653
|
-
console.error(`[${this.name}] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
2654
|
-
}
|
|
2655
|
-
return {};
|
|
2656
|
-
}
|
|
2657
|
-
}
|
|
2658
2763
|
};
|
|
2659
2764
|
}
|
|
2660
2765
|
});
|
|
@@ -4283,6 +4388,8 @@ var init_auth = __esm({
|
|
|
4283
4388
|
init_gcloud();
|
|
4284
4389
|
}
|
|
4285
4390
|
});
|
|
4391
|
+
|
|
4392
|
+
// src/providers/codex.ts
|
|
4286
4393
|
function parseJwtClaims(token) {
|
|
4287
4394
|
const parts = token.split(".");
|
|
4288
4395
|
if (parts.length !== 3 || !parts[1]) return void 0;
|
|
@@ -4298,21 +4405,6 @@ function extractAccountId(accessToken) {
|
|
|
4298
4405
|
const auth = claims["https://api.openai.com/auth"];
|
|
4299
4406
|
return claims["chatgpt_account_id"] || auth?.["chatgpt_account_id"] || claims["organizations"]?.[0]?.id;
|
|
4300
4407
|
}
|
|
4301
|
-
function parseArguments(args) {
|
|
4302
|
-
try {
|
|
4303
|
-
return args ? JSON.parse(args) : {};
|
|
4304
|
-
} catch {
|
|
4305
|
-
try {
|
|
4306
|
-
if (args) {
|
|
4307
|
-
const repaired = jsonrepair(args);
|
|
4308
|
-
return JSON.parse(repaired);
|
|
4309
|
-
}
|
|
4310
|
-
} catch {
|
|
4311
|
-
console.error(`[Codex] Cannot parse tool arguments: ${args.slice(0, 200)}`);
|
|
4312
|
-
}
|
|
4313
|
-
return {};
|
|
4314
|
-
}
|
|
4315
|
-
}
|
|
4316
4408
|
function createCodexProvider(config) {
|
|
4317
4409
|
const provider = new CodexProvider();
|
|
4318
4410
|
if (config) {
|
|
@@ -4327,6 +4419,7 @@ var init_codex = __esm({
|
|
|
4327
4419
|
init_errors();
|
|
4328
4420
|
init_auth();
|
|
4329
4421
|
init_retry();
|
|
4422
|
+
init_tool_call_normalizer();
|
|
4330
4423
|
CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
4331
4424
|
DEFAULT_MODEL3 = "gpt-5.4-codex";
|
|
4332
4425
|
CONTEXT_WINDOWS3 = {
|
|
@@ -4639,7 +4732,7 @@ var init_codex = __esm({
|
|
|
4639
4732
|
let inputTokens = 0;
|
|
4640
4733
|
let outputTokens = 0;
|
|
4641
4734
|
const toolCalls = [];
|
|
4642
|
-
const
|
|
4735
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
4643
4736
|
await this.readSSEStream(response, (event) => {
|
|
4644
4737
|
if (event.id) responseId = event.id;
|
|
4645
4738
|
switch (event.type) {
|
|
@@ -4650,31 +4743,35 @@ var init_codex = __esm({
|
|
|
4650
4743
|
content = event.text ?? content;
|
|
4651
4744
|
break;
|
|
4652
4745
|
case "response.output_item.added": {
|
|
4653
|
-
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
callId: item.call_id,
|
|
4658
|
-
name: item.name,
|
|
4659
|
-
arguments: ""
|
|
4660
|
-
});
|
|
4661
|
-
}
|
|
4746
|
+
toolCallAssembler.onOutputItemAdded({
|
|
4747
|
+
output_index: event.output_index,
|
|
4748
|
+
item: event.item
|
|
4749
|
+
});
|
|
4662
4750
|
break;
|
|
4663
4751
|
}
|
|
4664
4752
|
case "response.function_call_arguments.delta": {
|
|
4665
|
-
|
|
4666
|
-
|
|
4753
|
+
toolCallAssembler.onArgumentsDelta({
|
|
4754
|
+
item_id: event.item_id,
|
|
4755
|
+
output_index: event.output_index,
|
|
4756
|
+
delta: event.delta
|
|
4757
|
+
});
|
|
4667
4758
|
break;
|
|
4668
4759
|
}
|
|
4669
4760
|
case "response.function_call_arguments.done": {
|
|
4670
|
-
const
|
|
4671
|
-
|
|
4761
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
4762
|
+
{
|
|
4763
|
+
item_id: event.item_id,
|
|
4764
|
+
output_index: event.output_index,
|
|
4765
|
+
arguments: event.arguments
|
|
4766
|
+
},
|
|
4767
|
+
this.name
|
|
4768
|
+
);
|
|
4769
|
+
if (toolCall) {
|
|
4672
4770
|
toolCalls.push({
|
|
4673
|
-
id:
|
|
4674
|
-
name:
|
|
4675
|
-
input:
|
|
4771
|
+
id: toolCall.id,
|
|
4772
|
+
name: toolCall.name,
|
|
4773
|
+
input: toolCall.input
|
|
4676
4774
|
});
|
|
4677
|
-
fnCallBuilders.delete(event.item_id);
|
|
4678
4775
|
}
|
|
4679
4776
|
break;
|
|
4680
4777
|
}
|
|
@@ -4685,14 +4782,13 @@ var init_codex = __esm({
|
|
|
4685
4782
|
inputTokens = usage.input_tokens ?? 0;
|
|
4686
4783
|
outputTokens = usage.output_tokens ?? 0;
|
|
4687
4784
|
}
|
|
4688
|
-
for (const
|
|
4785
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
4689
4786
|
toolCalls.push({
|
|
4690
|
-
id:
|
|
4691
|
-
name:
|
|
4692
|
-
input:
|
|
4787
|
+
id: toolCall.id,
|
|
4788
|
+
name: toolCall.name,
|
|
4789
|
+
input: toolCall.input
|
|
4693
4790
|
});
|
|
4694
4791
|
}
|
|
4695
|
-
fnCallBuilders.clear();
|
|
4696
4792
|
break;
|
|
4697
4793
|
}
|
|
4698
4794
|
}
|
|
@@ -4786,7 +4882,7 @@ var init_codex = __esm({
|
|
|
4786
4882
|
const reader = response.body.getReader();
|
|
4787
4883
|
const decoder = new TextDecoder();
|
|
4788
4884
|
let buffer = "";
|
|
4789
|
-
const
|
|
4885
|
+
const toolCallAssembler = new ResponsesToolCallAssembler();
|
|
4790
4886
|
let lastActivityTime = Date.now();
|
|
4791
4887
|
const timeoutController = new AbortController();
|
|
4792
4888
|
const timeoutInterval = setInterval(() => {
|
|
@@ -4819,55 +4915,58 @@ var init_codex = __esm({
|
|
|
4819
4915
|
yield { type: "text", text: event.delta ?? "" };
|
|
4820
4916
|
break;
|
|
4821
4917
|
case "response.output_item.added": {
|
|
4822
|
-
const
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
4827
|
-
name: item.name,
|
|
4828
|
-
arguments: ""
|
|
4829
|
-
});
|
|
4918
|
+
const start = toolCallAssembler.onOutputItemAdded({
|
|
4919
|
+
output_index: event.output_index,
|
|
4920
|
+
item: event.item
|
|
4921
|
+
});
|
|
4922
|
+
if (start) {
|
|
4830
4923
|
yield {
|
|
4831
4924
|
type: "tool_use_start",
|
|
4832
|
-
toolCall: { id:
|
|
4925
|
+
toolCall: { id: start.id, name: start.name }
|
|
4833
4926
|
};
|
|
4834
4927
|
}
|
|
4835
4928
|
break;
|
|
4836
4929
|
}
|
|
4837
4930
|
case "response.function_call_arguments.delta": {
|
|
4838
|
-
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4931
|
+
toolCallAssembler.onArgumentsDelta({
|
|
4932
|
+
item_id: event.item_id,
|
|
4933
|
+
output_index: event.output_index,
|
|
4934
|
+
delta: event.delta
|
|
4935
|
+
});
|
|
4842
4936
|
break;
|
|
4843
4937
|
}
|
|
4844
4938
|
case "response.function_call_arguments.done": {
|
|
4845
|
-
const
|
|
4846
|
-
|
|
4939
|
+
const toolCall = toolCallAssembler.onArgumentsDone(
|
|
4940
|
+
{
|
|
4941
|
+
item_id: event.item_id,
|
|
4942
|
+
output_index: event.output_index,
|
|
4943
|
+
arguments: event.arguments
|
|
4944
|
+
},
|
|
4945
|
+
this.name
|
|
4946
|
+
);
|
|
4947
|
+
if (toolCall) {
|
|
4847
4948
|
yield {
|
|
4848
4949
|
type: "tool_use_end",
|
|
4849
4950
|
toolCall: {
|
|
4850
|
-
id:
|
|
4851
|
-
name:
|
|
4852
|
-
input:
|
|
4951
|
+
id: toolCall.id,
|
|
4952
|
+
name: toolCall.name,
|
|
4953
|
+
input: toolCall.input
|
|
4853
4954
|
}
|
|
4854
4955
|
};
|
|
4855
|
-
fnCallBuilders.delete(event.item_id);
|
|
4856
4956
|
}
|
|
4857
4957
|
break;
|
|
4858
4958
|
}
|
|
4859
4959
|
case "response.completed": {
|
|
4860
|
-
for (const
|
|
4960
|
+
for (const toolCall of toolCallAssembler.finalizeAll(this.name)) {
|
|
4861
4961
|
yield {
|
|
4862
4962
|
type: "tool_use_end",
|
|
4863
4963
|
toolCall: {
|
|
4864
|
-
id:
|
|
4865
|
-
name:
|
|
4866
|
-
input:
|
|
4964
|
+
id: toolCall.id,
|
|
4965
|
+
name: toolCall.name,
|
|
4966
|
+
input: toolCall.input
|
|
4867
4967
|
}
|
|
4868
4968
|
};
|
|
4869
4969
|
}
|
|
4870
|
-
fnCallBuilders.clear();
|
|
4871
4970
|
const resp = event.response;
|
|
4872
4971
|
const output = resp?.output ?? [];
|
|
4873
4972
|
const hasToolCalls = output.some((i) => i.type === "function_call");
|
|
@@ -5243,8 +5342,8 @@ var init_gemini = __esm({
|
|
|
5243
5342
|
const { history, lastMessage } = this.convertMessages(messages);
|
|
5244
5343
|
const chat = model.startChat({ history });
|
|
5245
5344
|
const result = await chat.sendMessageStream(lastMessage);
|
|
5246
|
-
const emittedToolCalls = /* @__PURE__ */ new Set();
|
|
5247
5345
|
let streamStopReason;
|
|
5346
|
+
let streamToolCallCounter = 0;
|
|
5248
5347
|
for await (const chunk of result.stream) {
|
|
5249
5348
|
const text13 = chunk.text();
|
|
5250
5349
|
if (text13) {
|
|
@@ -5259,30 +5358,23 @@ var init_gemini = __esm({
|
|
|
5259
5358
|
for (const part of candidate.content.parts) {
|
|
5260
5359
|
if ("functionCall" in part && part.functionCall) {
|
|
5261
5360
|
const funcCall = part.functionCall;
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
}
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
}
|
|
5280
|
-
};
|
|
5281
|
-
yield {
|
|
5282
|
-
type: "tool_use_end",
|
|
5283
|
-
toolCall
|
|
5284
|
-
};
|
|
5285
|
-
}
|
|
5361
|
+
streamToolCallCounter++;
|
|
5362
|
+
const toolCall = {
|
|
5363
|
+
id: `gemini_call_${streamToolCallCounter}`,
|
|
5364
|
+
name: funcCall.name,
|
|
5365
|
+
input: funcCall.args ?? {}
|
|
5366
|
+
};
|
|
5367
|
+
yield {
|
|
5368
|
+
type: "tool_use_start",
|
|
5369
|
+
toolCall: {
|
|
5370
|
+
id: toolCall.id,
|
|
5371
|
+
name: toolCall.name
|
|
5372
|
+
}
|
|
5373
|
+
};
|
|
5374
|
+
yield {
|
|
5375
|
+
type: "tool_use_end",
|
|
5376
|
+
toolCall
|
|
5377
|
+
};
|
|
5286
5378
|
}
|
|
5287
5379
|
}
|
|
5288
5380
|
}
|
|
@@ -5357,13 +5449,13 @@ var init_gemini = __esm({
|
|
|
5357
5449
|
* Convert messages to Gemini format
|
|
5358
5450
|
*/
|
|
5359
5451
|
convertMessages(messages) {
|
|
5452
|
+
const toolNameByUseId = this.buildToolUseNameMap(messages);
|
|
5453
|
+
const conversation = messages.filter((m) => m.role !== "system");
|
|
5360
5454
|
const history = [];
|
|
5361
5455
|
let lastUserMessage = "";
|
|
5362
|
-
for (
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
}
|
|
5366
|
-
const parts = this.convertContent(msg.content);
|
|
5456
|
+
for (let i = 0; i < conversation.length; i++) {
|
|
5457
|
+
const msg = conversation[i];
|
|
5458
|
+
const isLastMessage = i === conversation.length - 1;
|
|
5367
5459
|
if (msg.role === "user") {
|
|
5368
5460
|
if (Array.isArray(msg.content) && msg.content[0]?.type === "tool_result") {
|
|
5369
5461
|
const functionResponses = [];
|
|
@@ -5372,23 +5464,49 @@ var init_gemini = __esm({
|
|
|
5372
5464
|
const toolResult = block;
|
|
5373
5465
|
functionResponses.push({
|
|
5374
5466
|
functionResponse: {
|
|
5375
|
-
name
|
|
5376
|
-
//
|
|
5467
|
+
// Gemini expects the function name in functionResponse.name.
|
|
5468
|
+
// Recover it from prior assistant tool_use blocks when possible.
|
|
5469
|
+
name: toolNameByUseId.get(toolResult.tool_use_id) ?? toolResult.tool_use_id,
|
|
5377
5470
|
response: { result: toolResult.content }
|
|
5378
5471
|
}
|
|
5379
5472
|
});
|
|
5380
5473
|
}
|
|
5381
5474
|
}
|
|
5382
|
-
|
|
5475
|
+
if (isLastMessage) {
|
|
5476
|
+
lastUserMessage = functionResponses;
|
|
5477
|
+
} else {
|
|
5478
|
+
history.push({ role: "user", parts: functionResponses });
|
|
5479
|
+
}
|
|
5383
5480
|
} else {
|
|
5384
|
-
|
|
5481
|
+
const parts = this.convertContent(msg.content);
|
|
5482
|
+
if (isLastMessage) {
|
|
5483
|
+
lastUserMessage = parts;
|
|
5484
|
+
} else {
|
|
5485
|
+
history.push({ role: "user", parts });
|
|
5486
|
+
}
|
|
5385
5487
|
}
|
|
5386
5488
|
} else if (msg.role === "assistant") {
|
|
5489
|
+
const parts = this.convertContent(msg.content);
|
|
5387
5490
|
history.push({ role: "model", parts });
|
|
5388
5491
|
}
|
|
5389
5492
|
}
|
|
5390
5493
|
return { history, lastMessage: lastUserMessage };
|
|
5391
5494
|
}
|
|
5495
|
+
/**
|
|
5496
|
+
* Build a map from tool_use IDs to function names from assistant history.
|
|
5497
|
+
*/
|
|
5498
|
+
buildToolUseNameMap(messages) {
|
|
5499
|
+
const map = /* @__PURE__ */ new Map();
|
|
5500
|
+
for (const msg of messages) {
|
|
5501
|
+
if (msg.role !== "assistant" || !Array.isArray(msg.content)) continue;
|
|
5502
|
+
for (const block of msg.content) {
|
|
5503
|
+
if (block.type === "tool_use") {
|
|
5504
|
+
map.set(block.id, block.name);
|
|
5505
|
+
}
|
|
5506
|
+
}
|
|
5507
|
+
}
|
|
5508
|
+
return map;
|
|
5509
|
+
}
|
|
5392
5510
|
/**
|
|
5393
5511
|
* Convert content to Gemini parts
|
|
5394
5512
|
*/
|
|
@@ -5465,14 +5583,15 @@ var init_gemini = __esm({
|
|
|
5465
5583
|
let textContent = "";
|
|
5466
5584
|
const toolCalls = [];
|
|
5467
5585
|
if (candidate?.content?.parts) {
|
|
5586
|
+
let toolIndex = 0;
|
|
5468
5587
|
for (const part of candidate.content.parts) {
|
|
5469
5588
|
if ("text" in part && part.text) {
|
|
5470
5589
|
textContent += part.text;
|
|
5471
5590
|
}
|
|
5472
5591
|
if ("functionCall" in part && part.functionCall) {
|
|
5592
|
+
toolIndex++;
|
|
5473
5593
|
toolCalls.push({
|
|
5474
|
-
id:
|
|
5475
|
-
// Use name as ID for Gemini
|
|
5594
|
+
id: `gemini_call_${toolIndex}`,
|
|
5476
5595
|
name: part.functionCall.name,
|
|
5477
5596
|
input: part.functionCall.args ?? {}
|
|
5478
5597
|
});
|
|
@@ -6068,6 +6187,154 @@ var init_fallback = __esm({
|
|
|
6068
6187
|
}
|
|
6069
6188
|
});
|
|
6070
6189
|
|
|
6190
|
+
// src/providers/resilient.ts
|
|
6191
|
+
function sleep2(ms) {
|
|
6192
|
+
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
6193
|
+
}
|
|
6194
|
+
function computeRetryDelay(attempt, config) {
|
|
6195
|
+
const exp = config.initialDelayMs * Math.pow(config.backoffMultiplier, attempt);
|
|
6196
|
+
const capped = Math.min(exp, config.maxDelayMs);
|
|
6197
|
+
const jitter = capped * config.jitterFactor * (Math.random() * 2 - 1);
|
|
6198
|
+
return Math.max(0, Math.min(capped + jitter, config.maxDelayMs));
|
|
6199
|
+
}
|
|
6200
|
+
function getDefaultResilienceConfig(providerId) {
|
|
6201
|
+
if (providerId === "ollama" || providerId === "lmstudio") {
|
|
6202
|
+
return {
|
|
6203
|
+
retry: {
|
|
6204
|
+
maxRetries: 1,
|
|
6205
|
+
initialDelayMs: 300,
|
|
6206
|
+
maxDelayMs: 1500
|
|
6207
|
+
},
|
|
6208
|
+
streamRetry: {
|
|
6209
|
+
maxRetries: 0
|
|
6210
|
+
},
|
|
6211
|
+
circuitBreaker: {
|
|
6212
|
+
failureThreshold: 3,
|
|
6213
|
+
resetTimeout: 1e4
|
|
6214
|
+
}
|
|
6215
|
+
};
|
|
6216
|
+
}
|
|
6217
|
+
return {
|
|
6218
|
+
retry: {
|
|
6219
|
+
maxRetries: 3,
|
|
6220
|
+
initialDelayMs: 1e3,
|
|
6221
|
+
maxDelayMs: 3e4
|
|
6222
|
+
},
|
|
6223
|
+
streamRetry: {
|
|
6224
|
+
maxRetries: 1,
|
|
6225
|
+
initialDelayMs: 500,
|
|
6226
|
+
maxDelayMs: 5e3
|
|
6227
|
+
},
|
|
6228
|
+
circuitBreaker: {
|
|
6229
|
+
failureThreshold: 5,
|
|
6230
|
+
resetTimeout: 3e4
|
|
6231
|
+
}
|
|
6232
|
+
};
|
|
6233
|
+
}
|
|
6234
|
+
function createResilientProvider(provider, config) {
|
|
6235
|
+
return new ResilientProvider(provider, config ?? getDefaultResilienceConfig(provider.id));
|
|
6236
|
+
}
|
|
6237
|
+
var DEFAULT_STREAM_RETRY, ResilientProvider;
|
|
6238
|
+
var init_resilient = __esm({
|
|
6239
|
+
"src/providers/resilient.ts"() {
|
|
6240
|
+
init_retry();
|
|
6241
|
+
init_circuit_breaker();
|
|
6242
|
+
DEFAULT_STREAM_RETRY = {
|
|
6243
|
+
maxRetries: 1,
|
|
6244
|
+
initialDelayMs: 500,
|
|
6245
|
+
maxDelayMs: 5e3,
|
|
6246
|
+
backoffMultiplier: 2,
|
|
6247
|
+
jitterFactor: 0.1
|
|
6248
|
+
};
|
|
6249
|
+
ResilientProvider = class {
|
|
6250
|
+
id;
|
|
6251
|
+
name;
|
|
6252
|
+
provider;
|
|
6253
|
+
breaker;
|
|
6254
|
+
retryConfig;
|
|
6255
|
+
streamRetryConfig;
|
|
6256
|
+
constructor(provider, config = {}) {
|
|
6257
|
+
this.provider = provider;
|
|
6258
|
+
this.id = provider.id;
|
|
6259
|
+
this.name = provider.name;
|
|
6260
|
+
this.retryConfig = { ...DEFAULT_RETRY_CONFIG, ...config.retry };
|
|
6261
|
+
this.streamRetryConfig = { ...DEFAULT_STREAM_RETRY, ...config.streamRetry };
|
|
6262
|
+
this.breaker = new CircuitBreaker(
|
|
6263
|
+
{ ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config.circuitBreaker },
|
|
6264
|
+
provider.id
|
|
6265
|
+
);
|
|
6266
|
+
}
|
|
6267
|
+
async initialize(config) {
|
|
6268
|
+
await this.provider.initialize(config);
|
|
6269
|
+
}
|
|
6270
|
+
async chat(messages, options) {
|
|
6271
|
+
return this.breaker.execute(
|
|
6272
|
+
() => withRetry(() => this.provider.chat(messages, options), this.retryConfig)
|
|
6273
|
+
);
|
|
6274
|
+
}
|
|
6275
|
+
async chatWithTools(messages, options) {
|
|
6276
|
+
return this.breaker.execute(
|
|
6277
|
+
() => withRetry(() => this.provider.chatWithTools(messages, options), this.retryConfig)
|
|
6278
|
+
);
|
|
6279
|
+
}
|
|
6280
|
+
async *stream(messages, options) {
|
|
6281
|
+
yield* this.streamWithPolicy(() => this.provider.stream(messages, options));
|
|
6282
|
+
}
|
|
6283
|
+
async *streamWithTools(messages, options) {
|
|
6284
|
+
yield* this.streamWithPolicy(() => this.provider.streamWithTools(messages, options));
|
|
6285
|
+
}
|
|
6286
|
+
countTokens(text13) {
|
|
6287
|
+
return this.provider.countTokens(text13);
|
|
6288
|
+
}
|
|
6289
|
+
getContextWindow() {
|
|
6290
|
+
return this.provider.getContextWindow();
|
|
6291
|
+
}
|
|
6292
|
+
async isAvailable() {
|
|
6293
|
+
try {
|
|
6294
|
+
return await this.breaker.execute(() => this.provider.isAvailable());
|
|
6295
|
+
} catch (error) {
|
|
6296
|
+
if (error instanceof CircuitOpenError) {
|
|
6297
|
+
return false;
|
|
6298
|
+
}
|
|
6299
|
+
return false;
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
6302
|
+
getCircuitState() {
|
|
6303
|
+
return this.breaker.getState();
|
|
6304
|
+
}
|
|
6305
|
+
resetCircuit() {
|
|
6306
|
+
this.breaker.reset();
|
|
6307
|
+
}
|
|
6308
|
+
async *streamWithPolicy(createStream) {
|
|
6309
|
+
let attempt = 0;
|
|
6310
|
+
while (attempt <= this.streamRetryConfig.maxRetries) {
|
|
6311
|
+
if (this.breaker.isOpen()) {
|
|
6312
|
+
throw new CircuitOpenError(this.id, 0);
|
|
6313
|
+
}
|
|
6314
|
+
let emittedChunk = false;
|
|
6315
|
+
try {
|
|
6316
|
+
for await (const chunk of createStream()) {
|
|
6317
|
+
emittedChunk = true;
|
|
6318
|
+
yield chunk;
|
|
6319
|
+
}
|
|
6320
|
+
this.breaker.recordSuccess();
|
|
6321
|
+
return;
|
|
6322
|
+
} catch (error) {
|
|
6323
|
+
this.breaker.recordFailure();
|
|
6324
|
+
const shouldRetry = !emittedChunk && attempt < this.streamRetryConfig.maxRetries && isRetryableError(error);
|
|
6325
|
+
if (!shouldRetry) {
|
|
6326
|
+
throw error;
|
|
6327
|
+
}
|
|
6328
|
+
const delay = computeRetryDelay(attempt, this.streamRetryConfig);
|
|
6329
|
+
await sleep2(delay);
|
|
6330
|
+
attempt++;
|
|
6331
|
+
}
|
|
6332
|
+
}
|
|
6333
|
+
}
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
});
|
|
6337
|
+
|
|
6071
6338
|
// src/config/env.ts
|
|
6072
6339
|
var env_exports = {};
|
|
6073
6340
|
__export(env_exports, {
|
|
@@ -6471,6 +6738,7 @@ __export(providers_exports, {
|
|
|
6471
6738
|
MODEL_PRICING: () => MODEL_PRICING,
|
|
6472
6739
|
OpenAIProvider: () => OpenAIProvider,
|
|
6473
6740
|
ProviderFallback: () => ProviderFallback,
|
|
6741
|
+
ResilientProvider: () => ResilientProvider,
|
|
6474
6742
|
createAnthropicProvider: () => createAnthropicProvider,
|
|
6475
6743
|
createCircuitBreaker: () => createCircuitBreaker,
|
|
6476
6744
|
createCodexProvider: () => createCodexProvider,
|
|
@@ -6481,10 +6749,12 @@ __export(providers_exports, {
|
|
|
6481
6749
|
createOpenAIProvider: () => createOpenAIProvider,
|
|
6482
6750
|
createProvider: () => createProvider,
|
|
6483
6751
|
createProviderFallback: () => createProviderFallback,
|
|
6752
|
+
createResilientProvider: () => createResilientProvider,
|
|
6484
6753
|
createRetryableMethod: () => createRetryableMethod,
|
|
6485
6754
|
estimateCost: () => estimateCost,
|
|
6486
6755
|
formatCost: () => formatCost,
|
|
6487
6756
|
getDefaultProvider: () => getDefaultProvider2,
|
|
6757
|
+
getDefaultResilienceConfig: () => getDefaultResilienceConfig,
|
|
6488
6758
|
getModelPricing: () => getModelPricing,
|
|
6489
6759
|
hasKnownPricing: () => hasKnownPricing,
|
|
6490
6760
|
isRetryableError: () => isRetryableError,
|
|
@@ -6520,12 +6790,10 @@ async function createProvider(type, config = {}) {
|
|
|
6520
6790
|
break;
|
|
6521
6791
|
case "kimi":
|
|
6522
6792
|
provider = createKimiProvider(mergedConfig);
|
|
6523
|
-
|
|
6524
|
-
return provider;
|
|
6793
|
+
break;
|
|
6525
6794
|
case "kimi-code":
|
|
6526
6795
|
provider = createKimiCodeProvider(mergedConfig);
|
|
6527
|
-
|
|
6528
|
-
return provider;
|
|
6796
|
+
break;
|
|
6529
6797
|
case "lmstudio":
|
|
6530
6798
|
provider = new OpenAIProvider("lmstudio", "LM Studio");
|
|
6531
6799
|
mergedConfig.baseUrl = mergedConfig.baseUrl ?? "http://localhost:1234/v1";
|
|
@@ -6570,7 +6838,10 @@ async function createProvider(type, config = {}) {
|
|
|
6570
6838
|
});
|
|
6571
6839
|
}
|
|
6572
6840
|
await provider.initialize(mergedConfig);
|
|
6573
|
-
|
|
6841
|
+
const resilienceEnabled = !["0", "false", "off"].includes(
|
|
6842
|
+
(process.env["COCO_PROVIDER_RESILIENCE"] ?? "1").toLowerCase()
|
|
6843
|
+
);
|
|
6844
|
+
return resilienceEnabled ? createResilientProvider(provider) : provider;
|
|
6574
6845
|
}
|
|
6575
6846
|
async function getDefaultProvider2(config = {}) {
|
|
6576
6847
|
const { getDefaultProvider: getEnvProvider } = await Promise.resolve().then(() => (init_env(), env_exports));
|
|
@@ -6624,6 +6895,7 @@ var init_providers = __esm({
|
|
|
6624
6895
|
init_pricing();
|
|
6625
6896
|
init_circuit_breaker();
|
|
6626
6897
|
init_fallback();
|
|
6898
|
+
init_resilient();
|
|
6627
6899
|
init_copilot();
|
|
6628
6900
|
init_anthropic();
|
|
6629
6901
|
init_openai();
|
|
@@ -6632,6 +6904,7 @@ var init_providers = __esm({
|
|
|
6632
6904
|
init_copilot2();
|
|
6633
6905
|
init_errors();
|
|
6634
6906
|
init_env();
|
|
6907
|
+
init_resilient();
|
|
6635
6908
|
}
|
|
6636
6909
|
});
|
|
6637
6910
|
|
|
@@ -9394,7 +9667,7 @@ async function checkAndCompactContext(session, provider, signal, toolRegistry) {
|
|
|
9394
9667
|
session.contextManager.setUsedTokens(result.compactedTokens);
|
|
9395
9668
|
}
|
|
9396
9669
|
return result;
|
|
9397
|
-
} catch
|
|
9670
|
+
} catch {
|
|
9398
9671
|
return null;
|
|
9399
9672
|
}
|
|
9400
9673
|
}
|
|
@@ -10498,6 +10771,34 @@ function isAbortError(error, signal) {
|
|
|
10498
10771
|
if (error.message.endsWith("Request was aborted.")) return true;
|
|
10499
10772
|
return false;
|
|
10500
10773
|
}
|
|
10774
|
+
function classifyAgentLoopError(error, signal) {
|
|
10775
|
+
if (isAbortError(error, signal)) {
|
|
10776
|
+
return {
|
|
10777
|
+
kind: "abort",
|
|
10778
|
+
message: "Request was aborted.",
|
|
10779
|
+
original: error
|
|
10780
|
+
};
|
|
10781
|
+
}
|
|
10782
|
+
if (isNonRetryableProviderError(error)) {
|
|
10783
|
+
return {
|
|
10784
|
+
kind: "provider_non_retryable",
|
|
10785
|
+
message: error instanceof Error ? error.message : String(error),
|
|
10786
|
+
original: error
|
|
10787
|
+
};
|
|
10788
|
+
}
|
|
10789
|
+
if (error instanceof ProviderError) {
|
|
10790
|
+
return {
|
|
10791
|
+
kind: "provider_retryable",
|
|
10792
|
+
message: error.message,
|
|
10793
|
+
original: error
|
|
10794
|
+
};
|
|
10795
|
+
}
|
|
10796
|
+
return {
|
|
10797
|
+
kind: "unexpected",
|
|
10798
|
+
message: error instanceof Error ? error.message : String(error),
|
|
10799
|
+
original: error
|
|
10800
|
+
};
|
|
10801
|
+
}
|
|
10501
10802
|
function isNonRetryableProviderError(error) {
|
|
10502
10803
|
if (error instanceof ProviderError) {
|
|
10503
10804
|
const code = error.statusCode;
|
|
@@ -19048,7 +19349,7 @@ async function runCIChecks(ctx) {
|
|
|
19048
19349
|
cwd: ctx.cwd
|
|
19049
19350
|
});
|
|
19050
19351
|
if (result.checks.length === 0) {
|
|
19051
|
-
await
|
|
19352
|
+
await sleep3(pollMs);
|
|
19052
19353
|
continue;
|
|
19053
19354
|
}
|
|
19054
19355
|
if (result.allPassed) {
|
|
@@ -19098,7 +19399,7 @@ async function runCIChecks(ctx) {
|
|
|
19098
19399
|
spinner18.message(`CI: ${passed}/${result.checks.length} passed, ${pending} pending...`);
|
|
19099
19400
|
} catch {
|
|
19100
19401
|
}
|
|
19101
|
-
await
|
|
19402
|
+
await sleep3(pollMs);
|
|
19102
19403
|
}
|
|
19103
19404
|
spinner18.stop("CI check timeout");
|
|
19104
19405
|
const action = await p26.select({
|
|
@@ -19123,7 +19424,7 @@ async function runCIChecks(ctx) {
|
|
|
19123
19424
|
durationMs: performance.now() - start
|
|
19124
19425
|
};
|
|
19125
19426
|
}
|
|
19126
|
-
function
|
|
19427
|
+
function sleep3(ms) {
|
|
19127
19428
|
return new Promise((resolve4) => setTimeout(resolve4, ms));
|
|
19128
19429
|
}
|
|
19129
19430
|
var init_ci_checks = __esm({
|
|
@@ -31546,6 +31847,15 @@ async function selectModelInteractively(models, currentModelId) {
|
|
|
31546
31847
|
renderMenu();
|
|
31547
31848
|
});
|
|
31548
31849
|
}
|
|
31850
|
+
async function persistModelPreference(provider, model) {
|
|
31851
|
+
try {
|
|
31852
|
+
await saveProviderPreference(provider, model);
|
|
31853
|
+
} catch (error) {
|
|
31854
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
31855
|
+
console.log(chalk2.yellow(`\u26A0 Could not persist model preference: ${reason}`));
|
|
31856
|
+
console.log(chalk2.dim(" Model changed for this session only.\n"));
|
|
31857
|
+
}
|
|
31858
|
+
}
|
|
31549
31859
|
var modelCommand = {
|
|
31550
31860
|
name: "model",
|
|
31551
31861
|
aliases: ["m"],
|
|
@@ -31594,7 +31904,7 @@ var modelCommand = {
|
|
|
31594
31904
|
return false;
|
|
31595
31905
|
}
|
|
31596
31906
|
session.config.provider.model = selectedModel;
|
|
31597
|
-
await
|
|
31907
|
+
await persistModelPreference(currentProvider, selectedModel);
|
|
31598
31908
|
const modelInfo2 = providerDef.models.find((m) => m.id === selectedModel);
|
|
31599
31909
|
console.log(chalk2.green(`\u2713 Switched to ${modelInfo2?.name ?? selectedModel}
|
|
31600
31910
|
`));
|
|
@@ -31616,7 +31926,7 @@ var modelCommand = {
|
|
|
31616
31926
|
if (!foundInProvider) {
|
|
31617
31927
|
console.log(chalk2.yellow(`Model "${newModel}" not in known list, setting anyway...`));
|
|
31618
31928
|
session.config.provider.model = newModel;
|
|
31619
|
-
await
|
|
31929
|
+
await persistModelPreference(currentProvider, newModel);
|
|
31620
31930
|
console.log(chalk2.green(`\u2713 Model set to: ${newModel}
|
|
31621
31931
|
`));
|
|
31622
31932
|
return false;
|
|
@@ -31631,7 +31941,7 @@ var modelCommand = {
|
|
|
31631
31941
|
return false;
|
|
31632
31942
|
}
|
|
31633
31943
|
session.config.provider.model = newModel;
|
|
31634
|
-
await
|
|
31944
|
+
await persistModelPreference(currentProvider, newModel);
|
|
31635
31945
|
const modelInfo = providerDef.models.find((m) => m.id === newModel);
|
|
31636
31946
|
console.log(chalk2.green(`\u2713 Switched to ${modelInfo?.name ?? newModel}
|
|
31637
31947
|
`));
|
|
@@ -50052,6 +50362,60 @@ function extractDeniedPath(error) {
|
|
|
50052
50362
|
// src/cli/repl/agent-loop.ts
|
|
50053
50363
|
init_allow_path_prompt();
|
|
50054
50364
|
init_error_resilience();
|
|
50365
|
+
|
|
50366
|
+
// src/cli/repl/turn-quality.ts
|
|
50367
|
+
function clamp(value, min, max) {
|
|
50368
|
+
return Math.max(min, Math.min(max, value));
|
|
50369
|
+
}
|
|
50370
|
+
function computeTurnQualityMetrics(input) {
|
|
50371
|
+
const executedToolCalls = input.executedTools.length;
|
|
50372
|
+
const successfulToolCalls = input.executedTools.filter((t) => t.result.success).length;
|
|
50373
|
+
const failedToolCalls = executedToolCalls - successfulToolCalls;
|
|
50374
|
+
let score = 100;
|
|
50375
|
+
if (input.hadError) score -= 25;
|
|
50376
|
+
if (executedToolCalls > 0) {
|
|
50377
|
+
const failureRatio = failedToolCalls / executedToolCalls;
|
|
50378
|
+
score -= Math.round(failureRatio * 35);
|
|
50379
|
+
}
|
|
50380
|
+
if (input.maxIterations > 0) {
|
|
50381
|
+
const iterationRatio = input.iterationsUsed / input.maxIterations;
|
|
50382
|
+
if (iterationRatio > 0.8) score -= 10;
|
|
50383
|
+
if (iterationRatio >= 1) score -= 10;
|
|
50384
|
+
}
|
|
50385
|
+
score += Math.min(input.repeatedOutputsSuppressed * 2, 8);
|
|
50386
|
+
return {
|
|
50387
|
+
score: clamp(score, 0, 100),
|
|
50388
|
+
iterationsUsed: input.iterationsUsed,
|
|
50389
|
+
maxIterations: input.maxIterations,
|
|
50390
|
+
executedToolCalls,
|
|
50391
|
+
successfulToolCalls,
|
|
50392
|
+
failedToolCalls,
|
|
50393
|
+
hadError: input.hadError,
|
|
50394
|
+
repeatedOutputsSuppressed: input.repeatedOutputsSuppressed
|
|
50395
|
+
};
|
|
50396
|
+
}
|
|
50397
|
+
var RepeatedOutputSuppressor = class {
|
|
50398
|
+
seen = /* @__PURE__ */ new Map();
|
|
50399
|
+
transform(toolName, content) {
|
|
50400
|
+
const fingerprint = this.fingerprint(toolName, content);
|
|
50401
|
+
const count = this.seen.get(fingerprint) ?? 0;
|
|
50402
|
+
this.seen.set(fingerprint, count + 1);
|
|
50403
|
+
if (count === 0) {
|
|
50404
|
+
return { content, suppressed: false };
|
|
50405
|
+
}
|
|
50406
|
+
return {
|
|
50407
|
+
content: `[Repeated tool output suppressed: '${toolName}' produced the same output as before (occurrence ${count + 1}). If needed, re-run with different inputs.]`,
|
|
50408
|
+
suppressed: true
|
|
50409
|
+
};
|
|
50410
|
+
}
|
|
50411
|
+
fingerprint(toolName, content) {
|
|
50412
|
+
const head = content.slice(0, 200);
|
|
50413
|
+
const tail = content.slice(-200);
|
|
50414
|
+
return `${toolName}|${content.length}|${head}|${tail}`;
|
|
50415
|
+
}
|
|
50416
|
+
};
|
|
50417
|
+
|
|
50418
|
+
// src/cli/repl/agent-loop.ts
|
|
50055
50419
|
async function executeAgentTurn(session, userMessage, provider, toolRegistry, options = {}) {
|
|
50056
50420
|
resetLineBuffer();
|
|
50057
50421
|
const messageSnapshot = session.messages.length;
|
|
@@ -50060,12 +50424,23 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
50060
50424
|
let totalInputTokens = 0;
|
|
50061
50425
|
let totalOutputTokens = 0;
|
|
50062
50426
|
let finalContent = "";
|
|
50427
|
+
let hadTurnError = false;
|
|
50428
|
+
let repeatedOutputsSuppressed = 0;
|
|
50429
|
+
const repeatedOutputSuppressor = new RepeatedOutputSuppressor();
|
|
50430
|
+
const buildQualityMetrics = () => computeTurnQualityMetrics({
|
|
50431
|
+
iterationsUsed: iteration,
|
|
50432
|
+
maxIterations,
|
|
50433
|
+
executedTools,
|
|
50434
|
+
hadError: hadTurnError,
|
|
50435
|
+
repeatedOutputsSuppressed
|
|
50436
|
+
});
|
|
50063
50437
|
const abortReturn = () => {
|
|
50064
50438
|
session.messages.length = messageSnapshot;
|
|
50065
50439
|
return {
|
|
50066
50440
|
content: finalContent,
|
|
50067
50441
|
toolCalls: executedTools,
|
|
50068
50442
|
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
|
|
50443
|
+
quality: buildQualityMetrics(),
|
|
50069
50444
|
aborted: true,
|
|
50070
50445
|
partialContent: finalContent || void 0,
|
|
50071
50446
|
abortReason: "user_cancel"
|
|
@@ -50179,13 +50554,15 @@ ${tail}`;
|
|
|
50179
50554
|
options.onThinkingEnd?.();
|
|
50180
50555
|
thinkingEnded = true;
|
|
50181
50556
|
}
|
|
50182
|
-
|
|
50557
|
+
const classification = classifyAgentLoopError(streamError, options.signal);
|
|
50558
|
+
if (classification.kind === "abort") {
|
|
50183
50559
|
return abortReturn();
|
|
50184
50560
|
}
|
|
50185
|
-
if (
|
|
50186
|
-
throw
|
|
50561
|
+
if (classification.kind === "provider_non_retryable") {
|
|
50562
|
+
throw classification.original;
|
|
50187
50563
|
}
|
|
50188
|
-
|
|
50564
|
+
hadTurnError = true;
|
|
50565
|
+
const errorMsg = classification.message;
|
|
50189
50566
|
addMessage(session, {
|
|
50190
50567
|
role: "assistant",
|
|
50191
50568
|
content: `[Error during streaming: ${errorMsg}]`
|
|
@@ -50194,6 +50571,7 @@ ${tail}`;
|
|
|
50194
50571
|
content: finalContent || `[Error: ${errorMsg}]`,
|
|
50195
50572
|
toolCalls: executedTools,
|
|
50196
50573
|
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
|
|
50574
|
+
quality: buildQualityMetrics(),
|
|
50197
50575
|
aborted: false,
|
|
50198
50576
|
partialContent: finalContent || void 0,
|
|
50199
50577
|
error: errorMsg
|
|
@@ -50399,10 +50777,15 @@ ${tail}`;
|
|
|
50399
50777
|
}
|
|
50400
50778
|
const executedCall = executedTools.find((e) => e.id === toolCall.id);
|
|
50401
50779
|
if (executedCall) {
|
|
50780
|
+
const truncatedOutput = truncateInlineResult(executedCall.result.output, toolCall.name);
|
|
50781
|
+
const transformedOutput = repeatedOutputSuppressor.transform(toolCall.name, truncatedOutput);
|
|
50782
|
+
if (transformedOutput.suppressed) {
|
|
50783
|
+
repeatedOutputsSuppressed++;
|
|
50784
|
+
}
|
|
50402
50785
|
toolResults.push({
|
|
50403
50786
|
type: "tool_result",
|
|
50404
50787
|
tool_use_id: toolCall.id,
|
|
50405
|
-
content:
|
|
50788
|
+
content: transformedOutput.content,
|
|
50406
50789
|
is_error: !executedCall.result.success
|
|
50407
50790
|
});
|
|
50408
50791
|
} else {
|
|
@@ -50550,6 +50933,7 @@ I have reached the maximum iteration limit (${maxIterations}). The task may be i
|
|
|
50550
50933
|
content: finalContent,
|
|
50551
50934
|
toolCalls: executedTools,
|
|
50552
50935
|
usage: { inputTokens: totalInputTokens, outputTokens: totalOutputTokens },
|
|
50936
|
+
quality: buildQualityMetrics(),
|
|
50553
50937
|
aborted: false
|
|
50554
50938
|
};
|
|
50555
50939
|
}
|