feat(coding-agent): added opt-in task prewalk and tightened --tools and xdev behavior

- Added a `task.prewalk` option (default `false`), removed default task `prewalk` flags, and updated prewalk resolution so bunded generic task execution only prewalks when explicitly enabled.
- Enforced strict `--tools` validation in CLI parsing, making unknown tool names fail fast with `CliUsageError` instead of being silently filtered.
- Migrated legacy discovery settings (`tools.discoveryMode`, `tools.essentialOverride`, MCP discovery keys) into updated `tools.xdev` handling with preserved explicit override behavior.
- Hardened xdev/ACP execution flow by capping `docsAll` payloads with overflow listing and remapping `xd://` dispatches/approval gating for correct execute/read behavior and reduced duplicate prompts.
This commit is contained in:
can1357
2026-07-15 18:39:36 +02:00
parent 4da1bbf9ea
commit 9afedb591e
22 changed files with 368 additions and 54 deletions
@@ -7,6 +7,7 @@ import * as themeModule from "@oh-my-pi/pi-coding-agent/modes/theme/theme";
import { ToolChoiceQueue } from "@oh-my-pi/pi-coding-agent/session/tool-choice-queue";
import { createTools, type ToolSession } from "@oh-my-pi/pi-coding-agent/tools";
import { writeToolRenderer } from "@oh-my-pi/pi-coding-agent/tools/write";
import { XdevRegistry } from "@oh-my-pi/pi-coding-agent/tools/xdev";
import { removeWithRetries } from "@oh-my-pi/pi-utils";
// xdev mounting is default-on: discoverable tools like ast_edit unmount into
@@ -105,4 +106,30 @@ describe("read and write route xd:// device URLs", () => {
const rendered = writeToolRenderer.renderCall({ path: "xd://ast_edit", content }, options, uiTheme);
expect(rendered).toBeDefined();
});
it("docsAll inlines small device docs and falls back to a listing past the caps", async () => {
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), "write-xdev-docs-"));
try {
const session = xdevSession(tempDir);
await createTools(session);
const mounted = session.xdevRegistry?.list() ?? [];
expect(mounted.length).toBeGreaterThan(0);
// One device with a pathological description must fall back to the
// listing without starving the rest of the catalog.
const giant = Object.create(mounted[0]!) as (typeof mounted)[number];
Object.defineProperty(giant, "name", { value: "giant_mcp_tool" });
Object.defineProperty(giant, "description", { value: "x".repeat(XdevRegistry.DOCS_PER_DEVICE_CAP + 1) });
const registry = new XdevRegistry([...mounted, giant]);
const docs = registry.docsAll();
expect(docs.length).toBeLessThan(XdevRegistry.DOCS_TOTAL_BUDGET + XdevRegistry.DOCS_PER_DEVICE_CAP);
expect(docs).toContain(`## ${mounted[0]!.name}`);
expect(docs).toContain("## Additional devices (docs on demand)");
expect(docs).toContain("- xd://giant_mcp_tool —");
expect(docs).not.toContain("## giant_mcp_tool");
} finally {
await removeWithRetries(tempDir);
}
});
});