fix: honor sandboxed built-in tools
This commit is contained in:
@@ -1,6 +1,10 @@
|
||||
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { buildEmbeddedSandboxInfo } from "./pi-embedded-runner.js";
|
||||
import {
|
||||
buildEmbeddedSandboxInfo,
|
||||
splitSdkTools,
|
||||
} from "./pi-embedded-runner.js";
|
||||
import type { SandboxContext } from "./sandbox.js";
|
||||
|
||||
describe("buildEmbeddedSandboxInfo", () => {
|
||||
@@ -45,3 +49,52 @@ describe("buildEmbeddedSandboxInfo", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function createStubTool(name: string): AgentTool {
|
||||
return {
|
||||
name,
|
||||
label: name,
|
||||
description: "",
|
||||
parameters: Type.Object({}),
|
||||
execute: async () => ({ content: [], details: {} }),
|
||||
};
|
||||
}
|
||||
|
||||
describe("splitSdkTools", () => {
|
||||
const tools = [
|
||||
createStubTool("read"),
|
||||
createStubTool("bash"),
|
||||
createStubTool("edit"),
|
||||
createStubTool("write"),
|
||||
createStubTool("browser"),
|
||||
];
|
||||
|
||||
it("routes built-ins to custom tools when sandboxed", () => {
|
||||
const { builtInTools, customTools } = splitSdkTools({
|
||||
tools,
|
||||
sandboxEnabled: true,
|
||||
});
|
||||
expect(builtInTools).toEqual([]);
|
||||
expect(customTools.map((tool) => tool.name)).toEqual([
|
||||
"read",
|
||||
"bash",
|
||||
"edit",
|
||||
"write",
|
||||
"browser",
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps built-ins as SDK tools when not sandboxed", () => {
|
||||
const { builtInTools, customTools } = splitSdkTools({
|
||||
tools,
|
||||
sandboxEnabled: false,
|
||||
});
|
||||
expect(builtInTools.map((tool) => tool.name)).toEqual([
|
||||
"read",
|
||||
"bash",
|
||||
"edit",
|
||||
"write",
|
||||
]);
|
||||
expect(customTools.map((tool) => tool.name)).toEqual(["browser"]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import fs from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
|
||||
import type { AgentMessage, ThinkingLevel } from "@mariozechner/pi-agent-core";
|
||||
import type {
|
||||
AgentMessage,
|
||||
AgentTool,
|
||||
ThinkingLevel,
|
||||
} from "@mariozechner/pi-agent-core";
|
||||
import type { Api, AssistantMessage, Model } from "@mariozechner/pi-ai";
|
||||
import {
|
||||
buildSystemPrompt,
|
||||
@@ -12,6 +16,7 @@ import {
|
||||
SettingsManager,
|
||||
type Skill,
|
||||
} from "@mariozechner/pi-coding-agent";
|
||||
import type { TSchema } from "@sinclair/typebox";
|
||||
import { resolveHeartbeatPrompt } from "../auto-reply/heartbeat.js";
|
||||
import type {
|
||||
ReasoningLevel,
|
||||
@@ -249,6 +254,33 @@ export function buildEmbeddedSandboxInfo(
|
||||
};
|
||||
}
|
||||
|
||||
const BUILT_IN_TOOL_NAMES = new Set(["read", "bash", "edit", "write"]);
|
||||
|
||||
type AnyAgentTool = AgentTool<TSchema, unknown>;
|
||||
|
||||
export function splitSdkTools(options: {
|
||||
tools: AnyAgentTool[];
|
||||
sandboxEnabled: boolean;
|
||||
}): {
|
||||
builtInTools: AnyAgentTool[];
|
||||
customTools: ReturnType<typeof toToolDefinitions>;
|
||||
} {
|
||||
// SDK rebuilds built-ins from cwd; route sandboxed versions as custom tools.
|
||||
const { tools, sandboxEnabled } = options;
|
||||
if (sandboxEnabled) {
|
||||
return {
|
||||
builtInTools: [],
|
||||
customTools: toToolDefinitions(tools),
|
||||
};
|
||||
}
|
||||
return {
|
||||
builtInTools: tools.filter((tool) => BUILT_IN_TOOL_NAMES.has(tool.name)),
|
||||
customTools: toToolDefinitions(
|
||||
tools.filter((tool) => !BUILT_IN_TOOL_NAMES.has(tool.name)),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
export function queueEmbeddedPiMessage(
|
||||
sessionId: string,
|
||||
text: string,
|
||||
@@ -528,11 +560,10 @@ export async function compactEmbeddedPiSession(params: {
|
||||
agentDir,
|
||||
);
|
||||
|
||||
const builtInToolNames = new Set(["read", "bash", "edit", "write"]);
|
||||
const builtInTools = tools.filter((t) => builtInToolNames.has(t.name));
|
||||
const customTools = toToolDefinitions(
|
||||
tools.filter((t) => !builtInToolNames.has(t.name)),
|
||||
);
|
||||
const { builtInTools, customTools } = splitSdkTools({
|
||||
tools,
|
||||
sandboxEnabled: !!sandbox?.enabled,
|
||||
});
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
cwd: resolvedWorkspace,
|
||||
@@ -829,14 +860,10 @@ export async function runEmbeddedPiAgent(params: {
|
||||
agentDir,
|
||||
);
|
||||
|
||||
// Split tools into built-in (recognized by pi-coding-agent SDK) and custom (clawdbot-specific)
|
||||
const builtInToolNames = new Set(["read", "bash", "edit", "write"]);
|
||||
const builtInTools = tools.filter((t) =>
|
||||
builtInToolNames.has(t.name),
|
||||
);
|
||||
const customTools = toToolDefinitions(
|
||||
tools.filter((t) => !builtInToolNames.has(t.name)),
|
||||
);
|
||||
const { builtInTools, customTools } = splitSdkTools({
|
||||
tools,
|
||||
sandboxEnabled: !!sandbox?.enabled,
|
||||
});
|
||||
|
||||
const { session } = await createAgentSession({
|
||||
cwd: resolvedWorkspace,
|
||||
|
||||
@@ -162,7 +162,9 @@ describe("subscribeEmbeddedPiSession", () => {
|
||||
|
||||
expect(onBlockReply).toHaveBeenCalledTimes(1);
|
||||
const payload = onBlockReply.mock.calls[0][0];
|
||||
expect(payload.text).toBe("_Reasoning:_\n_Because it helps_\n\nFinal answer");
|
||||
expect(payload.text).toBe(
|
||||
"_Reasoning:_\n_Because it helps_\n\nFinal answer",
|
||||
);
|
||||
});
|
||||
|
||||
it("emits block replies on text_end and does not duplicate on message_end", () => {
|
||||
|
||||
Reference in New Issue
Block a user