fix(coding-agent): make prompt snippets opt in closes #2285
This commit is contained in:
@@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
- Changed custom tool system prompt behavior so extension and SDK tools are included in the default `Available tools` section only when they provide `promptSnippet`. Omitting `promptSnippet` now leaves the tool out of that section instead of falling back to `description` ([#2285](https://github.com/badlogic/pi-mono/issues/2285))
|
||||||
|
|
||||||
## [0.58.4] - 2026-03-16
|
## [0.58.4] - 2026-03-16
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
@@ -934,7 +934,7 @@ Register a custom tool callable by the LLM. See [Custom Tools](#custom-tools) fo
|
|||||||
|
|
||||||
Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
|
Use `pi.setActiveTools()` to enable or disable tools (including dynamically added tools) at runtime.
|
||||||
|
|
||||||
Use `promptSnippet` to customize that tool's one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
|
Use `promptSnippet` to opt a custom tool into a one-line entry in `Available tools`, and `promptGuidelines` to append tool-specific bullets to the default `Guidelines` section when the tool is active.
|
||||||
|
|
||||||
See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
|
See [dynamic-tools.ts](../examples/extensions/dynamic-tools.ts) for a full example.
|
||||||
|
|
||||||
@@ -1336,7 +1336,7 @@ export default function (pi: ExtensionAPI) {
|
|||||||
|
|
||||||
Register tools the LLM can call via `pi.registerTool()`. Tools appear in the system prompt and can have custom rendering.
|
Register tools the LLM can call via `pi.registerTool()`. Tools appear in the system prompt and can have custom rendering.
|
||||||
|
|
||||||
Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, pi falls back to `description`.
|
Use `promptSnippet` for a short one-line entry in the `Available tools` section in the default system prompt. If omitted, custom tools are left out of that section.
|
||||||
|
|
||||||
Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
|
Use `promptGuidelines` to add tool-specific bullets to the default system prompt `Guidelines` section. These bullets are included only while the tool is active (for example, after `pi.setActiveTools([...])`).
|
||||||
|
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ export interface ToolDefinition<TParams extends TSchema = TSchema, TDetails = un
|
|||||||
label: string;
|
label: string;
|
||||||
/** Description for LLM */
|
/** Description for LLM */
|
||||||
description: string;
|
description: string;
|
||||||
/** Optional one-line snippet for the Available tools section in the default system prompt. Falls back to description when omitted. */
|
/** Optional one-line snippet for the Available tools section in the default system prompt. Custom tools are omitted from that section when this is not provided. */
|
||||||
promptSnippet?: string;
|
promptSnippet?: string;
|
||||||
/** Optional guideline bullets appended to the default system prompt Guidelines section when this tool is active. */
|
/** Optional guideline bullets appended to the default system prompt Guidelines section when this tool is active. */
|
||||||
promptGuidelines?: string[];
|
promptGuidelines?: string[];
|
||||||
|
|||||||
@@ -94,9 +94,10 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions = {}): strin
|
|||||||
// Build tools list based on selected tools.
|
// Build tools list based on selected tools.
|
||||||
// Built-ins use toolDescriptions. Custom tools can provide one-line snippets.
|
// Built-ins use toolDescriptions. Custom tools can provide one-line snippets.
|
||||||
const tools = selectedTools || ["read", "bash", "edit", "write"];
|
const tools = selectedTools || ["read", "bash", "edit", "write"];
|
||||||
|
const visibleTools = tools.filter((name) => name in toolDescriptions || toolSnippets?.[name]);
|
||||||
const toolsList =
|
const toolsList =
|
||||||
tools.length > 0
|
visibleTools.length > 0
|
||||||
? tools
|
? visibleTools
|
||||||
.map((name) => {
|
.map((name) => {
|
||||||
const snippet = toolSnippets?.[name] ?? toolDescriptions[name] ?? name;
|
const snippet = toolSnippets?.[name] ?? toolDescriptions[name] ?? name;
|
||||||
return `- ${name}: ${snippet}`;
|
return `- ${name}: ${snippet}`;
|
||||||
|
|||||||
@@ -74,4 +74,50 @@ describe("AgentSession dynamic tool registration", () => {
|
|||||||
|
|
||||||
session.dispose();
|
session.dispose();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps custom tools active but omits them from available tools when promptSnippet is not provided", async () => {
|
||||||
|
const settingsManager = SettingsManager.create(tempDir, agentDir);
|
||||||
|
const sessionManager = SessionManager.inMemory();
|
||||||
|
|
||||||
|
const resourceLoader = new DefaultResourceLoader({
|
||||||
|
cwd: tempDir,
|
||||||
|
agentDir,
|
||||||
|
settingsManager,
|
||||||
|
extensionFactories: [
|
||||||
|
(pi) => {
|
||||||
|
pi.on("session_start", () => {
|
||||||
|
pi.registerTool({
|
||||||
|
name: "hidden_tool",
|
||||||
|
label: "Hidden Tool",
|
||||||
|
description: "Description should not appear in available tools",
|
||||||
|
parameters: Type.Object({}),
|
||||||
|
execute: async () => ({
|
||||||
|
content: [{ type: "text", text: "ok" }],
|
||||||
|
details: {},
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
await resourceLoader.reload();
|
||||||
|
|
||||||
|
const { session } = await createAgentSession({
|
||||||
|
cwd: tempDir,
|
||||||
|
agentDir,
|
||||||
|
model: getModel("anthropic", "claude-sonnet-4-5")!,
|
||||||
|
settingsManager,
|
||||||
|
sessionManager,
|
||||||
|
resourceLoader,
|
||||||
|
});
|
||||||
|
|
||||||
|
await session.bindExtensions({});
|
||||||
|
|
||||||
|
expect(session.getAllTools().map((tool) => tool.name)).toContain("hidden_tool");
|
||||||
|
expect(session.getActiveToolNames()).toContain("hidden_tool");
|
||||||
|
expect(session.systemPrompt).not.toContain("hidden_tool");
|
||||||
|
expect(session.systemPrompt).not.toContain("Description should not appear in available tools");
|
||||||
|
|
||||||
|
session.dispose();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ describe("buildSystemPrompt", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("custom tool snippets", () => {
|
describe("custom tool snippets", () => {
|
||||||
test("includes custom tools in available tools section", () => {
|
test("includes custom tools in available tools section when promptSnippet is provided", () => {
|
||||||
const prompt = buildSystemPrompt({
|
const prompt = buildSystemPrompt({
|
||||||
selectedTools: ["read", "dynamic_tool"],
|
selectedTools: ["read", "dynamic_tool"],
|
||||||
toolSnippets: {
|
toolSnippets: {
|
||||||
@@ -51,6 +51,16 @@ describe("buildSystemPrompt", () => {
|
|||||||
|
|
||||||
expect(prompt).toContain("- dynamic_tool: Run dynamic test behavior");
|
expect(prompt).toContain("- dynamic_tool: Run dynamic test behavior");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("omits custom tools from available tools section when promptSnippet is not provided", () => {
|
||||||
|
const prompt = buildSystemPrompt({
|
||||||
|
selectedTools: ["read", "dynamic_tool"],
|
||||||
|
contextFiles: [],
|
||||||
|
skills: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(prompt).not.toContain("dynamic_tool");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("prompt guidelines", () => {
|
describe("prompt guidelines", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user