mirror of
https://github.com/earendil-works/pi.git
synced 2026-06-18 15:54:04 +08:00
Merge pull request #5306 from xl0/system_prompt_options
Add system prompt options to extension commands
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
### Added
|
||||
|
||||
- Added `ctx.mode` to extension contexts so extensions can distinguish TUI, RPC, JSON, and print mode.
|
||||
- Added `ctx.getSystemPromptOptions()` for extension commands to inspect the current base system prompt inputs.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -982,6 +982,19 @@ pi.on("before_agent_start", (event, ctx) => {
|
||||
|
||||
Command handlers receive `ExtensionCommandContext`, which extends `ExtensionContext` with session control methods. These are only available in commands because they can deadlock if called from event handlers.
|
||||
|
||||
### ctx.getSystemPromptOptions()
|
||||
|
||||
Returns the base inputs Pi currently uses to build the system prompt.
|
||||
|
||||
```typescript
|
||||
const options = ctx.getSystemPromptOptions();
|
||||
const contextPaths = options.contextFiles?.map((file) => file.path) ?? [];
|
||||
```
|
||||
|
||||
This has the same shape and mutability as `before_agent_start` `event.systemPromptOptions`: custom prompt, active tools, tool snippets, prompt guidelines, appended system prompt text, cwd, loaded context files, and loaded skills. It may include full context file contents, so treat it as sensitive extension-local data and avoid exposing it through command lists, logs, or autocomplete metadata.
|
||||
|
||||
This reports the current base prompt inputs. It does not include per-turn `before_agent_start` chained system-prompt changes, later `context` event message mutations, or `before_provider_request` payload rewrites.
|
||||
|
||||
### ctx.waitForIdle()
|
||||
|
||||
Wait for the agent to finish streaming:
|
||||
|
||||
@@ -2264,6 +2264,7 @@ export class AgentSession {
|
||||
})();
|
||||
},
|
||||
getSystemPrompt: () => this.systemPrompt,
|
||||
getSystemPromptOptions: () => this._baseSystemPromptOptions,
|
||||
},
|
||||
{
|
||||
registerProvider: (name, config) => {
|
||||
|
||||
@@ -240,6 +240,7 @@ export class ExtensionRunner {
|
||||
private getContextUsageFn: () => ContextUsage | undefined = () => undefined;
|
||||
private compactFn: (options?: CompactOptions) => void = () => {};
|
||||
private getSystemPromptFn: () => string = () => "";
|
||||
private getSystemPromptOptionsFn: () => BuildSystemPromptOptions = () => ({ cwd: this.cwd });
|
||||
private newSessionHandler: NewSessionHandler = async () => ({ cancelled: false });
|
||||
private forkHandler: ForkHandler = async () => ({ cancelled: false });
|
||||
private navigateTreeHandler: NavigateTreeHandler = async () => ({ cancelled: false });
|
||||
@@ -299,6 +300,7 @@ export class ExtensionRunner {
|
||||
this.getContextUsageFn = contextActions.getContextUsage;
|
||||
this.compactFn = contextActions.compact;
|
||||
this.getSystemPromptFn = contextActions.getSystemPrompt;
|
||||
this.getSystemPromptOptionsFn = contextActions.getSystemPromptOptions ?? (() => ({ cwd: this.cwd }));
|
||||
|
||||
// Flush provider registrations queued during extension loading
|
||||
for (const { name, config, extensionPath } of this.runtime.pendingProviderRegistrations) {
|
||||
@@ -648,6 +650,10 @@ export class ExtensionRunner {
|
||||
{},
|
||||
Object.getOwnPropertyDescriptors(this.createContext()),
|
||||
) as ExtensionCommandContext;
|
||||
context.getSystemPromptOptions = () => {
|
||||
this.assertActive();
|
||||
return this.getSystemPromptOptionsFn();
|
||||
};
|
||||
context.waitForIdle = () => {
|
||||
this.assertActive();
|
||||
return this.waitForIdleFn();
|
||||
|
||||
@@ -335,6 +335,9 @@ export interface ExtensionContext {
|
||||
* Includes session control methods only safe in user-initiated commands.
|
||||
*/
|
||||
export interface ExtensionCommandContext extends ExtensionContext {
|
||||
/** Get the current base system-prompt construction options. */
|
||||
getSystemPromptOptions(): BuildSystemPromptOptions;
|
||||
|
||||
/** Wait for the agent to finish streaming */
|
||||
waitForIdle(): Promise<void>;
|
||||
|
||||
@@ -1506,6 +1509,7 @@ export interface ExtensionContextActions {
|
||||
getContextUsage: () => ContextUsage | undefined;
|
||||
compact: (options?: CompactOptions) => void;
|
||||
getSystemPrompt: () => string;
|
||||
getSystemPromptOptions?: () => BuildSystemPromptOptions;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { AgentTool, ThinkingLevel } from "@earendil-works/pi-agent-core";
|
||||
import { fauxAssistantMessage, fauxToolCall, type Model } from "@earendil-works/pi-ai";
|
||||
import { Type } from "typebox";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import type { ExtensionAPI } from "../../src/index.ts";
|
||||
import type { BuildSystemPromptOptions, ExtensionAPI } from "../../src/index.ts";
|
||||
import { createHarness, getAssistantTexts, type Harness } from "./harness.ts";
|
||||
|
||||
describe("AgentSession model and extension characterization", () => {
|
||||
@@ -260,6 +260,34 @@ describe("AgentSession model and extension characterization", () => {
|
||||
expect(extensionApi).toBeDefined();
|
||||
});
|
||||
|
||||
it("allows extension commands to inspect live system prompt options", async () => {
|
||||
const seenOptions: BuildSystemPromptOptions[] = [];
|
||||
const harness = await createHarness({
|
||||
extensionFactories: [
|
||||
(pi) => {
|
||||
pi.registerCommand("inspect-options", {
|
||||
description: "Inspect system prompt options",
|
||||
handler: async (_args, ctx) => {
|
||||
const options = ctx.getSystemPromptOptions();
|
||||
seenOptions.push(options);
|
||||
options.selectedTools?.push("mutated_tool");
|
||||
},
|
||||
});
|
||||
},
|
||||
],
|
||||
});
|
||||
harnesses.push(harness);
|
||||
|
||||
await harness.session.prompt("/inspect-options");
|
||||
await harness.session.prompt("/inspect-options");
|
||||
|
||||
expect(seenOptions).toHaveLength(2);
|
||||
expect(seenOptions[0]).toBe(seenOptions[1]);
|
||||
expect(seenOptions[0]?.cwd).toBe(harness.tempDir);
|
||||
expect(seenOptions[0]?.selectedTools).toContain("read");
|
||||
expect(seenOptions[1]?.selectedTools).toContain("mutated_tool");
|
||||
});
|
||||
|
||||
it("allows before_agent_start handlers to inject custom messages and modify the system prompt", async () => {
|
||||
const harness = await createHarness({
|
||||
extensionFactories: [
|
||||
|
||||
Reference in New Issue
Block a user