protoagent 0.1.13 → 0.1.14

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.
@@ -235,6 +235,41 @@ function sanitizeMessagesForRetry(messages, validToolNames) {
235
235
  });
236
236
  return { messages: sanitizedMessages, changed };
237
237
  }
238
+ /**
239
+ * Remove orphaned tool result messages that don't have a matching tool_call_id
240
+ * in any assistant message. This happens when messages are truncated and the
241
+ * assistant's tool_calls are removed but the tool results remain.
242
+ */
243
+ function removeOrphanedToolResults(messages) {
244
+ // Collect all valid tool_call_ids from assistant messages
245
+ const validToolCallIds = new Set();
246
+ for (const message of messages) {
247
+ const msgAny = message;
248
+ if (message.role === 'assistant' && Array.isArray(msgAny.tool_calls)) {
249
+ for (const tc of msgAny.tool_calls) {
250
+ if (tc.id) {
251
+ validToolCallIds.add(tc.id);
252
+ }
253
+ }
254
+ }
255
+ }
256
+ // Filter out tool messages with orphaned tool_call_ids
257
+ const filteredMessages = messages.filter((message) => {
258
+ const msgAny = message;
259
+ if (message.role === 'tool' && msgAny.tool_call_id) {
260
+ const isOrphaned = !validToolCallIds.has(msgAny.tool_call_id);
261
+ if (isOrphaned) {
262
+ logger.warn('Removing orphaned tool result', {
263
+ tool_call_id: msgAny.tool_call_id,
264
+ contentPreview: msgAny.content?.slice(0, 100),
265
+ });
266
+ }
267
+ return !isOrphaned;
268
+ }
269
+ return true;
270
+ });
271
+ return { messages: filteredMessages, changed: filteredMessages.length !== messages.length };
272
+ }
238
273
  function getValidToolNames() {
239
274
  return new Set([...getAllTools(), subAgentTool]
240
275
  .map((tool) => tool.function?.name)
@@ -695,6 +730,15 @@ export async function runAgenticLoop(client, model, messages, userInput, onEvent
695
730
  continue;
696
731
  }
697
732
  }
733
+ // Try removing orphaned tool results
734
+ const orphanedRemoved = removeOrphanedToolResults(updatedMessages);
735
+ if (orphanedRemoved.changed) {
736
+ updatedMessages.length = 0;
737
+ updatedMessages.push(...orphanedRemoved.messages);
738
+ logger.warn('400 response after orphaned tool results; retrying with cleaned messages');
739
+ // Silently retry without showing error to user
740
+ continue;
741
+ }
698
742
  // If sanitization didn't help, try removing messages one at a time (up to 5)
699
743
  if (truncateRetryCount < MAX_TRUNCATE_RETRIES) {
700
744
  truncateRetryCount++;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "protoagent",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",