@corbat-tech/coco 2.8.0 → 2.8.2
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 +228 -105
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +150 -87
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -1012,22 +1012,44 @@ var init_anthropic = __esm({
|
|
|
1012
1012
|
async *stream(messages, options) {
|
|
1013
1013
|
this.ensureInitialized();
|
|
1014
1014
|
try {
|
|
1015
|
-
const stream = await this.client.messages.stream(
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1015
|
+
const stream = await this.client.messages.stream(
|
|
1016
|
+
{
|
|
1017
|
+
model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
|
|
1018
|
+
max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
1019
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
1020
|
+
system: this.extractSystem(messages, options?.system),
|
|
1021
|
+
messages: this.convertMessages(messages)
|
|
1022
|
+
},
|
|
1023
|
+
{ signal: options?.signal }
|
|
1024
|
+
);
|
|
1025
|
+
const streamTimeout = this.config.timeout ?? 12e4;
|
|
1026
|
+
let lastActivityTime = Date.now();
|
|
1027
|
+
const checkTimeout = () => {
|
|
1028
|
+
if (Date.now() - lastActivityTime > streamTimeout) {
|
|
1029
|
+
throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
|
|
1030
|
+
}
|
|
1031
|
+
};
|
|
1032
|
+
const timeoutInterval = setInterval(checkTimeout, 5e3);
|
|
1033
|
+
try {
|
|
1034
|
+
let streamStopReason;
|
|
1035
|
+
for await (const event of stream) {
|
|
1036
|
+
lastActivityTime = Date.now();
|
|
1037
|
+
if (event.type === "content_block_delta") {
|
|
1038
|
+
const delta = event.delta;
|
|
1039
|
+
if (delta.type === "text_delta" && delta.text) {
|
|
1040
|
+
yield { type: "text", text: delta.text };
|
|
1041
|
+
}
|
|
1042
|
+
} else if (event.type === "message_delta") {
|
|
1043
|
+
const delta = event.delta;
|
|
1044
|
+
if (delta.stop_reason) {
|
|
1045
|
+
streamStopReason = this.mapStopReason(delta.stop_reason);
|
|
1046
|
+
}
|
|
1027
1047
|
}
|
|
1028
1048
|
}
|
|
1049
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
1050
|
+
} finally {
|
|
1051
|
+
clearInterval(timeoutInterval);
|
|
1029
1052
|
}
|
|
1030
|
-
yield { type: "done" };
|
|
1031
1053
|
} catch (error) {
|
|
1032
1054
|
throw this.handleError(error);
|
|
1033
1055
|
}
|
|
@@ -1038,90 +1060,112 @@ var init_anthropic = __esm({
|
|
|
1038
1060
|
async *streamWithTools(messages, options) {
|
|
1039
1061
|
this.ensureInitialized();
|
|
1040
1062
|
try {
|
|
1041
|
-
const stream = await this.client.messages.stream(
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1063
|
+
const stream = await this.client.messages.stream(
|
|
1064
|
+
{
|
|
1065
|
+
model: options?.model ?? this.config.model ?? DEFAULT_MODEL,
|
|
1066
|
+
max_tokens: options?.maxTokens ?? this.config.maxTokens ?? 8192,
|
|
1067
|
+
temperature: options?.temperature ?? this.config.temperature ?? 0,
|
|
1068
|
+
system: this.extractSystem(messages, options?.system),
|
|
1069
|
+
messages: this.convertMessages(messages),
|
|
1070
|
+
tools: this.convertTools(options.tools),
|
|
1071
|
+
tool_choice: options.toolChoice ? this.convertToolChoice(options.toolChoice) : void 0
|
|
1072
|
+
},
|
|
1073
|
+
{ signal: options?.signal }
|
|
1074
|
+
);
|
|
1050
1075
|
let currentToolCall = null;
|
|
1051
1076
|
let currentToolInputJson = "";
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1077
|
+
const streamTimeout = this.config.timeout ?? 12e4;
|
|
1078
|
+
let lastActivityTime = Date.now();
|
|
1079
|
+
const checkTimeout = () => {
|
|
1080
|
+
if (Date.now() - lastActivityTime > streamTimeout) {
|
|
1081
|
+
throw new Error(`Stream timeout: No response from LLM for ${streamTimeout / 1e3}s`);
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
const timeoutInterval = setInterval(checkTimeout, 5e3);
|
|
1085
|
+
try {
|
|
1086
|
+
let streamStopReason;
|
|
1087
|
+
for await (const event of stream) {
|
|
1088
|
+
lastActivityTime = Date.now();
|
|
1089
|
+
if (event.type === "message_delta") {
|
|
1090
|
+
const delta = event.delta;
|
|
1091
|
+
if (delta.stop_reason) {
|
|
1092
|
+
streamStopReason = this.mapStopReason(delta.stop_reason);
|
|
1093
|
+
}
|
|
1094
|
+
} else if (event.type === "content_block_start") {
|
|
1095
|
+
const contentBlock = event.content_block;
|
|
1096
|
+
if (contentBlock.type === "tool_use") {
|
|
1097
|
+
if (currentToolCall) {
|
|
1098
|
+
getLogger().warn(
|
|
1099
|
+
`[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
|
|
1100
|
+
);
|
|
1101
|
+
try {
|
|
1102
|
+
currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
|
|
1103
|
+
} catch {
|
|
1104
|
+
currentToolCall.input = {};
|
|
1105
|
+
}
|
|
1106
|
+
yield {
|
|
1107
|
+
type: "tool_use_end",
|
|
1108
|
+
toolCall: { ...currentToolCall }
|
|
1109
|
+
};
|
|
1110
|
+
}
|
|
1111
|
+
currentToolCall = {
|
|
1112
|
+
id: contentBlock.id,
|
|
1113
|
+
name: contentBlock.name
|
|
1114
|
+
};
|
|
1115
|
+
currentToolInputJson = "";
|
|
1116
|
+
yield {
|
|
1117
|
+
type: "tool_use_start",
|
|
1118
|
+
toolCall: { ...currentToolCall }
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
} else if (event.type === "content_block_delta") {
|
|
1122
|
+
const delta = event.delta;
|
|
1123
|
+
if (delta.type === "text_delta" && delta.text) {
|
|
1124
|
+
yield { type: "text", text: delta.text };
|
|
1125
|
+
} else if (delta.type === "input_json_delta" && delta.partial_json) {
|
|
1126
|
+
currentToolInputJson += delta.partial_json;
|
|
1127
|
+
yield {
|
|
1128
|
+
type: "tool_use_delta",
|
|
1129
|
+
toolCall: {
|
|
1130
|
+
...currentToolCall
|
|
1131
|
+
},
|
|
1132
|
+
text: delta.partial_json
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
} else if (event.type === "content_block_stop") {
|
|
1056
1136
|
if (currentToolCall) {
|
|
1057
|
-
getLogger().warn(
|
|
1058
|
-
`[Anthropic] content_block_stop missing for tool '${currentToolCall.name}' \u2014 finalizing early to prevent data bleed.`
|
|
1059
|
-
);
|
|
1060
1137
|
try {
|
|
1061
1138
|
currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
|
|
1062
1139
|
} catch {
|
|
1063
|
-
|
|
1140
|
+
let repaired = false;
|
|
1141
|
+
if (currentToolInputJson) {
|
|
1142
|
+
try {
|
|
1143
|
+
currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
|
|
1144
|
+
repaired = true;
|
|
1145
|
+
getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
|
|
1146
|
+
} catch {
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
if (!repaired) {
|
|
1150
|
+
getLogger().warn(
|
|
1151
|
+
`Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
|
|
1152
|
+
);
|
|
1153
|
+
currentToolCall.input = {};
|
|
1154
|
+
}
|
|
1064
1155
|
}
|
|
1065
1156
|
yield {
|
|
1066
1157
|
type: "tool_use_end",
|
|
1067
1158
|
toolCall: { ...currentToolCall }
|
|
1068
1159
|
};
|
|
1160
|
+
currentToolCall = null;
|
|
1161
|
+
currentToolInputJson = "";
|
|
1069
1162
|
}
|
|
1070
|
-
currentToolCall = {
|
|
1071
|
-
id: contentBlock.id,
|
|
1072
|
-
name: contentBlock.name
|
|
1073
|
-
};
|
|
1074
|
-
currentToolInputJson = "";
|
|
1075
|
-
yield {
|
|
1076
|
-
type: "tool_use_start",
|
|
1077
|
-
toolCall: { ...currentToolCall }
|
|
1078
|
-
};
|
|
1079
|
-
}
|
|
1080
|
-
} else if (event.type === "content_block_delta") {
|
|
1081
|
-
const delta = event.delta;
|
|
1082
|
-
if (delta.type === "text_delta" && delta.text) {
|
|
1083
|
-
yield { type: "text", text: delta.text };
|
|
1084
|
-
} else if (delta.type === "input_json_delta" && delta.partial_json) {
|
|
1085
|
-
currentToolInputJson += delta.partial_json;
|
|
1086
|
-
yield {
|
|
1087
|
-
type: "tool_use_delta",
|
|
1088
|
-
toolCall: {
|
|
1089
|
-
...currentToolCall
|
|
1090
|
-
},
|
|
1091
|
-
text: delta.partial_json
|
|
1092
|
-
};
|
|
1093
|
-
}
|
|
1094
|
-
} else if (event.type === "content_block_stop") {
|
|
1095
|
-
if (currentToolCall) {
|
|
1096
|
-
try {
|
|
1097
|
-
currentToolCall.input = currentToolInputJson ? JSON.parse(currentToolInputJson) : {};
|
|
1098
|
-
} catch {
|
|
1099
|
-
let repaired = false;
|
|
1100
|
-
if (currentToolInputJson) {
|
|
1101
|
-
try {
|
|
1102
|
-
currentToolCall.input = JSON.parse(jsonrepair(currentToolInputJson));
|
|
1103
|
-
repaired = true;
|
|
1104
|
-
getLogger().debug(`Repaired JSON for tool ${currentToolCall.name}`);
|
|
1105
|
-
} catch {
|
|
1106
|
-
}
|
|
1107
|
-
}
|
|
1108
|
-
if (!repaired) {
|
|
1109
|
-
getLogger().warn(
|
|
1110
|
-
`Failed to parse tool call arguments for ${currentToolCall.name}: ${currentToolInputJson?.slice(0, 300)}`
|
|
1111
|
-
);
|
|
1112
|
-
currentToolCall.input = {};
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
yield {
|
|
1116
|
-
type: "tool_use_end",
|
|
1117
|
-
toolCall: { ...currentToolCall }
|
|
1118
|
-
};
|
|
1119
|
-
currentToolCall = null;
|
|
1120
|
-
currentToolInputJson = "";
|
|
1121
1163
|
}
|
|
1122
1164
|
}
|
|
1165
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
1166
|
+
} finally {
|
|
1167
|
+
clearInterval(timeoutInterval);
|
|
1123
1168
|
}
|
|
1124
|
-
yield { type: "done" };
|
|
1125
1169
|
} catch (error) {
|
|
1126
1170
|
throw this.handleError(error);
|
|
1127
1171
|
}
|
|
@@ -1621,13 +1665,18 @@ var init_openai = __esm({
|
|
|
1621
1665
|
stream: true,
|
|
1622
1666
|
...supportsTemp && { temperature: options?.temperature ?? this.config.temperature ?? 0 }
|
|
1623
1667
|
});
|
|
1668
|
+
let streamStopReason;
|
|
1624
1669
|
for await (const chunk of stream) {
|
|
1625
1670
|
const delta = chunk.choices[0]?.delta;
|
|
1626
1671
|
if (delta?.content) {
|
|
1627
1672
|
yield { type: "text", text: delta.content };
|
|
1628
1673
|
}
|
|
1674
|
+
const finishReason = chunk.choices[0]?.finish_reason;
|
|
1675
|
+
if (finishReason) {
|
|
1676
|
+
streamStopReason = this.mapFinishReason(finishReason);
|
|
1677
|
+
}
|
|
1629
1678
|
}
|
|
1630
|
-
yield { type: "done" };
|
|
1679
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
1631
1680
|
} catch (error) {
|
|
1632
1681
|
throw this.handleError(error);
|
|
1633
1682
|
}
|
|
@@ -1692,6 +1741,7 @@ var init_openai = __esm({
|
|
|
1692
1741
|
return input;
|
|
1693
1742
|
};
|
|
1694
1743
|
try {
|
|
1744
|
+
let streamStopReason;
|
|
1695
1745
|
for await (const chunk of stream) {
|
|
1696
1746
|
const delta = chunk.choices[0]?.delta;
|
|
1697
1747
|
if (delta?.content || delta?.tool_calls) {
|
|
@@ -1738,6 +1788,9 @@ var init_openai = __esm({
|
|
|
1738
1788
|
}
|
|
1739
1789
|
}
|
|
1740
1790
|
const finishReason = chunk.choices[0]?.finish_reason;
|
|
1791
|
+
if (finishReason) {
|
|
1792
|
+
streamStopReason = this.mapFinishReason(finishReason);
|
|
1793
|
+
}
|
|
1741
1794
|
if (finishReason && toolCallBuilders.size > 0) {
|
|
1742
1795
|
for (const [, builder] of toolCallBuilders) {
|
|
1743
1796
|
yield {
|
|
@@ -1762,7 +1815,7 @@ var init_openai = __esm({
|
|
|
1762
1815
|
}
|
|
1763
1816
|
};
|
|
1764
1817
|
}
|
|
1765
|
-
yield { type: "done" };
|
|
1818
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
1766
1819
|
} finally {
|
|
1767
1820
|
clearInterval(timeoutInterval);
|
|
1768
1821
|
}
|
|
@@ -3569,7 +3622,7 @@ var init_codex = __esm({
|
|
|
3569
3622
|
}
|
|
3570
3623
|
}
|
|
3571
3624
|
}
|
|
3572
|
-
yield { type: "done" };
|
|
3625
|
+
yield { type: "done", stopReason: response.stopReason };
|
|
3573
3626
|
}
|
|
3574
3627
|
/**
|
|
3575
3628
|
* Stream a chat response with tool use
|
|
@@ -3737,13 +3790,18 @@ var init_gemini = __esm({
|
|
|
3737
3790
|
const { history, lastMessage } = this.convertMessages(messages);
|
|
3738
3791
|
const chat = model.startChat({ history });
|
|
3739
3792
|
const result = await chat.sendMessageStream(lastMessage);
|
|
3793
|
+
let streamStopReason;
|
|
3740
3794
|
for await (const chunk of result.stream) {
|
|
3741
3795
|
const text13 = chunk.text();
|
|
3742
3796
|
if (text13) {
|
|
3743
3797
|
yield { type: "text", text: text13 };
|
|
3744
3798
|
}
|
|
3799
|
+
const finishReason = chunk.candidates?.[0]?.finishReason;
|
|
3800
|
+
if (finishReason) {
|
|
3801
|
+
streamStopReason = this.mapFinishReason(finishReason);
|
|
3802
|
+
}
|
|
3745
3803
|
}
|
|
3746
|
-
yield { type: "done" };
|
|
3804
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
3747
3805
|
} catch (error) {
|
|
3748
3806
|
throw this.handleError(error);
|
|
3749
3807
|
}
|
|
@@ -3778,11 +3836,16 @@ var init_gemini = __esm({
|
|
|
3778
3836
|
const chat = model.startChat({ history });
|
|
3779
3837
|
const result = await chat.sendMessageStream(lastMessage);
|
|
3780
3838
|
const emittedToolCalls = /* @__PURE__ */ new Set();
|
|
3839
|
+
let streamStopReason;
|
|
3781
3840
|
for await (const chunk of result.stream) {
|
|
3782
3841
|
const text13 = chunk.text();
|
|
3783
3842
|
if (text13) {
|
|
3784
3843
|
yield { type: "text", text: text13 };
|
|
3785
3844
|
}
|
|
3845
|
+
const finishReason = chunk.candidates?.[0]?.finishReason;
|
|
3846
|
+
if (finishReason) {
|
|
3847
|
+
streamStopReason = this.mapFinishReason(finishReason);
|
|
3848
|
+
}
|
|
3786
3849
|
const candidate = chunk.candidates?.[0];
|
|
3787
3850
|
if (candidate?.content?.parts) {
|
|
3788
3851
|
for (const part of candidate.content.parts) {
|
|
@@ -3816,7 +3879,7 @@ var init_gemini = __esm({
|
|
|
3816
3879
|
}
|
|
3817
3880
|
}
|
|
3818
3881
|
}
|
|
3819
|
-
yield { type: "done" };
|
|
3882
|
+
yield { type: "done", stopReason: streamStopReason };
|
|
3820
3883
|
} catch (error) {
|
|
3821
3884
|
throw this.handleError(error);
|
|
3822
3885
|
}
|
|
@@ -6564,7 +6627,7 @@ CONVERSATION:
|
|
|
6564
6627
|
* @param provider - The LLM provider to use for summarization
|
|
6565
6628
|
* @returns Compacted messages with summary replacing older messages
|
|
6566
6629
|
*/
|
|
6567
|
-
async compact(messages, provider) {
|
|
6630
|
+
async compact(messages, provider, signal) {
|
|
6568
6631
|
const conversationMessages = messages.filter((m) => m.role !== "system");
|
|
6569
6632
|
if (conversationMessages.length <= this.config.preserveLastN) {
|
|
6570
6633
|
return {
|
|
@@ -6596,7 +6659,7 @@ CONVERSATION:
|
|
|
6596
6659
|
}
|
|
6597
6660
|
const originalTokens = this.estimateTokens(messages, provider);
|
|
6598
6661
|
const conversationText = this.formatMessagesForSummary(messagesToSummarize);
|
|
6599
|
-
const summary = await this.generateSummary(conversationText, provider);
|
|
6662
|
+
const summary = await this.generateSummary(conversationText, provider, signal);
|
|
6600
6663
|
const systemMessages = messages.filter((m) => m.role === "system");
|
|
6601
6664
|
const summaryMessage = {
|
|
6602
6665
|
role: "user",
|
|
@@ -6650,16 +6713,30 @@ ${summary}
|
|
|
6650
6713
|
/**
|
|
6651
6714
|
* Generate a summary of the conversation using the LLM
|
|
6652
6715
|
*/
|
|
6653
|
-
async generateSummary(conversationText, provider) {
|
|
6716
|
+
async generateSummary(conversationText, provider, signal) {
|
|
6717
|
+
if (signal?.aborted) return "[Compaction cancelled]";
|
|
6654
6718
|
const prompt = COMPACTION_PROMPT + conversationText;
|
|
6655
6719
|
try {
|
|
6656
|
-
const
|
|
6720
|
+
const chatPromise = provider.chat([{ role: "user", content: prompt }], {
|
|
6657
6721
|
maxTokens: this.config.summaryMaxTokens,
|
|
6658
6722
|
temperature: 0.3
|
|
6659
6723
|
// Lower temperature for more consistent summaries
|
|
6660
6724
|
});
|
|
6725
|
+
if (signal) {
|
|
6726
|
+
const abortPromise = new Promise((_, reject) => {
|
|
6727
|
+
signal.addEventListener(
|
|
6728
|
+
"abort",
|
|
6729
|
+
() => reject(new DOMException("Aborted", "AbortError")),
|
|
6730
|
+
{ once: true }
|
|
6731
|
+
);
|
|
6732
|
+
});
|
|
6733
|
+
const response2 = await Promise.race([chatPromise, abortPromise]);
|
|
6734
|
+
return response2.content;
|
|
6735
|
+
}
|
|
6736
|
+
const response = await chatPromise;
|
|
6661
6737
|
return response.content;
|
|
6662
6738
|
} catch (error) {
|
|
6739
|
+
if (error instanceof DOMException && error.name === "AbortError") throw error;
|
|
6663
6740
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6664
6741
|
return `[Summary generation failed: ${errorMessage}. Previous conversation had ${conversationText.length} characters.]`;
|
|
6665
6742
|
}
|
|
@@ -6795,7 +6872,14 @@ function addMessage(session, message) {
|
|
|
6795
6872
|
session.messages.push(message);
|
|
6796
6873
|
const maxMessages = session.config.ui.maxHistorySize * 2;
|
|
6797
6874
|
if (session.messages.length > maxMessages) {
|
|
6798
|
-
|
|
6875
|
+
let sliceStart = session.messages.length - session.config.ui.maxHistorySize;
|
|
6876
|
+
while (sliceStart > 0 && sliceStart < session.messages.length) {
|
|
6877
|
+
const msg = session.messages[sliceStart];
|
|
6878
|
+
const isToolResult = Array.isArray(msg?.content) && msg.content.length > 0 && msg.content[0]?.type === "tool_result";
|
|
6879
|
+
if (!isToolResult) break;
|
|
6880
|
+
sliceStart--;
|
|
6881
|
+
}
|
|
6882
|
+
session.messages = session.messages.slice(sliceStart);
|
|
6799
6883
|
}
|
|
6800
6884
|
}
|
|
6801
6885
|
function substituteDynamicContext(body, cwd) {
|
|
@@ -7062,7 +7146,7 @@ function updateContextTokens(session, provider) {
|
|
|
7062
7146
|
}
|
|
7063
7147
|
session.contextManager.setUsedTokens(totalTokens);
|
|
7064
7148
|
}
|
|
7065
|
-
async function checkAndCompactContext(session, provider) {
|
|
7149
|
+
async function checkAndCompactContext(session, provider, signal) {
|
|
7066
7150
|
if (!session.contextManager) {
|
|
7067
7151
|
initializeContextManager(session, provider);
|
|
7068
7152
|
}
|
|
@@ -7074,7 +7158,7 @@ async function checkAndCompactContext(session, provider) {
|
|
|
7074
7158
|
preserveLastN: 4,
|
|
7075
7159
|
summaryMaxTokens: 1e3
|
|
7076
7160
|
});
|
|
7077
|
-
const result = await compactor.compact(session.messages, provider);
|
|
7161
|
+
const result = await compactor.compact(session.messages, provider, signal);
|
|
7078
7162
|
if (result.wasCompacted) {
|
|
7079
7163
|
const compactedNonSystem = result.messages.filter((m) => m.role !== "system");
|
|
7080
7164
|
session.messages = compactedNonSystem;
|
|
@@ -44587,10 +44671,12 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
44587
44671
|
let responseContent = "";
|
|
44588
44672
|
const collectedToolCalls = [];
|
|
44589
44673
|
let thinkingEnded = false;
|
|
44674
|
+
let lastStopReason;
|
|
44590
44675
|
const toolCallBuilders = /* @__PURE__ */ new Map();
|
|
44591
44676
|
for await (const chunk of provider.streamWithTools(messages, {
|
|
44592
44677
|
tools,
|
|
44593
|
-
maxTokens: session.config.provider.maxTokens
|
|
44678
|
+
maxTokens: session.config.provider.maxTokens,
|
|
44679
|
+
signal: options.signal
|
|
44594
44680
|
})) {
|
|
44595
44681
|
if (options.signal?.aborted) {
|
|
44596
44682
|
break;
|
|
@@ -44640,6 +44726,9 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
44640
44726
|
}
|
|
44641
44727
|
}
|
|
44642
44728
|
if (chunk.type === "done") {
|
|
44729
|
+
if (chunk.stopReason) {
|
|
44730
|
+
lastStopReason = chunk.stopReason;
|
|
44731
|
+
}
|
|
44643
44732
|
if (!thinkingEnded) {
|
|
44644
44733
|
options.onThinkingEnd?.();
|
|
44645
44734
|
thinkingEnded = true;
|
|
@@ -44658,6 +44747,14 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
44658
44747
|
if (options.signal?.aborted) {
|
|
44659
44748
|
return abortReturn();
|
|
44660
44749
|
}
|
|
44750
|
+
if (lastStopReason === "max_tokens" && responseContent) {
|
|
44751
|
+
addMessage(session, { role: "assistant", content: responseContent });
|
|
44752
|
+
addMessage(session, {
|
|
44753
|
+
role: "user",
|
|
44754
|
+
content: "[System: Your previous response was cut off due to the output token limit. Continue exactly where you left off.]"
|
|
44755
|
+
});
|
|
44756
|
+
continue;
|
|
44757
|
+
}
|
|
44661
44758
|
addMessage(session, { role: "assistant", content: responseContent });
|
|
44662
44759
|
break;
|
|
44663
44760
|
}
|
|
@@ -44864,7 +44961,8 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
44864
44961
|
const finalMessages = getConversationContext(session, toolRegistry);
|
|
44865
44962
|
for await (const chunk of provider.streamWithTools(finalMessages, {
|
|
44866
44963
|
tools: [],
|
|
44867
|
-
maxTokens: session.config.provider.maxTokens
|
|
44964
|
+
maxTokens: session.config.provider.maxTokens,
|
|
44965
|
+
signal: options.signal
|
|
44868
44966
|
})) {
|
|
44869
44967
|
if (options.signal?.aborted) break;
|
|
44870
44968
|
if (chunk.type === "text" && chunk.text) {
|
|
@@ -44878,6 +44976,14 @@ async function executeAgentTurn(session, userMessage, provider, toolRegistry, op
|
|
|
44878
44976
|
break;
|
|
44879
44977
|
}
|
|
44880
44978
|
}
|
|
44979
|
+
if (iteration >= maxIterations) {
|
|
44980
|
+
const notice = `
|
|
44981
|
+
|
|
44982
|
+
---
|
|
44983
|
+
_Reached the iteration limit (${maxIterations}). The task may be incomplete. You can say "continue" to resume._`;
|
|
44984
|
+
finalContent += notice;
|
|
44985
|
+
options.onStream?.({ type: "text", text: notice });
|
|
44986
|
+
}
|
|
44881
44987
|
options.onStream?.({ type: "done" });
|
|
44882
44988
|
return {
|
|
44883
44989
|
content: finalContent,
|
|
@@ -46059,16 +46165,33 @@ async function startRepl(options = {}) {
|
|
|
46059
46165
|
const usageBefore = getContextUsagePercent(session);
|
|
46060
46166
|
let usageForDisplay = usageBefore;
|
|
46061
46167
|
try {
|
|
46062
|
-
const
|
|
46063
|
-
|
|
46064
|
-
|
|
46065
|
-
|
|
46066
|
-
|
|
46067
|
-
|
|
46068
|
-
|
|
46168
|
+
const compactAbort = new AbortController();
|
|
46169
|
+
const compactTimeout = setTimeout(() => compactAbort.abort(), 3e4);
|
|
46170
|
+
const compactSigint = () => compactAbort.abort();
|
|
46171
|
+
process.once("SIGINT", compactSigint);
|
|
46172
|
+
const compactSpinner = createSpinner("Compacting context");
|
|
46173
|
+
compactSpinner.start();
|
|
46174
|
+
try {
|
|
46175
|
+
const compactionResult = await checkAndCompactContext(
|
|
46176
|
+
session,
|
|
46177
|
+
provider,
|
|
46178
|
+
compactAbort.signal
|
|
46069
46179
|
);
|
|
46070
|
-
|
|
46071
|
-
|
|
46180
|
+
if (compactionResult?.wasCompacted) {
|
|
46181
|
+
usageForDisplay = getContextUsagePercent(session);
|
|
46182
|
+
compactSpinner.stop(
|
|
46183
|
+
`Context compacted (${usageBefore.toFixed(0)}% \u2192 ${usageForDisplay.toFixed(0)}%)`
|
|
46184
|
+
);
|
|
46185
|
+
warned75 = false;
|
|
46186
|
+
warned90 = false;
|
|
46187
|
+
} else {
|
|
46188
|
+
compactSpinner.clear();
|
|
46189
|
+
}
|
|
46190
|
+
} catch {
|
|
46191
|
+
compactSpinner.clear();
|
|
46192
|
+
} finally {
|
|
46193
|
+
clearTimeout(compactTimeout);
|
|
46194
|
+
process.off("SIGINT", compactSigint);
|
|
46072
46195
|
}
|
|
46073
46196
|
} catch {
|
|
46074
46197
|
}
|