diff options
| author | Adam Malczewski <[email protected]> | 2026-06-02 16:33:02 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-06-02 16:33:02 +0900 |
| commit | 0f99a0c92707b44aef7c627012c4711bad5a8efd (patch) | |
| tree | 4b9fdc0895b30339d6501ed6387b02bc364bcdb6 /packages/api/tests | |
| parent | c0c08720cceb75b5e635e71190ae1f956f535133 (diff) | |
| download | dispatch-0f99a0c92707b44aef7c627012c4711bad5a8efd.tar.gz dispatch-0f99a0c92707b44aef7c627012c4711bad5a8efd.zip | |
feat: add search_code tool wrapping the cs code-search engine
Add a dedicated, permission-gated search_code tool that wraps boyter/cs
(code spelunker) — a fast, relevance-ranked, structure-aware code search
engine — giving agents a better default than grep/find for exploratory
'where is X / how does Y work' searches (ranked results, snippets, ~5x
smaller payloads).
- packages/core/src/tools/search-code.ts: createSearchCodeTool factory;
-f json invocation, workdir path containment, graceful missing-binary
handling (DISPATCH_CS_BIN override), readable per-file formatted output.
- Wire-up: export from core; register in agent-manager (both child-whitelist
and parent perm paths) behind new perm_search_code; add to summon catalog
+ tools enum; frontend ToolPermissions + settings.
- Docker: build a patched, statically-linked cs (pinned v3.1.0 commit) in a
golang builder stage and bundle at /usr/local/bin/cs.
- docker/cs/luau-declarations.patch: additive Luau declaration table so
--only-declarations / definition ranking works for Roblox .luau files
(upstream has Lua but not Luau). Applied during the Docker build.
- Tests: new search-code.test.ts (stubbed JSON formatting + live-cs
integration, skipped when cs absent); agent-manager/routes mocks +
perm-gating assertions; loader pass-through.
All tests (596), biome, and tsc (core/api/frontend) pass. cs-builder Docker
stage verified to build and produce a working patched binary.
Diffstat (limited to 'packages/api/tests')
| -rw-r--r-- | packages/api/tests/agent-manager.test.ts | 29 | ||||
| -rw-r--r-- | packages/api/tests/routes.test.ts | 8 |
2 files changed, 37 insertions, 0 deletions
diff --git a/packages/api/tests/agent-manager.test.ts b/packages/api/tests/agent-manager.test.ts index 014022a..970ac1d 100644 --- a/packages/api/tests/agent-manager.test.ts +++ b/packages/api/tests/agent-manager.test.ts @@ -419,6 +419,14 @@ vi.mock("@dispatch/core", () => ({ execute: async () => "mock", }; }, + createSearchCodeTool(_wd: string) { + return { + name: "search_code", + description: "search code", + parameters: { _type: "z.ZodObject", shape: {} }, + execute: async () => "mock", + }; + }, createYoutubeTranscribeTool() { return { name: "youtube_transcribe", @@ -1441,6 +1449,27 @@ describe("AgentManager", () => { }); }); + describe("search_code permission gating", () => { + // Reuses the parent-path tool construction to confirm the perm flag wires + // the search_code tool on/off correctly. + async function toolsForPerms(tabId: string, perms: Record<string, string>): Promise<string[]> { + for (const [k, v] of Object.entries(perms)) setFakeSetting(k, v); + const manager = new AgentManager(); + await manager.processMessage(tabId, "go"); + return constructedAgents.at(-1)?.toolNames ?? []; + } + + it("grants search_code when perm_search_code is allowed", async () => { + const tools = await toolsForPerms("tab-cs-on", { perm_search_code: "allow" }); + expect(tools).toContain("search_code"); + }); + + it("omits search_code when perm_search_code is not allowed", async () => { + const tools = await toolsForPerms("tab-cs-off", {}); + expect(tools).not.toContain("search_code"); + }); + }); + // ─── Usage side-channel persistence ────────────────────────────── // // `usage` AgentEvents (one per LLM round-trip) are persisted as invisible diff --git a/packages/api/tests/routes.test.ts b/packages/api/tests/routes.test.ts index a8db5ce..c85d43d 100644 --- a/packages/api/tests/routes.test.ts +++ b/packages/api/tests/routes.test.ts @@ -283,6 +283,14 @@ vi.mock("@dispatch/core", () => ({ execute: async () => "mock", }; }, + createSearchCodeTool(_wd: string) { + return { + name: "search_code", + description: "search code", + parameters: { _type: "z.ZodObject", shape: {} }, + execute: async () => "mock", + }; + }, createYoutubeTranscribeTool() { return { name: "youtube_transcribe", |
