sam-coder-cli 1.0.62 → 1.0.64
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/bin/agi-cli.js +163 -4
- package/package.json +1 -1
package/bin/agi-cli.js
CHANGED
|
@@ -752,6 +752,133 @@ function normalizeToolCallsFromMessage(message) {
|
|
|
752
752
|
return message;
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
+
// Parse segmented format like <|start|>channel<|message|>...<|end|>
|
|
756
|
+
function parseSegmentedTranscript(text) {
|
|
757
|
+
if (!text || typeof text !== 'string') {
|
|
758
|
+
return { content: text || '', thought: '', recoveredToolCalls: null, segmented: false };
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
const blockRegex = /\<\|start\|>([^<|]+)\<\|message\|>([\s\S]*?)\<\|end\|>/gi;
|
|
762
|
+
let match;
|
|
763
|
+
let visibleParts = [];
|
|
764
|
+
let thoughts = [];
|
|
765
|
+
let commentaryParts = [];
|
|
766
|
+
let segmentedFound = false;
|
|
767
|
+
|
|
768
|
+
while ((match = blockRegex.exec(text)) !== null) {
|
|
769
|
+
const rawRole = (match[1] || '').trim().toLowerCase();
|
|
770
|
+
const body = (match[2] || '').trim();
|
|
771
|
+
if (!rawRole) continue;
|
|
772
|
+
segmentedFound = true;
|
|
773
|
+
|
|
774
|
+
if (rawRole === 'analysis') {
|
|
775
|
+
thoughts.push(body);
|
|
776
|
+
} else if (rawRole === 'commentary') {
|
|
777
|
+
commentaryParts.push(body);
|
|
778
|
+
} else if (rawRole === 'final' || rawRole === 'assistant' || rawRole === 'user' || rawRole === 'system' || rawRole === 'developer') {
|
|
779
|
+
// Prefer 'final' or 'assistant' as visible, but include others to preserve content order
|
|
780
|
+
visibleParts.push(body);
|
|
781
|
+
} else {
|
|
782
|
+
// Unknown channel: treat as visible content
|
|
783
|
+
visibleParts.push(body);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// If no blocks matched, return original
|
|
788
|
+
if (visibleParts.length === 0 && thoughts.length === 0 && commentaryParts.length === 0) {
|
|
789
|
+
// Try channel-only segments: <|channel|>X [to=Y] [<|constrain|>Z] <|message|>... (<|end|>|<|call|>|<|return|>)
|
|
790
|
+
const chanRegex = /\<\|channel\|>\s*([a-zA-Z]+)\s*(?:to=([^\s<]+))?\s*(?:\<\|constrain\|>(\w+))?\s*\<\|message\|>([\s\S]*?)(?:\<\|end\|>|\<\|call\|>|\<\|return\|>)/gi;
|
|
791
|
+
let anyChannel = false;
|
|
792
|
+
let commsWithRecipients = [];
|
|
793
|
+
while ((match = chanRegex.exec(text)) !== null) {
|
|
794
|
+
anyChannel = true;
|
|
795
|
+
segmentedFound = true;
|
|
796
|
+
const channel = (match[1] || '').trim().toLowerCase();
|
|
797
|
+
const recipient = (match[2] || '').trim();
|
|
798
|
+
const constraint = (match[3] || '').trim().toLowerCase();
|
|
799
|
+
const body = (match[4] || '').trim();
|
|
800
|
+
if (channel === 'analysis') {
|
|
801
|
+
thoughts.push(body);
|
|
802
|
+
} else if (channel === 'commentary') {
|
|
803
|
+
if (recipient) {
|
|
804
|
+
commsWithRecipients.push({ recipient, constraint, body });
|
|
805
|
+
} else {
|
|
806
|
+
// preamble visible to user per spec
|
|
807
|
+
visibleParts.push(body);
|
|
808
|
+
}
|
|
809
|
+
} else if (channel === 'final') {
|
|
810
|
+
visibleParts.push(body);
|
|
811
|
+
} else {
|
|
812
|
+
visibleParts.push(body);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
// Build recovered tool calls from commentary with recipients
|
|
816
|
+
let recoveredToolCalls = null;
|
|
817
|
+
if (commsWithRecipients.length) {
|
|
818
|
+
recoveredToolCalls = [];
|
|
819
|
+
for (const item of commsWithRecipients) {
|
|
820
|
+
// recipient format like functions.get_weather
|
|
821
|
+
let funcName = item.recipient;
|
|
822
|
+
if (funcName.startsWith('functions.')) {
|
|
823
|
+
funcName = funcName.slice('functions.'.length);
|
|
824
|
+
}
|
|
825
|
+
// parse args
|
|
826
|
+
let args = {};
|
|
827
|
+
if (item.constraint === 'json' || item.body.startsWith('{') || item.body.startsWith('[')) {
|
|
828
|
+
try {
|
|
829
|
+
const parsed = JSON.parse(item.body);
|
|
830
|
+
args = parsed;
|
|
831
|
+
} catch (_) {
|
|
832
|
+
// ignore parse error; leave empty
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
recoveredToolCalls.push({
|
|
836
|
+
id: `inline-${recoveredToolCalls.length + 1}`,
|
|
837
|
+
type: 'function',
|
|
838
|
+
function: {
|
|
839
|
+
name: funcName,
|
|
840
|
+
arguments: typeof args === 'string' ? args : JSON.stringify(args)
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (!anyChannel) {
|
|
847
|
+
return { content: text, thought: '', recoveredToolCalls: null, segmented: false };
|
|
848
|
+
}
|
|
849
|
+
return {
|
|
850
|
+
content: visibleParts.join('\n\n').trim(),
|
|
851
|
+
thought: thoughts.join('\n\n').trim(),
|
|
852
|
+
recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null,
|
|
853
|
+
segmented: segmentedFound
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Look for a Reasoning: level outside blocks as a hint
|
|
858
|
+
const reasoningMatch = text.match(/Reasoning:\s*(high|medium|low)/i);
|
|
859
|
+
if (reasoningMatch) {
|
|
860
|
+
thoughts.unshift(`Reasoning level: ${reasoningMatch[1]}`);
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Recover tool calls from commentary channels
|
|
864
|
+
let recoveredToolCalls = null;
|
|
865
|
+
if (commentaryParts.length) {
|
|
866
|
+
for (const part of commentaryParts) {
|
|
867
|
+
const found = parseInlineToolCalls(part);
|
|
868
|
+
if (found && found.length) {
|
|
869
|
+
recoveredToolCalls = (recoveredToolCalls || []).concat(found);
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
return {
|
|
875
|
+
content: visibleParts.join('\n\n').trim(),
|
|
876
|
+
thought: thoughts.join('\n\n').trim(),
|
|
877
|
+
recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null,
|
|
878
|
+
segmented: segmentedFound
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
|
|
755
882
|
// Call OpenRouter API with tool calling
|
|
756
883
|
async function callOpenRouter(messages, currentModel, useJson = false) {
|
|
757
884
|
const apiKey = OPENROUTER_API_KEY;
|
|
@@ -810,7 +937,18 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
810
937
|
const assistantMessage = response.choices[0].message;
|
|
811
938
|
// Handle thinking tags and optionally display them
|
|
812
939
|
if (assistantMessage && typeof assistantMessage.content === 'string') {
|
|
813
|
-
|
|
940
|
+
// First handle segmented transcripts, then fallback to <think>
|
|
941
|
+
const segmented = parseSegmentedTranscript(assistantMessage.content);
|
|
942
|
+
let thought = segmented.thought;
|
|
943
|
+
let content = segmented.content;
|
|
944
|
+
if (!segmented.thought && !segmented.recoveredToolCalls) {
|
|
945
|
+
const thinkSplit = splitThinking(assistantMessage.content);
|
|
946
|
+
thought = thought || thinkSplit.thought;
|
|
947
|
+
content = content || thinkSplit.content;
|
|
948
|
+
}
|
|
949
|
+
if (segmented.recoveredToolCalls && (!assistantMessage.tool_calls)) {
|
|
950
|
+
assistantMessage.tool_calls = segmented.recoveredToolCalls;
|
|
951
|
+
}
|
|
814
952
|
if (thought && SHOW_THOUGHTS) {
|
|
815
953
|
ui.showThought(thought);
|
|
816
954
|
}
|
|
@@ -834,7 +972,14 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
834
972
|
const finalResponseObj = await callOpenRouter(messages, currentModel);
|
|
835
973
|
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
836
974
|
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
837
|
-
const
|
|
975
|
+
const segmented = parseSegmentedTranscript(finalAssistantMessage.content);
|
|
976
|
+
let thought = segmented.thought;
|
|
977
|
+
let content = segmented.content;
|
|
978
|
+
if (!segmented.thought && !segmented.recoveredToolCalls) {
|
|
979
|
+
const thinkSplit = splitThinking(finalAssistantMessage.content);
|
|
980
|
+
thought = thought || thinkSplit.thought;
|
|
981
|
+
content = content || thinkSplit.content;
|
|
982
|
+
}
|
|
838
983
|
if (thought && SHOW_THOUGHTS) {
|
|
839
984
|
ui.showThought(thought);
|
|
840
985
|
}
|
|
@@ -859,7 +1004,14 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
|
|
|
859
1004
|
const finalResponseObj = await callOpenRouter(messages, currentModel);
|
|
860
1005
|
const finalAssistantMessage = finalResponseObj.choices[0].message;
|
|
861
1006
|
if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
|
|
862
|
-
const
|
|
1007
|
+
const segmented = parseSegmentedTranscript(finalAssistantMessage.content);
|
|
1008
|
+
let thought = segmented.thought;
|
|
1009
|
+
let content = segmented.content;
|
|
1010
|
+
if (!segmented.thought && !segmented.recoveredToolCalls) {
|
|
1011
|
+
const thinkSplit = splitThinking(finalAssistantMessage.content);
|
|
1012
|
+
thought = thought || thinkSplit.thought;
|
|
1013
|
+
content = content || thinkSplit.content;
|
|
1014
|
+
}
|
|
863
1015
|
if (thought && SHOW_THOUGHTS) {
|
|
864
1016
|
ui.showThought(thought);
|
|
865
1017
|
}
|
|
@@ -1001,7 +1153,14 @@ async function processQuery(query, conversation = [], currentModel) {
|
|
|
1001
1153
|
const responseObj = await callOpenRouter(messages, currentModel, true);
|
|
1002
1154
|
const assistantMessage = responseObj.choices[0].message;
|
|
1003
1155
|
if (assistantMessage && typeof assistantMessage.content === 'string') {
|
|
1004
|
-
const
|
|
1156
|
+
const segmented = parseSegmentedTranscript(assistantMessage.content);
|
|
1157
|
+
let thought = segmented.thought;
|
|
1158
|
+
let content = segmented.content;
|
|
1159
|
+
if (!segmented.thought && !segmented.recoveredToolCalls) {
|
|
1160
|
+
const thinkSplit = splitThinking(assistantMessage.content);
|
|
1161
|
+
thought = thought || thinkSplit.thought;
|
|
1162
|
+
content = content || thinkSplit.content;
|
|
1163
|
+
}
|
|
1005
1164
|
if (thought && SHOW_THOUGHTS) {
|
|
1006
1165
|
ui.showThought(thought);
|
|
1007
1166
|
}
|