@tyvm/knowhow 0.0.21 → 0.0.23

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.
Files changed (172) hide show
  1. package/package.json +3 -1
  2. package/src/agents/base/base.ts +16 -7
  3. package/src/agents/configurable/ConfigAgent.ts +5 -3
  4. package/src/agents/developer/developer.ts +3 -4
  5. package/src/agents/index.ts +26 -2
  6. package/src/agents/patcher/patcher.ts +3 -5
  7. package/src/agents/researcher/researcher.ts +3 -4
  8. package/src/agents/tools/agentCall.ts +5 -2
  9. package/src/agents/tools/executeScript/README.md +78 -0
  10. package/src/agents/tools/executeScript/definition.ts +73 -0
  11. package/src/agents/tools/executeScript/examples/dependency-injection-validation.ts +272 -0
  12. package/src/agents/tools/executeScript/examples/quick-test.ts +74 -0
  13. package/src/agents/tools/executeScript/examples/serialization-test.ts +321 -0
  14. package/src/agents/tools/executeScript/examples/test-runner.ts +197 -0
  15. package/src/agents/tools/executeScript/index.ts +93 -0
  16. package/src/agents/tools/index.ts +1 -0
  17. package/src/agents/tools/list.ts +2 -1
  18. package/src/agents/vim/vim.ts +3 -4
  19. package/src/ai.ts +2 -1
  20. package/src/chat.ts +4 -2
  21. package/src/cli.ts +7 -15
  22. package/src/clients/index.ts +23 -9
  23. package/src/dataset/diffs/test.ts +2 -1
  24. package/src/index.ts +3 -3
  25. package/src/services/AgentService.ts +9 -10
  26. package/src/services/EventService.ts +0 -2
  27. package/src/services/GitHub.ts +0 -1
  28. package/src/services/KnowhowClient.ts +0 -3
  29. package/src/services/Mcp.ts +0 -2
  30. package/src/services/S3.ts +0 -1
  31. package/src/services/Tools.ts +63 -8
  32. package/src/services/flags.ts +0 -1
  33. package/src/services/index.ts +56 -0
  34. package/src/services/modules/index.ts +53 -0
  35. package/src/{modules → services/modules}/types.ts +16 -5
  36. package/src/services/script-execution/SandboxContext.ts +278 -0
  37. package/src/services/script-execution/ScriptExecutor.ts +339 -0
  38. package/src/services/script-execution/ScriptPolicy.ts +236 -0
  39. package/src/services/script-execution/ScriptTracer.ts +249 -0
  40. package/src/services/script-execution/types.ts +134 -0
  41. package/src/worker.ts +3 -3
  42. package/tests/integration/fileblocks/readwrite.test.ts +2 -1
  43. package/tests/integration/patching.test.ts +5 -5
  44. package/ts_build/src/agents/base/base.d.ts +9 -4
  45. package/ts_build/src/agents/base/base.js +7 -10
  46. package/ts_build/src/agents/base/base.js.map +1 -1
  47. package/ts_build/src/agents/configurable/ConfigAgent.d.ts +2 -2
  48. package/ts_build/src/agents/configurable/ConfigAgent.js +2 -2
  49. package/ts_build/src/agents/configurable/ConfigAgent.js.map +1 -1
  50. package/ts_build/src/agents/developer/developer.d.ts +2 -3
  51. package/ts_build/src/agents/developer/developer.js +3 -4
  52. package/ts_build/src/agents/developer/developer.js.map +1 -1
  53. package/ts_build/src/agents/index.d.ts +11 -2
  54. package/ts_build/src/agents/index.js +19 -3
  55. package/ts_build/src/agents/index.js.map +1 -1
  56. package/ts_build/src/agents/patcher/patcher.d.ts +2 -3
  57. package/ts_build/src/agents/patcher/patcher.js +3 -4
  58. package/ts_build/src/agents/patcher/patcher.js.map +1 -1
  59. package/ts_build/src/agents/researcher/researcher.d.ts +2 -3
  60. package/ts_build/src/agents/researcher/researcher.js +3 -4
  61. package/ts_build/src/agents/researcher/researcher.js.map +1 -1
  62. package/ts_build/src/agents/tools/agentCall.js +4 -4
  63. package/ts_build/src/agents/tools/agentCall.js.map +1 -1
  64. package/ts_build/src/agents/tools/executeScript/definition.d.ts +2 -0
  65. package/ts_build/src/agents/tools/executeScript/definition.js +70 -0
  66. package/ts_build/src/agents/tools/executeScript/definition.js.map +1 -0
  67. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.d.ts +18 -0
  68. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js +192 -0
  69. package/ts_build/src/agents/tools/executeScript/examples/dependency-injection-validation.js.map +1 -0
  70. package/ts_build/src/agents/tools/executeScript/examples/quick-test.d.ts +3 -0
  71. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js +65 -0
  72. package/ts_build/src/agents/tools/executeScript/examples/quick-test.js.map +1 -0
  73. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.d.ts +15 -0
  74. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js +266 -0
  75. package/ts_build/src/agents/tools/executeScript/examples/serialization-test.js.map +1 -0
  76. package/ts_build/src/agents/tools/executeScript/examples/simple-example.d.ts +20 -0
  77. package/ts_build/src/agents/tools/executeScript/examples/simple-example.js +35 -0
  78. package/ts_build/src/agents/tools/executeScript/examples/simple-example.js.map +1 -0
  79. package/ts_build/src/agents/tools/executeScript/examples/test-runner.d.ts +4 -0
  80. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js +198 -0
  81. package/ts_build/src/agents/tools/executeScript/examples/test-runner.js.map +1 -0
  82. package/ts_build/src/agents/tools/executeScript/handler.d.ts +27 -0
  83. package/ts_build/src/agents/tools/executeScript/handler.js +64 -0
  84. package/ts_build/src/agents/tools/executeScript/handler.js.map +1 -0
  85. package/ts_build/src/agents/tools/executeScript/index.d.ts +27 -0
  86. package/ts_build/src/agents/tools/executeScript/index.js +70 -0
  87. package/ts_build/src/agents/tools/executeScript/index.js.map +1 -0
  88. package/ts_build/src/agents/tools/executeScript.d.ts +29 -0
  89. package/ts_build/src/agents/tools/executeScript.js +124 -0
  90. package/ts_build/src/agents/tools/executeScript.js.map +1 -0
  91. package/ts_build/src/agents/tools/index.d.ts +1 -0
  92. package/ts_build/src/agents/tools/index.js +1 -0
  93. package/ts_build/src/agents/tools/index.js.map +1 -1
  94. package/ts_build/src/agents/tools/list.js +2 -0
  95. package/ts_build/src/agents/tools/list.js.map +1 -1
  96. package/ts_build/src/agents/vim/vim.d.ts +2 -3
  97. package/ts_build/src/agents/vim/vim.js +3 -4
  98. package/ts_build/src/agents/vim/vim.js.map +1 -1
  99. package/ts_build/src/ai.js +2 -1
  100. package/ts_build/src/ai.js.map +1 -1
  101. package/ts_build/src/chat.js +10 -9
  102. package/ts_build/src/chat.js.map +1 -1
  103. package/ts_build/src/cli.js +12 -19
  104. package/ts_build/src/cli.js.map +1 -1
  105. package/ts_build/src/clients/index.d.ts +9 -2
  106. package/ts_build/src/clients/index.js +17 -4
  107. package/ts_build/src/clients/index.js.map +1 -1
  108. package/ts_build/src/dataset/diffs/test.js +2 -1
  109. package/ts_build/src/dataset/diffs/test.js.map +1 -1
  110. package/ts_build/src/index.js +10 -10
  111. package/ts_build/src/index.js.map +1 -1
  112. package/ts_build/src/services/AgentService.d.ts +7 -3
  113. package/ts_build/src/services/AgentService.js +11 -10
  114. package/ts_build/src/services/AgentService.js.map +1 -1
  115. package/ts_build/src/services/EventService.d.ts +0 -1
  116. package/ts_build/src/services/EventService.js +1 -2
  117. package/ts_build/src/services/EventService.js.map +1 -1
  118. package/ts_build/src/services/GitHub.d.ts +0 -1
  119. package/ts_build/src/services/GitHub.js +1 -2
  120. package/ts_build/src/services/GitHub.js.map +1 -1
  121. package/ts_build/src/services/KnowhowClient.d.ts +0 -1
  122. package/ts_build/src/services/KnowhowClient.js +1 -2
  123. package/ts_build/src/services/KnowhowClient.js.map +1 -1
  124. package/ts_build/src/services/Mcp.d.ts +0 -1
  125. package/ts_build/src/services/Mcp.js +1 -2
  126. package/ts_build/src/services/Mcp.js.map +1 -1
  127. package/ts_build/src/services/S3.d.ts +0 -1
  128. package/ts_build/src/services/S3.js +1 -2
  129. package/ts_build/src/services/S3.js.map +1 -1
  130. package/ts_build/src/services/Tools.d.ts +22 -1
  131. package/ts_build/src/services/Tools.js +32 -6
  132. package/ts_build/src/services/Tools.js.map +1 -1
  133. package/ts_build/src/services/flags.d.ts +0 -1
  134. package/ts_build/src/services/flags.js +1 -2
  135. package/ts_build/src/services/flags.js.map +1 -1
  136. package/ts_build/src/services/index.d.ts +25 -0
  137. package/ts_build/src/services/index.js +42 -1
  138. package/ts_build/src/services/index.js.map +1 -1
  139. package/ts_build/src/services/modules/example-usage.d.ts +11 -0
  140. package/ts_build/src/services/modules/example-usage.js +43 -0
  141. package/ts_build/src/services/modules/example-usage.js.map +1 -0
  142. package/ts_build/src/services/modules/index.d.ts +4 -0
  143. package/ts_build/src/services/modules/index.js +44 -0
  144. package/ts_build/src/services/modules/index.js.map +1 -0
  145. package/ts_build/src/services/modules/types.d.ts +47 -0
  146. package/ts_build/src/services/modules/types.js +3 -0
  147. package/ts_build/src/services/modules/types.js.map +1 -0
  148. package/ts_build/src/services/script-execution/SandboxContext.d.ts +34 -0
  149. package/ts_build/src/services/script-execution/SandboxContext.js +186 -0
  150. package/ts_build/src/services/script-execution/SandboxContext.js.map +1 -0
  151. package/ts_build/src/services/script-execution/ScriptExecutor.d.ts +17 -0
  152. package/ts_build/src/services/script-execution/ScriptExecutor.js +211 -0
  153. package/ts_build/src/services/script-execution/ScriptExecutor.js.map +1 -0
  154. package/ts_build/src/services/script-execution/ScriptPolicy.d.ts +27 -0
  155. package/ts_build/src/services/script-execution/ScriptPolicy.js +150 -0
  156. package/ts_build/src/services/script-execution/ScriptPolicy.js.map +1 -0
  157. package/ts_build/src/services/script-execution/ScriptTracer.d.ts +19 -0
  158. package/ts_build/src/services/script-execution/ScriptTracer.js +186 -0
  159. package/ts_build/src/services/script-execution/ScriptTracer.js.map +1 -0
  160. package/ts_build/src/services/script-execution/types.d.ts +108 -0
  161. package/ts_build/src/services/script-execution/types.js +3 -0
  162. package/ts_build/src/services/script-execution/types.js.map +1 -0
  163. package/ts_build/src/services/singletons.d.ts +17 -0
  164. package/ts_build/src/services/singletons.js +28 -0
  165. package/ts_build/src/services/singletons.js.map +1 -0
  166. package/ts_build/src/worker.js +4 -3
  167. package/ts_build/src/worker.js.map +1 -1
  168. package/ts_build/tests/integration/fileblocks/readwrite.test.js +10 -9
  169. package/ts_build/tests/integration/fileblocks/readwrite.test.js.map +1 -1
  170. package/ts_build/tests/integration/patching.test.js +9 -10
  171. package/ts_build/tests/integration/patching.test.js.map +1 -1
  172. package/src/modules/index.ts +0 -37
