praisonai 1.7.0 → 1.7.2
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/dist/agent/simple.js +19 -2
- package/dist/ai/agent-loop.d.ts +4 -0
- package/dist/ai/agent-loop.js +33 -10
- package/dist/cli/features/external-agents.d.ts +23 -0
- package/dist/cli/features/external-agents.js +84 -0
- package/dist/cli/features/sandbox-executor.d.ts +7 -1
- package/dist/cli/features/sandbox-executor.js +65 -15
- package/dist/index.d.ts +3 -2
- package/dist/index.js +79 -14
- package/dist/mcp/security.d.ts +4 -0
- package/dist/mcp/security.js +33 -10
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +25 -0
- package/dist/os/agentos.js +19 -0
- package/dist/os/config.d.ts +2 -0
- package/dist/os/config.js +3 -0
- package/dist/parity/index.d.ts +1104 -0
- package/dist/parity/index.js +1366 -0
- package/dist/tools/builtins/code-mode.js +16 -16
- package/dist/tools/index.d.ts +5 -2
- package/dist/tools/index.js +32 -5
- package/dist/tools/registry/index.d.ts +2 -2
- package/dist/tools/registry/index.js +10 -1
- package/dist/tools/registry/registry.d.ts +33 -0
- package/dist/tools/registry/registry.js +79 -1
- package/dist/tools/registry/types.d.ts +17 -0
- package/dist/tools/registry/types.js +22 -1
- package/dist/tools/utility-tools.js +39 -9
- package/dist/workflows/yaml-parser.d.ts +4 -1
- package/dist/workflows/yaml-parser.js +42 -5
- package/package.json +2 -2
package/dist/agent/simple.js
CHANGED
|
@@ -116,6 +116,23 @@ class Agent {
|
|
|
116
116
|
this.addAutoGeneratedToolDefinition(randomName, tool);
|
|
117
117
|
}
|
|
118
118
|
}
|
|
119
|
+
else if (tool && typeof tool === 'object' && typeof tool.execute === 'function' && tool.name) {
|
|
120
|
+
// If it's a FunctionTool instance (has execute method and name)
|
|
121
|
+
const funcName = tool.name;
|
|
122
|
+
// Register the execute function with args as object (not spread)
|
|
123
|
+
this.registerToolFunction(funcName, async (args) => {
|
|
124
|
+
return tool.execute(args);
|
|
125
|
+
});
|
|
126
|
+
// Add the tool definition from FunctionTool
|
|
127
|
+
processedTools.push(tool.toOpenAITool ? tool.toOpenAITool() : {
|
|
128
|
+
type: 'function',
|
|
129
|
+
function: {
|
|
130
|
+
name: tool.name,
|
|
131
|
+
description: tool.description || `Function ${tool.name}`,
|
|
132
|
+
parameters: tool.parameters || { type: 'object', properties: {} },
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
119
136
|
else {
|
|
120
137
|
// If it's already a tool definition, add it as is
|
|
121
138
|
processedTools.push(tool);
|
|
@@ -295,8 +312,8 @@ class Agent {
|
|
|
295
312
|
if (!this.toolFunctions[name]) {
|
|
296
313
|
throw new Error(`Function ${name} not registered`);
|
|
297
314
|
}
|
|
298
|
-
// Call the function
|
|
299
|
-
const result = await this.toolFunctions[name](
|
|
315
|
+
// Call the function - pass args as object (for FunctionTool) or spread (for legacy functions)
|
|
316
|
+
const result = await this.toolFunctions[name](args);
|
|
300
317
|
// Add result to messages
|
|
301
318
|
results.push({
|
|
302
319
|
role: 'tool',
|
package/dist/ai/agent-loop.d.ts
CHANGED
|
@@ -154,6 +154,10 @@ export declare class AgentLoop {
|
|
|
154
154
|
* Get the final result.
|
|
155
155
|
*/
|
|
156
156
|
getResult(): AgentLoopResult;
|
|
157
|
+
/**
|
|
158
|
+
* Wrap tools so onToolCall approval runs before execution.
|
|
159
|
+
*/
|
|
160
|
+
private wrapToolsWithApproval;
|
|
157
161
|
/**
|
|
158
162
|
* Check if the loop should stop.
|
|
159
163
|
*/
|
package/dist/ai/agent-loop.js
CHANGED
|
@@ -111,6 +111,9 @@ class AgentLoop {
|
|
|
111
111
|
maxSteps: 10,
|
|
112
112
|
...config,
|
|
113
113
|
};
|
|
114
|
+
if (this.config.onToolCall && this.config.tools) {
|
|
115
|
+
this.config.tools = this.wrapToolsWithApproval(this.config.tools);
|
|
116
|
+
}
|
|
114
117
|
// Add system message if provided
|
|
115
118
|
if (config.system) {
|
|
116
119
|
this.messages.push({ role: 'system', content: config.system });
|
|
@@ -182,16 +185,9 @@ class AgentLoop {
|
|
|
182
185
|
}
|
|
183
186
|
// Handle tool calls
|
|
184
187
|
if (step.toolCalls.length > 0) {
|
|
185
|
-
//
|
|
186
|
-
if (
|
|
187
|
-
|
|
188
|
-
const approved = await this.config.onToolCall(toolCall);
|
|
189
|
-
if (!approved) {
|
|
190
|
-
this.complete = true;
|
|
191
|
-
step.finishReason = 'tool_rejected';
|
|
192
|
-
break;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
188
|
+
// Rejection is handled in wrapped tool execute before execution
|
|
189
|
+
if (step.finishReason === 'tool_rejected') {
|
|
190
|
+
this.complete = true;
|
|
195
191
|
}
|
|
196
192
|
// Add tool call message
|
|
197
193
|
const toolCallParts = step.toolCalls.map(tc => ({
|
|
@@ -260,6 +256,33 @@ class AgentLoop {
|
|
|
260
256
|
finishReason: lastStep?.finishReason || 'unknown',
|
|
261
257
|
};
|
|
262
258
|
}
|
|
259
|
+
/**
|
|
260
|
+
* Wrap tools so onToolCall approval runs before execution.
|
|
261
|
+
*/
|
|
262
|
+
wrapToolsWithApproval(tools) {
|
|
263
|
+
const onToolCall = this.config.onToolCall;
|
|
264
|
+
if (!onToolCall) {
|
|
265
|
+
return tools;
|
|
266
|
+
}
|
|
267
|
+
const wrapped = {};
|
|
268
|
+
for (const [name, tool] of Object.entries(tools)) {
|
|
269
|
+
wrapped[name] = {
|
|
270
|
+
...tool,
|
|
271
|
+
execute: async (args) => {
|
|
272
|
+
const approved = await onToolCall({
|
|
273
|
+
toolCallId: `pending-${name}`,
|
|
274
|
+
toolName: name,
|
|
275
|
+
args,
|
|
276
|
+
});
|
|
277
|
+
if (!approved) {
|
|
278
|
+
throw new Error(`Tool call rejected: ${name}`);
|
|
279
|
+
}
|
|
280
|
+
return tool.execute(args);
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return wrapped;
|
|
285
|
+
}
|
|
263
286
|
/**
|
|
264
287
|
* Check if the loop should stop.
|
|
265
288
|
*/
|
|
@@ -16,6 +16,16 @@ export interface ExternalAgentResult {
|
|
|
16
16
|
exitCode: number;
|
|
17
17
|
duration: number;
|
|
18
18
|
}
|
|
19
|
+
export type StreamEvent = {
|
|
20
|
+
type: 'text';
|
|
21
|
+
content: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: 'json';
|
|
24
|
+
data: unknown;
|
|
25
|
+
} | {
|
|
26
|
+
type: 'error';
|
|
27
|
+
error: string;
|
|
28
|
+
};
|
|
19
29
|
/**
|
|
20
30
|
* Base class for external agent integrations
|
|
21
31
|
*/
|
|
@@ -30,6 +40,10 @@ export declare abstract class BaseExternalAgent {
|
|
|
30
40
|
* Execute a prompt with the external agent
|
|
31
41
|
*/
|
|
32
42
|
abstract execute(prompt: string): Promise<ExternalAgentResult>;
|
|
43
|
+
/**
|
|
44
|
+
* Stream output from the external agent
|
|
45
|
+
*/
|
|
46
|
+
abstract stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
33
47
|
/**
|
|
34
48
|
* Get the agent name
|
|
35
49
|
*/
|
|
@@ -38,6 +52,10 @@ export declare abstract class BaseExternalAgent {
|
|
|
38
52
|
* Execute a command and return result
|
|
39
53
|
*/
|
|
40
54
|
protected runCommand(args: string[]): Promise<ExternalAgentResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Stream command output line by line
|
|
57
|
+
*/
|
|
58
|
+
protected streamCommand(args: string[]): AsyncGenerator<StreamEvent, void, unknown>;
|
|
41
59
|
/**
|
|
42
60
|
* Check if a command exists
|
|
43
61
|
*/
|
|
@@ -50,6 +68,7 @@ export declare class ClaudeCodeAgent extends BaseExternalAgent {
|
|
|
50
68
|
constructor(cwd?: string);
|
|
51
69
|
isAvailable(): Promise<boolean>;
|
|
52
70
|
execute(prompt: string): Promise<ExternalAgentResult>;
|
|
71
|
+
stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
53
72
|
executeWithSession(prompt: string, sessionId?: string): Promise<ExternalAgentResult>;
|
|
54
73
|
}
|
|
55
74
|
/**
|
|
@@ -60,6 +79,7 @@ export declare class GeminiCliAgent extends BaseExternalAgent {
|
|
|
60
79
|
constructor(cwd?: string, model?: string);
|
|
61
80
|
isAvailable(): Promise<boolean>;
|
|
62
81
|
execute(prompt: string): Promise<ExternalAgentResult>;
|
|
82
|
+
stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
63
83
|
}
|
|
64
84
|
/**
|
|
65
85
|
* OpenAI Codex CLI integration
|
|
@@ -68,6 +88,7 @@ export declare class CodexCliAgent extends BaseExternalAgent {
|
|
|
68
88
|
constructor(cwd?: string);
|
|
69
89
|
isAvailable(): Promise<boolean>;
|
|
70
90
|
execute(prompt: string): Promise<ExternalAgentResult>;
|
|
91
|
+
stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
71
92
|
}
|
|
72
93
|
/**
|
|
73
94
|
* Aider CLI integration
|
|
@@ -76,6 +97,7 @@ export declare class AiderAgent extends BaseExternalAgent {
|
|
|
76
97
|
constructor(cwd?: string);
|
|
77
98
|
isAvailable(): Promise<boolean>;
|
|
78
99
|
execute(prompt: string): Promise<ExternalAgentResult>;
|
|
100
|
+
stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
79
101
|
}
|
|
80
102
|
/**
|
|
81
103
|
* Generic external agent for any CLI tool
|
|
@@ -87,6 +109,7 @@ export declare class GenericExternalAgent extends BaseExternalAgent {
|
|
|
87
109
|
});
|
|
88
110
|
isAvailable(): Promise<boolean>;
|
|
89
111
|
execute(prompt: string): Promise<ExternalAgentResult>;
|
|
112
|
+
stream(prompt: string): AsyncGenerator<StreamEvent, void, unknown>;
|
|
90
113
|
}
|
|
91
114
|
/**
|
|
92
115
|
* External Agent Registry
|
|
@@ -97,6 +97,59 @@ class BaseExternalAgent {
|
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Stream command output line by line
|
|
102
|
+
*/
|
|
103
|
+
async *streamCommand(args) {
|
|
104
|
+
const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
105
|
+
const proc = spawn(this.config.command, args, {
|
|
106
|
+
cwd: this.config.cwd || process.cwd(),
|
|
107
|
+
env: { ...process.env, ...this.config.env },
|
|
108
|
+
timeout: this.config.timeout,
|
|
109
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
110
|
+
});
|
|
111
|
+
if (!proc.stdout) {
|
|
112
|
+
throw new Error('Failed to create stdout stream');
|
|
113
|
+
}
|
|
114
|
+
let stderr = '';
|
|
115
|
+
proc.stderr?.on('data', (chunk) => {
|
|
116
|
+
stderr += chunk.toString();
|
|
117
|
+
});
|
|
118
|
+
const exit = new Promise((resolve, reject) => {
|
|
119
|
+
proc.once('error', reject);
|
|
120
|
+
proc.once('close', resolve);
|
|
121
|
+
});
|
|
122
|
+
const readline = await Promise.resolve().then(() => __importStar(require('readline')));
|
|
123
|
+
const rl = readline.createInterface({
|
|
124
|
+
input: proc.stdout,
|
|
125
|
+
crlfDelay: Infinity
|
|
126
|
+
});
|
|
127
|
+
try {
|
|
128
|
+
for await (const line of rl) {
|
|
129
|
+
if (line.trim()) {
|
|
130
|
+
// Try to parse as JSON first
|
|
131
|
+
try {
|
|
132
|
+
const event = JSON.parse(line);
|
|
133
|
+
yield { type: 'json', data: event };
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
// If not JSON, treat as text
|
|
137
|
+
yield { type: 'text', content: line };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const exitCode = await exit;
|
|
142
|
+
if (exitCode !== 0) {
|
|
143
|
+
throw new Error(stderr || `${this.config.command} exited with code ${exitCode}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
rl.close();
|
|
148
|
+
if (!proc.killed) {
|
|
149
|
+
proc.kill();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
100
153
|
/**
|
|
101
154
|
* Check if a command exists
|
|
102
155
|
*/
|
|
@@ -127,6 +180,9 @@ class ClaudeCodeAgent extends BaseExternalAgent {
|
|
|
127
180
|
async execute(prompt) {
|
|
128
181
|
return this.runCommand(['--print', prompt]);
|
|
129
182
|
}
|
|
183
|
+
async *stream(prompt) {
|
|
184
|
+
yield* this.streamCommand(['--print', '--output-format', 'stream-json', prompt]);
|
|
185
|
+
}
|
|
130
186
|
async executeWithSession(prompt, sessionId) {
|
|
131
187
|
const args = ['--print'];
|
|
132
188
|
if (sessionId) {
|
|
@@ -155,6 +211,9 @@ class GeminiCliAgent extends BaseExternalAgent {
|
|
|
155
211
|
async execute(prompt) {
|
|
156
212
|
return this.runCommand(['-m', this.model, prompt]);
|
|
157
213
|
}
|
|
214
|
+
async *stream(prompt) {
|
|
215
|
+
yield* this.streamCommand(['-m', this.model, '--json', prompt]);
|
|
216
|
+
}
|
|
158
217
|
}
|
|
159
218
|
exports.GeminiCliAgent = GeminiCliAgent;
|
|
160
219
|
/**
|
|
@@ -174,6 +233,9 @@ class CodexCliAgent extends BaseExternalAgent {
|
|
|
174
233
|
async execute(prompt) {
|
|
175
234
|
return this.runCommand(['exec', '--full-auto', prompt]);
|
|
176
235
|
}
|
|
236
|
+
async *stream(prompt) {
|
|
237
|
+
yield* this.streamCommand(['exec', '--full-auto', '--json', prompt]);
|
|
238
|
+
}
|
|
177
239
|
}
|
|
178
240
|
exports.CodexCliAgent = CodexCliAgent;
|
|
179
241
|
/**
|
|
@@ -193,6 +255,18 @@ class AiderAgent extends BaseExternalAgent {
|
|
|
193
255
|
async execute(prompt) {
|
|
194
256
|
return this.runCommand(['--message', prompt, '--yes']);
|
|
195
257
|
}
|
|
258
|
+
async *stream(prompt) {
|
|
259
|
+
// Aider doesn't support JSON streaming, so just yield text events
|
|
260
|
+
const result = await this.execute(prompt);
|
|
261
|
+
if (!result.success) {
|
|
262
|
+
throw new Error(result.error || 'Aider execution failed');
|
|
263
|
+
}
|
|
264
|
+
for (const line of result.output.split('\n')) {
|
|
265
|
+
if (line.trim()) {
|
|
266
|
+
yield { type: 'text', content: line };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
196
270
|
}
|
|
197
271
|
exports.AiderAgent = AiderAgent;
|
|
198
272
|
/**
|
|
@@ -216,6 +290,16 @@ class GenericExternalAgent extends BaseExternalAgent {
|
|
|
216
290
|
}
|
|
217
291
|
return this.runCommand(args);
|
|
218
292
|
}
|
|
293
|
+
async *stream(prompt) {
|
|
294
|
+
const args = [...(this.config.args || [])];
|
|
295
|
+
if (this.promptArg) {
|
|
296
|
+
args.push(this.promptArg, prompt);
|
|
297
|
+
}
|
|
298
|
+
else {
|
|
299
|
+
args.push(prompt);
|
|
300
|
+
}
|
|
301
|
+
yield* this.streamCommand(args);
|
|
302
|
+
}
|
|
219
303
|
}
|
|
220
304
|
exports.GenericExternalAgent = GenericExternalAgent;
|
|
221
305
|
/**
|
|
@@ -28,6 +28,11 @@ export declare const DEFAULT_BLOCKED_COMMANDS: string[];
|
|
|
28
28
|
* Default blocked paths
|
|
29
29
|
*/
|
|
30
30
|
export declare const DEFAULT_BLOCKED_PATHS: string[];
|
|
31
|
+
/** Shell metacharacters that enable command chaining or substitution */
|
|
32
|
+
export declare const SHELL_METACHAR_PATTERN: RegExp;
|
|
33
|
+
/** Commands that typically require network access */
|
|
34
|
+
export declare const NETWORK_COMMANDS: string[];
|
|
35
|
+
export declare function containsShellMetacharacters(command: string): boolean;
|
|
31
36
|
/**
|
|
32
37
|
* Command validator
|
|
33
38
|
*/
|
|
@@ -35,6 +40,7 @@ export declare class CommandValidator {
|
|
|
35
40
|
private blockedCommands;
|
|
36
41
|
private blockedPaths;
|
|
37
42
|
private allowedCommands?;
|
|
43
|
+
private networkIsolated;
|
|
38
44
|
constructor(config?: Partial<SandboxConfig>);
|
|
39
45
|
/**
|
|
40
46
|
* Validate a command
|
|
@@ -56,7 +62,7 @@ export declare class SandboxExecutor {
|
|
|
56
62
|
*/
|
|
57
63
|
execute(command: string): Promise<ExecutionResult>;
|
|
58
64
|
/**
|
|
59
|
-
* Spawn the command
|
|
65
|
+
* Spawn the command without shell when allowlisted or in strict mode
|
|
60
66
|
*/
|
|
61
67
|
private spawn;
|
|
62
68
|
/**
|
|
@@ -36,7 +36,8 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
36
36
|
};
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.SandboxExecutor = exports.CommandValidator = exports.DEFAULT_BLOCKED_PATHS = exports.DEFAULT_BLOCKED_COMMANDS = void 0;
|
|
39
|
+
exports.SandboxExecutor = exports.CommandValidator = exports.NETWORK_COMMANDS = exports.SHELL_METACHAR_PATTERN = exports.DEFAULT_BLOCKED_PATHS = exports.DEFAULT_BLOCKED_COMMANDS = void 0;
|
|
40
|
+
exports.containsShellMetacharacters = containsShellMetacharacters;
|
|
40
41
|
exports.createSandboxExecutor = createSandboxExecutor;
|
|
41
42
|
exports.sandboxExec = sandboxExec;
|
|
42
43
|
/**
|
|
@@ -80,6 +81,28 @@ exports.DEFAULT_BLOCKED_PATHS = [
|
|
|
80
81
|
'/sys',
|
|
81
82
|
'/proc'
|
|
82
83
|
];
|
|
84
|
+
/** Shell metacharacters that enable command chaining or substitution */
|
|
85
|
+
exports.SHELL_METACHAR_PATTERN = /[;|&`><]|\$\([^)]*\)|\$\{/;
|
|
86
|
+
/** Commands that typically require network access */
|
|
87
|
+
exports.NETWORK_COMMANDS = [
|
|
88
|
+
'curl', 'wget', 'nc', 'netcat', 'ssh', 'scp', 'sftp', 'ftp', 'telnet',
|
|
89
|
+
'ping', 'nslookup', 'dig', 'host', 'traceroute', 'tracepath', 'nmap',
|
|
90
|
+
'socat', 'openssl', 'node', 'python', 'python3', 'ruby', 'perl',
|
|
91
|
+
];
|
|
92
|
+
/** Environment variables commonly used for network/proxy configuration */
|
|
93
|
+
const NETWORK_ENV_VARS = [
|
|
94
|
+
'http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY',
|
|
95
|
+
'ALL_PROXY', 'all_proxy', 'NO_PROXY', 'no_proxy',
|
|
96
|
+
'FTP_PROXY', 'ftp_proxy', 'SOCKS_PROXY', 'socks_proxy',
|
|
97
|
+
];
|
|
98
|
+
function containsShellMetacharacters(command) {
|
|
99
|
+
return exports.SHELL_METACHAR_PATTERN.test(command);
|
|
100
|
+
}
|
|
101
|
+
function parseCommandParts(command) {
|
|
102
|
+
const parts = command.trim().match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
|
|
103
|
+
const unquoted = parts.map((part) => part.replace(/^["']|["']$/g, ''));
|
|
104
|
+
return { cmd: unquoted[0] ?? '', args: unquoted.slice(1) };
|
|
105
|
+
}
|
|
83
106
|
/**
|
|
84
107
|
* Command validator
|
|
85
108
|
*/
|
|
@@ -88,19 +111,31 @@ class CommandValidator {
|
|
|
88
111
|
this.blockedCommands = config.blockedCommands || exports.DEFAULT_BLOCKED_COMMANDS;
|
|
89
112
|
this.blockedPaths = config.blockedPaths || exports.DEFAULT_BLOCKED_PATHS;
|
|
90
113
|
this.allowedCommands = config.allowedCommands;
|
|
114
|
+
this.networkIsolated = config.mode === 'network-isolated';
|
|
91
115
|
}
|
|
92
116
|
/**
|
|
93
117
|
* Validate a command
|
|
94
118
|
*/
|
|
95
119
|
validate(command) {
|
|
96
|
-
const
|
|
120
|
+
const trimmed = command.trim();
|
|
121
|
+
if (!trimmed) {
|
|
122
|
+
return { valid: false, reason: 'Empty command' };
|
|
123
|
+
}
|
|
124
|
+
if (containsShellMetacharacters(trimmed)) {
|
|
125
|
+
return { valid: false, reason: 'Shell metacharacters are not allowed' };
|
|
126
|
+
}
|
|
127
|
+
const { cmd } = parseCommandParts(trimmed);
|
|
128
|
+
const normalized = trimmed.toLowerCase();
|
|
97
129
|
// Check allowlist first if specified
|
|
98
130
|
if (this.allowedCommands) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return { valid: false, reason: `Command '${baseCmd}' not in allowlist` };
|
|
131
|
+
if (!this.allowedCommands.includes(cmd.toLowerCase())) {
|
|
132
|
+
return { valid: false, reason: `Command '${cmd}' not in allowlist` };
|
|
102
133
|
}
|
|
103
134
|
}
|
|
135
|
+
// network-isolated: reject network-oriented commands (proxy env alone is insufficient)
|
|
136
|
+
if (this.networkIsolated && exports.NETWORK_COMMANDS.includes(cmd.toLowerCase())) {
|
|
137
|
+
return { valid: false, reason: `Network command '${cmd}' blocked in network-isolated mode` };
|
|
138
|
+
}
|
|
104
139
|
// Check blocked commands
|
|
105
140
|
for (const blocked of this.blockedCommands) {
|
|
106
141
|
if (normalized.includes(blocked.toLowerCase())) {
|
|
@@ -190,18 +225,28 @@ class SandboxExecutor {
|
|
|
190
225
|
}
|
|
191
226
|
}
|
|
192
227
|
/**
|
|
193
|
-
* Spawn the command
|
|
228
|
+
* Spawn the command without shell when allowlisted or in strict mode
|
|
194
229
|
*/
|
|
195
230
|
async spawn(command) {
|
|
196
231
|
const { spawn } = await Promise.resolve().then(() => __importStar(require('child_process')));
|
|
232
|
+
const env = this.buildEnv();
|
|
233
|
+
const useDirectSpawn = Boolean(this.config.allowedCommands) || this.config.mode === 'strict';
|
|
234
|
+
const { cmd, args } = parseCommandParts(command);
|
|
197
235
|
return new Promise((resolve) => {
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
236
|
+
const proc = useDirectSpawn
|
|
237
|
+
? spawn(cmd, args, {
|
|
238
|
+
cwd: this.config.cwd,
|
|
239
|
+
env,
|
|
240
|
+
timeout: this.config.timeout,
|
|
241
|
+
shell: false,
|
|
242
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
243
|
+
})
|
|
244
|
+
: spawn('sh', ['-c', command], {
|
|
245
|
+
cwd: this.config.cwd,
|
|
246
|
+
env,
|
|
247
|
+
timeout: this.config.timeout,
|
|
248
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
249
|
+
});
|
|
205
250
|
let stdout = '';
|
|
206
251
|
let stderr = '';
|
|
207
252
|
let truncated = false;
|
|
@@ -260,7 +305,11 @@ class SandboxExecutor {
|
|
|
260
305
|
...this.config.env
|
|
261
306
|
};
|
|
262
307
|
case 'network-isolated':
|
|
263
|
-
//
|
|
308
|
+
// Proxy env vars alone do not block network; also reject network commands in validator.
|
|
309
|
+
// Strip common proxy/network env vars as a defence-in-depth measure.
|
|
310
|
+
for (const key of NETWORK_ENV_VARS) {
|
|
311
|
+
delete baseEnv[key];
|
|
312
|
+
}
|
|
264
313
|
return {
|
|
265
314
|
...baseEnv,
|
|
266
315
|
http_proxy: 'http://localhost:0',
|
|
@@ -268,7 +317,7 @@ class SandboxExecutor {
|
|
|
268
317
|
HTTP_PROXY: 'http://localhost:0',
|
|
269
318
|
HTTPS_PROXY: 'http://localhost:0',
|
|
270
319
|
no_proxy: '',
|
|
271
|
-
NO_PROXY: ''
|
|
320
|
+
NO_PROXY: '',
|
|
272
321
|
};
|
|
273
322
|
case 'basic':
|
|
274
323
|
default:
|
|
@@ -296,6 +345,7 @@ class SandboxExecutor {
|
|
|
296
345
|
*/
|
|
297
346
|
setMode(mode) {
|
|
298
347
|
this.config.mode = mode;
|
|
348
|
+
this.validator = new CommandValidator(this.config);
|
|
299
349
|
}
|
|
300
350
|
}
|
|
301
351
|
exports.SandboxExecutor = SandboxExecutor;
|
package/dist/index.d.ts
CHANGED
|
@@ -48,9 +48,9 @@ export { BaseTool, ToolResult, ToolValidationError, validateTool, createTool, Fu
|
|
|
48
48
|
export * from './tools/arxivTools';
|
|
49
49
|
export * from './tools/mcpSse';
|
|
50
50
|
export { tools, registerBuiltinTools } from './tools/tools';
|
|
51
|
-
export { getToolsRegistry, createToolsRegistry, resetToolsRegistry, ToolsRegistry } from './tools/registry';
|
|
51
|
+
export { getToolsRegistry, createToolsRegistry, resetToolsRegistry, ToolsRegistry, get_registry, get_tool, register_tool, validate_tool } from './tools/registry';
|
|
52
52
|
export type { ToolExecutionContext, ToolLimits, RedactionHooks, ToolLogger, ToolCapabilities, InstallHints, ToolMetadata, ToolExecutionResult, PraisonTool, ToolParameterSchema, ToolParameterProperty, ToolMiddleware, ToolHooks, ToolFactory, RegisteredTool, ToolInstallStatus } from './tools/registry';
|
|
53
|
-
export { MissingDependencyError, MissingEnvVarError } from './tools/registry';
|
|
53
|
+
export { MissingDependencyError, MissingEnvVarError, BudgetExceededError } from './tools/registry';
|
|
54
54
|
export { createLoggingMiddleware, createTimeoutMiddleware, createRedactionMiddleware, createRateLimitMiddleware, createRetryMiddleware, createTracingMiddleware, createValidationMiddleware, composeMiddleware } from './tools/registry';
|
|
55
55
|
export { codeExecution, tavilySearch, tavilyExtract, tavilyCrawl, exaSearch, perplexitySearch, parallelSearch, firecrawlScrape, firecrawlCrawl, superagentGuard, superagentRedact, superagentVerify, valyuWebSearch, valyuFinanceSearch, valyuPaperSearch, valyuBioSearch, valyuPatentSearch, valyuSecSearch, valyuEconomicsSearch, valyuCompanyResearch, bedrockCodeInterpreter, bedrockBrowserNavigate, bedrockBrowserClick, bedrockBrowserFill, airweaveSearch, codeMode, registerCustomTool, createCustomTool, registerNpmTool, registerLocalTool } from './tools/builtins';
|
|
56
56
|
export * from './session';
|
|
@@ -115,3 +115,4 @@ export { type ConditionProtocol, type RoutingConditionProtocol, DictCondition, E
|
|
|
115
115
|
export { type BotConfig, type BotUser, type BotChannel, type BotMessage, type BotProtocol, type GatewayConfig, type GatewayEvent, type GatewayMessage, type GatewayProtocol, type GatewayClientProtocol, type GatewaySessionProtocol, type ProviderStatus, type FailoverConfig, type AuthProfile, type ResourceLimits, type SandboxResult, type SandboxProtocol, type ReflectionOutput, type AutoRagConfig, SandboxStatus, AutonomyLevel, RagRetrievalPolicy, FailoverManager, } from './gateway';
|
|
116
116
|
export { type TaskConfig as AgentTaskConfig, type TaskOutput, type Task as AgentTask, BaseTask, createTaskOutput, } from './task';
|
|
117
117
|
export { A2ATaskState, A2ARole, type A2ATextPart, type A2AFilePart, type A2ADataPart, type A2APart, type A2AMessage, type A2ATaskStatus, type A2ATask, type A2AArtifact, type A2AAgentSkill, type A2AAgentCapabilities, type A2AAgentCard, type A2ASendMessageRequest, A2A, AGUI, RetrievalPolicy as AutoRetrievalPolicy, type AutoRagAgentConfig, DEFAULT_AUTO_KEYWORDS, AutoRagAgent, type ToolDefinition, Tools, config, memory, obs, workflows, type GuardrailPolicy, resolveGuardrailPolicies, GUARDRAIL_POLICY_PRESETS, type AgentManager, } from './protocols';
|
|
118
|
+
export { type AudioConfig, type CodeConfig, type OCRConfig, type VisionConfig, type VideoConfig, type RealtimeConfig, CodeAgent, OCRAgent, VisionAgent, VideoAgent, RealtimeAgent, EmbeddingAgent, type MCPCall, type WebSearchCall, type FileSearchCall, type CodeExecutionStep, type DeepResearchResponse, type Provider, createContextAgent as create_context_agent, handoffFilters as handoff_filters, promptWithHandoffInstructions as prompt_with_handoff_instructions, Chunking, If, when, Knowledge, Parallel, Route, Session, ContextManager, MCP, type ManagerConfig, type OptimizerStrategy, type ContextPack, type GuardrailResult, type ContextConfig, enableTelemetry as enable_telemetry, disableTelemetry as disable_telemetry, getTelemetry as get_telemetry, enablePerformanceMode as enable_performance_mode, disablePerformanceMode as disable_performance_mode, cleanupTelemetryResources as cleanup_telemetry_resources, register_display_callback, sync_display_callbacks, async_display_callbacks, display_error, display_generating, display_instruction, display_interaction, display_self_reflection, display_tool_call, error_logs, get_plugin_manager, get_default_plugin_dirs, ensure_plugin_dir, get_plugin_template, load_plugin, parse_plugin_header, parse_plugin_header_from_file, discover_plugins, discover_and_load_plugins, evaluate_condition, get_dimensions, track_workflow, resolve_guardrail_policies, trace_context, } from './parity';
|