@blockrun/runcode 2.5.4 → 2.5.6

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.
@@ -9,10 +9,10 @@
9
9
  * 5. Pre-compact stripping — remove images/docs before summarization
10
10
  */
11
11
  // ─── Constants ─────────────────────────────────────────────────────────────
12
- /** Max chars per individual tool result before truncation */
13
- const MAX_TOOL_RESULT_CHARS = 50_000;
12
+ /** Max chars per individual tool result before truncation (history-level safety net) */
13
+ const MAX_TOOL_RESULT_CHARS = 32_000;
14
14
  /** Max aggregate tool result chars per user message */
15
- const MAX_TOOL_RESULTS_PER_MESSAGE_CHARS = 200_000;
15
+ const MAX_TOOL_RESULTS_PER_MESSAGE_CHARS = 100_000;
16
16
  /** Preview size when truncating */
17
17
  const PREVIEW_CHARS = 2_000;
18
18
  /** Default max_tokens (low to save output slot reservation) */
@@ -2,7 +2,8 @@
2
2
  * Bash capability — execute shell commands with timeout and output capture.
3
3
  */
4
4
  import { spawn } from 'node:child_process';
5
- const MAX_OUTPUT_BYTES = 512 * 1024; // 512KB output cap
5
+ const MAX_OUTPUT_BYTES = 512 * 1024; // 512KB capture buffer (prevents OOM)
6
+ const MAX_RETURN_CHARS = 32_000; // 32KB return cap (~8,000 tokens) — prevents context bloat
6
7
  const DEFAULT_TIMEOUT_MS = 120_000; // 2 minutes
