joonecli 0.1.1 → 0.2.1

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 (235) hide show
  1. package/dist/__tests__/config.test.js +1 -0
  2. package/dist/__tests__/config.test.js.map +1 -1
  3. package/dist/__tests__/installHostDeps.test.js +45 -0
  4. package/dist/__tests__/installHostDeps.test.js.map +1 -0
  5. package/dist/__tests__/whitelistedBackend.test.js +18 -0
  6. package/dist/__tests__/whitelistedBackend.test.js.map +1 -0
  7. package/dist/cli/config.d.ts +2 -0
  8. package/dist/cli/config.js +1 -0
  9. package/dist/cli/config.js.map +1 -1
  10. package/dist/cli/index.js +84 -97
  11. package/dist/cli/index.js.map +1 -1
  12. package/dist/commands/builtinCommands.js +6 -6
  13. package/dist/commands/builtinCommands.js.map +1 -1
  14. package/dist/commands/commandRegistry.d.ts +3 -1
  15. package/dist/commands/commandRegistry.js.map +1 -1
  16. package/dist/core/agentLoop.d.ts +11 -28
  17. package/dist/core/agentLoop.js +68 -229
  18. package/dist/core/agentLoop.js.map +1 -1
  19. package/dist/core/compactor.js +2 -2
  20. package/dist/core/compactor.js.map +1 -1
  21. package/dist/core/contextGuard.d.ts +5 -0
  22. package/dist/core/contextGuard.js +30 -3
  23. package/dist/core/contextGuard.js.map +1 -1
  24. package/dist/core/events.d.ts +45 -0
  25. package/dist/core/events.js +8 -0
  26. package/dist/core/events.js.map +1 -0
  27. package/dist/core/promptBuilder.js.map +1 -1
  28. package/dist/core/sessionStore.js +3 -2
  29. package/dist/core/sessionStore.js.map +1 -1
  30. package/dist/core/tokenCounter.d.ts +8 -1
  31. package/dist/core/tokenCounter.js +28 -0
  32. package/dist/core/tokenCounter.js.map +1 -1
  33. package/dist/hitl/bridge.js +1 -27
  34. package/dist/hitl/bridge.js.map +1 -1
  35. package/dist/middleware/loopDetection.d.ts +7 -23
  36. package/dist/middleware/loopDetection.js +38 -42
  37. package/dist/middleware/loopDetection.js.map +1 -1
  38. package/dist/sandbox/whitelistedBackend.d.ts +5 -0
  39. package/dist/sandbox/whitelistedBackend.js +27 -0
  40. package/dist/sandbox/whitelistedBackend.js.map +1 -0
  41. package/dist/tools/askUser.d.ts +12 -3
  42. package/dist/tools/askUser.js +16 -28
  43. package/dist/tools/askUser.js.map +1 -1
  44. package/dist/tools/bashTool.d.ts +11 -0
  45. package/dist/tools/bashTool.js +51 -0
  46. package/dist/tools/bashTool.js.map +1 -0
  47. package/dist/tools/index.d.ts +15 -27
  48. package/dist/tools/index.js +9 -181
  49. package/dist/tools/index.js.map +1 -1
  50. package/dist/tools/installHostDeps.d.ts +8 -0
  51. package/dist/tools/installHostDeps.js +44 -0
  52. package/dist/tools/installHostDeps.js.map +1 -0
  53. package/dist/tracing/sessionTracer.d.ts +1 -0
  54. package/dist/tracing/sessionTracer.js +4 -1
  55. package/dist/tracing/sessionTracer.js.map +1 -1
  56. package/dist/ui/App.js +116 -55
  57. package/dist/ui/App.js.map +1 -1
  58. package/dist/ui/components/ActionLog.d.ts +7 -0
  59. package/dist/ui/components/ActionLog.js +63 -0
  60. package/dist/ui/components/ActionLog.js.map +1 -0
  61. package/dist/ui/components/FileBrowser.d.ts +2 -0
  62. package/dist/ui/components/FileBrowser.js +41 -0
  63. package/dist/ui/components/FileBrowser.js.map +1 -0
  64. package/dist/ui/components/MessageBubble.js +1 -1
  65. package/dist/ui/components/MessageBubble.js.map +1 -1
  66. package/package.json +8 -5
  67. package/AGENTS.md +0 -56
  68. package/Handover.md +0 -115
  69. package/PROGRESS.md +0 -160
  70. package/dist/__tests__/m55.test.js +0 -160
  71. package/dist/__tests__/m55.test.js.map +0 -1
  72. package/dist/__tests__/middleware.test.js +0 -169
  73. package/dist/__tests__/middleware.test.js.map +0 -1
  74. package/dist/__tests__/optimizations.test.d.ts +0 -1
  75. package/dist/__tests__/optimizations.test.js +0 -136
  76. package/dist/__tests__/optimizations.test.js.map +0 -1
  77. package/dist/__tests__/security.test.d.ts +0 -1
  78. package/dist/__tests__/security.test.js +0 -86
  79. package/dist/__tests__/security.test.js.map +0 -1
  80. package/dist/__tests__/streaming.test.d.ts +0 -1
  81. package/dist/__tests__/streaming.test.js +0 -71
  82. package/dist/__tests__/streaming.test.js.map +0 -1
  83. package/dist/__tests__/toolRouter.test.d.ts +0 -1
  84. package/dist/__tests__/toolRouter.test.js +0 -37
  85. package/dist/__tests__/toolRouter.test.js.map +0 -1
  86. package/dist/__tests__/tools.test.d.ts +0 -1
  87. package/dist/__tests__/tools.test.js +0 -112
  88. package/dist/__tests__/tools.test.js.map +0 -1
  89. package/dist/core/subAgent.d.ts +0 -56
  90. package/dist/core/subAgent.js +0 -240
  91. package/dist/core/subAgent.js.map +0 -1
  92. package/dist/debug_google.d.ts +0 -1
  93. package/dist/debug_google.js +0 -23
  94. package/dist/debug_google.js.map +0 -1
  95. package/dist/middleware/commandSanitizer.d.ts +0 -18
  96. package/dist/middleware/commandSanitizer.js +0 -50
  97. package/dist/middleware/commandSanitizer.js.map +0 -1
  98. package/dist/middleware/permission.d.ts +0 -17
  99. package/dist/middleware/permission.js +0 -59
  100. package/dist/middleware/permission.js.map +0 -1
  101. package/dist/middleware/pipeline.d.ts +0 -31
  102. package/dist/middleware/pipeline.js +0 -62
  103. package/dist/middleware/pipeline.js.map +0 -1
  104. package/dist/middleware/preCompletion.d.ts +0 -29
  105. package/dist/middleware/preCompletion.js +0 -82
  106. package/dist/middleware/preCompletion.js.map +0 -1
  107. package/dist/middleware/types.d.ts +0 -40
  108. package/dist/middleware/types.js +0 -8
  109. package/dist/middleware/types.js.map +0 -1
  110. package/dist/skills/loader.d.ts +0 -55
  111. package/dist/skills/loader.js +0 -132
  112. package/dist/skills/loader.js.map +0 -1
  113. package/dist/skills/tools.d.ts +0 -5
  114. package/dist/skills/tools.js +0 -78
  115. package/dist/skills/tools.js.map +0 -1
  116. package/dist/test_cache.d.ts +0 -1
  117. package/dist/test_cache.js +0 -55
  118. package/dist/test_cache.js.map +0 -1
  119. package/dist/test_google.d.ts +0 -1
  120. package/dist/test_google.js +0 -36
  121. package/dist/test_google.js.map +0 -1
  122. package/dist/tools/browser.d.ts +0 -19
  123. package/dist/tools/browser.js +0 -111
  124. package/dist/tools/browser.js.map +0 -1
  125. package/dist/tools/registry.d.ts +0 -31
  126. package/dist/tools/registry.js +0 -168
  127. package/dist/tools/registry.js.map +0 -1
  128. package/dist/tools/router.d.ts +0 -34
  129. package/dist/tools/router.js +0 -75
  130. package/dist/tools/router.js.map +0 -1
  131. package/dist/tools/security.d.ts +0 -28
  132. package/dist/tools/security.js +0 -183
  133. package/dist/tools/security.js.map +0 -1
  134. package/dist/tools/spawnAgent.d.ts +0 -19
  135. package/dist/tools/spawnAgent.js +0 -130
  136. package/dist/tools/spawnAgent.js.map +0 -1
  137. package/dist/tools/webSearch.d.ts +0 -6
  138. package/dist/tools/webSearch.js +0 -120
  139. package/dist/tools/webSearch.js.map +0 -1
  140. package/docs/01_insights_and_patterns.md +0 -27
  141. package/docs/02_edge_cases_and_mitigations.md +0 -143
  142. package/docs/03_initial_implementation_plan.md +0 -66
  143. package/docs/04_tech_stack_proposal.md +0 -20
  144. package/docs/05_prd.md +0 -87
  145. package/docs/06_user_stories.md +0 -72
  146. package/docs/07_system_architecture.md +0 -138
  147. package/docs/08_roadmap.md +0 -200
  148. package/e2b/Dockerfile +0 -26
  149. package/src/__tests__/bootstrap.test.ts +0 -111
  150. package/src/__tests__/config.test.ts +0 -97
  151. package/src/__tests__/m55.test.ts +0 -238
  152. package/src/__tests__/middleware.test.ts +0 -219
  153. package/src/__tests__/modelFactory.test.ts +0 -63
  154. package/src/__tests__/optimizations.test.ts +0 -201
  155. package/src/__tests__/promptBuilder.test.ts +0 -141
  156. package/src/__tests__/sandbox.test.ts +0 -102
  157. package/src/__tests__/security.test.ts +0 -122
  158. package/src/__tests__/streaming.test.ts +0 -82
  159. package/src/__tests__/toolRouter.test.ts +0 -52
  160. package/src/__tests__/tools.test.ts +0 -146
  161. package/src/__tests__/tracing.test.ts +0 -196
  162. package/src/agents/agentRegistry.ts +0 -69
  163. package/src/agents/agentSpec.ts +0 -67
  164. package/src/agents/builtinAgents.ts +0 -142
  165. package/src/cli/config.ts +0 -124
  166. package/src/cli/index.ts +0 -742
  167. package/src/cli/modelFactory.ts +0 -174
  168. package/src/cli/postinstall.ts +0 -28
  169. package/src/cli/providers.ts +0 -107
  170. package/src/commands/builtinCommands.ts +0 -293
  171. package/src/commands/commandRegistry.ts +0 -194
  172. package/src/core/agentLoop.d.ts.map +0 -1
  173. package/src/core/agentLoop.ts +0 -312
  174. package/src/core/autoSave.ts +0 -95
  175. package/src/core/compactor.ts +0 -252
  176. package/src/core/contextGuard.ts +0 -129
  177. package/src/core/errors.ts +0 -202
  178. package/src/core/promptBuilder.d.ts.map +0 -1
  179. package/src/core/promptBuilder.ts +0 -139
  180. package/src/core/reasoningRouter.ts +0 -121
  181. package/src/core/retry.ts +0 -75
  182. package/src/core/sessionResumer.ts +0 -90
  183. package/src/core/sessionStore.ts +0 -216
  184. package/src/core/subAgent.ts +0 -339
  185. package/src/core/tokenCounter.ts +0 -64
  186. package/src/evals/dataset.ts +0 -67
  187. package/src/evals/evaluator.ts +0 -81
  188. package/src/hitl/bridge.ts +0 -160
  189. package/src/middleware/commandSanitizer.ts +0 -60
  190. package/src/middleware/loopDetection.ts +0 -63
  191. package/src/middleware/permission.ts +0 -72
  192. package/src/middleware/pipeline.ts +0 -75
  193. package/src/middleware/preCompletion.ts +0 -94
  194. package/src/middleware/types.ts +0 -45
  195. package/src/sandbox/bootstrap.ts +0 -121
  196. package/src/sandbox/manager.ts +0 -239
  197. package/src/sandbox/sync.ts +0 -157
  198. package/src/skills/loader.ts +0 -143
  199. package/src/skills/tools.ts +0 -99
  200. package/src/skills/types.ts +0 -13
  201. package/src/test_cache.ts +0 -72
  202. package/src/tools/askUser.ts +0 -47
  203. package/src/tools/browser.ts +0 -137
  204. package/src/tools/index.d.ts.map +0 -1
  205. package/src/tools/index.ts +0 -237
  206. package/src/tools/registry.ts +0 -198
  207. package/src/tools/router.ts +0 -78
  208. package/src/tools/security.ts +0 -220
  209. package/src/tools/spawnAgent.ts +0 -158
  210. package/src/tools/webSearch.ts +0 -142
  211. package/src/tracing/analyzer.ts +0 -265
  212. package/src/tracing/langsmith.ts +0 -63
  213. package/src/tracing/sessionTracer.ts +0 -202
  214. package/src/tracing/types.ts +0 -49
  215. package/src/types/valyu.d.ts +0 -37
  216. package/src/ui/App.tsx +0 -404
  217. package/src/ui/components/HITLPrompt.tsx +0 -119
  218. package/src/ui/components/Header.tsx +0 -51
  219. package/src/ui/components/MessageBubble.tsx +0 -46
  220. package/src/ui/components/StatusBar.tsx +0 -138
  221. package/src/ui/components/StreamingText.tsx +0 -48
  222. package/src/ui/components/ToolCallPanel.tsx +0 -80
  223. package/tests/commands/commands.test.ts +0 -356
  224. package/tests/core/compactor.test.ts +0 -217
  225. package/tests/core/retryAndErrors.test.ts +0 -164
  226. package/tests/core/sessionResumer.test.ts +0 -95
  227. package/tests/core/sessionStore.test.ts +0 -84
  228. package/tests/core/stability.test.ts +0 -165
  229. package/tests/core/subAgent.test.ts +0 -238
  230. package/tests/hitl/hitlBridge.test.ts +0 -115
  231. package/tsconfig.json +0 -16
  232. package/vitest.config.ts +0 -10
  233. package/vitest.out +0 -48
  234. /package/dist/__tests__/{m55.test.d.ts → installHostDeps.test.d.ts} +0 -0
  235. /package/dist/__tests__/{middleware.test.d.ts → whitelistedBackend.test.d.ts} +0 -0
