minimal-agent 0.6.2 → 0.6.3

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.
@@ -50,6 +50,12 @@ import { scanDestructiveCommand } from './warnings.js';
50
50
  // ---------------- 0. 常量 ----------------
51
51
  const DEFAULT_TIMEOUT_MS = 120_000; // 2 min
52
52
  const MAX_TIMEOUT_MS = 600_000; // 10 min
53
+ /**
54
+ * G1:stdout / stderr 即时缓冲上限(各自)。命令狂输出(如 cat 大文件 / yes)时,
55
+ * 在最终统一截断之前先把内存封顶,防 OOM。取最终展示截断阈值的 2 倍 —— 正常输出不受影响,
56
+ * 超大输出也只占有限内存(最终仍按 DEFAULT_MAX_RESULT_SIZE_CHARS 截断展示)。
57
+ */
58
+ const MAX_STREAM_BUFFER_CHARS = DEFAULT_MAX_RESULT_SIZE_CHARS * 2;
53
59
  /**
54
60
  * 灾难性命令黑名单。
55
61
  * 命中任意一条就直接拒绝执行 —— 这层是"防猪队友"而不是真沙盒。
@@ -142,6 +148,27 @@ export function checkForbidden(command) {
142
148
  }
143
149
  return null;
144
150
  }
151
+ /**
152
+ * 子进程环境变量:从 process.env 复制后**剥离 agent 自己的密钥**
153
+ * (`MINIMAL_AGENT_*` / `TAVILY_*`)。
154
+ *
155
+ * 动机(安全):main.tsx 启动时会把 saved config 里的 key 注入 process.env。
156
+ * 若原样把 process.env 透传给 Bash 子进程,模型只要跑 `echo $MINIMAL_AGENT_API_KEY`
157
+ * 就能把 key(绿色版里是卖家预置的)读进 tool result、回传 LLM、甚至打到 -p stdout。
158
+ * 这些 env 是 agent 自身配置,子进程命令并不需要 —— 直接删掉,最小化泄露面。
159
+ * 其余变量(PATH / HOME / 用户自己 export 的)照常透传。
160
+ *
161
+ * 导出供测试断言。
162
+ */
163
+ export function sanitizedChildEnv(base = process.env) {
164
+ const env = { ...base };
165
+ for (const key of Object.keys(env)) {
166
+ if (key.startsWith('MINIMAL_AGENT_') || key.startsWith('TAVILY_')) {
167
+ delete env[key];
168
+ }
169
+ }
170
+ return env;
171
+ }
145
172
  // ---------------- 1. Zod 输入 schema ----------------
146
173
  const inputSchema = z.object({
147
174
  command: z
@@ -256,17 +283,20 @@ async function call(input, signal) {
256
283
  shell: true,
257
284
  cwd: getWorkingDir(),
258
285
  signal: ac.signal,
259
- env: process.env,
286
+ env: sanitizedChildEnv(),
260
287
  windowsHide: true,
261
288
  });
262
289
  child.stdout?.setEncoding('utf8');
263
290
  child.stderr?.setEncoding('utf8');
264
291
  child.stdout?.on('data', (chunk) => {
265
- stdout += chunk;
266
- // 读到一半就超额时不强行 kill;最后再统一截断,避免日志噪声
292
+ // G1:即时封顶,防止狂输出命令在最终截断前撑爆内存(OOM)。不强行 kill,
293
+ // 让命令自己跑完或被 timeout 收走;超出上限的部分直接丢弃,最终仍按上限截断展示。
294
+ if (stdout.length < MAX_STREAM_BUFFER_CHARS)
295
+ stdout += chunk;
267
296
  });
268
297
  child.stderr?.on('data', (chunk) => {
269
- stderr += chunk;
298
+ if (stderr.length < MAX_STREAM_BUFFER_CHARS)
299
+ stderr += chunk;
270
300
  });
271
301
  child.on('error', (e) => {
272
302
  // signal abort 会先触发 error('AbortError');把这种 case 转成正常分支
@@ -15,11 +15,12 @@
15
15
  * 再额外传入版本号 prop 配合依赖即可。
16
16
  * ============================================================
17
17
  */
18
- import { AUTOCOMPACT_BUFFER_TOKENS } from '../../context/compact.js';
18
+ import { getCompactThreshold } from '../../context/compact.js';
19
19
  import { countMessagesTokens } from '../../context/tokenCounter.js';
20
20
  export function useTokenUsage(messages, provider) {
21
21
  const tokens = countMessagesTokens(messages);
22
- const threshold = Math.max(1000, provider.contextWindow - AUTOCOMPACT_BUFFER_TOKENS);
22
+ // loop 实际触发同源(getCompactThreshold:按 contextWindow × compactRatio 比例 + 8K 下限)
23
+ const threshold = getCompactThreshold(provider);
23
24
  return {
24
25
  tokens,
25
26
  threshold,