Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions packages/provider-pi/src/server/mcpBridge.testkit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,15 @@ interface RegisteredTool {
}

type AgentStartHook = (
event: { systemPrompt: string },
event: {
systemPrompt: string;
systemPromptOptions?: { sections?: Record<string, string> | null; forceSystemPrompt?: string };
},
ctx: {
model?: { api: string };
ui: { notify: (message: string, severity: string) => void };
},
) => Promise<{ systemPrompt: string }>;
) => Promise<{ systemPrompt: string } | undefined>;

export async function loadMcpBridge(
options: {
Expand Down
53 changes: 52 additions & 1 deletion packages/provider-pi/src/server/mcpExtensionSource.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,57 @@ async function loadHooks(commands: ReadonlyArray<SkillCommand> = []) {
return handlers;
}

describe("Pi orchestration prompt section", () => {
it("adds a stable section without overriding the prompt or skipping discovery", async () => {
const bridge = await loadMcpBridge({ modern: true, toolSearchAvailable: true });
const hook = bridge.handlers.get("before_agent_start")!;
const sections: Record<string, string> = { existing: "keep me" };
const event = { systemPrompt: "base", systemPromptOptions: { sections } };
const ctx = { model: { api: "openai-completions" }, ui: { notify: assert.fail } };

assert.isUndefined(await hook(event, ctx));
assert.include(sections.t3_code_orchestration, "orchestrator_capabilities");
assert.equal(event.systemPrompt, "base");
assert.include(bridge.getActiveTools(), "mcp__t3-code__preview_snapshot");

const guidance = sections.t3_code_orchestration!;
assert.isUndefined(await hook(event, ctx));
assert.deepEqual(event.systemPromptOptions.sections, {
existing: "keep me",
t3_code_orchestration: guidance,
});
});

it.each([
{
name: "on Pi builds without prompt sections",
events: [
{ systemPrompt: "base" },
{ systemPrompt: "base", systemPromptOptions: {} },
{ systemPrompt: "base", systemPromptOptions: { sections: null } },
],
},
{
name: "when an earlier extension forces the system prompt",
events: [
{
systemPrompt: "forced",
systemPromptOptions: { sections: {}, forceSystemPrompt: "forced" },
},
],
},
])("appends guidance $name", async ({ events }) => {
const bridge = await loadMcpBridge();
const hook = bridge.handlers.get("before_agent_start")!;
for (const event of events) {
const result = await hook(event, { ui: { notify: assert.fail } });
assert.isDefined(result);
assert.isTrue(result!.systemPrompt.startsWith(`${event.systemPrompt}\n\n`));
assert.include(result!.systemPrompt, "orchestrator_capabilities");
}
});
});

describe("Pi MCP tool results", () => {
it("returns mixed screenshot blocks and typed script output without repeating structured text", async () => {
const content = [
Expand Down Expand Up @@ -143,7 +194,7 @@ describe("Pi MCP tool exposure", () => {
const start = bridge.handlers.get("before_agent_start");
assert.isDefined(start);
const prompt = await start!({ systemPrompt: "Pi system prompt" }, discoveryContext);
assert.include(prompt.systemPrompt, "orchestrator_capabilities");
assert.include(prompt?.systemPrompt ?? "", "orchestrator_capabilities");
assert.equal(bridge.servers.length, 0);
assert.equal(bridge.tools.length, 8);
assert.deepEqual(bridge.getActiveTools(), ["read", "tool_search"]);
Expand Down
10 changes: 7 additions & 3 deletions packages/provider-pi/src/server/mcpExtensionSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,11 +448,15 @@ export default async function t3McpExtension(pi: ExtensionAPI) {
}
});

// Deliver orchestration guidance through pi's real system-prompt channel.
// Wrapping the first user message instead would stop it from starting
// with "/" and silently break slash-command expansion.
// Keep guidance in the system prompt so leading slash commands still expand.
pi.on("before_agent_start", (event, ctx) => {
reconcileDiscovery(ctx);
const sections = event.systemPromptOptions?.sections;
if (sections && typeof sections === "object" && event.systemPromptOptions?.forceSystemPrompt === undefined) {
// A full prompt override bypasses Pi's later section rendering.
sections.t3_code_orchestration = ORCHESTRATION_INSTRUCTIONS;
return undefined;
}
return { systemPrompt: event.systemPrompt + "\\n\\n" + ORCHESTRATION_INSTRUCTIONS };
});
}
Expand Down
Loading