joonecli 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/config.test.js +1 -0
- package/dist/__tests__/config.test.js.map +1 -1
- package/dist/__tests__/installHostDeps.test.js +45 -0
- package/dist/__tests__/installHostDeps.test.js.map +1 -0
- package/dist/__tests__/whitelistedBackend.test.js +18 -0
- package/dist/__tests__/whitelistedBackend.test.js.map +1 -0
- package/dist/cli/config.d.ts +2 -0
- package/dist/cli/config.js +1 -0
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/index.js +120 -107
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/startupBenchmark.d.ts +13 -0
- package/dist/cli/startupBenchmark.js +27 -0
- package/dist/cli/startupBenchmark.js.map +1 -0
- package/dist/core/agentLoop.d.ts +10 -29
- package/dist/core/agentLoop.js +73 -238
- package/dist/core/agentLoop.js.map +1 -1
- package/dist/core/promptBuilder.js.map +1 -1
- package/dist/hitl/bridge.js +1 -27
- package/dist/hitl/bridge.js.map +1 -1
- package/dist/middleware/loopDetection.d.ts +7 -23
- package/dist/middleware/loopDetection.js +38 -42
- package/dist/middleware/loopDetection.js.map +1 -1
- package/dist/sandbox/whitelistedBackend.d.ts +5 -0
- package/dist/sandbox/whitelistedBackend.js +27 -0
- package/dist/sandbox/whitelistedBackend.js.map +1 -0
- package/dist/tools/askUser.d.ts +12 -3
- package/dist/tools/askUser.js +16 -28
- package/dist/tools/askUser.js.map +1 -1
- package/dist/tools/bashTool.d.ts +11 -0
- package/dist/tools/bashTool.js +51 -0
- package/dist/tools/bashTool.js.map +1 -0
- package/dist/tools/index.d.ts +15 -28
- package/dist/tools/index.js +9 -189
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/installHostDeps.d.ts +8 -2
- package/dist/tools/installHostDeps.js +38 -31
- package/dist/tools/installHostDeps.js.map +1 -1
- package/dist/ui/App.d.ts +4 -1
- package/dist/ui/App.js +200 -63
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/MessageBubble.js +1 -1
- package/dist/ui/components/MessageBubble.js.map +1 -1
- package/package.json +7 -2
- package/dist/__tests__/m55.test.js +0 -160
- package/dist/__tests__/m55.test.js.map +0 -1
- package/dist/__tests__/middleware.test.js +0 -169
- package/dist/__tests__/middleware.test.js.map +0 -1
- package/dist/__tests__/optimizations.test.d.ts +0 -1
- package/dist/__tests__/optimizations.test.js +0 -136
- package/dist/__tests__/optimizations.test.js.map +0 -1
- package/dist/__tests__/security.test.d.ts +0 -1
- package/dist/__tests__/security.test.js +0 -86
- package/dist/__tests__/security.test.js.map +0 -1
- package/dist/__tests__/streaming.test.d.ts +0 -1
- package/dist/__tests__/streaming.test.js +0 -71
- package/dist/__tests__/streaming.test.js.map +0 -1
- package/dist/__tests__/toolRouter.test.d.ts +0 -1
- package/dist/__tests__/toolRouter.test.js +0 -37
- package/dist/__tests__/toolRouter.test.js.map +0 -1
- package/dist/__tests__/tools.test.d.ts +0 -1
- package/dist/__tests__/tools.test.js +0 -112
- package/dist/__tests__/tools.test.js.map +0 -1
- package/dist/core/subAgent.d.ts +0 -56
- package/dist/core/subAgent.js +0 -240
- package/dist/core/subAgent.js.map +0 -1
- package/dist/debug_google.d.ts +0 -1
- package/dist/debug_google.js +0 -23
- package/dist/debug_google.js.map +0 -1
- package/dist/middleware/commandSanitizer.d.ts +0 -18
- package/dist/middleware/commandSanitizer.js +0 -50
- package/dist/middleware/commandSanitizer.js.map +0 -1
- package/dist/middleware/permission.d.ts +0 -17
- package/dist/middleware/permission.js +0 -60
- package/dist/middleware/permission.js.map +0 -1
- package/dist/middleware/pipeline.d.ts +0 -31
- package/dist/middleware/pipeline.js +0 -62
- package/dist/middleware/pipeline.js.map +0 -1
- package/dist/middleware/preCompletion.d.ts +0 -29
- package/dist/middleware/preCompletion.js +0 -82
- package/dist/middleware/preCompletion.js.map +0 -1
- package/dist/middleware/types.d.ts +0 -40
- package/dist/middleware/types.js +0 -8
- package/dist/middleware/types.js.map +0 -1
- package/dist/skills/loader.d.ts +0 -55
- package/dist/skills/loader.js +0 -132
- package/dist/skills/loader.js.map +0 -1
- package/dist/skills/tools.d.ts +0 -5
- package/dist/skills/tools.js +0 -78
- package/dist/skills/tools.js.map +0 -1
- package/dist/test_cache.d.ts +0 -1
- package/dist/test_cache.js +0 -55
- package/dist/test_cache.js.map +0 -1
- package/dist/test_google.d.ts +0 -1
- package/dist/test_google.js +0 -36
- package/dist/test_google.js.map +0 -1
- package/dist/tools/browser.d.ts +0 -19
- package/dist/tools/browser.js +0 -114
- package/dist/tools/browser.js.map +0 -1
- package/dist/tools/registry.d.ts +0 -31
- package/dist/tools/registry.js +0 -168
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/router.d.ts +0 -34
- package/dist/tools/router.js +0 -76
- package/dist/tools/router.js.map +0 -1
- package/dist/tools/security.d.ts +0 -28
- package/dist/tools/security.js +0 -183
- package/dist/tools/security.js.map +0 -1
- package/dist/tools/spawnAgent.d.ts +0 -19
- package/dist/tools/spawnAgent.js +0 -132
- package/dist/tools/spawnAgent.js.map +0 -1
- package/dist/tools/webSearch.d.ts +0 -6
- package/dist/tools/webSearch.js +0 -120
- package/dist/tools/webSearch.js.map +0 -1
- /package/dist/__tests__/{m55.test.d.ts → installHostDeps.test.d.ts} +0 -0
- /package/dist/__tests__/{middleware.test.d.ts → whitelistedBackend.test.d.ts} +0 -0
|
@@ -32,6 +32,7 @@ describe("Config Manager", () => {
|
|
|
32
32
|
expect(config.maxTokens).toBe(4096);
|
|
33
33
|
expect(config.temperature).toBe(0);
|
|
34
34
|
expect(config.streaming).toBe(true);
|
|
35
|
+
expect(config.executionMode).toBe("host");
|
|
35
36
|
});
|
|
36
37
|
// ─── RED Test #2: saveConfig roundtrips with loadConfig ───
|
|
37
38
|
it("saves config to disk and loads it back correctly", () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AACrE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAE9B,qDAAqD;AACrD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE1E,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,2DAA2D;IAC3D,IAAI,OAAe,CAAC;IACpB,IAAI,UAAkB,CAAC;IACvB,IAAI,iBAAqC,CAAC;IAE1C,UAAU,CAAC,GAAG,EAAE;QACd,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,aAAa,CAAC,CAAC,CAAC;QAChE,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;QAC/C,yCAAyC;QACzC,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAClD,OAAO,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACvC,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,kBAAkB;QAClB,IAAI,iBAAiB,KAAK,SAAS,EAAE,CAAC;YACpC,OAAO,CAAC,GAAG,CAAC,iBAAiB,GAAG,iBAAiB,CAAC;QACpD,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,2EAA2E;IAE3E,EAAE,CAAC,wDAAwD,EAAE,GAAG,EAAE;QAChE,MAAM,MAAM,GAAG,UAAU,CAAC,UAAU,CAAC,CAAC;QAEtC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,aAAa,EAAE,CAAC;QACtC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"config.test.js","sourceRoot":"","sources":["../../src/__tests__/config.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { installHostDependenciesTool } from "../tools/installHostDeps.js";
|
|
3
|
+
describe("InstallHostDependenciesTool (Security)", () => {
|
|
4
|
+
it("allows permitted package manager commands", async () => {
|
|
5
|
+
// We test with a dummy emitter to just ensure it doesn't fail early
|
|
6
|
+
// We can't easily mock execAsync purely without mocking module,
|
|
7
|
+
// but we can check if it gets past the security check and fails on execution
|
|
8
|
+
// (meaning it was allowed). Better yet, we can mock exec if needed, but for now
|
|
9
|
+
// we'll just check the error message structure if it fails.
|
|
10
|
+
// Actually, since this runs a real command, let's use a safe one or expect an execution error, not a security error.
|
|
11
|
+
});
|
|
12
|
+
it("blocks unknown binary", async () => {
|
|
13
|
+
const result = await installHostDependenciesTool.invoke({
|
|
14
|
+
command: "malicious_binary install foo",
|
|
15
|
+
});
|
|
16
|
+
expect(typeof result).toBe("string");
|
|
17
|
+
expect(result).toMatch(/Security Error/i);
|
|
18
|
+
expect(result).toMatch(/is not allowed/);
|
|
19
|
+
});
|
|
20
|
+
it("blocks disallowed subcommands for allowed binary", async () => {
|
|
21
|
+
const result = await installHostDependenciesTool.invoke({
|
|
22
|
+
command: "npm publish",
|
|
23
|
+
});
|
|
24
|
+
expect(typeof result).toBe("string");
|
|
25
|
+
expect(result).toMatch(/Security Error/i);
|
|
26
|
+
expect(result).toMatch(/npm publish/);
|
|
27
|
+
});
|
|
28
|
+
it("blocks forbidden shell operators", async () => {
|
|
29
|
+
const result = await installHostDependenciesTool.invoke({
|
|
30
|
+
command: "npm install express && rm -rf /",
|
|
31
|
+
});
|
|
32
|
+
expect(typeof result).toBe("string");
|
|
33
|
+
expect(result).toMatch(/Security Error/i);
|
|
34
|
+
expect(result).toMatch(/forbidden shell operators/);
|
|
35
|
+
});
|
|
36
|
+
it("blocks backticks", async () => {
|
|
37
|
+
const result = await installHostDependenciesTool.invoke({
|
|
38
|
+
command: "npm install `whoami`",
|
|
39
|
+
});
|
|
40
|
+
expect(typeof result).toBe("string");
|
|
41
|
+
expect(result).toMatch(/Security Error/i);
|
|
42
|
+
expect(result).toMatch(/forbidden shell operators/);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=installHostDeps.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"installHostDeps.test.js","sourceRoot":"","sources":["../../src/__tests__/installHostDeps.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAE1E,QAAQ,CAAC,wCAAwC,EAAE,GAAG,EAAE;IACpD,EAAE,CAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;QACvD,oEAAoE;QACpE,iEAAiE;QACjE,8EAA8E;QAC9E,iFAAiF;QACjF,4DAA4D;QAC5D,qHAAqH;IACzH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,MAAM,CAAC;YACpD,OAAO,EAAE,8BAA8B;SAC1C,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,MAAM,CAAC;YACpD,OAAO,EAAE,aAAa;SACzB,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAC9C,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,MAAM,CAAC;YACpD,OAAO,EAAE,iCAAiC;SAC7C,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kBAAkB,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,MAAM,GAAG,MAAM,2BAA2B,CAAC,MAAM,CAAC;YACpD,OAAO,EAAE,sBAAsB;SAClC,CAAC,CAAC;QAEH,MAAM,CAAC,OAAO,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QAC1C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,2BAA2B,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { WhitelistedLocalShellBackend } from "../sandbox/whitelistedBackend.js";
|
|
3
|
+
describe("WhitelistedLocalShellBackend", () => {
|
|
4
|
+
it("allows safe commands", async () => {
|
|
5
|
+
const backend = new WhitelistedLocalShellBackend({ rootDir: process.cwd() });
|
|
6
|
+
const result = await backend.execute("echo hello");
|
|
7
|
+
expect(result.output).toMatch(/hello/i);
|
|
8
|
+
expect(result.exitCode).toBe(0); // Works on Windows native echo
|
|
9
|
+
});
|
|
10
|
+
it("blocks dangerous commands", async () => {
|
|
11
|
+
const backend = new WhitelistedLocalShellBackend({ rootDir: process.cwd() });
|
|
12
|
+
const result = await backend.execute("rm -rf /");
|
|
13
|
+
expect(result.exitCode).toBe(1);
|
|
14
|
+
expect(result.output).toMatch(/Security Error/i);
|
|
15
|
+
expect(result.output).toMatch(/not allowed/i);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
//# sourceMappingURL=whitelistedBackend.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whitelistedBackend.test.js","sourceRoot":"","sources":["../../src/__tests__/whitelistedBackend.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAEhF,QAAQ,CAAC,8BAA8B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;QAClC,MAAM,OAAO,GAAG,IAAI,4BAA4B,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAE7E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;QACnD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACxC,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,+BAA+B;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACvC,MAAM,OAAO,GAAG,IAAI,4BAA4B,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QAE7E,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;QACjD,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;AACP,CAAC,CAAC,CAAC"}
|
package/dist/cli/config.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export interface JooneConfig {
|
|
|
30
30
|
compactModel?: string;
|
|
31
31
|
/** Override model for sub-agents (default: auto-selected fast model from same provider). */
|
|
32
32
|
subAgentModel?: string;
|
|
33
|
+
/** Execution mode: 'host' (local shell) or 'sandbox' (secure cloud). */
|
|
34
|
+
executionMode?: "host" | "sandbox";
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Sensible defaults — Anthropic Claude as the default provider.
|
package/dist/cli/config.js
CHANGED
package/dist/cli/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/cli/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAsClC;;GAEG;AACH,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,QAAQ,EAAE,WAAW;IACrB,KAAK,EAAE,0BAA0B;IACjC,SAAS,EAAE,IAAI;IACf,WAAW,EAAE,CAAC;IACd,SAAS,EAAE,IAAI;IACf,cAAc,EAAE,MAAM;IACtB,aAAa,EAAE,MAAM;CACtB,CAAC;AAEF;;GAEG;AACH,MAAM,iBAAiB,GAA2B;IAChD,SAAS,EAAE,mBAAmB;IAC9B,MAAM,EAAE,gBAAgB;IACxB,MAAM,EAAE,gBAAgB;IACxB,OAAO,EAAE,iBAAiB;IAC1B,IAAI,EAAE,cAAc;IACpB,QAAQ,EAAE,kBAAkB;IAC5B,SAAS,EAAE,mBAAmB;IAC9B,QAAQ,EAAE,kBAAkB;IAC5B,yCAAyC;CAC1C,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,IAAI,MAAmB,CAAC;IAExB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAyB,CAAC;YACvD,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;QAC5C,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,IAAI,CAAC,sCAAsC,UAAU,mBAAmB,CAAC,CAAC;YAClF,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,CAAC;QACjC,CAAC;IACH,CAAC;IACD,uEAAuE;IACvE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAClC,MAAM,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,UAAU,CAAC,UAAkB,EAAE,MAAmB;IAChE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACxB,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;QAC5D,QAAQ,EAAE,OAAO;QACjB,IAAI,EAAE,KAAK,EAAE,sCAAsC;KACpD,CAAC,CAAC;IAEH,oEAAoE;IACpE,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAAC,MAAM,CAAC;QACP,8CAA8C;IAChD,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,CAAC;AACrC,CAAC"}
|
package/dist/cli/index.js
CHANGED
|
@@ -6,23 +6,7 @@ import * as os from "node:os";
|
|
|
6
6
|
import chalk from "chalk";
|
|
7
7
|
import { intro, outro, select, text, password, confirm, spinner, isCancel, cancel, } from "@clack/prompts";
|
|
8
8
|
import { loadConfig, saveConfig } from "./config.js";
|
|
9
|
-
import {
|
|
10
|
-
import { installProvider, uninstallProvider } from "./providers.js";
|
|
11
|
-
import { tryEnableLangSmithFromConfig } from "../tracing/langsmith.js";
|
|
12
|
-
import { TraceAnalyzer } from "../tracing/analyzer.js";
|
|
13
|
-
import { SessionTracer } from "../tracing/sessionTracer.js";
|
|
14
|
-
import { SandboxManager } from "../sandbox/manager.js";
|
|
15
|
-
import { MiddlewarePipeline } from "../middleware/pipeline.js";
|
|
16
|
-
import { LoopDetectionMiddleware } from "../middleware/loopDetection.js";
|
|
17
|
-
import { CommandSanitizerMiddleware } from "../middleware/commandSanitizer.js";
|
|
18
|
-
import { ExecutionHarness } from "../core/agentLoop.js";
|
|
19
|
-
import { SessionStore } from "../core/sessionStore.js";
|
|
20
|
-
import { SessionResumer } from "../core/sessionResumer.js";
|
|
21
|
-
import { PermissionMiddleware } from "../middleware/permission.js";
|
|
22
|
-
import { AskUserQuestionTool } from "../tools/askUser.js";
|
|
23
|
-
import { createDefaultAgentRegistry } from "../agents/builtinAgents.js";
|
|
24
|
-
import { SubAgentManager } from "../core/subAgent.js";
|
|
25
|
-
import { createSpawnAgentTools } from "../tools/spawnAgent.js";
|
|
9
|
+
import { StartupBenchmark } from "./startupBenchmark.js";
|
|
26
10
|
const CONFIG_PATH = path.join(os.homedir(), ".joone", "config.json");
|
|
27
11
|
const SUPPORTED_PROVIDERS = [
|
|
28
12
|
{ value: "anthropic", label: "Anthropic", hint: "Claude 4, 3.5 Sonnet, Opus, Haiku" },
|
|
@@ -82,6 +66,14 @@ const PROVIDER_MODELS = {
|
|
|
82
66
|
],
|
|
83
67
|
};
|
|
84
68
|
const program = new Command();
|
|
69
|
+
const startupBenchmark = new StartupBenchmark();
|
|
70
|
+
startupBenchmark.mark("cli:entry");
|
|
71
|
+
async function loadModelFactory() {
|
|
72
|
+
return import("./modelFactory.js");
|
|
73
|
+
}
|
|
74
|
+
async function loadProviderManager() {
|
|
75
|
+
return import("./providers.js");
|
|
76
|
+
}
|
|
85
77
|
program
|
|
86
78
|
.name("joone")
|
|
87
79
|
.description("An autonomous coding agent powered by prompt caching and harness engineering")
|
|
@@ -218,6 +210,7 @@ async function runOnboarding() {
|
|
|
218
210
|
langsmithKey = existingConfig.langsmithApiKey ?? "";
|
|
219
211
|
const s = spinner();
|
|
220
212
|
try {
|
|
213
|
+
const { installProvider } = await loadProviderManager();
|
|
221
214
|
s.start(`Downloading and installing the ${provider} provider package...`);
|
|
222
215
|
await installProvider(provider);
|
|
223
216
|
s.stop(`Installed ${provider} provider package!`);
|
|
@@ -279,6 +272,7 @@ providerCmd
|
|
|
279
272
|
const s = spinner();
|
|
280
273
|
s.start(`Installing ${providerName}...`);
|
|
281
274
|
try {
|
|
275
|
+
const { installProvider } = await loadProviderManager();
|
|
282
276
|
await installProvider(providerName);
|
|
283
277
|
s.stop(`Successfully installed ${providerName}`);
|
|
284
278
|
}
|
|
@@ -295,6 +289,7 @@ providerCmd
|
|
|
295
289
|
const s = spinner();
|
|
296
290
|
s.start(`Uninstalling ${providerName}...`);
|
|
297
291
|
try {
|
|
292
|
+
const { uninstallProvider } = await loadProviderManager();
|
|
298
293
|
await uninstallProvider(providerName);
|
|
299
294
|
s.stop(`Successfully uninstalled ${providerName}`);
|
|
300
295
|
}
|
|
@@ -343,9 +338,11 @@ program
|
|
|
343
338
|
.command("start", { isDefault: true })
|
|
344
339
|
.description("Start a new Joone agent session")
|
|
345
340
|
.option("--no-stream", "Disable streaming output")
|
|
341
|
+
.option("--benchmark-startup", "Print startup timing milestones and exit")
|
|
346
342
|
.option("-r, --resume <sessionId>", "Resume a previous session by ID")
|
|
347
343
|
.action(async (options) => {
|
|
348
344
|
let config = loadConfig(CONFIG_PATH);
|
|
345
|
+
startupBenchmark.mark("cli:config-loaded");
|
|
349
346
|
// Auto-trigger onboarding if no API key is configured
|
|
350
347
|
if (!config.apiKey && config.provider !== "ollama") {
|
|
351
348
|
console.log(chalk.yellow("\n ⚠ No configuration found.") +
|
|
@@ -355,6 +352,7 @@ program
|
|
|
355
352
|
if (options.stream === false) {
|
|
356
353
|
config.streaming = false;
|
|
357
354
|
}
|
|
355
|
+
const { tryEnableLangSmithFromConfig } = await import("../tracing/langsmith.js");
|
|
358
356
|
const tracingEnabled = tryEnableLangSmithFromConfig(config);
|
|
359
357
|
console.log(chalk.cyan("\n ◆ joone") +
|
|
360
358
|
chalk.dim(" v0.1.0\n") +
|
|
@@ -363,54 +361,18 @@ program
|
|
|
363
361
|
chalk.dim(" ├ Stream: ") + chalk.white(config.streaming ? "on" : "off") + "\n" +
|
|
364
362
|
chalk.dim(" └ Tracing: ") + (tracingEnabled ? chalk.green("LangSmith") : chalk.dim("local only")) + "\n");
|
|
365
363
|
try {
|
|
366
|
-
const model = await createModel(config);
|
|
367
|
-
const pipeline = new MiddlewarePipeline();
|
|
368
|
-
pipeline.use(new LoopDetectionMiddleware(3));
|
|
369
|
-
pipeline.use(new CommandSanitizerMiddleware());
|
|
370
|
-
pipeline.use(new PermissionMiddleware(config.permissionMode ?? "auto"));
|
|
371
|
-
const tracer = new SessionTracer();
|
|
372
|
-
const { bindSandbox } = await import("../tools/index.js");
|
|
373
|
-
const s = spinner();
|
|
374
|
-
s.start("Initializing Sandbox Environment...");
|
|
375
|
-
const sandboxManager = new SandboxManager({
|
|
376
|
-
template: config.sandboxTemplate,
|
|
377
|
-
apiKey: config.e2bApiKey,
|
|
378
|
-
openSandboxApiKey: config.openSandboxApiKey,
|
|
379
|
-
openSandboxDomain: config.openSandboxDomain,
|
|
380
|
-
});
|
|
381
|
-
await sandboxManager.create();
|
|
382
|
-
const { FileSync } = await import("../sandbox/sync.js");
|
|
383
|
-
const fileSync = new FileSync(process.cwd());
|
|
384
|
-
bindSandbox(sandboxManager, fileSync);
|
|
385
|
-
// Sync user-level skills into the sandbox
|
|
386
|
-
const { SkillLoader } = await import("../skills/loader.js");
|
|
387
|
-
const skillLoader = new SkillLoader();
|
|
388
|
-
const skillPaths = skillLoader.getDiscoveryPaths();
|
|
389
|
-
await fileSync.syncSkillsToSandbox(sandboxManager, skillPaths);
|
|
390
|
-
s.stop("Sandbox initialized");
|
|
391
|
-
// For the CLI, we start by loading the CORE tools
|
|
392
|
-
// Advanced tools (search, browser, etc.) will be dynamically loaded by the agent later
|
|
393
|
-
// via the SearchToolsTool when the registry is fully integrated
|
|
394
|
-
const { CORE_TOOLS } = await import("../tools/index.js");
|
|
395
|
-
let tools = [...CORE_TOOLS, AskUserQuestionTool];
|
|
396
|
-
// Initialize Sub-Agent Orchestration
|
|
397
|
-
const agentRegistry = createDefaultAgentRegistry();
|
|
398
|
-
const subAgentModelName = config.subAgentModel ?? config.model;
|
|
399
|
-
// Use the config to determine sub-agent model, with FAST_MODEL_DEFAULTS fallback
|
|
400
|
-
const { resolveFastModel } = await import("../core/compactor.js");
|
|
401
|
-
const resolvedSubModel = resolveFastModel(config.provider, config.model, config.subAgentModel);
|
|
402
|
-
const subAgentLlm = await createModel({ ...config, model: resolvedSubModel });
|
|
403
|
-
const subAgentManager = new SubAgentManager(agentRegistry, tools, subAgentLlm);
|
|
404
|
-
const spawnAgentTools = createSpawnAgentTools(subAgentManager, agentRegistry);
|
|
405
|
-
tools = [...tools, ...spawnAgentTools];
|
|
406
364
|
let initialState;
|
|
407
365
|
let sessionId = undefined;
|
|
366
|
+
let cleanupRuntime = async () => { };
|
|
367
|
+
let pendingStartupBenchmarkReport;
|
|
408
368
|
if (options.resume) {
|
|
409
369
|
const s = spinner();
|
|
410
370
|
s.start(`Loading session ${chalk.bold(options.resume)}...`);
|
|
371
|
+
const { SessionStore } = await import("../core/sessionStore.js");
|
|
411
372
|
const store = new SessionStore();
|
|
412
373
|
try {
|
|
413
374
|
const payload = await store.loadSession(options.resume);
|
|
375
|
+
const { SessionResumer } = await import("../core/sessionResumer.js");
|
|
414
376
|
const resumer = new SessionResumer(process.cwd());
|
|
415
377
|
initialState = resumer.prepareForResume(payload);
|
|
416
378
|
sessionId = options.resume;
|
|
@@ -425,22 +387,52 @@ program
|
|
|
425
387
|
else {
|
|
426
388
|
initialState = {
|
|
427
389
|
globalSystemInstructions: `You are Joone, a highly capable autonomous coding agent.
|
|
428
|
-
You run in a hybrid environment
|
|
429
|
-
Always use
|
|
390
|
+
You run in a hybrid environment based on user configuration. You execute commands using 'bash' and can safely evaluate tests and install dependencies.
|
|
391
|
+
Always use the tools provided to you. Never read or write outside the current project directory unless explicitly requested.
|
|
430
392
|
|
|
431
393
|
IMPORTANT CAPABILITIES:
|
|
432
394
|
- You have access to an 'ask_user_question' tool. Use it to ask the user for clarification, preferences, or approval before making significant changes.
|
|
433
395
|
- Some tool calls may require user approval before execution, depending on the user's permission settings. If a tool call is denied, try an alternative approach or ask the user for guidance.
|
|
434
396
|
- You have access to Skills — reusable instruction sets for specialized tasks. Use 'search_skills' to discover them and 'load_skill' to activate their instructions.`,
|
|
435
|
-
projectMemory:
|
|
397
|
+
projectMemory: `Initial working directory: ${process.cwd()}`,
|
|
436
398
|
sessionContext: `Environment: ${process.platform}\nCWD: ${process.cwd()}`,
|
|
437
399
|
conversationHistory: []
|
|
438
400
|
};
|
|
439
401
|
}
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
402
|
+
const createHarness = async () => {
|
|
403
|
+
startupBenchmark.mark("runtime:harness-init-start");
|
|
404
|
+
const { createModel } = await loadModelFactory();
|
|
405
|
+
const model = await createModel(config);
|
|
406
|
+
startupBenchmark.mark("runtime:model-ready");
|
|
407
|
+
const { SessionTracer } = await import("../tracing/sessionTracer.js");
|
|
408
|
+
const tracer = new SessionTracer();
|
|
409
|
+
let sandboxManager;
|
|
410
|
+
const { bindSandbox, CORE_TOOLS } = await import("../tools/index.js");
|
|
411
|
+
if (config.executionMode !== "host") {
|
|
412
|
+
const { SandboxManager } = await import("../sandbox/manager.js");
|
|
413
|
+
sandboxManager = new SandboxManager({
|
|
414
|
+
template: config.sandboxTemplate,
|
|
415
|
+
apiKey: config.e2bApiKey,
|
|
416
|
+
openSandboxApiKey: config.openSandboxApiKey,
|
|
417
|
+
openSandboxDomain: config.openSandboxDomain,
|
|
418
|
+
});
|
|
419
|
+
await sandboxManager.create();
|
|
420
|
+
const { FileSync } = await import("../sandbox/sync.js");
|
|
421
|
+
const fileSync = new FileSync(process.cwd());
|
|
422
|
+
bindSandbox(sandboxManager, fileSync);
|
|
423
|
+
}
|
|
424
|
+
const { askUserQuestionTool } = await import("../tools/askUser.js");
|
|
425
|
+
const tools = [...CORE_TOOLS, askUserQuestionTool];
|
|
426
|
+
const { ExecutionHarness } = await import("../core/agentLoop.js");
|
|
427
|
+
const harness = new ExecutionHarness(model, tools, tracer, config.provider, config.model, sessionId, config.maxTokens, config.permissionMode, config.executionMode);
|
|
428
|
+
cleanupRuntime = async () => {
|
|
429
|
+
tracer.save();
|
|
430
|
+
if (sandboxManager) {
|
|
431
|
+
await sandboxManager.destroy();
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
return harness;
|
|
435
|
+
};
|
|
444
436
|
const { render } = await import("ink");
|
|
445
437
|
const React = await import("react");
|
|
446
438
|
const { App } = await import("../ui/App.js");
|
|
@@ -450,14 +442,21 @@ IMPORTANT CAPABILITIES:
|
|
|
450
442
|
provider: config.provider,
|
|
451
443
|
model: config.model,
|
|
452
444
|
streaming: config.streaming,
|
|
453
|
-
|
|
445
|
+
createHarness,
|
|
454
446
|
initialState,
|
|
455
447
|
maxTokens: config.maxTokens,
|
|
448
|
+
benchmarkStartup: Boolean(options.benchmarkStartup),
|
|
449
|
+
onStartupBenchmarkMark: (name) => startupBenchmark.mark(name),
|
|
450
|
+
onStartupBenchmarkComplete: () => {
|
|
451
|
+
pendingStartupBenchmarkReport = startupBenchmark.format("Joone Startup Benchmark");
|
|
452
|
+
},
|
|
456
453
|
}));
|
|
454
|
+
startupBenchmark.mark("ui:render-mounted");
|
|
457
455
|
await waitUntilExit();
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
456
|
+
if (pendingStartupBenchmarkReport) {
|
|
457
|
+
console.log(`\n${pendingStartupBenchmarkReport}\n`);
|
|
458
|
+
}
|
|
459
|
+
await cleanupRuntime();
|
|
461
460
|
}
|
|
462
461
|
catch (error) {
|
|
463
462
|
if (error instanceof Error) {
|
|
@@ -474,6 +473,7 @@ program
|
|
|
474
473
|
.command("sessions")
|
|
475
474
|
.description("List all persistent sessions available for resumption")
|
|
476
475
|
.action(async () => {
|
|
476
|
+
const { SessionStore } = await import("../core/sessionStore.js");
|
|
477
477
|
const store = new SessionStore();
|
|
478
478
|
const sessions = await store.listSessions();
|
|
479
479
|
if (sessions.length === 0) {
|
|
@@ -494,7 +494,7 @@ program
|
|
|
494
494
|
program
|
|
495
495
|
.command("analyze [sessionId]")
|
|
496
496
|
.description("Analyze a session trace for performance insights")
|
|
497
|
-
.action((sessionId) => {
|
|
497
|
+
.action(async (sessionId) => {
|
|
498
498
|
let tracePath;
|
|
499
499
|
const tracesDir = path.join(os.homedir(), ".joone", "traces");
|
|
500
500
|
if (sessionId) {
|
|
@@ -521,6 +521,8 @@ program
|
|
|
521
521
|
process.exit(1);
|
|
522
522
|
}
|
|
523
523
|
try {
|
|
524
|
+
const { SessionTracer } = await import("../tracing/sessionTracer.js");
|
|
525
|
+
const { TraceAnalyzer } = await import("../tracing/analyzer.js");
|
|
524
526
|
const trace = SessionTracer.load(tracePath);
|
|
525
527
|
const analyzer = new TraceAnalyzer(trace);
|
|
526
528
|
const report = analyzer.analyze();
|
|
@@ -541,6 +543,7 @@ program
|
|
|
541
543
|
console.error(chalk.red("\n ✗ LangSmith API key is missing. Run `joone config` to set it.\n"));
|
|
542
544
|
process.exit(1);
|
|
543
545
|
}
|
|
546
|
+
const { tryEnableLangSmithFromConfig } = await import("../tracing/langsmith.js");
|
|
544
547
|
tryEnableLangSmithFromConfig(config);
|
|
545
548
|
console.log(chalk.cyan("\n ◆ joone evals") + chalk.dim(` (Model: ${config.model})\n`));
|
|
546
549
|
try {
|
|
@@ -551,29 +554,33 @@ program
|
|
|
551
554
|
s.start("Verifying baseline dataset...");
|
|
552
555
|
const datasetName = await ensureBaselineDataset();
|
|
553
556
|
s.stop(`Dataset verified: ${chalk.white(datasetName)}`);
|
|
557
|
+
const { createModel } = await loadModelFactory();
|
|
554
558
|
const model = await createModel(config);
|
|
555
|
-
const
|
|
556
|
-
pipeline.use(new LoopDetectionMiddleware(3));
|
|
557
|
-
pipeline.use(new CommandSanitizerMiddleware());
|
|
559
|
+
const { SessionTracer } = await import("../tracing/sessionTracer.js");
|
|
558
560
|
const tracer = new SessionTracer();
|
|
559
561
|
const { bindSandbox, CORE_TOOLS } = await import("../tools/index.js");
|
|
560
562
|
const tools = [...CORE_TOOLS];
|
|
561
563
|
s.start("Running evaluations across dataset (this may take a few minutes)...");
|
|
562
564
|
// We define a target function that the generic `evaluate` engine will call for each example
|
|
563
565
|
const agentTargetFn = async (inputs) => {
|
|
566
|
+
const { ExecutionHarness } = await import("../core/agentLoop.js");
|
|
564
567
|
const runTracer = new SessionTracer();
|
|
565
|
-
const harness = new ExecutionHarness(model, tools,
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
568
|
+
const harness = new ExecutionHarness(model, tools, runTracer, config.provider, config.model, undefined, config.maxTokens, config.permissionMode, config.executionMode);
|
|
569
|
+
let sandboxManager;
|
|
570
|
+
if (config.executionMode !== "host") {
|
|
571
|
+
const { SandboxManager } = await import("../sandbox/manager.js");
|
|
572
|
+
// Initialize an empty sandbox just for this run
|
|
573
|
+
sandboxManager = new SandboxManager({
|
|
574
|
+
template: config.sandboxTemplate,
|
|
575
|
+
apiKey: config.e2bApiKey,
|
|
576
|
+
openSandboxApiKey: config.openSandboxApiKey,
|
|
577
|
+
openSandboxDomain: config.openSandboxDomain,
|
|
578
|
+
});
|
|
579
|
+
await sandboxManager.create();
|
|
580
|
+
const { FileSync } = await import("../sandbox/sync.js");
|
|
581
|
+
const fileSync = new FileSync(process.cwd());
|
|
582
|
+
bindSandbox(sandboxManager, fileSync);
|
|
583
|
+
}
|
|
577
584
|
const { HumanMessage, AIMessage, ToolMessage } = await import("@langchain/core/messages");
|
|
578
585
|
let conversationHistory = [
|
|
579
586
|
new HumanMessage(inputs.instruction)
|
|
@@ -582,30 +589,32 @@ program
|
|
|
582
589
|
let turnCount = 0;
|
|
583
590
|
const MAX_TURNS = 15; // Anti-doom-loop for evals
|
|
584
591
|
try {
|
|
585
|
-
|
|
592
|
+
const state = {
|
|
593
|
+
globalSystemInstructions: `You are Joone, a highly capable autonomous coding agent.
|
|
594
|
+
You run in a hybrid environment based on user configuration. You execute commands using 'bash' and can safely evaluate tests and install dependencies.
|
|
595
|
+
Always use the tools provided to you. Never read or write outside the current project directory unless explicitly requested.`,
|
|
596
|
+
projectMemory: `Evaluation run.\nInitial working directory: ${process.cwd()}`,
|
|
597
|
+
sessionContext: `Environment: ${process.platform}\nCWD: ${process.cwd()}`,
|
|
598
|
+
conversationHistory
|
|
599
|
+
};
|
|
600
|
+
const stream = harness.run(state);
|
|
601
|
+
for await (const event of stream) {
|
|
586
602
|
turnCount++;
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
conversationHistory.push(response);
|
|
597
|
-
if (response.content && typeof response.content === "string") {
|
|
598
|
-
finalOutput += response.content + "\n";
|
|
599
|
-
}
|
|
600
|
-
if (!response.tool_calls || response.tool_calls.length === 0) {
|
|
601
|
-
break; // Task complete
|
|
603
|
+
if (event.event === "on_chat_model_end" && event.data?.output) {
|
|
604
|
+
const msg = event.data.output;
|
|
605
|
+
if (msg.content && typeof msg.content === "string") {
|
|
606
|
+
finalOutput += msg.content + "\n";
|
|
607
|
+
}
|
|
608
|
+
if (msg.tool_calls && msg.tool_calls.length > 0) {
|
|
609
|
+
// LangGraph will automatically execute the tools under the hood
|
|
610
|
+
// We don't need to manually executeToolCalls here!
|
|
611
|
+
}
|
|
602
612
|
}
|
|
603
|
-
const toolResults = await harness.executeToolCalls(response, state);
|
|
604
|
-
conversationHistory.push(...toolResults);
|
|
605
613
|
}
|
|
606
614
|
}
|
|
607
615
|
catch (e) {
|
|
608
|
-
|
|
616
|
+
if (sandboxManager)
|
|
617
|
+
await sandboxManager.destroy();
|
|
609
618
|
throw e; // LangSmith catches this for the error evaluation
|
|
610
619
|
}
|
|
611
620
|
// Gather metrics
|
|
@@ -620,15 +629,19 @@ Always use 'bash' to run terminal commands. Never read or write outside the curr
|
|
|
620
629
|
// Check sandbox for uploaded/created files before ripping it down
|
|
621
630
|
let fileManifest = [];
|
|
622
631
|
try {
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
632
|
+
if (sandboxManager) {
|
|
633
|
+
const result = await sandboxManager.exec(`find /workspace -type f`);
|
|
634
|
+
if (result.stdout) {
|
|
635
|
+
fileManifest = result.stdout.split('\n').map((l) => l.trim()).filter(Boolean);
|
|
636
|
+
}
|
|
626
637
|
}
|
|
627
638
|
}
|
|
628
639
|
catch {
|
|
629
640
|
// Ignore, fallback to empty array
|
|
630
641
|
}
|
|
631
|
-
|
|
642
|
+
if (sandboxManager) {
|
|
643
|
+
await sandboxManager.destroy();
|
|
644
|
+
}
|
|
632
645
|
return {
|
|
633
646
|
finalOutput,
|
|
634
647
|
metrics,
|