nothumanallowed 15.0.14 → 15.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "15.0.14",
3
+ "version": "15.0.15",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 80 tools, Studio (visual agentic workflows). Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, Alexandria E2E messaging, GitHub, Notion, Slack, voice chat, free AI (Liara), 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -731,11 +731,14 @@ WORKFLOW:
731
731
  const isDone = stepResponse.includes('<done/>') || stepResponse.includes('<done />');
732
732
 
733
733
  // Extract and execute ALL tool calls from this step
734
+ // First try matched pairs, then handle truncated (unclosed) tool tags
734
735
  const toolRegex = /<tool>([\s\S]*?)<\/tool>/g;
735
736
  let match;
736
737
  const toolResults = [];
738
+ const matchedRanges = [];
737
739
 
738
740
  while ((match = toolRegex.exec(stepResponse)) !== null) {
741
+ matchedRanges.push([match.index, match.index + match[0].length]);
739
742
  let toolCall;
740
743
  try {
741
744
  let raw = match[1].trim();
@@ -1072,6 +1075,60 @@ WORKFLOW:
1072
1075
  }
1073
1076
  }
1074
1077
 
1078
+ // Handle truncated tool call — <tool> without </tool> (response cut off by max_tokens)
1079
+ const lastToolOpen = stepResponse.lastIndexOf('<tool>');
1080
+ if (lastToolOpen >= 0) {
1081
+ const alreadyMatched = matchedRanges.some(([start, end]) => lastToolOpen >= start && lastToolOpen < end);
1082
+ if (!alreadyMatched) {
1083
+ const truncatedRaw = stepResponse.slice(lastToolOpen + 6).trim();
1084
+ if (truncatedRaw.length > 20) {
1085
+ console.log('[TOOL-TRUNCATED] Found unclosed <tool> tag, attempting robust parse. Length:', truncatedRaw.length);
1086
+ try {
1087
+ const toolCall = _parseToolCallRobust(truncatedRaw);
1088
+ // Execute the truncated tool call
1089
+ const { op, path: relPath, old: oldStr, new: newStr, content } = toolCall;
1090
+ if (op === 'edit' && relPath && oldStr) {
1091
+ const src = ProjectStore.readFile(projectName, relPath);
1092
+ if (src && src.includes(oldStr)) {
1093
+ const newSrc = src.replace(oldStr, newStr ?? '');
1094
+ ProjectStore.writeFile(projectName, relPath, newSrc);
1095
+ hasChanges = true;
1096
+ modifiedFiles.add(relPath);
1097
+ toolResults.push({ op: 'edit', path: relPath, result: 'ok' });
1098
+ emit({ type: 'tool', op: 'edit', path: relPath, result: 'ok', oldSnippet: oldStr.slice(0, 2000), newSnippet: newStr?.slice(0, 2000) ?? '' });
1099
+ console.log('[TOOL-TRUNCATED] Edit applied successfully from truncated tool call');
1100
+ } else if (src) {
1101
+ // Try fuzzy match
1102
+ const oldLines = oldStr.split('\n').map(l => l.trim());
1103
+ const srcLines = src.split('\n');
1104
+ let matchStart = -1;
1105
+ for (let i = 0; i <= srcLines.length - oldLines.length; i++) {
1106
+ let ok = true;
1107
+ for (let j = 0; j < oldLines.length; j++) {
1108
+ if (srcLines[i + j].trim() !== oldLines[j]) { ok = false; break; }
1109
+ }
1110
+ if (ok) { matchStart = i; break; }
1111
+ }
1112
+ if (matchStart >= 0) {
1113
+ const before = srcLines.slice(0, matchStart).join('\n');
1114
+ const after = srcLines.slice(matchStart + oldLines.length).join('\n');
1115
+ const result = (before ? before + '\n' : '') + (newStr ?? '') + (after ? '\n' + after : '');
1116
+ ProjectStore.writeFile(projectName, relPath, result);
1117
+ hasChanges = true;
1118
+ modifiedFiles.add(relPath);
1119
+ toolResults.push({ op: 'edit', path: relPath, result: 'ok' });
1120
+ emit({ type: 'tool', op: 'edit', path: relPath, result: 'ok', oldSnippet: oldStr.slice(0, 2000), newSnippet: newStr?.slice(0, 2000) ?? '' });
1121
+ console.log('[TOOL-TRUNCATED] Edit applied via fuzzy match from truncated tool call');
1122
+ }
1123
+ }
1124
+ }
1125
+ } catch (e) {
1126
+ console.log('[TOOL-TRUNCATED] Failed to parse truncated tool:', e.message);
1127
+ }
1128
+ }
1129
+ }
1130
+ }
1131
+
1075
1132
  // If any edit failed, ignore <done/> — force retry
1076
1133
  const hasFailedEdit = toolResults.some((r) => r.op === 'edit' && (r.result?.includes('not_found') || r.result?.includes('blocked')));
1077
1134
  if (hasFailedEdit && step < MAX_STEPS - 1) {