Repository navigation
Where should a package put a one-shot pre-turn routing decision? (before_agent_start with async sidecar I/O) #10841
hoangngochuong24947-gif
started this conversation in
General
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Context
We maintain MatPilot, a Pi package (
@earendil-works/pi-coding-agent@1.1.0) for materials research. We want to add a routing layer: one cheap classification per user prompt (a call to a Python sidecar runningsemantic-router, budgeted well under 100ms) that decides which research path the prompt belongs to, so we can hand the model a narrower skill/tool context.Our hard constraints are the ones your docs already push us toward: MatPilot does not own a second agent loop, does not poll, and does not build a scheduler. So this has to be a per-turn decision made inside an existing hook — not a background task.
From
dist/core/extensions/types.d.tswe're working against:BeforeAgentStartEvent = { type, prompt, images?, readonly systemPrompt, systemPromptOptions }(:708-718)BeforeAgentStartEventResult = { message?, systemPrompt?: string }(:1101-1105)Questions
1. Is
before_agent_startthe right seam, and how often does it fire?It reads as "once per agent loop". Does it also fire for follow-up/steer messages, and again after compaction or a retry? We want the routing hint re-evaluated at most once per real user prompt — if it re-fires on every internal retry, we'd pay the sidecar round-trip repeatedly for the same prompt.
2. Async I/O inside the handler.
ExtensionHandlerallows async. If abefore_agent_starthandler awaits a network call to our sidecar: is there a timeout imposed by Pi, and what happens if the handler throws or the promise rejects — does the turn abort, or does Pi continue without the result? Our design is to fail open (returnundefined, inject nothing), and we'd like to confirm that's the actual contract rather than assume it.3. Mutating
systemPromptOptionsvs returning a replacementsystemPrompt.The event exposes a mutable
systemPromptOptions: NormalizedBuildSystemPromptOptions, while the result exposessystemPrompt?: string, documented as "Replace the complete system prompt for this turn". Today we append by string-concatenatingevent.systemPromptand returning it — seesrc/agent/research-extension.ts:79.Is mutating
systemPromptOptionsthe preferred way to add a section (so later handlers, ordering, and any caching behave correctly), with the string replacement reserved for cases where you genuinely need to override everything?All reactions