sam-coder-cli 1.0.62 → 1.0.63

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.
Files changed (2) hide show
  1. package/bin/agi-cli.js +95 -4
  2. package/package.json +1 -1
package/bin/agi-cli.js CHANGED
@@ -752,6 +752,65 @@ 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 };
759
+ }
760
+
761
+ const blockRegex = /\<\|start\|>([^<|]+)\<\|message\|>([\s\S]*?)\<\|end\|>/gi;
762
+ let match;
763
+ let visibleParts = [];
764
+ let thoughts = [];
765
+ let commentaryParts = [];
766
+
767
+ while ((match = blockRegex.exec(text)) !== null) {
768
+ const rawRole = (match[1] || '').trim().toLowerCase();
769
+ const body = (match[2] || '').trim();
770
+ if (!rawRole) continue;
771
+
772
+ if (rawRole === 'analysis') {
773
+ thoughts.push(body);
774
+ } else if (rawRole === 'commentary') {
775
+ commentaryParts.push(body);
776
+ } else if (rawRole === 'final' || rawRole === 'assistant' || rawRole === 'user' || rawRole === 'system' || rawRole === 'developer') {
777
+ // Prefer 'final' or 'assistant' as visible, but include others to preserve content order
778
+ visibleParts.push(body);
779
+ } else {
780
+ // Unknown channel: treat as visible content
781
+ visibleParts.push(body);
782
+ }
783
+ }
784
+
785
+ // If no blocks matched, return original
786
+ if (visibleParts.length === 0 && thoughts.length === 0 && commentaryParts.length === 0) {
787
+ return { content: text, thought: '', recoveredToolCalls: null };
788
+ }
789
+
790
+ // Look for a Reasoning: level outside blocks as a hint
791
+ const reasoningMatch = text.match(/Reasoning:\s*(high|medium|low)/i);
792
+ if (reasoningMatch) {
793
+ thoughts.unshift(`Reasoning level: ${reasoningMatch[1]}`);
794
+ }
795
+
796
+ // Recover tool calls from commentary channels
797
+ let recoveredToolCalls = null;
798
+ if (commentaryParts.length) {
799
+ for (const part of commentaryParts) {
800
+ const found = parseInlineToolCalls(part);
801
+ if (found && found.length) {
802
+ recoveredToolCalls = (recoveredToolCalls || []).concat(found);
803
+ }
804
+ }
805
+ }
806
+
807
+ return {
808
+ content: visibleParts.join('\n\n').trim(),
809
+ thought: thoughts.join('\n\n').trim(),
810
+ recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null
811
+ };
812
+ }
813
+
755
814
  // Call OpenRouter API with tool calling
756
815
  async function callOpenRouter(messages, currentModel, useJson = false) {
757
816
  const apiKey = OPENROUTER_API_KEY;
@@ -810,7 +869,18 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
810
869
  const assistantMessage = response.choices[0].message;
811
870
  // Handle thinking tags and optionally display them
812
871
  if (assistantMessage && typeof assistantMessage.content === 'string') {
813
- const { thought, content } = splitThinking(assistantMessage.content);
872
+ // First handle segmented transcripts, then fallback to <think>
873
+ const segmented = parseSegmentedTranscript(assistantMessage.content);
874
+ let thought = segmented.thought;
875
+ let content = segmented.content;
876
+ if (!segmented.thought && !segmented.recoveredToolCalls) {
877
+ const thinkSplit = splitThinking(assistantMessage.content);
878
+ thought = thought || thinkSplit.thought;
879
+ content = content || thinkSplit.content;
880
+ }
881
+ if (segmented.recoveredToolCalls && (!assistantMessage.tool_calls)) {
882
+ assistantMessage.tool_calls = segmented.recoveredToolCalls;
883
+ }
814
884
  if (thought && SHOW_THOUGHTS) {
815
885
  ui.showThought(thought);
816
886
  }
@@ -834,7 +904,14 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
834
904
  const finalResponseObj = await callOpenRouter(messages, currentModel);
835
905
  const finalAssistantMessage = finalResponseObj.choices[0].message;
836
906
  if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
837
- const { thought, content } = splitThinking(finalAssistantMessage.content);
907
+ const segmented = parseSegmentedTranscript(finalAssistantMessage.content);
908
+ let thought = segmented.thought;
909
+ let content = segmented.content;
910
+ if (!segmented.thought && !segmented.recoveredToolCalls) {
911
+ const thinkSplit = splitThinking(finalAssistantMessage.content);
912
+ thought = thought || thinkSplit.thought;
913
+ content = content || thinkSplit.content;
914
+ }
838
915
  if (thought && SHOW_THOUGHTS) {
839
916
  ui.showThought(thought);
840
917
  }
@@ -859,7 +936,14 @@ async function processQueryWithTools(query, conversation = [], currentModel) {
859
936
  const finalResponseObj = await callOpenRouter(messages, currentModel);
860
937
  const finalAssistantMessage = finalResponseObj.choices[0].message;
861
938
  if (finalAssistantMessage && typeof finalAssistantMessage.content === 'string') {
862
- const { thought, content } = splitThinking(finalAssistantMessage.content);
939
+ const segmented = parseSegmentedTranscript(finalAssistantMessage.content);
940
+ let thought = segmented.thought;
941
+ let content = segmented.content;
942
+ if (!segmented.thought && !segmented.recoveredToolCalls) {
943
+ const thinkSplit = splitThinking(finalAssistantMessage.content);
944
+ thought = thought || thinkSplit.thought;
945
+ content = content || thinkSplit.content;
946
+ }
863
947
  if (thought && SHOW_THOUGHTS) {
864
948
  ui.showThought(thought);
865
949
  }
@@ -1001,7 +1085,14 @@ async function processQuery(query, conversation = [], currentModel) {
1001
1085
  const responseObj = await callOpenRouter(messages, currentModel, true);
1002
1086
  const assistantMessage = responseObj.choices[0].message;
1003
1087
  if (assistantMessage && typeof assistantMessage.content === 'string') {
1004
- const { thought, content } = splitThinking(assistantMessage.content);
1088
+ const segmented = parseSegmentedTranscript(assistantMessage.content);
1089
+ let thought = segmented.thought;
1090
+ let content = segmented.content;
1091
+ if (!segmented.thought && !segmented.recoveredToolCalls) {
1092
+ const thinkSplit = splitThinking(assistantMessage.content);
1093
+ thought = thought || thinkSplit.thought;
1094
+ content = content || thinkSplit.content;
1095
+ }
1005
1096
  if (thought && SHOW_THOUGHTS) {
1006
1097
  ui.showThought(thought);
1007
1098
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.62",
3
+ "version": "1.0.63",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {