@probelabs/probe 0.6.0-rc330 → 0.6.0-rc332
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/bin/binaries/{probe-v0.6.0-rc330-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc332-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc330-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc332-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc330-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc332-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc330-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc332-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc330-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc332-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/ProbeAgent.d.ts +105 -4
- package/build/agent/ProbeAgent.js +209 -12
- package/build/agent/bashExecutor.js +36 -101
- package/build/agent/engines/codex.js +367 -88
- package/build/agent/engines/governed-answer-failure.js +152 -0
- package/build/agent/engines/governed-codex-profile.js +198 -0
- package/build/agent/governance/acknowledgedJsonlChannel.js +328 -0
- package/build/agent/governance/atomicTerminalReceipt.js +188 -0
- package/build/agent/governance/index.d.ts +130 -0
- package/build/agent/governance/index.js +8 -0
- package/build/agent/mcp/built-in-server.js +152 -53
- package/build/agent/mcp/index.d.ts +65 -0
- package/build/agent/mcp/index.js +6 -1
- package/build/agent/probeTool.js +1 -1
- package/build/agent/processSupervisor.js +351 -0
- package/build/agent/tools.js +14 -8
- package/build/extract.js +4 -3
- package/build/grep.js +2 -2
- package/build/index.js +2 -0
- package/build/query.js +2 -2
- package/build/search.js +2 -1
- package/build/symbols.js +3 -2
- package/build/utils.js +23 -0
- package/cjs/agent/ProbeAgent.cjs +19173 -17845
- package/cjs/index.cjs +76212 -74325
- package/index.d.ts +149 -4
- package/package.json +6 -2
- package/src/agent/ProbeAgent.d.ts +105 -4
- package/src/agent/ProbeAgent.js +209 -12
- package/src/agent/bashExecutor.js +36 -101
- package/src/agent/engines/codex.js +367 -88
- package/src/agent/engines/governed-answer-failure.js +152 -0
- package/src/agent/engines/governed-codex-profile.js +198 -0
- package/src/agent/governance/acknowledgedJsonlChannel.js +328 -0
- package/src/agent/governance/atomicTerminalReceipt.js +188 -0
- package/src/agent/governance/index.d.ts +130 -0
- package/src/agent/governance/index.js +8 -0
- package/src/agent/mcp/built-in-server.js +152 -53
- package/src/agent/mcp/index.d.ts +65 -0
- package/src/agent/mcp/index.js +6 -1
- package/src/agent/probeTool.js +1 -1
- package/src/agent/processSupervisor.js +351 -0
- package/src/agent/tools.js +14 -8
- package/src/extract.js +4 -3
- package/src/grep.js +2 -2
- package/src/index.js +2 -0
- package/src/query.js +2 -2
- package/src/search.js +2 -1
- package/src/symbols.js +3 -2
- package/src/utils.js +23 -0
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Governed child-process execution with bounded output and explicit lifecycle facts.
|
|
3
|
+
* The ChildProcess and its PID intentionally never cross this module boundary.
|
|
4
|
+
* @module agent/processSupervisor
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
import { randomBytes } from 'crypto';
|
|
9
|
+
|
|
10
|
+
const DEFAULT_TERMINATION_GRACE_MS = 5000;
|
|
11
|
+
const DEFAULT_CLEANUP_TIMEOUT_MS = 10000;
|
|
12
|
+
const DEFAULT_STREAM_BYTE_CAP = 10 * 1024 * 1024;
|
|
13
|
+
|
|
14
|
+
let nextProcessSequence = 0;
|
|
15
|
+
|
|
16
|
+
function makeId() {
|
|
17
|
+
nextProcessSequence += 1;
|
|
18
|
+
return `process-${nextProcessSequence.toString(36)}-${randomBytes(6).toString('hex')}`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function nonNegativeNumber(value, fallback, name) {
|
|
22
|
+
if (value === undefined) return fallback;
|
|
23
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < 0) {
|
|
24
|
+
throw new TypeError(`${name} must be a non-negative finite number`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function byteCap(value, fallback, name) {
|
|
30
|
+
const cap = nonNegativeNumber(value, fallback, name);
|
|
31
|
+
if (!Number.isSafeInteger(cap)) {
|
|
32
|
+
throw new TypeError(`${name} must be a safe integer`);
|
|
33
|
+
}
|
|
34
|
+
return cap;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function makeSettledHandle(id, error) {
|
|
38
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
39
|
+
const receipt = Object.freeze({
|
|
40
|
+
id,
|
|
41
|
+
classification: 'spawn_error',
|
|
42
|
+
reason: 'spawn_error',
|
|
43
|
+
error: message,
|
|
44
|
+
stdout: '',
|
|
45
|
+
stderr: '',
|
|
46
|
+
stdoutBytes: 0,
|
|
47
|
+
stderrBytes: 0,
|
|
48
|
+
exitCode: null,
|
|
49
|
+
signal: null,
|
|
50
|
+
barriers: Object.freeze({ close: false, stdoutEOF: false, stderrEOF: false }),
|
|
51
|
+
observed: Object.freeze([Object.freeze({ sequence: 1, fact: 'spawn-error', error: message })])
|
|
52
|
+
});
|
|
53
|
+
const result = Promise.resolve(receipt);
|
|
54
|
+
return Object.freeze({ id, terminate: () => result, result });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Spawn a process whose resources and termination are governed by fixed deadlines.
|
|
59
|
+
*
|
|
60
|
+
* @param {Object} spec
|
|
61
|
+
* @param {string} spec.command
|
|
62
|
+
* @param {string[]} [spec.args]
|
|
63
|
+
* @param {string} [spec.cwd]
|
|
64
|
+
* @param {NodeJS.ProcessEnv} [spec.env]
|
|
65
|
+
* @param {AbortSignal} [spec.signal]
|
|
66
|
+
* @param {number} [spec.executionTimeoutMs=0]
|
|
67
|
+
* @param {number} [spec.terminationGraceMs=5000]
|
|
68
|
+
* @param {number} [spec.cleanupTimeoutMs=10000] Must be at least terminationGraceMs.
|
|
69
|
+
* @param {number} [spec.stdoutByteCap=10485760]
|
|
70
|
+
* @param {number} [spec.stderrByteCap=10485760]
|
|
71
|
+
* @param {'child'|'process-group'} [spec.signalScope='child']
|
|
72
|
+
* @returns {{id: string, terminate: (reason?: string) => Promise<Object>, result: Promise<Object>}}
|
|
73
|
+
*/
|
|
74
|
+
function governProcess(spec, attachedChild = null) {
|
|
75
|
+
if (!spec || typeof spec !== 'object') {
|
|
76
|
+
throw new TypeError('spec must be an object');
|
|
77
|
+
}
|
|
78
|
+
if (typeof spec.command !== 'string' || spec.command.length === 0) {
|
|
79
|
+
throw new TypeError('command must be a non-empty string');
|
|
80
|
+
}
|
|
81
|
+
if (spec.args !== undefined && (!Array.isArray(spec.args) || spec.args.some(arg => typeof arg !== 'string'))) {
|
|
82
|
+
throw new TypeError('args must be an array of strings');
|
|
83
|
+
}
|
|
84
|
+
if (spec.signal !== undefined && (
|
|
85
|
+
!spec.signal ||
|
|
86
|
+
typeof spec.signal !== 'object' ||
|
|
87
|
+
typeof spec.signal.aborted !== 'boolean' ||
|
|
88
|
+
typeof spec.signal.addEventListener !== 'function' ||
|
|
89
|
+
typeof spec.signal.removeEventListener !== 'function'
|
|
90
|
+
)) {
|
|
91
|
+
throw new TypeError('signal must be an AbortSignal');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const executionTimeoutMs = nonNegativeNumber(spec.executionTimeoutMs, 0, 'executionTimeoutMs');
|
|
95
|
+
const terminationGraceMs = nonNegativeNumber(spec.terminationGraceMs, DEFAULT_TERMINATION_GRACE_MS, 'terminationGraceMs');
|
|
96
|
+
const cleanupTimeoutMs = nonNegativeNumber(spec.cleanupTimeoutMs, DEFAULT_CLEANUP_TIMEOUT_MS, 'cleanupTimeoutMs');
|
|
97
|
+
if (cleanupTimeoutMs < terminationGraceMs) {
|
|
98
|
+
throw new TypeError('cleanupTimeoutMs must be greater than or equal to terminationGraceMs');
|
|
99
|
+
}
|
|
100
|
+
const stdoutByteCap = byteCap(spec.stdoutByteCap, DEFAULT_STREAM_BYTE_CAP, 'stdoutByteCap');
|
|
101
|
+
const stderrByteCap = byteCap(spec.stderrByteCap, DEFAULT_STREAM_BYTE_CAP, 'stderrByteCap');
|
|
102
|
+
const captureStdout = !attachedChild || spec.captureStdout !== false;
|
|
103
|
+
const signalScope = spec.signalScope ?? 'child';
|
|
104
|
+
if (signalScope !== 'child' && signalScope !== 'process-group') {
|
|
105
|
+
throw new TypeError("signalScope must be 'child' or 'process-group'");
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const id = makeId();
|
|
109
|
+
let child;
|
|
110
|
+
if (attachedChild) {
|
|
111
|
+
child = attachedChild;
|
|
112
|
+
} else {
|
|
113
|
+
try {
|
|
114
|
+
child = spawn(spec.command, spec.args ?? [], {
|
|
115
|
+
cwd: spec.cwd,
|
|
116
|
+
env: spec.env,
|
|
117
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
118
|
+
shell: false,
|
|
119
|
+
detached: signalScope === 'process-group',
|
|
120
|
+
windowsHide: true
|
|
121
|
+
});
|
|
122
|
+
} catch (error) {
|
|
123
|
+
return makeSettledHandle(id, error);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let resolveResult;
|
|
128
|
+
const result = new Promise(resolve => { resolveResult = resolve; });
|
|
129
|
+
const observed = [];
|
|
130
|
+
const barriers = { close: false, stdoutEOF: false, stderrEOF: false };
|
|
131
|
+
const stdoutChunks = [];
|
|
132
|
+
const stderrChunks = [];
|
|
133
|
+
let stdoutBytes = 0;
|
|
134
|
+
let stderrBytes = 0;
|
|
135
|
+
let exitCode = null;
|
|
136
|
+
let exitSignal = null;
|
|
137
|
+
let exitObserved = false;
|
|
138
|
+
let settled = false;
|
|
139
|
+
let terminalReason = null;
|
|
140
|
+
let terminalClassification = null;
|
|
141
|
+
let terminalError = null;
|
|
142
|
+
let lastSignalDelivery = null;
|
|
143
|
+
let executionTimer = null;
|
|
144
|
+
let escalationTimer = null;
|
|
145
|
+
let cleanupTimer = null;
|
|
146
|
+
|
|
147
|
+
const observe = (fact, details = {}) => {
|
|
148
|
+
if (settled) return;
|
|
149
|
+
observed.push(Object.freeze({ sequence: observed.length + 1, fact, ...details }));
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const startTimer = (callback, delay) => {
|
|
153
|
+
const timer = setTimeout(callback, delay);
|
|
154
|
+
timer.unref?.();
|
|
155
|
+
return timer;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const clearTimer = (timer) => {
|
|
159
|
+
if (timer) clearTimeout(timer);
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const settle = (classification, reason = terminalReason) => {
|
|
163
|
+
if (settled) return;
|
|
164
|
+
settled = true;
|
|
165
|
+
clearTimer(executionTimer);
|
|
166
|
+
clearTimer(cleanupTimer);
|
|
167
|
+
clearTimer(escalationTimer);
|
|
168
|
+
if (spec.signal) spec.signal.removeEventListener('abort', onAbort);
|
|
169
|
+
|
|
170
|
+
resolveResult(Object.freeze({
|
|
171
|
+
id,
|
|
172
|
+
classification,
|
|
173
|
+
reason: reason ?? null,
|
|
174
|
+
...(terminalError ? { error: terminalError } : {}),
|
|
175
|
+
stdout: Buffer.concat(stdoutChunks, stdoutBytes).toString(),
|
|
176
|
+
stderr: Buffer.concat(stderrChunks, stderrBytes).toString(),
|
|
177
|
+
stdoutBytes,
|
|
178
|
+
stderrBytes,
|
|
179
|
+
exitCode,
|
|
180
|
+
signal: exitSignal,
|
|
181
|
+
barriers: Object.freeze({ ...barriers }),
|
|
182
|
+
observed: Object.freeze([...observed])
|
|
183
|
+
}));
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const fullBarrierObserved = () => barriers.close && barriers.stdoutEOF && barriers.stderrEOF;
|
|
187
|
+
|
|
188
|
+
const maybeSettle = () => {
|
|
189
|
+
if (settled || !exitObserved || !fullBarrierObserved()) return;
|
|
190
|
+
settle(terminalClassification ?? 'exited', terminalReason);
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const startCleanupDeadline = () => {
|
|
194
|
+
if (cleanupTimer || settled) return;
|
|
195
|
+
cleanupTimer = startTimer(() => {
|
|
196
|
+
observe('barrier', { barrier: 'cleanup_deadline' });
|
|
197
|
+
child.stdout.destroy();
|
|
198
|
+
child.stderr.destroy();
|
|
199
|
+
settle('cleanup_timeout', terminalReason ?? 'cleanup_timeout');
|
|
200
|
+
}, cleanupTimeoutMs);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const attemptSignal = (signal) => {
|
|
204
|
+
let accepted = false;
|
|
205
|
+
if (settled || exitObserved) return;
|
|
206
|
+
if (!child.pid) {
|
|
207
|
+
observe('signal-attempt', {
|
|
208
|
+
signal,
|
|
209
|
+
requestedScope: signalScope,
|
|
210
|
+
actualScope: null,
|
|
211
|
+
accepted: false
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
let actualScope = null;
|
|
216
|
+
try {
|
|
217
|
+
if (signalScope === 'process-group') {
|
|
218
|
+
process.kill(-child.pid, signal);
|
|
219
|
+
accepted = true;
|
|
220
|
+
actualScope = 'process-group';
|
|
221
|
+
} else {
|
|
222
|
+
accepted = child.kill(signal);
|
|
223
|
+
if (accepted) actualScope = 'child';
|
|
224
|
+
}
|
|
225
|
+
} catch {
|
|
226
|
+
try {
|
|
227
|
+
accepted = child.kill(signal);
|
|
228
|
+
if (accepted) actualScope = 'child';
|
|
229
|
+
} catch {
|
|
230
|
+
accepted = false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
observe('signal-attempt', {
|
|
234
|
+
signal,
|
|
235
|
+
requestedScope: signalScope,
|
|
236
|
+
actualScope,
|
|
237
|
+
accepted: Boolean(accepted)
|
|
238
|
+
});
|
|
239
|
+
if (accepted) lastSignalDelivery = { signal, actualScope };
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const beginTermination = (reason, classification = 'terminated') => {
|
|
243
|
+
if (settled || exitObserved || terminalClassification) return result;
|
|
244
|
+
terminalReason = typeof reason === 'string' && reason.length > 0 ? reason : 'terminated';
|
|
245
|
+
terminalClassification = classification;
|
|
246
|
+
clearTimer(executionTimer);
|
|
247
|
+
attemptSignal('SIGTERM');
|
|
248
|
+
escalationTimer = startTimer(() => attemptSignal('SIGKILL'), terminationGraceMs);
|
|
249
|
+
startCleanupDeadline();
|
|
250
|
+
return result;
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
const appendChunk = (stream, data) => {
|
|
254
|
+
if (settled) return;
|
|
255
|
+
const chunk = Buffer.isBuffer(data) ? data : Buffer.from(data);
|
|
256
|
+
const isStdout = stream === 'stdout';
|
|
257
|
+
const used = isStdout ? stdoutBytes : stderrBytes;
|
|
258
|
+
const cap = isStdout ? stdoutByteCap : stderrByteCap;
|
|
259
|
+
const remaining = Math.max(0, cap - used);
|
|
260
|
+
const kept = remaining >= chunk.length ? chunk : chunk.subarray(0, remaining);
|
|
261
|
+
if (kept.length > 0) {
|
|
262
|
+
(isStdout ? stdoutChunks : stderrChunks).push(kept);
|
|
263
|
+
if (isStdout) stdoutBytes += kept.length;
|
|
264
|
+
else stderrBytes += kept.length;
|
|
265
|
+
}
|
|
266
|
+
if (chunk.length > remaining) {
|
|
267
|
+
beginTermination(`${stream}_overflow`, 'output_overflow');
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
const observeBarrier = (barrier) => {
|
|
272
|
+
if (settled || barriers[barrier]) return;
|
|
273
|
+
barriers[barrier] = true;
|
|
274
|
+
observe('barrier', { barrier });
|
|
275
|
+
maybeSettle();
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const onAbort = () => beginTermination('aborted', 'aborted');
|
|
279
|
+
|
|
280
|
+
if (captureStdout) child.stdout.on('data', data => appendChunk('stdout', data));
|
|
281
|
+
child.stderr.on('data', data => appendChunk('stderr', data));
|
|
282
|
+
child.stdout.once('end', () => observeBarrier('stdoutEOF'));
|
|
283
|
+
child.stderr.once('end', () => observeBarrier('stderrEOF'));
|
|
284
|
+
|
|
285
|
+
child.once('exit', (code, signal) => {
|
|
286
|
+
if (settled) return;
|
|
287
|
+
exitObserved = true;
|
|
288
|
+
clearTimer(executionTimer);
|
|
289
|
+
clearTimer(escalationTimer);
|
|
290
|
+
exitCode = code;
|
|
291
|
+
exitSignal = signal;
|
|
292
|
+
observe('exit', { code, signal });
|
|
293
|
+
if (signal) {
|
|
294
|
+
observe('signal', {
|
|
295
|
+
signal,
|
|
296
|
+
requestedScope: lastSignalDelivery?.signal === signal ? signalScope : null,
|
|
297
|
+
actualScope: lastSignalDelivery?.signal === signal ? lastSignalDelivery.actualScope : null
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
startCleanupDeadline();
|
|
301
|
+
maybeSettle();
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
child.once('close', () => observeBarrier('close'));
|
|
305
|
+
|
|
306
|
+
child.once('error', error => {
|
|
307
|
+
if (settled) return;
|
|
308
|
+
terminalError = error.message;
|
|
309
|
+
observe('spawn-error', { error: error.message });
|
|
310
|
+
settle('spawn_error', 'spawn_error');
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
if (executionTimeoutMs > 0) {
|
|
314
|
+
executionTimer = startTimer(
|
|
315
|
+
() => beginTermination('execution_timeout', 'execution_timeout'),
|
|
316
|
+
executionTimeoutMs
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (spec.signal) {
|
|
321
|
+
spec.signal.addEventListener('abort', onAbort, { once: true });
|
|
322
|
+
if (spec.signal.aborted) onAbort();
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return Object.freeze({
|
|
326
|
+
id,
|
|
327
|
+
terminate: reason => beginTermination(reason),
|
|
328
|
+
result
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function spawnGovernedProcess(spec) {
|
|
333
|
+
return governProcess(spec);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Internal duplex adapter for engines that must retain protocol access to a child.
|
|
338
|
+
* The returned handle keeps the same bounded termination and close/EOF barriers as
|
|
339
|
+
* spawnGovernedProcess without exposing the child through the public governance API.
|
|
340
|
+
*
|
|
341
|
+
* @param {import('child_process').ChildProcess} child
|
|
342
|
+
* @param {Object} [spec]
|
|
343
|
+
* @returns {{id: string, terminate: (reason?: string) => Promise<Object>, result: Promise<Object>}}
|
|
344
|
+
*/
|
|
345
|
+
export function governSpawnedProcess(child, spec = {}) {
|
|
346
|
+
if (!child || typeof child !== 'object' || typeof child.kill !== 'function' ||
|
|
347
|
+
!child.stdout || !child.stderr) {
|
|
348
|
+
throw new TypeError('child must be a spawned process with stdout and stderr pipes');
|
|
349
|
+
}
|
|
350
|
+
return governProcess({ ...spec, command: 'attached-child' }, child);
|
|
351
|
+
}
|
package/build/agent/tools.js
CHANGED
|
@@ -5,14 +5,23 @@ import {
|
|
|
5
5
|
extractTool,
|
|
6
6
|
delegateTool,
|
|
7
7
|
analyzeAllTool,
|
|
8
|
-
symbolsTool
|
|
8
|
+
symbolsTool
|
|
9
|
+
} from '../tools/vercel.js';
|
|
10
|
+
import {
|
|
9
11
|
createExecutePlanTool,
|
|
10
|
-
createCleanupExecutePlanTool
|
|
11
|
-
|
|
12
|
+
createCleanupExecutePlanTool
|
|
13
|
+
} from '../tools/executePlan.js';
|
|
14
|
+
import { bashTool } from '../tools/bash.js';
|
|
15
|
+
import {
|
|
12
16
|
editTool,
|
|
13
17
|
createTool,
|
|
14
18
|
multiEditTool,
|
|
15
|
-
|
|
19
|
+
editSchema,
|
|
20
|
+
createSchema,
|
|
21
|
+
multiEditSchema
|
|
22
|
+
} from '../tools/edit.js';
|
|
23
|
+
import { DEFAULT_SYSTEM_MESSAGE } from '../tools/system-message.js';
|
|
24
|
+
import {
|
|
16
25
|
searchSchema,
|
|
17
26
|
querySchema,
|
|
18
27
|
extractSchema,
|
|
@@ -21,9 +30,6 @@ import {
|
|
|
21
30
|
executePlanSchema,
|
|
22
31
|
cleanupExecutePlanSchema,
|
|
23
32
|
bashSchema,
|
|
24
|
-
editSchema,
|
|
25
|
-
createSchema,
|
|
26
|
-
multiEditSchema,
|
|
27
33
|
listFilesSchema,
|
|
28
34
|
searchFilesSchema,
|
|
29
35
|
readImageSchema,
|
|
@@ -31,7 +37,7 @@ import {
|
|
|
31
37
|
symbolsSchema,
|
|
32
38
|
listSkillsSchema,
|
|
33
39
|
useSkillSchema
|
|
34
|
-
} from '../
|
|
40
|
+
} from '../tools/common.js';
|
|
35
41
|
|
|
36
42
|
// Create configured tool instances
|
|
37
43
|
export function createTools(configOptions) {
|
package/build/extract.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { exec, spawn } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
|
|
8
|
+
import { getBinaryPath, buildCliArgs, escapeString, getCleanEnv } from './utils.js';
|
|
9
9
|
import { validateCwdPath } from './utils/path-validation.js';
|
|
10
10
|
|
|
11
11
|
const execAsync = promisify(exec);
|
|
@@ -104,7 +104,7 @@ export async function extract(options) {
|
|
|
104
104
|
const command = `${binaryPath} extract ${cliArgs.join(' ')}`;
|
|
105
105
|
|
|
106
106
|
try {
|
|
107
|
-
const { stdout, stderr } = await execAsync(command, { cwd });
|
|
107
|
+
const { stdout, stderr } = await execAsync(command, { cwd, env: getCleanEnv() });
|
|
108
108
|
|
|
109
109
|
if (stderr) {
|
|
110
110
|
console.error(`stderr: ${stderr}`);
|
|
@@ -126,7 +126,8 @@ function extractWithStdin(binaryPath, cliArgs, content, options, cwd) {
|
|
|
126
126
|
return new Promise((resolve, reject) => {
|
|
127
127
|
const childProcess = spawn(binaryPath, ['extract', ...cliArgs], {
|
|
128
128
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
129
|
-
cwd
|
|
129
|
+
cwd,
|
|
130
|
+
env: getCleanEnv()
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
let stdout = '';
|
package/build/grep.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { execFile } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { getBinaryPath } from './utils.js';
|
|
8
|
+
import { getBinaryPath, getCleanEnv } from './utils.js';
|
|
9
9
|
|
|
10
10
|
const execFileAsync = promisify(execFile);
|
|
11
11
|
|
|
@@ -131,7 +131,7 @@ export async function grep(options) {
|
|
|
131
131
|
const { stdout, stderr } = await execFileAsync(binaryPath, cliArgs, {
|
|
132
132
|
maxBuffer: 10 * 1024 * 1024, // 10MB buffer
|
|
133
133
|
env: {
|
|
134
|
-
...
|
|
134
|
+
...getCleanEnv(),
|
|
135
135
|
// Disable colors in stderr for cleaner output
|
|
136
136
|
NO_COLOR: '1'
|
|
137
137
|
}
|
package/build/index.js
CHANGED
|
@@ -50,6 +50,7 @@ import { editTool, createTool, multiEditTool } from './tools/edit.js';
|
|
|
50
50
|
import { FileTracker } from './tools/fileTracker.js';
|
|
51
51
|
import { ProbeAgent, ENGINE_ACTIVITY_TIMEOUT_DEFAULT, ENGINE_ACTIVITY_TIMEOUT_MIN, ENGINE_ACTIVITY_TIMEOUT_MAX } from './agent/ProbeAgent.js';
|
|
52
52
|
import { SimpleTelemetry, SimpleAppTracer, initializeSimpleTelemetryFromOptions } from './agent/simpleTelemetry.js';
|
|
53
|
+
import { spawnGovernedProcess } from './agent/processSupervisor.js';
|
|
53
54
|
import { listFilesToolInstance, searchFilesToolInstance } from './agent/probeTool.js';
|
|
54
55
|
import { StorageAdapter, InMemoryStorageAdapter } from './agent/storage/index.js';
|
|
55
56
|
import { HookManager, HOOK_TYPES } from './agent/hooks/index.js';
|
|
@@ -88,6 +89,7 @@ export {
|
|
|
88
89
|
SimpleTelemetry,
|
|
89
90
|
SimpleAppTracer,
|
|
90
91
|
initializeSimpleTelemetryFromOptions,
|
|
92
|
+
spawnGovernedProcess,
|
|
91
93
|
// Export tool generators directly
|
|
92
94
|
searchTool,
|
|
93
95
|
queryTool,
|
package/build/query.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { exec } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { getBinaryPath, buildCliArgs, escapeString } from './utils.js';
|
|
8
|
+
import { getBinaryPath, buildCliArgs, escapeString, getCleanEnv } from './utils.js';
|
|
9
9
|
import { validateCwdPath } from './utils/path-validation.js';
|
|
10
10
|
|
|
11
11
|
const execAsync = promisify(exec);
|
|
@@ -84,7 +84,7 @@ export async function query(options) {
|
|
|
84
84
|
const command = `${binaryPath} query ${cliArgs.join(' ')}`;
|
|
85
85
|
|
|
86
86
|
try {
|
|
87
|
-
const { stdout, stderr } = await execAsync(command, { cwd });
|
|
87
|
+
const { stdout, stderr } = await execAsync(command, { cwd, env: getCleanEnv() });
|
|
88
88
|
|
|
89
89
|
if (stderr) {
|
|
90
90
|
console.error(`stderr: ${stderr}`);
|
package/build/search.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
import { execFile } from 'child_process';
|
|
7
7
|
import { promisify } from 'util';
|
|
8
|
-
import { getBinaryPath, buildCliArgs } from './utils.js';
|
|
8
|
+
import { getBinaryPath, buildCliArgs, getCleanEnv } from './utils.js';
|
|
9
9
|
import { validateCwdPath } from './utils/path-validation.js';
|
|
10
10
|
import { TimeoutError, categorizeError } from './utils/error-types.js';
|
|
11
11
|
|
|
@@ -164,6 +164,7 @@ export async function search(options) {
|
|
|
164
164
|
// Execute with execFile (no shell, prevents command injection)
|
|
165
165
|
const { stdout, stderr } = await execFileAsync(binaryPath, args, {
|
|
166
166
|
cwd,
|
|
167
|
+
env: getCleanEnv(),
|
|
167
168
|
timeout: options.timeout * 1000, // Convert seconds to milliseconds
|
|
168
169
|
maxBuffer: 50 * 1024 * 1024 // 50MB buffer for large outputs
|
|
169
170
|
});
|
package/build/symbols.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { spawn } from 'child_process';
|
|
7
|
-
import { getBinaryPath, escapeString } from './utils.js';
|
|
7
|
+
import { getBinaryPath, escapeString, getCleanEnv } from './utils.js';
|
|
8
8
|
import { validateCwdPath } from './utils/path-validation.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -47,7 +47,8 @@ export async function symbols(options) {
|
|
|
47
47
|
return new Promise((resolve, reject) => {
|
|
48
48
|
const childProcess = spawn(binaryPath, args, {
|
|
49
49
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
50
|
-
cwd
|
|
50
|
+
cwd,
|
|
51
|
+
env: getCleanEnv()
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
let stdout = '';
|
package/build/utils.js
CHANGED
|
@@ -122,6 +122,29 @@ export function buildCliArgs(options, flagMap) {
|
|
|
122
122
|
return cliArgs;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
/**
|
|
126
|
+
* Build a minimal environment for spawning the probe binary.
|
|
127
|
+
* Prevents E2BIG when the host process accumulates a large process.env.
|
|
128
|
+
* @returns {Record<string, string>} - Clean environment with only essential vars
|
|
129
|
+
*/
|
|
130
|
+
export function getCleanEnv() {
|
|
131
|
+
const keep = [
|
|
132
|
+
'PATH', 'HOME', 'USER', 'SHELL', 'TERM', 'LANG', 'LC_ALL',
|
|
133
|
+
'TMPDIR', 'TMP', 'TEMP',
|
|
134
|
+
'SystemRoot', 'SYSTEMROOT', 'COMSPEC', // Windows
|
|
135
|
+
'PROBE_PATH', 'PROBE_CONFIG_DIR', 'DEBUG',
|
|
136
|
+
'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY',
|
|
137
|
+
'http_proxy', 'https_proxy', 'no_proxy',
|
|
138
|
+
];
|
|
139
|
+
const env = {};
|
|
140
|
+
for (const key of keep) {
|
|
141
|
+
if (process.env[key] !== undefined) {
|
|
142
|
+
env[key] = process.env[key];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return env;
|
|
146
|
+
}
|
|
147
|
+
|
|
125
148
|
/**
|
|
126
149
|
* Escape a string for use in a command line
|
|
127
150
|
* @param {string} str - String to escape
|