mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
Mid conversation system messages (#9548)
This change makes system prompt text and tool changes part of the transcript rather than silently rewriting its starting conditions. This lets Pi record when instructions changed or tools became available, restore that state after resuming or navigating branches, and preserve cached prompt prefixes where the upstream supports it.
This commit is contained in:
parent
6671c60476
commit
9e05370b29
165 changed files with 3512 additions and 2552 deletions
|
|
@ -538,10 +538,13 @@ pi.on("before_agent_start", async (event, ctx) => {
|
|||
// event.systemPrompt - current chained system prompt for this handler
|
||||
// (includes changes from earlier before_agent_start handlers)
|
||||
// event.systemPromptOptions - structured options used to build the system prompt
|
||||
// .customPrompt - any custom system prompt (from --system-prompt, SYSTEM.md, or custom templates)
|
||||
// .customPrompt - exact prompt prefix from --system-prompt, SYSTEM.md, or custom templates
|
||||
// .forceSystemPrompt - optional exact replacement for the complete prompt
|
||||
// .selectedTools - tools currently active in the prompt
|
||||
// .toolSnippets - one-line descriptions for each tool
|
||||
// .promptGuidelines - custom guideline bullets
|
||||
// .toolGuidelines - guideline bullets keyed by tool name
|
||||
// .promptGuidelines - additional custom guideline bullets
|
||||
// .sections - custom XML-wrapped sections keyed by tag name
|
||||
// .appendSystemPrompt - text from --append-system-prompt flags
|
||||
// .cwd - working directory
|
||||
// .contextFiles - AGENTS.md files and other loaded context files
|
||||
|
|
@ -560,7 +563,7 @@ pi.on("before_agent_start", async (event, ctx) => {
|
|||
});
|
||||
```
|
||||
|
||||
The `systemPromptOptions` field gives extensions access to the same structured data Pi uses to build the system prompt. This lets you inspect what Pi has loaded — custom prompts, guidelines, tool snippets, context files, skills — without re-discovering resources or re-parsing flags. Use it when your extension needs to make deep, informed changes to the system prompt while respecting user-provided configuration.
|
||||
The `systemPromptOptions` field gives extensions access to the same structured data Pi uses to build the system prompt. Collections are mutable. Prefer changing `sections`, `selectedTools`, or `promptGuidelines`: Pi diffs the resulting prompt sections against what the model already has and appends one system message patching only the changed sections. Returning `systemPrompt`, or setting `forceSystemPrompt`, replaces the whole prompt with a single untagged `preamble` section. Tool selection changes update both the prompt contributions and executable provider tools; calling `pi.setActiveTools()` inside the handler has the same effect as editing `selectedTools`. Models that accept system messages mid-conversation receive the patch in place and keep their cached prefix; other models get the replayed prompt as their system prompt, which is a cache miss once per change.
|
||||
|
||||
Inside `before_agent_start`, `event.systemPrompt` and `ctx.getSystemPrompt()` both reflect the chained system prompt as of the current handler. Later `before_agent_start` handlers can still modify it again.
|
||||
|
||||
|
|
@ -1125,7 +1128,7 @@ 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 has the same shape and mutability as `before_agent_start` `event.systemPromptOptions`: custom or forced prompt, active tools, tool snippets, per-tool and custom rules, custom sections, appended 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.
|
||||
|
||||
|
|
@ -2370,42 +2373,13 @@ If a slot renderer is not defined or throws:
|
|||
|
||||
### Dynamic Tool Loading
|
||||
|
||||
Extensions can register many tools while keeping only a small initial set active. A tool can then add more tools with `pi.setActiveTools()` during execution. Pi detects purely additive changes, records the newly available tool names on that tool result, and applies the updated active set before the next model request.
|
||||
|
||||
This works with every model. Models with native deferred-loading support preserve the stable prompt prefix and load the new definitions at the tool-result position. Other models use the fallback described below.
|
||||
Extensions can register many tools while keeping only a small initial set active. A tool can then change the active set with `pi.setActiveTools()` during execution. Pi stores the initial prompt and tool loadout in the transcript's first system message, then appends tool and prompt deltas before the next model request. Providers that cannot represent a transition receive a complete transcript checkpoint, which may invalidate the cached prefix.
|
||||
|
||||
The lifecycle is:
|
||||
|
||||
1. Register every tool with `pi.registerTool()` so it appears in `pi.getAllTools()`.
|
||||
2. Keep loader tools, such as `search_tools`, active and leave searchable tools inactive.
|
||||
3. During loader execution, call `pi.setActiveTools([...currentTools, ...matchingTools])`. The change must be additive: do not remove currently active tools in the same call.
|
||||
4. Pi records which tools were added on the loader's tool result.
|
||||
5. Before the next model response, Pi exposes the added definitions using native deferred loading when supported, or the normal active tool list otherwise.
|
||||
|
||||
You do not need to return provider-specific tool references or mark the loader as a special search tool. The active-tool change is the signal. Names passed to `pi.setActiveTools()` must already be registered; unknown names are ignored.
|
||||
|
||||
#### Models with native deferred loading
|
||||
|
||||
- **Anthropic**
|
||||
- **Models:** Sonnet, Opus, Fable version 4.5 or newer (without Haiku)
|
||||
- **Native representation:** Deferred definitions use `defer_loading`; the load point uses `tool_reference` content.
|
||||
- **Fireworks Messages API**
|
||||
- **Native representation:** Deferred definitions use `defer_loading`; the load point uses `tool_reference` content.
|
||||
- **Loader names:** Use `ToolSearch` or `tool_search` for prefix deferral. Other loader names still work, but Fireworks includes the loaded schemas in the initial tool prefix, losing the cache benefit.
|
||||
- This does not change API routing: Fireworks GLM models and Kimi K3 use Chat Completions, not Messages.
|
||||
- **OpenAI**
|
||||
- **Models:** `gpt-5.4` and newer family
|
||||
- **Native representation:** Pi adds completed client `tool_search_call` and `tool_search_output` items at the load point.
|
||||
|
||||
For a verified custom model or proxy, native handling can be enabled with `compat.supportsToolReferences: true` for `anthropic-messages`, or `compat.supportsToolSearch: true` for `openai-responses` and `openai-codex-responses`. Leave these disabled unless the endpoint and model accept the corresponding native protocol.
|
||||
|
||||
#### Fallback behavior
|
||||
|
||||
For all other models and providers, dynamic activation still works: Pi sends the complete current active tool list normally on the next request. The model can call the newly activated tools, but adding their definitions may invalidate the provider's cached prompt prefix.
|
||||
|
||||
Pi also uses this safe fallback when the active set is not purely additive, such as replacing one group of tools with another. Tool removals therefore work, but they do not use deferred loading.
|
||||
|
||||
For the best cache behavior, keep the loader tool active for the whole session and add tools instead of replacing the active set. Also note that activating a tool with `promptSnippet` or `promptGuidelines` rebuilds the system prompt; that system-prompt change can invalidate the prefix even when the provider supports deferred schemas. Lazily loaded tools should usually rely on their tool `description` and omit active-only prompt metadata.
|
||||
3. During loader execution, call `pi.setActiveTools()` with the desired active tool names. Names must already be registered; unknown names are ignored.
|
||||
|
||||
#### Search tool example
|
||||
|
||||
|
|
@ -2507,7 +2481,7 @@ export default function (pi: ExtensionAPI) {
|
|||
}
|
||||
```
|
||||
|
||||
When `search_tools` adds a match, the model receives that definition on the immediately following request. On a native-capable model the definition is anchored after the search result without changing the initial tool-schema prefix. On other models it appears in the normal tool list on that same following request.
|
||||
When `search_tools` adds a match, the model receives the complete updated tool list on the immediately following request.
|
||||
|
||||
## Custom UI
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue