Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tidy-summary-no-dup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@voltagent/core": patch
---

fix(core): avoid duplicating stdout/stderr stream content in sandbox summary metadata
24 changes: 9 additions & 15 deletions packages/core/src/workspace/sandbox/toolkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,25 +241,19 @@ const truncateByBytes = (value: string, maxBytes: number): string => {
return `${truncated}${TRUNCATION_SUFFIX}`;
};

const formatStreamOutput = (label: string, info: StreamEvictionResult): string[] => {
const lines: string[] = [];
const formatStreamSummary = (label: string, info: StreamEvictionResult): string => {
if (info.evicted && info.path) {
const note = info.truncated ? " (truncated)" : "";
lines.push(`${label}: saved to ${info.path} (${info.bytes} bytes${note})`);
if (info.error) {
lines.push(`${label} eviction error: ${info.error}`);
}
return lines;
const note = info.truncated ? ", truncated" : "";
const errorNote = info.error ? `, eviction error: ${info.error}` : "";
return `${label}: saved to ${info.path} (${info.bytes} bytes${note}${errorNote})`;
}

if (info.content) {
lines.push(`${label}:`);
lines.push(info.content);
return lines;
const note = info.truncated ? ", truncated" : "";
return `${label}: captured inline (${info.bytes} bytes${note})`;
}

lines.push(`${label}: (empty)`);
return lines;
return `${label}: (empty)`;
Comment on lines +244 to +256
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Preserve failed-eviction metadata in the summary.

evictStream() only sets info.error on fallback results with evicted: false, so the errorNote here never renders. When a write fails, this formatter falls through to "captured inline" and drops both the attempted save path and the eviction failure, which makes that case indistinguishable from a normal inline capture.

Suggested fix
 const formatStreamSummary = (label: string, info: StreamEvictionResult): string => {
+  const note = info.truncated ? ", truncated" : "";
+
   if (info.evicted && info.path) {
-    const note = info.truncated ? ", truncated" : "";
-    const errorNote = info.error ? `, eviction error: ${info.error}` : "";
-    return `${label}: saved to ${info.path} (${info.bytes} bytes${note}${errorNote})`;
+    return `${label}: saved to ${info.path} (${info.bytes} bytes${note})`;
+  }
+
+  if (info.path && info.error) {
+    return `${label}: captured inline after save to ${info.path} failed (${info.bytes} bytes${note}, eviction error: ${info.error})`;
   }
 
   if (info.content) {
-    const note = info.truncated ? ", truncated" : "";
     return `${label}: captured inline (${info.bytes} bytes${note})`;
   }
 
   return `${label}: (empty)`;
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const formatStreamSummary = (label: string, info: StreamEvictionResult): string => {
if (info.evicted && info.path) {
const note = info.truncated ? " (truncated)" : "";
lines.push(`${label}: saved to ${info.path} (${info.bytes} bytes${note})`);
if (info.error) {
lines.push(`${label} eviction error: ${info.error}`);
}
return lines;
const note = info.truncated ? ", truncated" : "";
const errorNote = info.error ? `, eviction error: ${info.error}` : "";
return `${label}: saved to ${info.path} (${info.bytes} bytes${note}${errorNote})`;
}
if (info.content) {
lines.push(`${label}:`);
lines.push(info.content);
return lines;
const note = info.truncated ? ", truncated" : "";
return `${label}: captured inline (${info.bytes} bytes${note})`;
}
lines.push(`${label}: (empty)`);
return lines;
return `${label}: (empty)`;
const formatStreamSummary = (label: string, info: StreamEvictionResult): string => {
const note = info.truncated ? ", truncated" : "";
if (info.evicted && info.path) {
return `${label}: saved to ${info.path} (${info.bytes} bytes${note})`;
}
if (info.path && info.error) {
return `${label}: captured inline after save to ${info.path} failed (${info.bytes} bytes${note}, eviction error: ${info.error})`;
}
if (info.content) {
return `${label}: captured inline (${info.bytes} bytes${note})`;
}
return `${label}: (empty)`;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/workspace/sandbox/toolkit.ts` around lines 244 - 256,
formatStreamSummary currently only shows eviction error when info.evicted is
true, but evictStream() sets info.error on fallback results with evicted: false
so errors get dropped and the message falls back to "captured inline"; update
formatStreamSummary to preserve and display failed-eviction metadata: if
info.error exists (regardless of info.evicted) include the attempted save path
(info.path if present) and the eviction error note, and when evicted is false
but path exists and error exists, render a message like "<label>: attempted save
to <path> failed (eviction error: <error>, <bytes> bytes...)" so failed write
attempts are distinguishable from normal inline captures; modify the logic in
formatStreamSummary (which consumes StreamEvictionResult returned by
evictStream) to check info.error before falling back to inline.

};

export const createWorkspaceSandboxToolkit = (
Expand Down Expand Up @@ -437,8 +431,8 @@ export const createWorkspaceSandboxToolkit = (

const lines: string[] = [];
lines.push(...formatSandboxHeader(result));
lines.push(...formatStreamOutput("STDOUT", stdoutInfo));
lines.push(...formatStreamOutput("STDERR", stderrInfo));
lines.push(formatStreamSummary("STDOUT", stdoutInfo));
lines.push(formatStreamSummary("STDERR", stderrInfo));

const summary = lines.join("\n");
const streamErrors = [stdoutInfo.error, stderrInfo.error].filter(
Expand Down
Loading