@@ -0,0 +1,278 @@
1
+ import { AIClient } from "../../clients";
2
+ import { ScriptTracer } from "./ScriptTracer";
3
+ import { ScriptPolicyEnforcer } from "./ScriptPolicy";
4
+ import { Artifact, QuotaUsage } from "./types";
5
+ import { Message } from "../../clients/types";
6
+ import { ToolsService } from "../Tools";
7
+
8
+ /**
9
+ * Provides the execution context for scripts with controlled access to tools and AI
10
+ */
11
+ export class SandboxContext {
12
+ private artifacts: Artifact[] = [];
13
+ private consoleOutput: string[] = [];
14
+
15
+ constructor(
16
+ private toolsService: ToolsService,
17
+ private clients: AIClient,
18
+ private tracer: ScriptTracer,
19
+ private policyEnforcer: ScriptPolicyEnforcer
20
+ ) {}
21
+
22
+ /**
23
+ * Console implementation that captures output
24
+ */
25
+ console = {
26
+ log: (...args: any[]) => {
27
+ const message = args
28
+ .map((arg) =>
29
+ typeof arg === "object" ? JSON.stringify(arg) : String(arg)
30
+ )
31
+ .join(" ");
32
+ this.consoleOutput.push(`[LOG] ${message}`);
33
+ this.tracer.emitEvent("console_log", { message, args });
34
+ },
35
+
36
+ error: (...args: any[]) => {
37
+ const message = args
38
+ .map((arg) =>
39
+ typeof arg === "object" ? JSON.stringify(arg) : String(arg)
40
+ )
41
+ .join(" ");
42
+ this.consoleOutput.push(`[ERROR] ${message}`);
43
+ this.tracer.emitEvent("console_error", { message, args });
44
+ },
45
+
46
+ warn: (...args: any[]) => {
47
+ const message = args
48
+ .map((arg) =>
49
+ typeof arg === "object" ? JSON.stringify(arg) : String(arg)
50
+ )
51
+ .join(" ");
52
+ this.consoleOutput.push(`[WARN] ${message}`);
53
+ this.tracer.emitEvent("console_warn", { message, args });
54
+ },
55
+
56
+ info: (...args: any[]) => {
57
+ const message = args
58
+ .map((arg) =>
59
+ typeof arg === "object" ? JSON.stringify(arg) : String(arg)
60
+ )
61
+ .join(" ");
62
+ this.consoleOutput.push(`[INFO] ${message}`);
63
+ this.tracer.emitEvent("console_info", { message, args });
64
+ },
65
+ };
66
+
67
+ /**
68
+ * Call a tool through the tools service
69
+ */
70
+ async callTool(toolName: string, parameters: any): Promise<any> {
71
+ // Check policy first
72
+ if (!this.policyEnforcer.checkToolCall(toolName)) {
73
+ throw new Error(`Tool call '${toolName}' blocked by policy`);
74
+ }
75
+
76
+ this.tracer.emitEvent("tool_call_start", {
77
+ toolName,
78
+ parameters: this.sanitizeForLogging(parameters),
79
+ });
80
+
81
+ try {
82
+ // Record the tool call
83
+ this.policyEnforcer.recordToolCall();
84
+
85
+ // Create a proper ToolCall object
86
+ const toolCall = {
87
+ id: `script-tool-${Date.now()}-${Math.random()
88
+ .toString(36)
89
+ .substr(2, 9)}`,
90
+ type: "function" as const,
91
+ function: {
92
+ name: toolName,
93
+ arguments: JSON.stringify(parameters),
94
+ },
95
+ };
96
+
97
+ // Call the actual tool through the Tools service
98
+ const result = await this.toolsService.callTool(toolCall);
99
+
100
+ this.tracer.emitEvent("tool_call_success", {
101
+ toolName,
102
+ result: this.sanitizeForLogging(result),
103
+ });
104
+
105
+ return result;
106
+ } catch (error) {
107
+ this.tracer.emitEvent("tool_call_error", {
108
+ toolName,
109
+ error: error instanceof Error ? error.message : String(error),
110
+ });
111
+ throw error;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Call LLM through the clients service
117
+ */
118
+ async llm(
119
+ messages: Message[],
120
+ options: {
121
+ model?: string;
122
+ maxTokens?: number;
123
+ temperature?: number;
124
+ } = {}
125
+ ) {
126
+ const estimatedTokens = this.estimateTokens(messages);
127
+
128
+ // Check token quota
129
+ if (!this.policyEnforcer.checkTokenUsage(estimatedTokens)) {
130
+ throw new Error("Token quota would be exceeded");
131
+ }
132
+
133
+ this.tracer.emitEvent("llm_call_start", {
134
+ messageCount: messages.length,
135
+ estimatedTokens,
136
+ model: options.model,
137
+ options: this.sanitizeForLogging(options),
138
+ });
139
+
140
+ try {
141
+ // Record token usage
142
+ this.policyEnforcer.recordTokenUsage(estimatedTokens);
143
+
144
+ // Use the actual Clients service to make LLM calls
145
+ const completionOptions = {
146
+ model: options.model,
147
+ messages,
148
+ max_tokens: options.maxTokens,
149
+ };
150
+
151
+ // Detect provider from model or use default
152
+ const response = await this.clients.createCompletion(
153
+ "",
154
+ completionOptions
155
+ );
156
+
157
+ this.tracer.emitEvent("llm_call_success", {
158
+ model: response.model,
159
+ usage: response.usage,
160
+ usdCost: response.usd_cost,
161
+ });
162
+
163
+ return response;
164
+ } catch (error) {
165
+ this.tracer.emitEvent("llm_call_error", {
166
+ error: error instanceof Error ? error.message : String(error),
167
+ });
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Get current quota usage
174
+ */
175
+ getQuotaUsage(): QuotaUsage {
176
+ return this.policyEnforcer.getUsage();
177
+ }
178
+
179
+ /**
180
+ * Create an artifact
181
+ */
182
+ async createArtifact(
183
+ name: string,
184
+ content: string,
185
+ type: "text" | "json" | "csv" | "html" | "markdown" = "text"
186
+ ): Promise<Artifact> {
187
+ const artifact: Artifact = {
188
+ id: `artifact-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
189
+ name,
190
+ type,
191
+ content,
192
+ createdAt: new Date().toISOString(),
193
+ };
194
+
195
+ this.artifacts.push(artifact);
196
+
197
+ this.tracer.emitEvent("artifact_created", {
198
+ artifactId: artifact.id,
199
+ name,
200
+ type,
201
+ contentLength: content.length,
202
+ });
203
+
204
+ return artifact;
205
+ }
206
+
207
+ async sleep(ms: number): Promise<void> {
208
+ if (typeof ms !== "number" || ms < 0 || ms > 2000) {
209
+ throw new Error("Invalid sleep duration");
210
+ }
211
+ await new Promise((res) => setTimeout(res, ms));
212
+ this.tracer.emitEvent("sleep", { durationMs: ms });
213
+ }
214
+
215
+ /**
216
+ * Get all created artifacts
217
+ */
218
+ getArtifacts(): Artifact[] {
219
+ return [...this.artifacts];
220
+ }
221
+
222
+ /**
223
+ * Get console output
224
+ */
225
+ getConsoleOutput(): string[] {
226
+ return [...this.consoleOutput];
227
+ }
228
+
229
+ /**
230
+ * Estimate tokens for text (rough approximation)
231
+ */
232
+ private estimateTokens(messages: any[]): number {
233
+ let totalText = "";
234
+ for (const message of messages) {
235
+ if (typeof message === "string") {
236
+ totalText += message;
237
+ } else if (message && typeof message.content === "string") {
238
+ totalText += message.content;
239
+ }
240
+ }
241
+ // Rough estimation: ~4 characters per token
242
+ return Math.ceil(totalText.length / 4);
243
+ }
244
+
245
+ /**
246
+ * Sanitize data for logging (remove sensitive information)
247
+ */
248
+ private sanitizeForLogging(data: any): any {
249
+ if (data === null || data === undefined) {
250
+ return data;
251
+ }
252
+
253
+ if (typeof data === "string") {
254
+ // Truncate very long strings
255
+ return data.length > 500 ? data.substring(0, 500) + "..." : data;
256
+ }
257
+
258
+ if (typeof data === "object") {
259
+ const sanitized: any = {};
260
+ for (const [key, value] of Object.entries(data)) {
261
+ // Skip potentially sensitive keys
262
+ if (
263
+ key.toLowerCase().includes("password") ||
264
+ key.toLowerCase().includes("token") ||
265
+ key.toLowerCase().includes("secret") ||
266
+ key.toLowerCase().includes("key")
267
+ ) {
268
+ sanitized[key] = "[REDACTED]";
269
+ } else {
270
+ sanitized[key] = this.sanitizeForLogging(value);
271
+ }
272
+ }
273
+ return sanitized;
274
+ }
275
+
276
+ return data;
277
+ }
278
+ }
@@ -0,0 +1,339 @@
1
+ import ivm from "isolated-vm";
2
+ import { services, ToolsService } from "../../services";
3
+ import { AIClient, Clients } from "../../clients";
4
+ import { SandboxContext } from "./SandboxContext";
5
+ import { ScriptTracer } from "./ScriptTracer";
6
+ import { ScriptPolicyEnforcer } from "./ScriptPolicy";
7
+ import {
8
+ ExecutionRequest,
9
+ ExecutionResult,
10
+ ResourceQuotas,
11
+ SecurityPolicy,
12
+ ExecutionTrace,
13
+ } from "./types";
14
+
15
+ /**
16
+ * Executes TypeScript scripts in a secure sandbox environment
17
+ */
18
+ export class ScriptExecutor {
19
+ private defaultQuotas: ResourceQuotas = {
20
+ maxToolCalls: 50,
21
+ maxTokens: 10000,
22
+ maxExecutionTimeMs: 30000, // 30 seconds
23
+ maxCostUsd: 1.0,
24
+ maxMemoryMb: 100,
25
+ };
26
+
27
+ private defaultPolicy: SecurityPolicy = {
28
+ allowlistedTools: [], // Empty means all tools allowed
29
+ denylistedTools: [
30
+ "execCommand", // Dangerous system commands
31
+ "writeFileChunk", // File system write access
32
+ "patchFile", // File system modification
33
+ ],
34
+ maxScriptLength: 50000, // 50KB
35
+ allowNetworkAccess: false,
36
+ allowFileSystemAccess: false,
37
+ };
38
+
39
+ constructor(private toolsService: ToolsService, private clients: AIClient) {}
40
+
41
+ /**
42
+ * Execute a TypeScript script in sandbox
43
+ */
44
+ async execute(request: ExecutionRequest): Promise<ExecutionResult> {
45
+ const tracer = new ScriptTracer();
46
+ const quotas = { ...this.defaultQuotas, ...request.quotas };
47
+ const policy = { ...this.defaultPolicy, ...request.policy };
48
+ const policyEnforcer = new ScriptPolicyEnforcer(quotas, policy);
49
+
50
+ tracer.emitEvent("execution_start", {
51
+ scriptLength: request.script.length,
52
+ quotas,
53
+ policy: {
54
+ ...policy,
55
+ // Don't log the full tool lists
56
+ allowlistedTools: `${policy.allowlistedTools.length} tools`,
57
+ denylistedTools: `${policy.denylistedTools.length} tools`,
58
+ },
59
+ });
60
+
61
+ try {
62
+ // Validate script
63
+ const validation = policyEnforcer.validateScript(request.script);
64
+ if (!validation.valid) {
65
+ tracer.emitEvent("script_validation_failed", {
66
+ issues: validation.issues,
67
+ });
68
+
69
+ return {
70
+ success: false,
71
+ error: `Script validation failed: ${validation.issues.join(", ")}`,
72
+ result: null,
73
+ trace: tracer.getTrace(),
74
+ artifacts: [],
75
+ consoleOutput: [],
76
+ };
77
+ }
78
+
79
+ tracer.emitEvent("script_validation_passed", {});
80
+
81
+ // Create sandbox context
82
+ const context = new SandboxContext(
83
+ this.toolsService,
84
+ this.clients,
85
+ tracer,
86
+ policyEnforcer
87
+ );
88
+
89
+ // Execute script with timeout
90
+ const startTime = Date.now();
91
+ const timeoutMs = quotas.maxExecutionTimeMs;
92
+
93
+ const result = await this.executeWithTimeout(
94
+ request.script,
95
+ context,
96
+ timeoutMs,
97
+ tracer,
98
+ policyEnforcer
99
+ );
100
+
101
+ const executionTime = Date.now() - startTime;
102
+ tracer.emitEvent("execution_complete", {
103
+ executionTimeMs: executionTime,
104
+ finalUsage: policyEnforcer.getUsage(),
105
+ });
106
+
107
+ return {
108
+ success: true,
109
+ error: null,
110
+ result,
111
+ trace: tracer.getTrace(),
112
+ artifacts: context.getArtifacts(),
113
+ consoleOutput: context.getConsoleOutput(),
114
+ };
115
+ } catch (error) {
116
+ const errorMessage =
117
+ error instanceof Error ? error.message : String(error);
118
+
119
+ tracer.emitEvent("execution_error", {
120
+ error: errorMessage,
121
+ finalUsage: policyEnforcer.getUsage(),
122
+ });
123
+
124
+ return {
125
+ success: false,
126
+ error: errorMessage,
127
+ result: null,
128
+ trace: tracer.getTrace(),
129
+ artifacts: [],
130
+ consoleOutput: [],
131
+ };
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Execute script with timeout protection
137
+ */
138
+ private async executeWithTimeout(
139
+ script: string,
140
+ context: SandboxContext,
141
+ timeoutMs: number,
142
+ tracer: ScriptTracer,
143
+ policyEnforcer: ScriptPolicyEnforcer
144
+ ): Promise<any> {
145
+ return new Promise((resolve, reject) => {
146
+ const timeoutId = setTimeout(() => {
147
+ tracer.emitEvent("execution_timeout", { timeoutMs });
148
+ reject(new Error(`Script execution timed out after ${timeoutMs}ms`));
149
+ }, timeoutMs);
150
+
151
+ // Use isolated-vm for secure execution
152
+ this.executeScriptSecure(script, context, tracer, policyEnforcer)
153
+ .then((result) => {
154
+ clearTimeout(timeoutId);
155
+ resolve(result);
156
+ })
157
+ .catch((error) => {
158
+ clearTimeout(timeoutId);
159
+ reject(error);
160
+ });
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Secure script execution using isolated-vm
166
+ */
167
+ private async executeScriptSecure(
168
+ script: string,
169
+ context: SandboxContext,
170
+ tracer: ScriptTracer,
171
+ policyEnforcer: ScriptPolicyEnforcer
172
+ ) {
173
+ tracer.emitEvent("secure_execution_start", {
174
+ note: "Using isolated-vm for secure execution",
175
+ });
176
+
177
+ // Create isolated VM instance with memory limit
178
+ const isolate = new ivm.Isolate({
179
+ memoryLimit: policyEnforcer.getQuotas().maxMemoryMb,
180
+ });
181
+
182
+ try {
183
+ // Create new context within the isolate
184
+ const vmContext = await isolate.createContext();
185
+
186
+ tracer.emitEvent("vm_context_created", {});
187
+
188
+ // Set up the global environment in the isolated context
189
+ await this.setupIsolatedContext(vmContext, context, tracer);
190
+
191
+ tracer.emitEvent("script_compilation_start", {});
192
+
193
+ // Compile the script
194
+ const wrappedScript = `
195
+ (async function() {
196
+ "use strict";
197
+ ${script}
198
+ })()
199
+ `;
200
+
201
+ const compiledScript = await isolate.compileScript(wrappedScript);
202
+
203
+ tracer.emitEvent("script_compilation_complete", {});
204
+ tracer.emitEvent("script_execution_start", {});
205
+
206
+ // Execute the script and get the result
207
+ const result = await compiledScript.run(vmContext, {
208
+ timeout: policyEnforcer.getQuotas().maxExecutionTimeMs,
209
+ promise: true,
210
+ copy: true,
211
+ });
212
+
213
+ tracer.emitEvent("script_execution_complete", {
214
+ resultType: typeof result,
215
+ });
216
+
217
+ return result;
218
+ } finally {
219
+ // Clean up the isolate
220
+ isolate.dispose();
221
+ tracer.emitEvent("vm_cleanup_complete", {});
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Set up the isolated context with safe globals and sandbox functions
227
+ */
228
+ private async setupIsolatedContext(
229
+ vmContext: ivm.Context,
230
+ sandboxContext: SandboxContext,
231
+ tracer: ScriptTracer
232
+ ): Promise<void> {
233
+ tracer.emitEvent("context_setup_start", {});
234
+
235
+ const globalRef = vmContext.global;
236
+ await globalRef.set("globalThis", globalRef.derefInto());
237
+
238
+ // Helper function to expose async host functions
239
+ const exposeAsync = async (
240
+ name: string,
241
+ fn: (...a: any[]) => Promise<any>
242
+ ) => {
243
+ await globalRef.set(
244
+ `__host_${name}`,
245
+ new ivm.Reference(async (...args: any[]) => {
246
+ const result = await fn(...args);
247
+ return new ivm.ExternalCopy(result).copyInto();
248
+ })
249
+ );
250
+ await vmContext.eval(`
251
+ globalThis.${name} = (...a) =>
252
+ __host_${name}.apply(undefined, a,
253
+ { arguments: { copy: true }, result: { promise: true, copy: true } });
254
+ `);
255
+ };
256
+
257
+ // Helper function to expose sync host functions
258
+ const exposeSync = async (name: string, fn: (...a: any[]) => any) => {
259
+ await globalRef.set(
260
+ `__host_${name}`,
261
+ new ivm.Reference((...args: any[]) => {
262
+ const result = fn(...args);
263
+ return new ivm.ExternalCopy(result).copyInto();
264
+ })
265
+ );
266
+ await vmContext.eval(`
267
+ globalThis.${name} = (...a) =>
268
+ __host_${name}.apply(undefined, a,
269
+ { arguments: { copy: true }, result: { copy: true } });
270
+ `);
271
+ };
272
+
273
+ // Expose async sandbox functions
274
+ await exposeAsync("callTool", async (tool, params) => {
275
+ const { functionResp } = await sandboxContext.callTool(
276
+ tool as string,
277
+ params
278
+ );
279
+ return functionResp;
280
+ });
281
+ await exposeAsync("llm", (messages, options) =>
282
+ sandboxContext.llm(messages, options || {})
283
+ );
284
+ await exposeAsync("sleep", (ms) => sandboxContext.sleep(ms));
285
+
286
+ // Expose sync sandbox functions
287
+ await exposeSync("createArtifact", (name, content, type) =>
288
+ sandboxContext.createArtifact(name as string, content, type)
289
+ );
290
+ await exposeSync("getQuotaUsage", () => sandboxContext.getQuotaUsage());
291
+
292
+ // Set up console bridging with individual function references
293
+ for (const level of ["log", "info", "warn", "error"] as const) {
294
+ await globalRef.set(
295
+ `__console_${level}`,
296
+ new ivm.Reference((...args: any[]) =>
297
+ sandboxContext.console[level](...args)
298
+ )
299
+ );
300
+ }
301
+ await vmContext.eval(`
302
+ globalThis.console = {};
303
+ for (const lvl of ["log", "info", "warn", "error"]) {
304
+ globalThis.console[lvl] = (...a) =>
305
+ globalThis["__console_" + lvl].apply(undefined, a,
306
+ { arguments: { copy: true } });
307
+ }
308
+ `);
309
+
310
+ tracer.emitEvent("context_setup_complete", {});
311
+ }
312
+
313
+ /**
314
+ * Legacy fallback execution method
315
+ */
316
+ private async executeScriptFallback(
317
+ script: string,
318
+ context: SandboxContext,
319
+ tracer: ScriptTracer,
320
+ policyEnforcer: ScriptPolicyEnforcer
321
+ ): Promise<any> {
322
+ // This is a fallback method that could use vm2 or other sandboxing
323
+ throw new Error("Isolated-vm execution failed, no fallback available");
324
+ }
325
+
326
+ /**
327
+ * Get default quotas
328
+ */
329
+ getDefaultQuotas(): ResourceQuotas {
330
+ return { ...this.defaultQuotas };
331
+ }
332
+
333
+ /**
334
+ * Get default policy
335
+ */
336
+ getDefaultPolicy(): SecurityPolicy {
337
+ return { ...this.defaultPolicy };
338
+ }
339
+ }