package/src/test_cache.ts DELETED
@@ -1,72 +0,0 @@
1
- import "dotenv/config";
2
- import { ChatAnthropic } from "@langchain/anthropic";
3
- import { ExecutionHarness } from "./core/agentLoop.js";
4
- import { ContextState } from "./core/promptBuilder.js";
5
- import { HumanMessage } from "@langchain/core/messages";
6
-
7
- async function runCacheTest() {
8
- console.log("=== Starting Prompt Caching Test ===\n");
9
-
10
- // We need Anthropic API Key for this to actually hit the network and measure cache
11
- if (!process.env.ANTHROPIC_API_KEY) {
12
- console.warn("WARNING: ANTHROPIC_API_KEY is not set in .env file. The LLM call will fail.");
13
- }
14
-
15
- const model = new ChatAnthropic({
16
- modelName: "claude-3-5-sonnet-20241022",
17
- temperature: 0,
18
- maxTokens: 4096,
19
- }); // No tools needed for this basic string test
20
-
21
- const harness = new ExecutionHarness(model, []);
22
-
23
- // 1. Create a massive static prefix (simulating a lot of project context)
24
- // We repeat a string many times to ensure we pass the 1024 token minimum for Anthropic caching.
25
- const massiveProjectMemory = Array(500).fill("Project Rule: Always write clean, modular TypeScript code with strict typings. ").join("\n");
26
-
27
- const state: ContextState = {
28
- globalSystemInstructions: "You are a helpful coding assistant. You remember rules carefully.",
29
- projectMemory: massiveProjectMemory,
30
- sessionContext: "User OS: Windows 11",
31
- conversationHistory: []
32
- };
33
-
34
- console.log("Turn 1: Initial Query (Should create cache)");
35
- state.conversationHistory.push(new HumanMessage("Hello! What is one of the project rules?"));
36
-
37
- const response1 = await harness.step(state);
38
- state.conversationHistory.push(response1);
39
-
40
- // In @langchain/anthropic, the actual usage stats (including cache hits/misses)
41
- // are stored in the response_metadata of the AIMessage.
42
- console.log(`Response 1: ${response1.content}`);
43
- console.log(`Token Usage 1: ${JSON.stringify(response1.response_metadata?.usage, null, 2)}\n`);
44
-
45
- // --- TURN 2 ---
46
- console.log("Turn 2: Follow-up Query (Should hit cache)");
47
- // We DO NOT change the globalSystemInstructions, projectMemory, or sessionContext.
48
- // We only append to the conversation history. This preserves the prefix!
49
- state.conversationHistory.push(new HumanMessage("Could you summarize the rule again briefly?"));
50
-
51
- const response2 = await harness.step(state);
52
- state.conversationHistory.push(response2);
53
-
54
- console.log(`Response 2: ${response2.content}`);
55
- console.log(`Token Usage 2: ${JSON.stringify(response2.response_metadata?.usage, null, 2)}\n`);
56
-
57
- // --- TURN 3: The System Reminder Pattern ---
58
- console.log("Turn 3: Using <system-reminder> to simulate environment change without breaking cache");
59
- // If a file changed, we DON'T update `state.projectMemory`. We inject a reminder.
60
- state.conversationHistory.push(new HumanMessage(
61
- "<system-reminder>\nThe file 'auth.ts' has just been deleted by the user.\n</system-reminder>\nWhat should we do if we need auth now?"
62
- ));
63
-
64
- const response3 = await harness.step(state);
65
- state.conversationHistory.push(response3);
66
-
67
- console.log(`Response 3: ${response3.content}`);
68
- console.log(`Token Usage 3: ${JSON.stringify(response3.response_metadata?.usage, null, 2)}\n`);}
69
-
70
- if (require.main === module) {
71
- runCacheTest().catch(console.error);
72
- }
@@ -1,47 +0,0 @@
1
- import { HITLBridge } from "../hitl/bridge.js";
2
- import { DynamicToolInterface, ToolResult } from "./index.js";
3
-
4
- /**
5
- * AskUserQuestionTool — allows the agent to ask the user a clarifying question mid-turn.
6
- *
7
- * Use cases:
8
- * - Resolving ambiguous requirements before coding.
9
- * - Getting user preferences (framework choice, styling, naming).
10
- * - Requesting approval of an implementation plan before proceeding.
11
- */
12
- export const AskUserQuestionTool: DynamicToolInterface = {
13
- name: "ask_user_question",
14
- description:
15
- "Ask the user a question and wait for their response. " +
16
- "Use this when you need clarification on the task, user preferences, " +
17
- "or approval before proceeding with a significant change. " +
18
- "You may optionally provide a list of answer choices.",
19
- schema: {
20
- type: "object" as const,
21
- properties: {
22
- question: {
23
- type: "string",
24
- description: "The question to ask the user.",
25
- },
26
- options: {
27
- type: "array",
28
- items: { type: "string" },
29
- description: "Optional list of predefined answer choices.",
30
- },
31
- },
32
- required: ["question"],
33
- },
34
- async execute(args: Record<string, unknown>): Promise<ToolResult> {
35
- const question = args.question as string;
36
- const options = args.options as string[] | undefined;
37
-
38
- if (!question || question.trim() === "") {
39
- return { content: "Error: You must provide a non-empty question.", isError: true };
40
- }
41
-
42
- const bridge = HITLBridge.getInstance();
43
- const answer = await bridge.askUser(question, options);
44
-
45
- return { content: answer };
46
- },
47
- };
@@ -1,137 +0,0 @@
1
- import { SandboxManager } from "../sandbox/manager.js";
2
- import { LazyInstaller } from "../sandbox/bootstrap.js";
3
- import { DynamicToolInterface, ToolResult } from "./index.js";
4
-
5
- // ─── Sandbox + Installer references ─────────────────────────────────────────
6
-
7
- let _sandboxManager: SandboxManager | null = null;
8
- let _installer: LazyInstaller | null = null;
9
-
10
- export function bindBrowserSandbox(
11
- sandbox: SandboxManager,
12
- installer: LazyInstaller
13
- ): void {
14
- _sandboxManager = sandbox;
15
- _installer = installer;
16
- }
17
-
18
- // ─── Helpers ────────────────────────────────────────────────────────────────────
19
-
20
- /**
21
- * Escapes a string so it can be safely used as an argument in a Bash shell command.
22
- * It wraps the string in single quotes and safely escapes internal single quotes.
23
- */
24
- function escapeBashArg(arg: string): string {
25
- return `'${arg.replace(/'/g, "'\\''")}'`;
26
- }
27
-
28
- // ─── BrowserTool ────────────────────────────────────────────────────────────────
29
-
30
- /**
31
- * Web Browser Tool — wraps Vercel Labs' `agent-browser` CLI.
32
- *
33
- * Provides compact accessibility-tree output optimized for LLMs
34
- * (low token usage vs raw HTML). Runs inside the E2B sandbox.
35
- *
36
- * Supported actions:
37
- * - navigate: Go to a URL
38
- * - snapshot: Get the accessibility tree (compact text representation)
39
- * - click: Click an element by ref
40
- * - type: Type text into a form field by ref
41
- * - screenshot: Capture a screenshot
42
- * - scroll: Scroll the page up or down
43
- */
44
- export const BrowserTool: DynamicToolInterface = {
45
- name: "browser",
46
- description:
47
- "Interact with web pages using a headless browser. Actions: navigate, snapshot, click, type, screenshot, scroll. " +
48
- "Returns compact accessibility-tree text output optimized for AI consumption.",
49
- schema: {
50
- type: "object",
51
- properties: {
52
- action: {
53
- type: "string",
54
- enum: ["navigate", "snapshot", "click", "type", "screenshot", "scroll"],
55
- description: "The browser action to perform",
56
- },
57
- url: {
58
- type: "string",
59
- description: "URL to navigate to (required for 'navigate')",
60
- },
61
- ref: {
62
- type: "string",
63
- description:
64
- "Element reference from the accessibility tree (required for 'click' and 'type')",
65
- },
66
- text: {
67
- type: "string",
68
- description: "Text to type (required for 'type')",
69
- },
70
- direction: {
71
- type: "string",
72
- enum: ["up", "down"],
73
- description: "Scroll direction for 'scroll' action (optional, defaults to 'down')",
74
- }, },
75
- required: ["action"],
76
- },
77
- execute: async (args: {
78
- action: string;
79
- url?: string;
80
- ref?: string;
81
- text?: string;
82
- direction?: string;
83
- }): Promise<ToolResult> => {
84
- if (!_sandboxManager || !_sandboxManager.isActive()) {
85
- return { content: "Sandbox is not active. Cannot use browser tool.", isError: true };
86
- }
87
-
88
- // Build the CLI command
89
- let command: string;
90
-
91
- switch (args.action) {
92
- case "navigate":
93
- if (!args.url) return { content: "Error: 'url' is required for navigate action.", isError: true };
94
- command = `agent-browser navigate ${escapeBashArg(args.url)} 2>&1`;
95
- break;
96
-
97
- case "snapshot":
98
- command = "agent-browser snapshot 2>&1";
99
- break;
100
-
101
- case "click":
102
- if (!args.ref) return { content: "Error: 'ref' is required for click action.", isError: true };
103
- command = `agent-browser click ${escapeBashArg(args.ref)} 2>&1`;
104
- break;
105
-
106
- case "type":
107
- if (!args.ref) return { content: "Error: 'ref' is required for type action.", isError: true };
108
- if (!args.text) return { content: "Error: 'text' is required for type action.", isError: true };
109
- command = `agent-browser type ${escapeBashArg(args.ref)} ${escapeBashArg(args.text)} 2>&1`;
110
- break;
111
-
112
- case "screenshot":
113
- command = "agent-browser screenshot 2>&1";
114
- break;
115
-
116
- case "scroll":
117
- const dir = args.direction || "down";
118
- command = `agent-browser scroll ${escapeBashArg(dir)} 2>&1`;
119
- break;
120
-
121
- default:
122
- return { content: `Error: Unknown action "${args.action}". Use: navigate, snapshot, click, type, screenshot, scroll.`, isError: true };
123
- }
124
-
125
- const result = await _sandboxManager.exec(command);
126
-
127
- if (result.exitCode !== 0) {
128
- return {
129
- content: `Browser action failed (exit code ${result.exitCode}):\n${result.stdout}\n${result.stderr}`,
130
- metadata: { exitCode: result.exitCode },
131
- isError: true
132
- };
133
- }
134
-
135
- return { content: result.stdout || "(no output)", metadata: { exitCode: result.exitCode }, isError: false };
136
- },
137
- };
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,OAAO,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;CACpD;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,EAAE,oBActB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,oBAc1B,CAAC;AAEF,eAAO,MAAM,UAAU,wBAA2B,CAAC"}
@@ -1,237 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { SandboxManager } from "../sandbox/manager.js";
4
- import { FileSync } from "../sandbox/sync.js";
5
-
6
- export interface ToolResult {
7
- content: string;
8
- metadata?: Record<string, any>;
9
- isError?: boolean;
10
- }
11
-
12
- export interface DynamicToolInterface {
13
- name: string;
14
- description: string;
15
- schema: Record<string, any>;
16
- execute: (args: any) => Promise<ToolResult> | ToolResult;
17
- }
18
-
19
- // ─── Configuration ──────────────────────────────────────────────────────────────
20
-
21
- /** Maximum file size (in bytes) the agent is allowed to read into context. */
22
- const MAX_FILE_SIZE_BYTES = 512 * 1024; // 512 KB
23
-
24
- /** Maximum number of lines to return if file exceeds line limit. */
25
- const MAX_FILE_LINES = 2000;
26
-
27
- // ─── Sandbox reference (set at session start) ───────────────────────────────────
28
-
29
- let _sandboxManager: SandboxManager | null = null;
30
- let _fileSync: FileSync | null = null;
31
-
32
- /**
33
- * Binds the tools to a SandboxManager and FileSync instance.
34
- * Must be called at session start before any tool executions.
35
- */
36
- export function bindSandbox(sandbox: SandboxManager, fileSync: FileSync): void {
37
- _sandboxManager = sandbox;
38
- _fileSync = fileSync;
39
- }
40
-
41
- /**
42
- * Security: Validates that a resolved path is strictly inside the given workspace dir.
43
- * Prevents directory traversal attacks from accessing sensitive host files.
44
- */
45
- export function isPathInsideWorkspace(
46
- resolvedPath: string,
47
- workspaceDir = process.cwd()
48
- ): boolean {
49
- const normalizedWorkspace = path.resolve(workspaceDir);
50
- // path.relative returns a path relative to the first argument.
51
- // If it starts with '..' or is absolute, it means resolvedPath has escaped normalizedWorkspace.
52
- const relative = path.relative(normalizedWorkspace, resolvedPath);
53
- return relative !== "" && !relative.startsWith("..") && !path.isAbsolute(relative);
54
- }
55
-
56
- // ─── BashTool ───────────────────────────────────────────────────────────────────
57
- // Executes shell commands inside the E2B sandbox.
58
- // The ToolRouter routes this to SANDBOX — the host machine is never exposed.
59
-
60
- export const BashTool: DynamicToolInterface = {
61
- name: "bash",
62
- description:
63
- "Runs a shell command inside an isolated sandbox. Use for tests, scripts, or installing dependencies. The host machine is never exposed.",
64
- schema: {
65
- type: "object",
66
- properties: {
67
- command: {
68
- type: "string",
69
- description: "The shell command to execute",
70
- },
71
- },
72
- required: ["command"],
73
- },
74
- execute: async (args: { command: string }): Promise<ToolResult> => {
75
- if (!_sandboxManager || !_sandboxManager.isActive()) {
76
- throw new Error(
77
- "Sandbox is not active. Cannot execute bash commands without an active sandbox session."
78
- );
79
- }
80
-
81
- // Sync any dirty files from host → sandbox before executing
82
- if (_fileSync && _fileSync.pendingCount() > 0) {
83
- await _fileSync.syncToSandbox(_sandboxManager);
84
- }
85
-
86
- const result = await _sandboxManager.exec(args.command);
87
-
88
- if (result.exitCode !== 0) {
89
- return {
90
- content: `Command failed (exit code ${result.exitCode}):\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}`,
91
- metadata: { exitCode: result.exitCode },
92
- isError: true
93
- };
94
- }
95
-
96
- return {
97
- content: result.stdout || "(no output)",
98
- metadata: { exitCode: result.exitCode },
99
- isError: false
100
- };
101
- },
102
- };
103
-
104
- // ─── ReadFileTool ───────────────────────────────────────────────────────────────
105
- // Reads files from the HOST filesystem (so the user's real project is visible).
106
- // Includes a built-in file size guardrail to prevent sending huge files to the LLM.
107
-
108
- export const ReadFileTool: DynamicToolInterface = {
109
- name: "read_file",
110
- description:
111
- "Reads a file from the host filesystem. Includes a file size guardrail — files over 512KB are truncated to prevent context overflow.",
112
- schema: {
113
- type: "object",
114
- properties: {
115
- path: {
116
- type: "string",
117
- description: "Absolute or relative path to the file",
118
- },
119
- startLine: {
120
- type: "number",
121
- description: "Optional 1-indexed start line for partial reads",
122
- },
123
- endLine: {
124
- type: "number",
125
- description: "Optional 1-indexed end line for partial reads",
126
- },
127
- },
128
- required: ["path"],
129
- },
130
- execute: async (args: { path: string; startLine?: number; endLine?: number }): Promise<ToolResult> => {
131
- const filePath = path.resolve(args.path);
132
-
133
- // ── Security Guardrail ──
134
- if (!isPathInsideWorkspace(filePath)) {
135
- return {
136
- content: `Security Error: Access Denied. Cannot read files outside the current project workspace (${process.cwd()}).`,
137
- isError: true
138
- };
139
- }
140
-
141
- // ── Check existence ──
142
- if (!fs.existsSync(filePath)) {
143
- return {
144
- content: `Error: File not found — ${filePath}`,
145
- isError: true
146
- };
147
- }
148
-
149
- // ── File Size Guardrail ──
150
- const stat = fs.statSync(filePath);
151
- if (stat.size > MAX_FILE_SIZE_BYTES) {
152
- return {
153
- content: `Error: File too large (${Math.round(stat.size / 1024)}KB). Maximum size is 512KB. Use line ranges or grep_search.`,
154
- isError: true
155
- };
156
- }
157
-
158
- const fileContent = fs.readFileSync(filePath, "utf-8");
159
- const lines = fileContent.split("\n");
160
-
161
- if (args.startLine !== undefined || args.endLine !== undefined) {
162
- const start = Math.max(1, args.startLine ?? 1) - 1;
163
- const end = Math.min(lines.length, args.endLine ?? lines.length);
164
- const sliced = lines.slice(start, end);
165
- return { content: sliced.map((line, i) => `${start + i + 1}: ${line}`).join("\n") };
166
- }
167
-
168
- // ── Line count guardrail ──
169
- if (lines.length > MAX_FILE_LINES) {
170
- const truncated = lines.slice(0, MAX_FILE_LINES);
171
- return {
172
- content: truncated.map((line, i) => `${i + 1}: ${line}`).join("\n") +
173
- `\n\n--- Truncated at ${MAX_FILE_LINES} lines (total: ${lines.length}) ---`
174
- };
175
- }
176
-
177
- return { content: fileContent };
178
- },
179
- };
180
-
181
- // ─── WriteFileTool ──────────────────────────────────────────────────────────────
182
- // Writes files to the HOST filesystem (so the user sees changes in their IDE).
183
- // Marks written files as dirty so FileSync uploads them before sandbox execution.
184
-
185
- export const WriteFileTool: DynamicToolInterface = {
186
- name: "write_file",
187
- description:
188
- "Writes content to a file on the host filesystem. The user will see changes in their IDE immediately. The file is automatically synced to the sandbox before the next command execution.",
189
- schema: {
190
- type: "object",
191
- properties: {
192
- path: {
193
- type: "string",
194
- description: "Absolute or relative path to the file",
195
- },
196
- content: {
197
- type: "string",
198
- description: "The full file content to write",
199
- },
200
- },
201
- required: ["path", "content"],
202
- },
203
- execute: async (args: { path: string; content: string }): Promise<ToolResult> => {
204
- const filePath = path.resolve(args.path);
205
-
206
- // ── Security Guardrail ──
207
- if (!isPathInsideWorkspace(filePath)) {
208
- return {
209
- content: `Security Error: Access Denied. Cannot write files outside the current project workspace (${process.cwd()}).`,
210
- isError: true
211
- };
212
- }
213
-
214
- // Create parent directories if needed
215
- const dir = path.dirname(filePath);
216
- if (!fs.existsSync(dir)) {
217
- fs.mkdirSync(dir, { recursive: true });
218
- }
219
-
220
- fs.writeFileSync(filePath, args.content, "utf-8");
221
-
222
- // Mark file as dirty for next sandbox sync
223
- if (_fileSync) {
224
- _fileSync.markDirty(filePath);
225
- }
226
-
227
- return { content: `File written: ${filePath}` };
228
- },
229
- };
230
-
231
- // ─── Core Tool Set ──────────────────────────────────────────────────────────────
232
-
233
- export const CORE_TOOLS: DynamicToolInterface[] = [
234
- BashTool,
235
- ReadFileTool,
236
- WriteFileTool,
237
- ];