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
@@ -1,165 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { ContextGuard } from "../../src/core/contextGuard.js";
3
- import { AutoSave } from "../../src/core/autoSave.js";
4
- import { ContextState, CacheOptimizedPromptBuilder } from "../../src/core/promptBuilder.js";
5
- import { HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages";
6
-
7
- // ─── Mocks ──────────────────────────────────────────────────────────────────────
8
-
9
- const mockLLM = {
10
- invoke: vi.fn(),
11
- };
12
-
13
- // ─── ContextGuard Tests ─────────────────────────────────────────────────────────
14
-
15
- describe("ContextGuard", () => {
16
- let promptBuilder: CacheOptimizedPromptBuilder;
17
-
18
- beforeEach(() => {
19
- promptBuilder = new CacheOptimizedPromptBuilder();
20
- vi.clearAllMocks();
21
- });
22
-
23
- const createHistory = (numMessages: number) => {
24
- return Array.from({ length: numMessages }).map((_, i) => new HumanMessage(`Message ${i}`));
25
- };
26
-
27
- const createDummyState = (numMessages: number): ContextState => ({
28
- globalSystemInstructions: "System",
29
- projectMemory: "Memory",
30
- sessionContext: "Context",
31
- conversationHistory: createHistory(numMessages),
32
- });
33
-
34
- it("does nothing when under the warn threshold", async () => {
35
- // 1000 max tokens. A state with 5 short messages is well under 800 tokens.
36
- const guard = new ContextGuard(mockLLM as any, 1000, promptBuilder);
37
- const state = createDummyState(5);
38
-
39
- const { state: updatedState, metrics } = await guard.ensureCapacity(state, 0.8, 0.95);
40
-
41
- expect(metrics.actionTaken).toBe("none");
42
- expect(updatedState.conversationHistory.length).toBe(5);
43
- });
44
-
45
- it("triggers LLM compaction when over WARN but under CRITICAL threshold", async () => {
46
- // We create a very small maxTokens, so the dummy state blows past 80%
47
- const guard = new ContextGuard(mockLLM as any, 100, promptBuilder);
48
-
49
- // 30 messages will definitely be hundreds of tokens, exceeding 100 * 0.8
50
- const state = createDummyState(30);
51
-
52
- // We mock promptBuilder.compactHistoryWithLLM directly
53
- vi.spyOn(promptBuilder, "compactHistoryWithLLM").mockResolvedValue({
54
- compactedHistory: createHistory(8),
55
- tokensBefore: 400,
56
- tokensAfter: 20,
57
- evictedCount: 22,
58
- llmUsed: true,
59
- });
60
-
61
- const { metrics } = await guard.ensureCapacity(state, 0.8, 0.95);
62
-
63
- // If it exceeds 95 it hits emergency, if it's < 95 it hits compacted.
64
- // 30 short messages is actually huge for a 100 token max, so it will hit 95% immediately.
65
- // So let's test for what actually happens algebraically:
66
- expect(["compacted", "emergency_truncated"]).toContain(metrics.actionTaken);
67
- });
68
- });
69
-
70
- // Since vitest mocks affect the whole module, we'll test the logic algebraically without deep module overriding.
71
- describe("ContextGuard Algebraic Logic", () => {
72
- it("compacts via emergency truncation when 95% full and history > 4", async () => {
73
- const builder = new CacheOptimizedPromptBuilder();
74
- vi.spyOn(builder, "compactHistoryWithLLM").mockResolvedValue({
75
- compactedHistory: [new SystemMessage("Compacted")],
76
- tokensBefore: 100,
77
- tokensAfter: 10,
78
- evictedCount: 2,
79
- llmUsed: true,
80
- });
81
-
82
- const guard = new ContextGuard(mockLLM as any, 100, builder);
83
- // Needs > 4 messages to allow emergency truncation
84
- const history = Array.from({ length: 10 }).map((_, i) => new HumanMessage("A reasonably sized message structure " + i));
85
- const state: ContextState = {
86
- globalSystemInstructions: "System instructions taking up exactly enough tokens to push us to 85. ".repeat(7),
87
- projectMemory: "",
88
- sessionContext: "",
89
- conversationHistory: history,
90
- };
91
-
92
- const { metrics } = await guard.ensureCapacity(state);
93
-
94
- expect(["compacted", "emergency_truncated"]).toContain(metrics.actionTaken);
95
- });
96
- });
97
-
98
- // ─── AutoSave Tests ─────────────────────────────────────────────────────────────
99
-
100
- describe("AutoSave", () => {
101
- it("only saves when frequency and debounce thresholds are met", async () => {
102
- const mockStore = { saveSession: vi.fn().mockResolvedValue(true) };
103
- const autoSave = new AutoSave("test_session", mockStore as any, 3, 100);
104
-
105
- const dummyData = { config: { provider: "test", model: "test" }, state: { conversationHistory: [] } as any };
106
-
107
- // Turn 1
108
- let saved = await autoSave.tick(dummyData);
109
- expect(saved).toBe(false);
110
-
111
- // Turn 2
112
- saved = await autoSave.tick(dummyData);
113
- expect(saved).toBe(false);
114
-
115
- // Turn 3 (Hits frequency)
116
- saved = await autoSave.tick(dummyData);
117
- expect(saved).toBe(true);
118
- expect(mockStore.saveSession).toHaveBeenCalledTimes(1);
119
-
120
- // Turn 4 (Frequency reset, hasn't hit 3 again)
121
- saved = await autoSave.tick(dummyData);
122
- expect(saved).toBe(false);
123
- });
124
-
125
- it("respects debounce time even if frequency is met", async () => {
126
- const mockStore = { saveSession: vi.fn().mockResolvedValue(true) };
127
- // Huge debounce, frequency of 1
128
- const autoSave = new AutoSave("test_session", mockStore as any, 1, 10000);
129
-
130
- const dummyData = { config: { provider: "test", model: "test" }, state: { conversationHistory: [] } as any };
131
-
132
- // Turn 1 (Hits frequency 1, saves and resets timer)
133
- let saved = await autoSave.tick(dummyData);
134
- expect(saved).toBe(true);
135
- expect(mockStore.saveSession).toHaveBeenCalledTimes(1);
136
-
137
- // Turn 2 (Hits frequency 1 again! But debounce rejects it)
138
- saved = await autoSave.tick(dummyData);
139
- expect(saved).toBe(false);
140
- expect(mockStore.saveSession).toHaveBeenCalledTimes(1); // Still 1
141
- });
142
-
143
- it("forceSave bypasses thresholds", async () => {
144
- const mockStore = { saveSession: vi.fn().mockResolvedValue(true) };
145
- const autoSave = new AutoSave("test_session", mockStore as any, 5, 10000);
146
-
147
- const dummyData = { config: { provider: "test", model: "test" }, state: { conversationHistory: [] } as any };
148
-
149
- await autoSave.forceSave(dummyData);
150
- expect(mockStore.saveSession).toHaveBeenCalledTimes(1);
151
- });
152
-
153
- it("swallows errors to prevent crashing the agent loop", async () => {
154
- const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
155
- const mockStore = { saveSession: vi.fn().mockRejectedValue(new Error("Disk full")) };
156
-
157
- const autoSave = new AutoSave("test_session", mockStore as any, 1, 0);
158
- const dummyData = { config: { provider: "test", model: "test" }, state: { conversationHistory: [] } as any };
159
-
160
- // Should not throw
161
- await expect(autoSave.tick(dummyData)).resolves.toBe(true);
162
- expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("Disk full"));
163
- consoleSpy.mockRestore();
164
- });
165
- });
@@ -1,238 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { SubAgentManager } from "../../src/core/subAgent.js";
3
- import { AgentRegistry } from "../../src/agents/agentRegistry.js";
4
- import { createSpawnAgentTools } from "../../src/tools/spawnAgent.js";
5
- import { AgentSpec } from "../../src/agents/agentSpec.js";
6
- import { AIMessage, ToolMessage } from "@langchain/core/messages";
7
- import { DynamicToolInterface } from "../../src/tools/index.js";
8
-
9
- // ─── Mocks ──────────────────────────────────────────────────────────────────────
10
-
11
- const mockRegistry = new AgentRegistry();
12
- const mockSpec: AgentSpec = {
13
- name: "test_agent",
14
- description: "A test agent",
15
- systemPrompt: "You are a test agent.",
16
- tools: ["test_tool"],
17
- maxTurns: 3,
18
- };
19
- mockRegistry.register(mockSpec);
20
-
21
- const mockTool: DynamicToolInterface = {
22
- name: "test_tool",
23
- description: "A test tool",
24
- schema: { type: "object", properties: {} },
25
- execute: async () => ({ content: "Tool success" }),
26
- };
27
-
28
- const mockTools = [mockTool];
29
-
30
- function createMockLLM(responses: any[]) {
31
- let callCount = 0;
32
- return {
33
- bindTools: vi.fn().mockReturnThis(),
34
- invoke: vi.fn().mockImplementation(() => {
35
- const resp = responses[callCount++] || responses[responses.length - 1];
36
- return Promise.resolve(resp);
37
- }),
38
- };
39
- }
40
-
41
- // ─── SubAgentManager Tests ──────────────────────────────────────────────────────
42
-
43
- describe("SubAgentManager", () => {
44
- it("rejects unknown agents", async () => {
45
- const llm = createMockLLM([new AIMessage("Hello")]);
46
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
47
-
48
- const result = await manager.spawn("unknown_agent", "Do something");
49
- expect(result.outcome).toBe("failure");
50
- expect(result.result).toContain("Unknown agent");
51
- });
52
-
53
- it("safely filters out spawn_agent and check_agent from available tools", async () => {
54
- // Tests depth-1 safety limit
55
- const unsafeTools = [
56
- mockTool,
57
- { name: "spawn_agent", description: "", schema: {}, execute: async () => ({ content: "" }) },
58
- { name: "check_agent", description: "", schema: {}, execute: async () => ({ content: "" }) }
59
- ];
60
-
61
- // We must cast these unsafeTools since they bypass DynamicToolInterface loosely here
62
- const manager = new SubAgentManager(mockRegistry, unsafeTools as any, createMockLLM([]) as any);
63
-
64
- // Access private allTools to verify
65
- const allTools = (manager as any).allTools;
66
- expect(allTools.length).toBe(1);
67
- expect(allTools[0].name).toBe("test_tool");
68
- });
69
-
70
- it("handles a successful sync execution without tool calls", async () => {
71
- const llm = createMockLLM([new AIMessage("I have completed the task.")]);
72
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
73
-
74
- const result = await manager.spawn("test_agent", "Do something");
75
-
76
- expect(result.outcome).toBe("success");
77
- expect(result.result).toBe("I have completed the task.");
78
- expect(result.turnsUsed).toBe(1);
79
- expect(result.toolCallCount).toBe(0);
80
- });
81
-
82
- it("handles tool calls recursively until finished", async () => {
83
- const llm = createMockLLM([
84
- new AIMessage({
85
- content: "I need to use a tool.",
86
- tool_calls: [{ id: "call_1", name: "test_tool", args: {} }]
87
- }),
88
- new AIMessage("I have finished the task with the tool.")
89
- ]);
90
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
91
-
92
- const result = await manager.spawn("test_agent", "Do something");
93
-
94
- expect(result.outcome).toBe("success");
95
- expect(result.result).toBe("I have finished the task with the tool.");
96
- expect(result.turnsUsed).toBe(2);
97
- expect(result.toolCallCount).toBe(1);
98
- });
99
-
100
- it("returns partial outcome if maxTurns is exceeded", async () => {
101
- // LLM keeps returning tool calls, but agent has maxTurns = 3
102
- const llm = createMockLLM([
103
- new AIMessage({ content: "Loop 1", tool_calls: [{ id: "c1", name: "test_tool", args: {} }] }),
104
- new AIMessage({ content: "Loop 2", tool_calls: [{ id: "c2", name: "test_tool", args: {} }] }),
105
- new AIMessage({ content: "Loop 3", tool_calls: [{ id: "c3", name: "test_tool", args: {} }] }),
106
- new AIMessage("This should never be reached")
107
- ]);
108
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
109
-
110
- const result = await manager.spawn("test_agent", "Loop forever");
111
-
112
- expect(result.outcome).toBe("partial"); // Caught by loop protection
113
- expect(result.turnsUsed).toBe(3);
114
- });
115
-
116
- it("tracks modified files when write_file is called", async () => {
117
- const writeTool: DynamicToolInterface = {
118
- name: "write_file",
119
- description: "Writes a file",
120
- schema: { type: "object", properties: { path: { type: "string" } } },
121
- execute: async () => ({ content: "Written" }),
122
- };
123
-
124
- const reg = new AgentRegistry();
125
- reg.register({ name: "writer", description: "", systemPrompt: "", tools: ["write_file"] });
126
-
127
- const llm = createMockLLM([
128
- new AIMessage({
129
- content: "",
130
- tool_calls: [{ id: "c1", name: "write_file", args: { path: "/test/file.ts" } }]
131
- }),
132
- new AIMessage("Done writing.")
133
- ]);
134
-
135
- const manager = new SubAgentManager(reg, [writeTool], llm as any);
136
- const result = await manager.spawn("writer", "Write it");
137
-
138
- expect(result.filesModified).toContain("/test/file.ts");
139
- });
140
-
141
- describe("Async execution", () => {
142
- it("spawns a non-blocking async task and checks its result", async () => {
143
- // Delay the LLM so it's realistically async
144
- const llm = {
145
- bindTools: vi.fn().mockReturnThis(),
146
- invoke: vi.fn().mockImplementation(async () => {
147
- await new Promise((r) => setTimeout(r, 50));
148
- return new AIMessage("Async done");
149
- }),
150
- };
151
-
152
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
153
-
154
- // Spawn async
155
- const taskId = await manager.spawnAsync("test_agent", "Do async task");
156
- expect(taskId).toMatch(/^task_\d+_\d+$/);
157
-
158
- // Check immediately — should be running
159
- const initialCheck = await manager.getResult(taskId);
160
- expect(typeof initialCheck).toBe("string");
161
- expect(initialCheck).toContain("still running");
162
-
163
- // Wait for it to finish
164
- await new Promise((r) => setTimeout(r, 100));
165
-
166
- // Check again — should be the result object
167
- const finalCheck = await manager.getResult(taskId);
168
- expect(typeof finalCheck).toBe("object");
169
- expect((finalCheck as any).outcome).toBe("success");
170
- expect((finalCheck as any).result).toBe("Async done");
171
- });
172
-
173
- it("prevents spawning beyond MAX_CONCURRENT_ASYNC", async () => {
174
- // LLM that hangs forever so tasks stay active
175
- const llm = {
176
- bindTools: vi.fn().mockReturnThis(),
177
- invoke: vi.fn().mockImplementation(() => new Promise(() => {})), // never resolves
178
- };
179
-
180
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
181
-
182
- // Spawn 3 (max)
183
- await manager.spawnAsync("test_agent", "Task 1");
184
- await manager.spawnAsync("test_agent", "Task 2");
185
- await manager.spawnAsync("test_agent", "Task 3");
186
-
187
- // 4th should throw an error
188
- await expect(manager.spawnAsync("test_agent", "Task 4"))
189
- .rejects.toThrow(/Maximum concurrent async agents reached/);
190
- });
191
- });
192
- });
193
-
194
- // ─── spawn_agent & check_agent tools ────────────────────────────────────────────
195
-
196
- describe("spawn_agent and check_agent tools", () => {
197
- it("formats the spawn result correctly in sync mode", async () => {
198
- const llm = createMockLLM([new AIMessage("Test complete")]);
199
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
200
- const tools = createSpawnAgentTools(manager, mockRegistry);
201
- const spawnAgentTool = tools.find(t => t.name === "spawn_agent")!;
202
-
203
- const result = await spawnAgentTool.execute({ agent: "test_agent", task: "Run test" });
204
-
205
- expect(result.isError).toBe(false);
206
- expect(typeof result.content).toBe("string");
207
- expect(result.content).toContain("Sub-Agent Result: test_agent");
208
- expect(result.content).toContain("Outcome: success");
209
- expect(result.content).toContain("Test complete");
210
- expect(result.metadata).toBeDefined();
211
- expect((result.metadata as any).agentName).toBe("test_agent");
212
- });
213
-
214
- it("handles async mode and pairs with check_agent", async () => {
215
- const llm = {
216
- bindTools: vi.fn().mockReturnThis(),
217
- invoke: vi.fn().mockResolvedValue(new AIMessage("Delayed finish")),
218
- };
219
- const manager = new SubAgentManager(mockRegistry, mockTools, llm as any);
220
- const tools = createSpawnAgentTools(manager, mockRegistry);
221
- const spawnTool = tools.find(t => t.name === "spawn_agent")!;
222
- const checkTool = tools.find(t => t.name === "check_agent")!;
223
-
224
- // Spawn
225
- const spawnRes = await spawnTool.execute({ agent: "test_agent", task: "Test", mode: "async" });
226
- expect(spawnRes.content).toContain("task_");
227
- const taskId = (spawnRes.metadata as any).taskId;
228
-
229
- // Wait a tick for the microtask to finish (the mock resolves immediately)
230
- await new Promise(r => setTimeout(r, 10));
231
-
232
- // Check
233
- const checkRes = await checkTool.execute({ taskId });
234
- expect(checkRes.isError).toBe(false);
235
- expect(checkRes.content).toContain("Outcome: success");
236
- expect(checkRes.content).toContain("Delayed finish");
237
- });
238
- });
@@ -1,115 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from "vitest";
2
- import { HITLBridge } from "../../src/hitl/bridge.js";
3
- import { PermissionMiddleware } from "../../src/middleware/permission.js";
4
-
5
- describe("HITLBridge", () => {
6
- beforeEach(() => {
7
- HITLBridge.resetInstance();
8
- });
9
-
10
- it("resolves askUser when the TUI calls resolveAnswer", async () => {
11
- const bridge = HITLBridge.getInstance(1000);
12
-
13
- // Simulate TUI responding to a question
14
- bridge.on("question", (q) => {
15
- setTimeout(() => bridge.resolveAnswer(q.id, "TypeScript"), 50);
16
- });
17
-
18
- const answer = await bridge.askUser("What language?");
19
- expect(answer).toBe("TypeScript");
20
- });
21
-
22
- it("auto-resolves askUser on timeout with no-response message", async () => {
23
- const bridge = HITLBridge.getInstance(100); // 100ms timeout for test speed
24
-
25
- const answer = await bridge.askUser("Are you there?");
26
- expect(answer).toContain("No response");
27
- });
28
-
29
- it("resolves requestPermission to true on 'y' answer", async () => {
30
- const bridge = HITLBridge.getInstance(1000);
31
-
32
- bridge.on("permission", (p) => {
33
- setTimeout(() => bridge.resolveAnswer(p.id, "y"), 50);
34
- });
35
-
36
- const approved = await bridge.requestPermission("bash", { command: "rm -rf /" });
37
- expect(approved).toBe(true);
38
- });
39
-
40
- it("resolves requestPermission to false on 'n' answer", async () => {
41
- const bridge = HITLBridge.getInstance(1000);
42
-
43
- bridge.on("permission", (p) => {
44
- setTimeout(() => bridge.resolveAnswer(p.id, "n"), 50);
45
- });
46
-
47
- const approved = await bridge.requestPermission("bash", { command: "ls" });
48
- expect(approved).toBe(false);
49
- });
50
-
51
- it("auto-denies requestPermission on timeout", async () => {
52
- const bridge = HITLBridge.getInstance(100);
53
-
54
- const approved = await bridge.requestPermission("bash", { command: "ls" });
55
- expect(approved).toBe(false);
56
- });
57
- });
58
-
59
- describe("PermissionMiddleware", () => {
60
- it("passes through all tools in 'auto' mode", async () => {
61
- const mw = new PermissionMiddleware("auto");
62
- const ctx = { toolName: "bash", args: { command: "ls" }, callId: "1" };
63
-
64
- const result = await mw.before!(ctx);
65
- expect(result).toEqual(ctx); // Unchanged
66
- });
67
-
68
- it("allows safe tools in 'ask_dangerous' mode without asking", async () => {
69
- const mw = new PermissionMiddleware("ask_dangerous");
70
- const ctx = { toolName: "read_file", args: { path: "/foo" }, callId: "2" };
71
-
72
- const result = await mw.before!(ctx);
73
- expect(result).toEqual(ctx);
74
- });
75
-
76
- it("blocks dangerous tools in 'ask_dangerous' mode when denied", async () => {
77
- HITLBridge.resetInstance();
78
- const bridge = HITLBridge.getInstance(100); // Auto-deny on timeout
79
-
80
- const mw = new PermissionMiddleware("ask_dangerous");
81
- const ctx = { toolName: "bash", args: { command: "rm -rf /" }, callId: "3" };
82
-
83
- const result = await mw.before!(ctx);
84
- // Should return short-circuit string (denial)
85
- expect(typeof result).toBe("string");
86
- expect(result as string).toContain("Permission denied");
87
- });
88
-
89
- it("allows dangerous tools in 'ask_dangerous' mode when approved", async () => {
90
- HITLBridge.resetInstance();
91
- const bridge = HITLBridge.getInstance(5000);
92
-
93
- bridge.on("permission", (p) => {
94
- setTimeout(() => bridge.resolveAnswer(p.id, "yes"), 50);
95
- });
96
-
97
- const mw = new PermissionMiddleware("ask_dangerous");
98
- const ctx = { toolName: "bash", args: { command: "ls" }, callId: "4" };
99
-
100
- const result = await mw.before!(ctx);
101
- expect(result).toEqual(ctx); // Approved, return context
102
- });
103
-
104
- it("blocks non-safe tools in 'ask_all' mode when denied", async () => {
105
- HITLBridge.resetInstance();
106
- const bridge = HITLBridge.getInstance(100);
107
-
108
- const mw = new PermissionMiddleware("ask_all");
109
- const ctx = { toolName: "run_tests", args: {}, callId: "5" };
110
-
111
- const result = await mw.before!(ctx);
112
- expect(typeof result).toBe("string");
113
- expect(result as string).toContain("Permission denied");
114
- });
115
- });
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "esModuleInterop": true,
7
- "forceConsistentCasingInFileNames": true,
8
- "strict": true,
9
- "skipLibCheck": true,
10
- "outDir": "./dist",
11
- "jsx": "react-jsx",
12
- "declaration": true,
13
- "sourceMap": true
14
- },
15
- "include": ["src/**/*"]
16
- }
package/vitest.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import { defineConfig } from "vitest/config";
2
-
3
- export default defineConfig({
4
- test: {
5
- env: {
6
- ANTHROPIC_API_KEY: "test-key-for-vitest",
7
- OPENAI_API_KEY: "test-key-for-vitest",
8
- },
9
- },
10
- });
package/vitest.out DELETED
@@ -1,48 +0,0 @@
1
-
2
-  RUN  v4.0.18 C:/Users/Lenovo/Desktop/New folder/joone
3
-
4
- ❯ tests/core/stability.test.ts (7 tests | 2 failed) 84ms
5
-  × does nothing when under the warn threshold 26ms
6
- ✓ triggers LLM compaction when over WARN but under CRITICAL threshold 2ms
7
-  × compacts via promptBuilder when 80-95% full 8ms
8
- ✓ only saves when frequency and debounce thresholds are met 3ms
9
- ✓ respects debounce time even if frequency is met 1ms
10
- ✓ forceSave bypasses thresholds 1ms
11
- ✓ swallows errors to prevent crashing the agent loop 37ms
12
-
13
- ⎯⎯⎯⎯⎯⎯⎯ Failed Tests 2 ⎯⎯⎯⎯⎯⎯⎯
14
-
15
-  FAIL  tests/core/stability.test.ts > ContextGuard > does nothing when under the warn threshold
16
- AssertionError: expected 'compacted' to be 'none' // Object.is equality
17
-
18
- Expected: "none"
19
- Received: "compacted"
20
-
21
-  ❯ tests/core/stability.test.ts:41:33
22
-  39|  const { state: updatedState, metrics } = await guard.ensureCapacit…
23
-  40| 
24
-  41|  expect(metrics.actionTaken).toBe("none");
25
-  |  ^
26
-  42|  expect(updatedState.conversationHistory.length).toBe(5);
27
-  43|  });
28
-
29
- ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/2]⎯
30
-
31
-  FAIL  tests/core/stability.test.ts > ContextGuard Algebraic Logic > compacts via promptBuilder when 80-95% full
32
- AssertionError: expected [ 'compacted', 'emergency_truncated' ] to include 'none'
33
-  ❯ tests/core/stability.test.ts:95:50
34
-  93| 
35
-  94|  // It should hit compactness or emergency
36
-  95|  expect(["compacted", "emergency_truncated"]).toContain(metrics.act…
37
-  |  ^
38
-  96|  });
39
-  97| });
40
-
41
- ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/2]⎯
42
-
43
-
44
-  Test Files  1 failed (1)
45
-  Tests  2 failed | 5 passed (7)
46
-  Start at  16:46:44
47
-  Duration  2.69s (transform 488ms, setup 0ms, import 2.01s, tests 84ms, environment 1ms)
48
-