7
8
  async function execute(input, ctx) {
8
9
  const { command, timeout } = input;
@@ -99,7 +100,21 @@ async function execute(input, ctx) {
99
100
  result += stderr;
100
101
  }
101
102
  if (truncated) {
102
- result += '\n\n... (output truncated at 512KB)';
103
+ result += '\n\n... (output truncated command produced >512KB)';
104
+ }
105
+ // Cap returned output to prevent context bloat.
106
+ // Keep the LAST part (most relevant for errors/test failures/build output).
107
+ if (result.length > MAX_RETURN_CHARS) {
108
+ const lines = result.split('\n');
109
+ let trimmed = '';
110
+ for (let i = lines.length - 1; i >= 0; i--) {
111
+ const candidate = lines[i] + '\n' + trimmed;
112
+ if (candidate.length > MAX_RETURN_CHARS)
113
+ break;
114
+ trimmed = candidate;
115
+ }
116
+ const omitted = result.length - trimmed.length;
117
+ result = `... (${omitted.toLocaleString()} chars omitted from start)\n${trimmed}`;
103
118
  }
104
119
  if (killed) {
105
120
  resolve({
@@ -3,7 +3,8 @@
3
3
  */
4
4
  import fs from 'node:fs';
5
5
  import path from 'node:path';
6
- const MAX_RESULTS = 500;
6
+ const MAX_RESULTS = 200;
7
+ const MAX_OUTPUT_CHARS = 12_000; // ~3,000 tokens — prevents huge glob results from blowing up context
7
8
  /**
8
9
  * Simple glob matcher supporting *, **, and ? wildcards.
9
10
  * No external dependencies.
@@ -115,7 +116,23 @@ async function execute(input, ctx) {
115
116
  }
116
117
  let output = sorted.join('\n');
117
118
  if (sorted.length >= MAX_RESULTS) {
118
- output += `\n\n... (limited to ${MAX_RESULTS} results)`;
119
+ output += `\n\n... (limited to ${MAX_RESULTS} results. Use a more specific pattern to narrow results.)`;
120
+ }
121
+ // Cap total output length to prevent context bloat
122
+ if (output.length > MAX_OUTPUT_CHARS) {
123
+ const lines = output.split('\n');
124
+ let trimmed = '';
125
+ let count = 0;
126
+ for (const line of lines) {
127
+ if ((trimmed + line).length > MAX_OUTPUT_CHARS)
128
+ break;
129
+ trimmed += (trimmed ? '\n' : '') + line;
130
+ count++;
131
+ }
132
+ const remaining = lines.length - count;
133
+ if (remaining > 0) {
134
+ output = `${trimmed}\n... (${remaining} more paths not shown — use a more specific pattern)`;
135
+ }
119
136
  }
120
137
  return { output };
121
138
  }
@@ -4,6 +4,7 @@
4
4
  import { execSync, execFileSync } from 'node:child_process';
5
5
  import fs from 'node:fs';
6
6
  import path from 'node:path';
7
+ const MAX_GREP_OUTPUT_CHARS = 16_000; // ~4,000 tokens — prevents huge grep results
7
8
  let _hasRipgrep = null;
8
9
  function hasRipgrep() {
9
10
  if (_hasRipgrep !== null)
@@ -63,8 +64,8 @@ function runRipgrep(opts, searchPath, mode, limit) {
63
64
  args.push('-U', '--multiline-dotall');
64
65
  if (opts.glob)
65
66
  args.push(`--glob=${opts.glob}`);
66
- // Always exclude common noise
67
- args.push('--glob=!node_modules', '--glob=!.git', '--glob=!dist');
67
+ // Always exclude common noise + lock files (huge, rarely useful)
68
+ args.push('--glob=!node_modules', '--glob=!.git', '--glob=!dist', '--glob=!*.lock', '--glob=!package-lock.json', '--glob=!pnpm-lock.yaml');
68
69
  args.push('--', opts.pattern);
69
70
  args.push(searchPath);
70
71
  try {
@@ -79,6 +80,10 @@ function runRipgrep(opts, searchPath, mode, limit) {
79
80
  if (lines.length > limited.length) {
80
81
  output += `\n\n... (${lines.length - limited.length} more results, use head_limit to see more)`;
81
82
  }
83
+ // Cap total output to prevent context bloat
84
+ if (output.length > MAX_GREP_OUTPUT_CHARS) {
85
+ output = output.slice(0, MAX_GREP_OUTPUT_CHARS) + `\n... (output capped at ${MAX_GREP_OUTPUT_CHARS / 1000}KB — use more specific pattern or head_limit)`;
86
+ }
82
87
  return { output: output || 'No matches found' };
83
88
  }
84
89
  catch (err) {
@@ -113,7 +118,7 @@ function runNativeGrep(opts, searchPath, mode, limit) {
113
118
  .replace(/\*\*/, '*'); // Convert ** to * for flat matching
114
119
  args.push(`--include=${nativeGlob}`);
115
120
  }
116
- args.push('--exclude-dir=node_modules', '--exclude-dir=.git', '--exclude-dir=dist');
121
+ args.push('--exclude-dir=node_modules', '--exclude-dir=.git', '--exclude-dir=dist', '--exclude=*.lock', '--exclude=package-lock.json', '--exclude=pnpm-lock.yaml');
117
122
  args.push('-e', opts.pattern, searchPath);
118
123
  try {
119
124
  const result = execFileSync('grep', args, {
@@ -127,6 +132,9 @@ function runNativeGrep(opts, searchPath, mode, limit) {
127
132
  if (lines.length > limited.length) {
128
133
  output += `\n\n... (${lines.length - limited.length} more results)`;
129
134
  }
135
+ if (output.length > MAX_GREP_OUTPUT_CHARS) {
136
+ output = output.slice(0, MAX_GREP_OUTPUT_CHARS) + `\n... (output capped at ${MAX_GREP_OUTPUT_CHARS / 1000}KB)`;
137
+ }
130
138
  return { output: output || 'No matches found' };
131
139
  }
132
140
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/runcode",
3
- "version": "2.5.4",
3
+ "version": "2.5.6",
4
4
  "description": "RunCode — AI coding agent powered by 41+ models. Pay per use with USDC.",
5
5
  "type": "module",
6
6
  "bin": {