pi-agenticoding is a Pi extension. It registers tools and hooks into the agent lifecycle, and keeps session state in one AgenticodingState instance.
| Hook | Role |
|---|---|
before_agent_start |
Refreshes Model Groups, resolves deferred readonly frontmatter, then injects the context-management primer, names-only group guidance, and live notebook index |
context |
Advisory watchdog reminders when context is elevated; readonly toggle nudges |
input |
Resolves model-selection frontmatter during idle input; blocks it during streaming; queues readonly resolution |
tool_call |
Readonly blocks write/edit/unguarded bash; blocks handoff unless a requested bypass is active |
session_start |
Reconstructs notebook pages/epoch/watermark from the active branch and rehydrates readonly state; loads and validates Model Groups, registers group autocomplete, reports config issues, and resets session state on /new |
session_tree |
Invalidates branch-local handoff work, reconstructs notebook pages/epoch/watermark from the newly active branch, rehydrates readonly state, refreshes indicators |
turn_end |
Updates TUI indicators (context %, notebook count, topic, readonly) |
agent_end |
Records last context usage percent; handoff enforcement cleanup |
session_before_compact |
Consumes the pending handoff task and sets it as the compaction summary |
interface AgenticodingState {
notebookPages: Map<string, string>
activeNotebookTopic: string | null
activeNotebookTopicSource: "human" | "agent" | null
pendingTopicBoundaryHint: { from, to } | null
readonlyEnabled: boolean
modelGroups: {
groups: ResolvedModelGroup[]
validation: ModelGroupsBootValidation | null
}
epoch: number
discardEpochWatermark: number
lastContextPercent: number | null
pendingHandoff: { task, source } | null
pendingRequestedHandoff: { direction, resumeReadonlyAfterHandoff, ... } | null
childSessions: Map<string, AgentSession>
liveChildSessions: Map<string, AgentSession>
childSessionEpoch: number
}Spawn — Without a Model Group, the child inherits the parent's public model and explicit/default thinking level. An exact known group randomly selects a registry-resolved, authenticated entry; an entry-specific thinking level overrides explicit/inherited thinking and is clamped for the selected model. An unknown group reports fallback and uses the parent model/thinking. A known empty group or one with no usable authenticated entries fails before child-session creation. The selected public model enters a child-owned runtime. Children also inherit cwd and active registered tools executable in that session, retain child-local notebook tools, cannot spawn or handoff, and inherit readonly posture.
Model Groups — /model-groups manages versioned global and trusted-project JSON configuration. Project groups shadow same-named global groups. Configuration is loaded and validated against Pi's model registry into the modelGroups snapshot; only names are injected into the agent prompt. Routing uses the parent registry only to select configured/authenticated entries—the registry/auth objects are not passed into the child runtime.
Notebook — Agent-curated named pages scoped to the active session branch, not a long-lived memory product. Stored as session custom entries so pages survive handoff and resume of the same work stream; /new (fresh session) clears them with the conversation. The visible pages and the committed generation epoch follow the branch the user navigated to: /tree reconstruction rehydrates from the newly active branch, so branches diverge without cross-contamination and returning to an earlier branch restores its state; writes land on the current branch's generation. Discard is transactional — survivors are staged at the next epoch and committed only on successful handoff compaction — and the epoch high-water mark is derived from the branch during reconstruction, so a failed attempt can never resurrect staged pages after a restart. Active topic (notebook_topic_set or /notebook <topic>) frames spawn-vs-handoff preference; human-set topics are authoritative. Topic clears after a successful handoff.
Handoff — Requires a real prompt and a meaningful context load (rejects empty prompts, very small sessions, or missing usage). Notebook bodies are not inlined into the prompt; the next context in this work stream fetches pages by name. Under readonly, handoff is blocked unless the user runs /handoff or crosses an eligible human topic boundary; readonly can resume after compaction. Compaction replaces the prior transcript with the prompt: the next turns see a small context again (quality), and providers start a new input prefix for billing/cache (the dropped history is no longer in that prefix). Spawn runs children in separate context so their token use does not permanently inflate the parent. This extension does not configure provider cache TTLs or breakpoints.
Skill and prompt frontmatter — Interactive TUI invocations resolve model selection during idle input; headless/RPC invocations ignore it. readonly: true|false alone is deferred to before_agent_start, where complete skill metadata is available.
model: <provider>/<model-id>selects a configured, authenticated model. Only the first slash separates the provider, so model IDs may contain slashes.model-group: <group-name>routes through a configured Model Group. An explicitmodeltakes precedence.thinking: off|minimal|low|medium|high|xhigh|maxis capability-clamped for the selected or current model and overrides group thinking.
A failed model change visibly blocks command expansion and records no success entry. During streaming steer/follow-up, commands with model-selection frontmatter are visibly blocked without model or thinking mutation; commands without it continue. Invalid frontmatter is ignored with a TUI warning.
Readonly — Session-persisted research posture. Toggle via /readonly, Ctrl+Shift+R, or --readonly. Write/edit always blocked at the tool boundary. Bash uses a two-layer guard:
| Platform | Enforcement |
|---|---|
| macOS | OS sandbox via sandbox-exec (Seatbelt) — kernel denies file writes outside the OS temp dir; classifier is secondary |
| Linux | OS sandbox via bwrap when available — read-only root + writable temp; classifier is secondary. Without bwrap, classifier only |
| Windows | No OS / syscall-level sandbox. canUseOsSandbox() is always false. Only the shell-command classifier runs — best-effort pattern matching. Known gaps: interpreter one-liners (node -e, python -c, …), piped indirection (xargs, etc.). Not true write protection. |
Coding-agent guardrail on every OS — not a hardened security boundary. Strongest on macOS/Linux with sandbox binaries present; weakest on Windows. UI-oriented toggle; state rehydrates on resume.
Watchdog — Band-throttled advisory reminders as context crosses practical pressure bands; high-usage TUI widget reinforces spawn-vs-handoff guidance (topic- and readonly-aware).
| Area | Role |
|---|---|
index.ts |
Extension entry: tools, hooks, wiring |
spawn/ |
Child sessions and live TUI rendering |
model-groups/ |
Persistence, boot validation, CRUD TUI/autocomplete, and spawn routing |
notebook/ |
Page store, tools, topic, rehydration |
handoff/ |
Eligibility, prompt, compaction bridge |
readonly-*.ts / os-sandbox.ts |
Readonly posture, bash policy, sandbox |
watchdog.ts / tui.ts / state.ts |
Pressure advisories, status UI, shared state |