sam-coder-cli 1.0.63 → 1.0.65

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 +105 -3
  2. package/package.json +1 -1
package/bin/agi-cli.js CHANGED
@@ -429,6 +429,9 @@ const agentUtils = {
429
429
 
430
430
  async writeFile(filePath, content) {
431
431
  try {
432
+ // Ensure directory exists
433
+ const dir = path.dirname(filePath);
434
+ await fs.mkdir(dir, { recursive: true });
432
435
  await fs.writeFile(filePath, content, 'utf-8');
433
436
  return `Successfully wrote to ${filePath}`;
434
437
  } catch (error) {
@@ -755,7 +758,7 @@ function normalizeToolCallsFromMessage(message) {
755
758
  // Parse segmented format like <|start|>channel<|message|>...<|end|>
756
759
  function parseSegmentedTranscript(text) {
757
760
  if (!text || typeof text !== 'string') {
758
- return { content: text || '', thought: '', recoveredToolCalls: null };
761
+ return { content: text || '', thought: '', recoveredToolCalls: null, segmented: false };
759
762
  }
760
763
 
761
764
  const blockRegex = /\<\|start\|>([^<|]+)\<\|message\|>([\s\S]*?)\<\|end\|>/gi;
@@ -763,11 +766,13 @@ function parseSegmentedTranscript(text) {
763
766
  let visibleParts = [];
764
767
  let thoughts = [];
765
768
  let commentaryParts = [];
769
+ let segmentedFound = false;
766
770
 
767
771
  while ((match = blockRegex.exec(text)) !== null) {
768
772
  const rawRole = (match[1] || '').trim().toLowerCase();
769
773
  const body = (match[2] || '').trim();
770
774
  if (!rawRole) continue;
775
+ segmentedFound = true;
771
776
 
772
777
  if (rawRole === 'analysis') {
773
778
  thoughts.push(body);
@@ -784,7 +789,72 @@ function parseSegmentedTranscript(text) {
784
789
 
785
790
  // If no blocks matched, return original
786
791
  if (visibleParts.length === 0 && thoughts.length === 0 && commentaryParts.length === 0) {
787
- return { content: text, thought: '', recoveredToolCalls: null };
792
+ // Try channel-only segments: <|channel|>X [to=Y] [<|constrain|>Z] <|message|>... (<|end|>|<|call|>|<|return|>)
793
+ const chanRegex = /\<\|channel\|>\s*([a-zA-Z]+)\s*(?:to=([^\s<]+))?\s*(?:\<\|constrain\|>(\w+))?\s*\<\|message\|>([\s\S]*?)(?:\<\|end\|>|\<\|call\|>|\<\|return\|>)/gi;
794
+ let anyChannel = false;
795
+ let commsWithRecipients = [];
796
+ while ((match = chanRegex.exec(text)) !== null) {
797
+ anyChannel = true;
798
+ segmentedFound = true;
799
+ const channel = (match[1] || '').trim().toLowerCase();
800
+ const recipient = (match[2] || '').trim();
801
+ const constraint = (match[3] || '').trim().toLowerCase();
802
+ const body = (match[4] || '').trim();
803
+ if (channel === 'analysis') {
804
+ thoughts.push(body);
805
+ } else if (channel === 'commentary') {
806
+ if (recipient) {
807
+ commsWithRecipients.push({ recipient, constraint, body });
808
+ } else {
809
+ // preamble visible to user per spec
810
+ visibleParts.push(body);
811
+ }
812
+ } else if (channel === 'final') {
813
+ visibleParts.push(body);
814
+ } else {
815
+ visibleParts.push(body);
816
+ }
817
+ }
818
+ // Build recovered tool calls from commentary with recipients
819
+ let recoveredToolCalls = null;
820
+ if (commsWithRecipients.length) {
821
+ recoveredToolCalls = [];
822
+ for (const item of commsWithRecipients) {
823
+ // recipient format like functions.get_weather
824
+ let funcName = item.recipient;
825
+ if (funcName.startsWith('functions.')) {
826
+ funcName = funcName.slice('functions.'.length);
827
+ }
828
+ // parse args
829
+ let args = {};
830
+ if (item.constraint === 'json' || item.body.startsWith('{') || item.body.startsWith('[')) {
831
+ try {
832
+ const parsed = JSON.parse(item.body);
833
+ args = parsed;
834
+ } catch (_) {
835
+ // ignore parse error; leave empty
836
+ }
837
+ }
838
+ recoveredToolCalls.push({
839
+ id: `inline-${recoveredToolCalls.length + 1}`,
840
+ type: 'function',
841
+ function: {
842
+ name: funcName,
843
+ arguments: typeof args === 'string' ? args : JSON.stringify(args)
844
+ }
845
+ });
846
+ }
847
+ }
848
+
849
+ if (!anyChannel) {
850
+ return { content: text, thought: '', recoveredToolCalls: null, segmented: false };
851
+ }
852
+ return {
853
+ content: visibleParts.join('\n\n').trim(),
854
+ thought: thoughts.join('\n\n').trim(),
855
+ recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null,
856
+ segmented: segmentedFound
857
+ };
788
858
  }
789
859
 
790
860
  // Look for a Reasoning: level outside blocks as a hint
@@ -807,7 +877,8 @@ function parseSegmentedTranscript(text) {
807
877
  return {
808
878
  content: visibleParts.join('\n\n').trim(),
809
879
  thought: thoughts.join('\n\n').trim(),
810
- recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null
880
+ recoveredToolCalls: recoveredToolCalls && recoveredToolCalls.length ? recoveredToolCalls : null,
881
+ segmented: segmentedFound
811
882
  };
812
883
  }
813
884
 
@@ -1251,6 +1322,37 @@ async function chat(rl, useToolCalling, initialModel) {
1251
1322
  return;
1252
1323
  }
1253
1324
 
1325
+ // Direct Harmony tool-call execution from user input (bypass model)
1326
+ try {
1327
+ const seg = parseSegmentedTranscript(input);
1328
+ if (seg && seg.segmented && seg.recoveredToolCalls && seg.recoveredToolCalls.length) {
1329
+ const messages = [];
1330
+ if (useToolCalling) {
1331
+ messages.push({ role: 'system', content: TOOL_CALLING_PROMPT });
1332
+ } else {
1333
+ messages.push({ role: 'system', content: FUNCTION_CALLING_PROMPT });
1334
+ }
1335
+ messages.push({ role: 'user', content: input });
1336
+ ui.startThinking();
1337
+ const results = await handleToolCalls(seg.recoveredToolCalls, messages);
1338
+ ui.stopThinking();
1339
+ if (results.length === 1) {
1340
+ ui.showSuccess(`Tool '${results[0].name}' executed.`);
1341
+ } else {
1342
+ ui.showSuccess(`Executed ${results.length} tool calls.`);
1343
+ }
1344
+ // Show concise outputs
1345
+ results.forEach(r => {
1346
+ const preview = typeof r.content === 'string' ? r.content : JSON.stringify(r.content);
1347
+ ui.showInfo(`${r.name}: ${preview.length > 300 ? preview.slice(0, 300) + '...' : preview}`);
1348
+ });
1349
+ rl.prompt();
1350
+ return;
1351
+ }
1352
+ } catch (e) {
1353
+ // Fall through to normal processing if parsing/execution fails
1354
+ }
1355
+
1254
1356
  const result = useToolCalling
1255
1357
  ? await processQueryWithTools(input, conversation, currentModel)
1256
1358
  : await processQuery(input, conversation, currentModel);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sam-coder-cli",
3
- "version": "1.0.63",
3
+ "version": "1.0.65",
4
4
  "description": "SAM-CODER: An animated command-line AI assistant with agency capabilities.",
5
5
  "main": "bin/agi-cli.js",
6
6
  "bin": {