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
package/dist/hitl/bridge.js
CHANGED
|
@@ -41,23 +41,10 @@ export class HITLBridge extends EventEmitter {
|
|
|
41
41
|
options,
|
|
42
42
|
createdAt: Date.now(),
|
|
43
43
|
};
|
|
44
|
-
return new Promise((resolve
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
45
|
this.pendingResolvers.set(id, resolve);
|
|
46
46
|
// Emit the question so the TUI can render it
|
|
47
47
|
this.emit("question", payload);
|
|
48
|
-
// Timeout: auto-reject if user doesn't respond
|
|
49
|
-
const timer = setTimeout(() => {
|
|
50
|
-
if (this.pendingResolvers.has(id)) {
|
|
51
|
-
this.pendingResolvers.delete(id);
|
|
52
|
-
resolve("[No response — the user did not answer within the timeout period.]");
|
|
53
|
-
}
|
|
54
|
-
}, this.timeoutMs);
|
|
55
|
-
// Clean up timer if resolved before timeout
|
|
56
|
-
const originalResolve = this.pendingResolvers.get(id);
|
|
57
|
-
this.pendingResolvers.set(id, (answer) => {
|
|
58
|
-
clearTimeout(timer);
|
|
59
|
-
originalResolve(answer);
|
|
60
|
-
});
|
|
61
48
|
});
|
|
62
49
|
}
|
|
63
50
|
/**
|
|
@@ -82,19 +69,6 @@ export class HITLBridge extends EventEmitter {
|
|
|
82
69
|
this.pendingResolvers.set(id, wrappedResolve);
|
|
83
70
|
// Emit so the TUI can render the permission prompt
|
|
84
71
|
this.emit("permission", payload);
|
|
85
|
-
// Timeout: auto-deny
|
|
86
|
-
const timer = setTimeout(() => {
|
|
87
|
-
if (this.pendingResolvers.has(id)) {
|
|
88
|
-
this.pendingResolvers.delete(id);
|
|
89
|
-
resolve(false); // Denied by timeout
|
|
90
|
-
}
|
|
91
|
-
}, this.timeoutMs);
|
|
92
|
-
// Clean up timer on resolve
|
|
93
|
-
const current = this.pendingResolvers.get(id);
|
|
94
|
-
this.pendingResolvers.set(id, (answer) => {
|
|
95
|
-
clearTimeout(timer);
|
|
96
|
-
current(answer);
|
|
97
|
-
});
|
|
98
72
|
});
|
|
99
73
|
}
|
|
100
74
|
/**
|
package/dist/hitl/bridge.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/hitl/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwB3C;;;;;;;;;GASG;AACH,MAAM,OAAO,UAAW,SAAQ,YAAY;IAChC,MAAM,CAAC,QAAQ,GAAsB,IAAI,CAAC;IAC1C,gBAAgB,GAAG,IAAI,GAAG,EAAoC,CAAC;IAC/D,SAAS,CAAS;IAClB,eAAe,GAAG,CAAC,CAAC;IAE5B,YAAY,YAAoB,CAAC,GAAG,EAAE,GAAG,IAAI;QACzC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,SAAkB;QACjC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YACvB,UAAU,CAAC,QAAQ,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,aAAa;QAChB,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,OAAkB;QAC9C,MAAM,EAAE,GAAG,UAAU,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAE5D,MAAM,OAAO,GAAiB;YAC1B,EAAE;YACF,QAAQ;YACR,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;QAEF,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"bridge.js","sourceRoot":"","sources":["../../src/hitl/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAwB3C;;;;;;;;;GASG;AACH,MAAM,OAAO,UAAW,SAAQ,YAAY;IAChC,MAAM,CAAC,QAAQ,GAAsB,IAAI,CAAC;IAC1C,gBAAgB,GAAG,IAAI,GAAG,EAAoC,CAAC;IAC/D,SAAS,CAAS;IAClB,eAAe,GAAG,CAAC,CAAC;IAE5B,YAAY,YAAoB,CAAC,GAAG,EAAE,GAAG,IAAI;QACzC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,WAAW,CAAC,SAAkB;QACjC,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;YACvB,UAAU,CAAC,QAAQ,GAAG,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,UAAU,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,MAAM,CAAC,aAAa;QAChB,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;IAC/B,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,OAAO,CAAC,QAAgB,EAAE,OAAkB;QAC9C,MAAM,EAAE,GAAG,UAAU,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAE5D,MAAM,OAAO,GAAiB;YAC1B,EAAE;YACF,QAAQ;YACR,OAAO;YACP,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;QAEF,OAAO,IAAI,OAAO,CAAS,CAAC,OAAO,EAAE,EAAE;YACnC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;YAEvC,6CAA6C;YAC7C,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,iBAAiB,CAAC,QAAgB,EAAE,IAA6B;QACnE,MAAM,EAAE,GAAG,aAAa,EAAE,IAAI,CAAC,eAAe,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC;QAE/D,MAAM,OAAO,GAA0B;YACnC,EAAE;YACF,QAAQ;YACR,IAAI;YACJ,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;SACxB,CAAC;QAEF,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE;YACpC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAE,EAAE;gBACtC,MAAM,UAAU,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;gBAC/C,OAAO,CAAC,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,KAAK,IAAI,UAAU,KAAK,SAAS,CAAC,CAAC;YACpF,CAAC,CAAC;YAEF,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,EAAE,cAAqB,CAAC,CAAC;YAErD,mDAAmD;YACnD,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,EAAU,EAAE,MAAc;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC/C,IAAI,QAAQ,EAAE,CAAC;YACX,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACjC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACrB,CAAC;IACL,CAAC;IAED;;OAEG;IACH,kBAAkB;QACd,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,CAAC;IAC1C,CAAC"}
|
|
@@ -1,28 +1,12 @@
|
|
|
1
|
-
import { ToolCallContext, ToolMiddleware } from "./types.js";
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
2
|
+
* Creates a middleware that prevents the "Blind Retry" doom loop.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* Inspects the conversation history right before the model is called.
|
|
5
|
+
* If the last N AI messages contain the exact same tool calls, it
|
|
6
|
+
* injects a warning message to force the model to try a different approach.
|
|
8
7
|
*
|
|
9
8
|
* Reference: docs/02_edge_cases_and_mitigations.md — "The Blind Retry Doom Loop"
|
|
9
|
+
*
|
|
10
|
+
* @param threshold - Number of identical consecutive calls before blocking (default: 3).
|
|
10
11
|
*/
|
|
11
|
-
export declare
|
|
12
|
-
readonly name = "LoopDetection";
|
|
13
|
-
private history;
|
|
14
|
-
private readonly threshold;
|
|
15
|
-
/**
|
|
16
|
-
* @param threshold - Number of identical consecutive calls before blocking (default: 3).
|
|
17
|
-
*/
|
|
18
|
-
constructor(threshold?: number);
|
|
19
|
-
/**
|
|
20
|
-
* Creates a signature string for a tool call (name + sorted args JSON).
|
|
21
|
-
*/
|
|
22
|
-
private signature;
|
|
23
|
-
before(ctx: ToolCallContext): ToolCallContext | string;
|
|
24
|
-
/**
|
|
25
|
-
* Resets the history. Useful for testing or session boundaries.
|
|
26
|
-
*/
|
|
27
|
-
reset(): void;
|
|
28
|
-
}
|
|
12
|
+
export declare function createLoopDetectionMiddleware(threshold?: number): import("langchain").AgentMiddleware<undefined, undefined, unknown, readonly (import("@langchain/core/tools").ClientTool | import("@langchain/core/tools").ServerTool)[]>;
|
|
@@ -1,49 +1,45 @@
|
|
|
1
|
+
import { createMiddleware } from "langchain";
|
|
2
|
+
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
1
3
|
/**
|
|
2
|
-
*
|
|
4
|
+
* Creates a middleware that prevents the "Blind Retry" doom loop.
|
|
3
5
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
6
|
+
* Inspects the conversation history right before the model is called.
|
|
7
|
+
* If the last N AI messages contain the exact same tool calls, it
|
|
8
|
+
* injects a warning message to force the model to try a different approach.
|
|
7
9
|
*
|
|
8
10
|
* Reference: docs/02_edge_cases_and_mitigations.md — "The Blind Retry Doom Loop"
|
|
11
|
+
*
|
|
12
|
+
* @param threshold - Number of identical consecutive calls before blocking (default: 3).
|
|
9
13
|
*/
|
|
10
|
-
export
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return (
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return ctx;
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Resets the history. Useful for testing or session boundaries.
|
|
44
|
-
*/
|
|
45
|
-
reset() {
|
|
46
|
-
this.history = [];
|
|
47
|
-
}
|
|
14
|
+
export function createLoopDetectionMiddleware(threshold = 3) {
|
|
15
|
+
const signature = (calls) => {
|
|
16
|
+
return calls
|
|
17
|
+
.map((c) => `${c.name}:${JSON.stringify(c.args, Object.keys(c.args || {}).sort())}`)
|
|
18
|
+
.join("|");
|
|
19
|
+
};
|
|
20
|
+
return createMiddleware({
|
|
21
|
+
name: "LoopDetectionMiddleware",
|
|
22
|
+
wrapModelCall: async (request, handler) => {
|
|
23
|
+
// Extract recent AI messages that have tool calls
|
|
24
|
+
const aiMessagesWithTools = request.messages.filter((m) => m instanceof AIMessage && m.tool_calls !== undefined && m.tool_calls.length > 0);
|
|
25
|
+
if (aiMessagesWithTools.length >= threshold) {
|
|
26
|
+
const recent = aiMessagesWithTools.slice(-threshold);
|
|
27
|
+
const sigs = recent.map((m) => signature(m.tool_calls));
|
|
28
|
+
const allIdentical = sigs.every((sig) => sig === sigs[0]);
|
|
29
|
+
if (allIdentical) {
|
|
30
|
+
// Identify the tools for the warning
|
|
31
|
+
const toolNames = recent[0].tool_calls.map(c => c.name).join(", ");
|
|
32
|
+
// Inject a strong human message to break the loop
|
|
33
|
+
const updatedMessages = [
|
|
34
|
+
...request.messages,
|
|
35
|
+
new HumanMessage(`⚠ Loop detected: You have called the tools [${toolNames}] with identical arguments ` +
|
|
36
|
+
`${threshold} times consecutively. Stop this approach and try a different strategy immediately.`)
|
|
37
|
+
];
|
|
38
|
+
return handler({ ...request, messages: updatedMessages });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return handler(request);
|
|
42
|
+
},
|
|
43
|
+
});
|
|
48
44
|
}
|
|
49
45
|
//# sourceMappingURL=loopDetection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loopDetection.js","sourceRoot":"","sources":["../../src/middleware/loopDetection.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"loopDetection.js","sourceRoot":"","sources":["../../src/middleware/loopDetection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,YAAY,EAAe,MAAM,0BAA0B,CAAC;AAGhF;;;;;;;;;;GAUG;AACH,MAAM,UAAU,6BAA6B,CAAC,SAAS,GAAG,CAAC;IACzD,MAAM,SAAS,GAAG,CAAC,KAAwC,EAAU,EAAE;QACrE,OAAO,KAAK;aACT,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;aACnF,IAAI,CAAC,GAAG,CAAC,CAAC;IACf,CAAC,CAAC;IAEF,OAAO,gBAAgB,CAAC;QACtB,IAAI,EAAE,yBAAyB;QAC/B,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE;YACxC,kDAAkD;YAClD,MAAM,mBAAmB,GAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CACjD,CAAC,CAAC,EAAkB,EAAE,CAAC,CAAC,YAAY,SAAS,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CACvG,CAAC;YAEF,IAAI,mBAAmB,CAAC,MAAM,IAAI,SAAS,EAAE,CAAC;gBAC5C,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC;gBACrD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,UAAW,CAAC,CAAC,CAAC;gBAEzD,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;gBAE1D,IAAI,YAAY,EAAE,CAAC;oBACjB,qCAAqC;oBACrC,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,UAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAEpE,kDAAkD;oBAClD,MAAM,eAAe,GAAG;wBACtB,GAAG,OAAO,CAAC,QAAQ;wBACnB,IAAI,YAAY,CACd,+CAA+C,SAAS,6BAA6B;4BACrF,GAAG,SAAS,oFAAoF,CACjG;qBACF,CAAC;oBAEF,OAAO,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,CAAC,CAAC;gBAC5D,CAAC;YACH,CAAC;YAED,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { LocalShellBackend } from "deepagents";
|
|
2
|
+
const ALLOWED_BINARIES = new Set(["npm", "npx", "node", "ls", "dir", "echo", "cat", "git"]);
|
|
3
|
+
export class WhitelistedLocalShellBackend extends LocalShellBackend {
|
|
4
|
+
constructor(config) {
|
|
5
|
+
super(config);
|
|
6
|
+
}
|
|
7
|
+
async execute(command) {
|
|
8
|
+
const trimmed = command.trim();
|
|
9
|
+
const binary = trimmed.split(/\s+/)[0];
|
|
10
|
+
if (!ALLOWED_BINARIES.has(binary)) {
|
|
11
|
+
return {
|
|
12
|
+
output: `Security Error: Command '${binary}' is not allowed. Only [${Array.from(ALLOWED_BINARIES).join(", ")}] are permitted.`,
|
|
13
|
+
exitCode: 1,
|
|
14
|
+
truncated: false
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
if (/[&|;`$]/.test(trimmed)) {
|
|
18
|
+
return {
|
|
19
|
+
output: `Security Error: Command contains forbidden shell operators (&, |, ;, \`, $).`,
|
|
20
|
+
exitCode: 1,
|
|
21
|
+
truncated: false
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return super.execute(command);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=whitelistedBackend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whitelistedBackend.js","sourceRoot":"","sources":["../../src/sandbox/whitelistedBackend.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAI/C,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;AAE5F,MAAM,OAAO,4BAA6B,SAAQ,iBAAiB;IAC/D,YAAY,MAAY;QACpB,KAAK,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,OAAe;QACzB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAEvC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO;gBACH,MAAM,EAAE,4BAA4B,MAAM,2BAA2B,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,kBAAkB;gBAC9H,QAAQ,EAAE,CAAC;gBACX,SAAS,EAAE,KAAK;aACnB,CAAC;QACN,CAAC;QAED,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YAC1B,OAAO;gBACH,MAAM,EAAE,8EAA8E;gBACtF,QAAQ,EAAE,CAAC;gBACX,SAAS,EAAE,KAAK;aACnB,CAAC;QACN,CAAC;QAED,OAAO,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAClC,CAAC;CACJ"}
|
package/dist/tools/askUser.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { z } from "zod";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* askUserQuestionTool — allows the agent to ask the user a clarifying question mid-turn.
|
|
4
4
|
*
|
|
5
5
|
* Use cases:
|
|
6
6
|
* - Resolving ambiguous requirements before coding.
|
|
7
7
|
* - Getting user preferences (framework choice, styling, naming).
|
|
8
8
|
* - Requesting approval of an implementation plan before proceeding.
|
|
9
9
|
*/
|
|
10
|
-
export declare const
|
|
10
|
+
export declare const askUserQuestionTool: import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
11
|
+
question: z.ZodString;
|
|
12
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
}, z.core.$strip>, {
|
|
14
|
+
question: string;
|
|
15
|
+
options?: string[];
|
|
16
|
+
}, {
|
|
17
|
+
question: string;
|
|
18
|
+
options?: string[] | undefined;
|
|
19
|
+
}, string, "ask_user_question">;
|
package/dist/tools/askUser.js
CHANGED
|
@@ -1,42 +1,30 @@
|
|
|
1
1
|
import { HITLBridge } from "../hitl/bridge.js";
|
|
2
|
+
import { tool } from "langchain";
|
|
3
|
+
import { z } from "zod";
|
|
2
4
|
/**
|
|
3
|
-
*
|
|
5
|
+
* askUserQuestionTool — allows the agent to ask the user a clarifying question mid-turn.
|
|
4
6
|
*
|
|
5
7
|
* Use cases:
|
|
6
8
|
* - Resolving ambiguous requirements before coding.
|
|
7
9
|
* - Getting user preferences (framework choice, styling, naming).
|
|
8
10
|
* - Requesting approval of an implementation plan before proceeding.
|
|
9
11
|
*/
|
|
10
|
-
export const
|
|
12
|
+
export const askUserQuestionTool = tool(async ({ question, options }) => {
|
|
13
|
+
if (!question || question.trim() === "") {
|
|
14
|
+
return "Error: You must provide a non-empty question.";
|
|
15
|
+
}
|
|
16
|
+
const bridge = HITLBridge.getInstance();
|
|
17
|
+
const answer = await bridge.askUser(question, options);
|
|
18
|
+
return answer;
|
|
19
|
+
}, {
|
|
11
20
|
name: "ask_user_question",
|
|
12
21
|
description: "Ask the user a question and wait for their response. " +
|
|
13
22
|
"Use this when you need clarification on the task, user preferences, " +
|
|
14
23
|
"or approval before proceeding with a significant change. " +
|
|
15
24
|
"You may optionally provide a list of answer choices.",
|
|
16
|
-
schema: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
description: "The question to ask the user.",
|
|
22
|
-
},
|
|
23
|
-
options: {
|
|
24
|
-
type: "array",
|
|
25
|
-
items: { type: "string" },
|
|
26
|
-
description: "Optional list of predefined answer choices.",
|
|
27
|
-
},
|
|
28
|
-
},
|
|
29
|
-
required: ["question"],
|
|
30
|
-
},
|
|
31
|
-
async execute(args) {
|
|
32
|
-
const question = args.question;
|
|
33
|
-
const options = args.options;
|
|
34
|
-
if (!question || question.trim() === "") {
|
|
35
|
-
return { content: "Error: You must provide a non-empty question.", isError: true };
|
|
36
|
-
}
|
|
37
|
-
const bridge = HITLBridge.getInstance();
|
|
38
|
-
const answer = await bridge.askUser(question, options);
|
|
39
|
-
return { content: answer };
|
|
40
|
-
},
|
|
41
|
-
};
|
|
25
|
+
schema: z.object({
|
|
26
|
+
question: z.string().describe("The question to ask the user."),
|
|
27
|
+
options: z.array(z.string()).optional().describe("Optional list of predefined answer choices."),
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
42
30
|
//# sourceMappingURL=askUser.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askUser.js","sourceRoot":"","sources":["../../src/tools/askUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"askUser.js","sourceRoot":"","sources":["../../src/tools/askUser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CACnC,KAAK,EAAE,EAAE,QAAQ,EAAE,OAAO,EAA4C,EAAE,EAAE;IACtE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC;QACtC,OAAO,+CAA+C,CAAC;IAC3D,CAAC;IAED,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAEvD,OAAO,MAAM,CAAC;AAClB,CAAC,EACD;IACI,IAAI,EAAE,mBAAmB;IACzB,WAAW,EACP,uDAAuD;QACvD,sEAAsE;QACtE,2DAA2D;QAC3D,sDAAsD;IAC1D,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,+BAA+B,CAAC;QAC9D,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6CAA6C,CAAC;KAClG,CAAC;CACL,CACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { SandboxManager } from "../sandbox/manager.js";
|
|
3
|
+
import { FileSync } from "../sandbox/sync.js";
|
|
4
|
+
export declare function bindSandbox(sandbox: SandboxManager, fileSync: FileSync): void;
|
|
5
|
+
export declare const bashTool: import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
6
|
+
command: z.ZodString;
|
|
7
|
+
}, z.core.$strip>, {
|
|
8
|
+
command: string;
|
|
9
|
+
}, {
|
|
10
|
+
command: string;
|
|
11
|
+
}, string, "bash">;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { tool } from "langchain";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const BLOCKED_PATTERNS = [
|
|
4
|
+
// Destructive
|
|
5
|
+
[/rm\s+(-\w*r\w*f\w*|-\w*f\w*r\w*)\s+\/(\*)?(?:\s|$)/, "destructive: rm -rf /"],
|
|
6
|
+
[/mkfs\b/, "destructive: filesystem format"],
|
|
7
|
+
[/\bdd\s+.*of=\/dev\//, "destructive: raw disk write"],
|
|
8
|
+
[/chmod\s+(-\w+\s+)*777\s+\//, "dangerous: chmod 777 on root"],
|
|
9
|
+
// Interactive / hanging
|
|
10
|
+
[/\b(vim|vi|nano|emacs|pico)\b/, "interactive: text editor (hangs the sandbox)"],
|
|
11
|
+
[/\b(less|more)\b/, "interactive: pager (hangs the sandbox)"],
|
|
12
|
+
[/\b(top|htop|glances)\b/, "interactive: process monitor (hangs the sandbox)"],
|
|
13
|
+
[/\bman\s+\w+/, "interactive: man page (hangs the sandbox)"],
|
|
14
|
+
// Network abuse: pipe-to-shell
|
|
15
|
+
[/curl\s+.*\|\s*(sh|bash|zsh)/, "unsafe: pipe remote script to shell"],
|
|
16
|
+
[/wget\s+.*\|\s*(sh|bash|zsh)/, "unsafe: pipe remote script to shell"],
|
|
17
|
+
];
|
|
18
|
+
let _sandboxManager = null;
|
|
19
|
+
let _fileSync = null;
|
|
20
|
+
export function bindSandbox(sandbox, fileSync) {
|
|
21
|
+
_sandboxManager = sandbox;
|
|
22
|
+
_fileSync = fileSync;
|
|
23
|
+
}
|
|
24
|
+
export const bashTool = tool(async ({ command }) => {
|
|
25
|
+
for (const [pattern, reason] of BLOCKED_PATTERNS) {
|
|
26
|
+
if (pattern.test(command)) {
|
|
27
|
+
return (`⚠ Blocked: Command rejected by sanitizer.\n` +
|
|
28
|
+
`Reason: ${reason}\n` +
|
|
29
|
+
`Command: ${command}\n` +
|
|
30
|
+
`Use a safer alternative or refine your approach.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!_sandboxManager || !_sandboxManager.isActive()) {
|
|
34
|
+
throw new Error("Sandbox is not active. Cannot execute bash commands without an active sandbox session.");
|
|
35
|
+
}
|
|
36
|
+
if (_fileSync && _fileSync.pendingCount() > 0) {
|
|
37
|
+
await _fileSync.syncToSandbox(_sandboxManager);
|
|
38
|
+
}
|
|
39
|
+
const result = await _sandboxManager.exec(command);
|
|
40
|
+
if (result.exitCode !== 0) {
|
|
41
|
+
return `Command failed (exit code ${result.exitCode}):\nSTDOUT:\n${result.stdout}\nSTDERR:\n${result.stderr}`;
|
|
42
|
+
}
|
|
43
|
+
return result.stdout || "(no output)";
|
|
44
|
+
}, {
|
|
45
|
+
name: "bash",
|
|
46
|
+
description: "Runs a shell command inside an isolated sandbox. Use for tests, scripts, or installing dependencies. The host machine is never exposed.",
|
|
47
|
+
schema: z.object({
|
|
48
|
+
command: z.string().describe("The shell command to execute"),
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
//# sourceMappingURL=bashTool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bashTool.js","sourceRoot":"","sources":["../../src/tools/bashTool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,MAAM,gBAAgB,GAAuB;IACzC,cAAc;IACd,CAAC,oDAAoD,EAAE,uBAAuB,CAAC;IAC/E,CAAC,QAAQ,EAAE,gCAAgC,CAAC;IAC5C,CAAC,qBAAqB,EAAE,6BAA6B,CAAC;IACtD,CAAC,4BAA4B,EAAE,8BAA8B,CAAC;IAE9D,wBAAwB;IACxB,CAAC,8BAA8B,EAAE,8CAA8C,CAAC;IAChF,CAAC,iBAAiB,EAAE,wCAAwC,CAAC;IAC7D,CAAC,wBAAwB,EAAE,kDAAkD,CAAC;IAC9E,CAAC,aAAa,EAAE,2CAA2C,CAAC;IAE5D,+BAA+B;IAC/B,CAAC,6BAA6B,EAAE,qCAAqC,CAAC;IACtE,CAAC,6BAA6B,EAAE,qCAAqC,CAAC;CACzE,CAAC;AAEF,IAAI,eAAe,GAA0B,IAAI,CAAC;AAClD,IAAI,SAAS,GAAoB,IAAI,CAAC;AAEtC,MAAM,UAAU,WAAW,CAAC,OAAuB,EAAE,QAAkB;IACnE,eAAe,GAAG,OAAO,CAAC;IAC1B,SAAS,GAAG,QAAQ,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,IAAI,CACxB,KAAK,EAAE,EAAE,OAAO,EAAuB,EAAE,EAAE;IACvC,KAAK,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,gBAAgB,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACxB,OAAO,CACH,6CAA6C;gBAC7C,WAAW,MAAM,IAAI;gBACrB,YAAY,OAAO,IAAI;gBACvB,kDAAkD,CACrD,CAAC;QACN,CAAC;IACL,CAAC;IAED,IAAI,CAAC,eAAe,IAAI,CAAC,eAAe,CAAC,QAAQ,EAAE,EAAE,CAAC;QAClD,MAAM,IAAI,KAAK,CACX,wFAAwF,CAC3F,CAAC;IACN,CAAC;IAED,IAAI,SAAS,IAAI,SAAS,CAAC,YAAY,EAAE,GAAG,CAAC,EAAE,CAAC;QAC5C,MAAM,SAAS,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IACnD,CAAC;IAED,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACnD,IAAI,MAAM,CAAC,QAAQ,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,6BAA6B,MAAM,CAAC,QAAQ,gBAAgB,MAAM,CAAC,MAAM,cAAc,MAAM,CAAC,MAAM,EAAE,CAAC;IAClH,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,IAAI,aAAa,CAAC;AAC1C,CAAC,EACD;IACI,IAAI,EAAE,MAAM;IACZ,WAAW,EACP,yIAAyI;IAC7I,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC;QACb,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;KAC/D,CAAC;CACL,CACJ,CAAC"}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,28 +1,15 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
* Binds the tools to a SandboxManager and FileSync instance.
|
|
17
|
-
* Must be called at session start before any tool executions.
|
|
18
|
-
*/
|
|
19
|
-
export declare function bindSandbox(sandbox: SandboxManager, fileSync: FileSync): void;
|
|
20
|
-
/**
|
|
21
|
-
* Security: Validates that a resolved path is strictly inside the given workspace dir.
|
|
22
|
-
* Prevents directory traversal attacks from accessing sensitive host files.
|
|
23
|
-
*/
|
|
24
|
-
export declare function isPathInsideWorkspace(resolvedPath: string, workspaceDir?: string): boolean;
|
|
25
|
-
export declare const BashTool: DynamicToolInterface;
|
|
26
|
-
export declare const ReadFileTool: DynamicToolInterface;
|
|
27
|
-
export declare const WriteFileTool: DynamicToolInterface;
|
|
28
|
-
export declare const CORE_TOOLS: DynamicToolInterface[];
|
|
1
|
+
import { bindSandbox } from "./bashTool.js";
|
|
2
|
+
export { bindSandbox };
|
|
3
|
+
export declare const CORE_TOOLS: (import("langchain").DynamicStructuredTool<import("zod").ZodObject<{
|
|
4
|
+
command: import("zod").ZodString;
|
|
5
|
+
}, import("zod/v4/core").$strip>, {
|
|
6
|
+
command: string;
|
|
7
|
+
}, {
|
|
8
|
+
command: string;
|
|
9
|
+
}, string, "install_host_dependencies"> | import("langchain").DynamicStructuredTool<import("zod").ZodObject<{
|
|
10
|
+
command: import("zod").ZodString;
|
|
11
|
+
}, import("zod/v4/core").$strip>, {
|
|
12
|
+
command: string;
|
|
13
|
+
}, {
|
|
14
|
+
command: string;
|
|
15
|
+
}, string, "bash">)[];
|