# Viewer reference

Detailed views, data provenance, bounds and security for `pi-inspect`.

## Views and data provenance

The primary tabs below the header are **Context**, **Session**, **Cache** and **Environment**. Session has History, Branch view and Captured executions secondary tabs. Environment has Tools, MCP Servers and Skills secondary tabs. Each tab list supports Left/Right and Home/End with one keyboard tab target and an associated content panel. Environment opens Tools on first use and retains its selected resource tab, scroll and disclosure state across navigation.

**Context composition is the primary view.** A warm off-white document layout uses muted, consistent system/developer, user, assistant, tool-call, tool-result and neutral colors. Each ordered message or supported content block is an expandable row; assistant thinking/text/tool calls remain distinct. Desktop details place source metadata beside content, with lightweight structured/metadata/raw accordions. Copy acts on captured display data; the raw message and original role remain available. Missing segment token sizes are explicitly unavailable: persisted assistant totalTokens is usage, not the size of an individual context block.

A narrow right-hand minimap summarizes the same filtered segment sequence in up to 160 contiguous buckets, with category proportions and the actual viewport region. Clicking a bucket jumps/focuses its first segment. Variable-height virtualization renders the viewport plus five-row overscan on each side; measured heights and source-ID anchoring preserve older scroll while expanding or appending. Search covers available bounded captured message content, not uncaptured data. Category filters and source-mode navigation retain row expansion; keyboard ArrowUp/ArrowDown/Home/End navigate and Enter/Space or Left/Right control disclosure. Light is the default; dark and responsive inline details remain available.

**Source distinction is explicit.** After consent, context_with_system observes Pi messages after conversation-only context handlers and prompt/tool restoration. It returns nothing and does not modify the event. The primary view labels this as the last observed Pi-stage context, including its leaf/time when available; later context_with_system hooks, provider conversion and payload hooks can still differ. The advanced provider-payload copy is an independently timestamped hook-stage observation, not a guaranteed final transport body. Public callbacks have no request identity proving turn ownership: cache-warming and retry bodies may appear, so they are never attributed to the context leaf or observation time. Until a request is observed, the native active-leaf projection is labeled session-derived, not exact model input. Compaction/context edits use native Pi semantics, not the full session log. Session events and execution events are never silently appended to context.

**Context and Session have independent selection and reading state.** Context is full-width: it never shows Session filters, Event details or an execution list. The current effective prompt remains under **Current runtime information** and may not yet have been sent to a model. Full tool and skill catalogs are in Environment, not mixed into historical event evidence. Resource catalogs mount on first visit; closed schema/Data bodies do not mount recursive JSON or text previews, while bounded Copy remains available. JSON object disclosure state is retained within the existing bounded scope cache.

**Session** answers what happened, with one primary navigation area and a content-first **Event details** panel. **History** defaults to List and follows the active branch; entries are ordered by recorded ancestry, not timestamps, and grouped by user turn. Entries before the first user turn have their own group. Groups are reading aids, not execution parentage. User/assistant/tool summaries come from bounded redacted content; truncation is labeled. Missing leaves/parents and cycles produce explicit partial-history notices, never repaired links.

**Show internal events** includes generic Pi bookkeeping: custom state entries, labels, session metadata, and usage entries. Classification uses the original entry type, not a private extension name or message role; model-visible custom messages, including legacy messages with role `custom`, remain visible. Errors, compaction, branch summaries, system/context changes, model/thinking changes, and unknown types are not silently hidden. Matching hidden internal records are counted, excluding selected records retained visibly by the navigation exception. History searches show direct matches without every ancestor. Search covers the bounded summary/label inventory, not uncaptured full entry content.

**Branch view** replaces the duplicate sidebar tree. It retains actual session-entry `parentId` links and access to off-branch entries. Its filters retain actual ancestors, visibly marked as context; child counts are visible counts, not persisted totals. **History at selected entry** explicitly changes the browser's history scope; **Follow active branch** restores automatic active-leaf tracking. Neither changes Pi's leaf. A selection outside the displayed branch is explicitly labeled. Related-entry navigation can reveal an entry outside filters without clearing them; changing filters ends that exception.

Clicking a log row selects it without changing disclosure; its chevron toggles disclosure without selecting. Enter selects, Space toggles, ArrowUp/ArrowDown move focus, ArrowRight expands or enters a child in Branch view, and ArrowLeft collapses or moves to the recorded parent. Multiple rows can be expanded; collapsing a parent retains descendant expansion state. Inline Overview/Content/Raw/Metadata sections lazily load bounded recorded text/output, tool arguments, usage, and structured JSON. Complete inspection is available in Event details.

History and Branch pages contain at most 50 visible rows, with one visible keyboard tab target; ArrowUp/ArrowDown can cross pages. Branch ancestry traversal is iterative and uncapped by visual depth: page-local/scaled indentation preserves absolute ARIA levels and original parents. Initial reveal does not fetch every ancestor's details. Session search and All/Model/Tool/Custom filters appear above the result list; advanced conditions expand inline without a filter drawer or duplicate title. Event details is a collapsible, resizable desktop panel; pointer dragging and focused-separator ArrowLeft/ArrowRight/Home/End resize it. Cancellation, Escape, blur and unmount release drag listeners. Narrow layouts use one focus-trapped Event details drawer with Escape dismissal and focus restoration. Entry selection opens the panel; its toggle belongs to Session, and closing it preserves selection. Crossing the responsive breakpoint preserves the open selection without duplicate inspectors. Leaving Session cancels its owned detail/branch requests; a dismissed drawer does not reopen when returning. View choices, filters, widths and disclosure are ephemeral, not saved settings.

Entry and branch failures have separate visible retries; unavailable historical checkpoints remain distinct from request failures. Live snapshots do not clear a failed selected-branch alert. Inline loads use four concurrent requests and a 64-entry immutable historical-detail cache; collapse, inactive views, disposal, and terminal session failures cancel owned requests. Historical section JSON disclosure is isolated by entry/section scope within a bounded 128-scope cache. Existing capture bounds still apply; UI expansion cannot recover omitted data.
Malformed entry/parent IDs, non-string type/role/timestamp fields and all duplicated entry identities are excluded from navigation without manufacturing replacement IDs. Duplicate ancestor/detail identities are explicitly unavailable rather than selecting the last indexed occurrence. The overview labels the count and exposes up to 20 indexed raw diagnostic samples, each bounded to 2 KiB-equivalent characters; source entries are unchanged. Invalid leaf identities cannot become a browser selection or captured transcript anchor.

A session-owned structural index is rebuilt on structural changes and released on stop/replacement. Inline requests reuse it and project only their validated ancestor path, not every off-branch entry; valid context-edit/compaction replay remains the native Pi result.

Before historical replay, ancestry is validated with a visited set and a 10,000-entry inspection budget. Recorded parent cycles, missing parents or a budget overflow keep the raw entry and original parent links visible but mark projection unavailable; the inspector never asks Pi to traverse those malformed paths or mutates the session to repair them.

The compact overview distinguishes persisted entries, indexed assistant messages/tokens/log errors and captured calls. Recorded span is the indexed log timestamp range, not execution duration. Tokens are persisted assistant `totalTokens`, excluding compaction/auxiliary usage; inline input/output bars appear only when recorded usage supplies both values, with cache excluded explicitly. Missing metrics are omitted or neutral. No model durations, transport latency, p95 or request totals are invented.

Each captured execution has a unique `occurrenceId`; `id` remains the provider/native raw tool-call ID. Repeated raw IDs retain separate history and occurrence-parent links. Transcript correlation requires the recorded owning-assistant anchor, not raw ID alone; overlapping invalid IDs are explicitly marked unavailable rather than assigning an ambiguous result. Evicting a selected occurrence returns selection to its remembered recorded anchor (or current leaf).

**Captured executions** is an explicit Session view, not a drawer attached to Context. Running/error counts remain visible in Session navigation without switching the user's view on arrival. Rows use occurrence-parent relationships; chevrons expand child navigation only. Arguments, results, relationships, correlation availability, reported duration and observed times appear once in the selection-owned Event details panel. Root calls have no recorded parent; unresolved or invalid parent evidence is labeled unavailable. Correlation requires a recorded anchor and unambiguous IDs, not merely the absence of an error flag. Related execution buttons navigate real parent/child occurrences; **Inspect recorded anchor** returns to Branch view only when that anchor is represented. Eviction returns selection to its represented remembered anchor or current leaf; it switches Captured executions to Branch view but preserves History when already reading it.

Timeline is an optional shared-axis view. Log timestamps are points, not duration spans. `observedStartedAt`/`observedEndedAt` are wall-clock samples taken on entry to this collector\'s actual start/end callbacks, after activation; their intervals are labeled observations, not provider transport timing. Public `durationMs` remains the separate monotonic time spent in `execute()`, absent if it did not run. Missing starts are never synthesized from durations; update/end-only, running or reversed-clock records do not become closed spans. Earlier callbacks or framework work can separate observation intervals from reported execution durations.

**Event details** defaults to Content: literal recorded message blocks, thinking, tool arguments/results, or internal-event payload. Metadata and Raw JSON are secondary; historical Prompt/Tools/Context/Skills/Codemode remain accessible in a horizontally scrollable tab strip. Live executions disable historical transcript tabs; their recorded anchor can be inspected separately. Inapplicable selections fall back to Content. Historical prompt sections, updates, previous-node diffs, declared tools, selected-entry contribution, and skill-read evidence never borrow the current runtime inventory as historical evidence. Full IDs remain in metadata/copy while identity headings use short IDs. Technical JSON can scroll within the pane without page-wide overflow.

**View branch context**, in Event details’ Context tab, opens the same composition reader with a **Session-derived preview** identity and selected leaf. Native Pi compaction/context edits apply, but request-local hooks and provider serialization remain unknown: this is not a captured historical request. Missing checkpoints or invalid ancestry remain explicitly unavailable. The preview has no unrelated provider observation or current runtime inventory. **Back to last observed context** restores the observed reader and its independent search/disclosure/scroll state.

| Capability | Location | Provenance / limitation |
| --- | --- | --- |
| Ordered model input, search, minimap, inline message/metadata/raw/copy | Context | Last observed Pi context; explicit session-derived active-leaf fallback until observed |
| Current effective prompt | Context → Current runtime information | Current runtime prompt; may not yet be sent |
| Cache tokens, weighted hit rate, trend, comparisons and estimated cost | Cache → Recorded usage → Event details | Active branch persisted usage; at most 200 rows and 32 conversational trend points; unknown accounting stays unavailable |
| Tool metadata, schemas, sources and exposure | Environment → Tools | Current public tool catalog; active ≠ provider-visible; namespace/source metadata does not prove server ownership |
| Extension-registered MCP server names | Environment → MCP Servers | Partial registration catalog; complete configuration, connection/auth health and authoritative tool ownership unavailable |
| Startup skill metadata and paths | Environment → Skills | Catalog captured at inspector startup, not continuously refreshed; content and current file availability unavailable |
| Provider payload | Context → Advanced independent provider observation | Independent time; request/leaf association unavailable, including warming/retries |
| Conversation turns, summaries, internal entries, List/Timeline | Session → History | Selected branch ancestry; original entries unchanged; up to 50 visible rows per page |
| Roots, branches, labels, unknown/malformed ancestry | Session → Branch view and overview diagnostics | Up to 10,000 nodes reserving active ancestry; original parent relationships, not call parentage |
| Historical prompt, sections, updates, previous-node diff | Event details → Prompt | Pi replay from stored checkpoints; missing historical checkpoints unavailable |
| Historical declared tools and skill invocation/read evidence | Event details → Tools / Skills | Branch-local stored evidence; old catalogs and model compliance unknown |
| Branch projection and selected-entry contribution | Event details → Context → View branch context | Explicit native session-derived preview, not final HTTP input |
| Script source, printed output, persisted nested metadata | Event details → Codemode and Raw | Historical child results are not saved by Pi; discovery/model helpers unavailable unless printed |
| Live nested arguments/results, statuses, occurrence IDs, observed intervals | Session → Captured executions → Event details | Session-wide public execution events; no invented Promise graph or request association |

Timeline uses log timestamps as points and observed execution callbacks as intervals, never invented model/transport spans. History/List and History/Timeline show the same branch scope. The timing axis includes the indexed session and captured observations for comparison; it does not infer execution causality from adjacent log rows.
Live results come from public `tool_execution_end` events, after the runtime's tool-result transformation pipeline (verified with a real nested call and a transforming hook). Partial updates are provisional. Unknown tools, schema failures, and permission blocks still have nested execution events. Direct transcript results remain authoritative persisted history. The inspector observes only: its context_with_system and before_provider_request handlers return nothing and never mutate messages or payloads; it registers no result-transforming handler.

Codemode discovery helpers (`searchTools`, `describeTool`, `describeNamespace`, catalog helpers), sandbox local variables, and internal Promise scheduling are not normal child-tool events. `models.classify` and `models.generateImages` are also not normal nested tools: their complete input/output is unavailable unless printed. The generic Raw tab can show documented persisted metadata but the inspector never imports another extension or depends on private codemode implementation types.

## Cache usage

Cache follows Pi's active branch independently of browser history selection. It shows weighted branch totals, a bounded conversational hit-rate trend and expandable recorded-usage rows with Event details links. Summary and warming usage stay separate from conversational comparisons. Native disclosure controls support keyboard use; labels render literally in both themes and narrow layouts. The [cache history contract](./cache-history.md) defines formulas, unknown values, estimates, record classes, bounds and refresh behavior. It is a persisted-usage view, not a streaming or transport-attempt trace.

## Environment resource evidence

Tools uses public `getAllTools()` metadata with source, path, scope and origin from `sourceInfo`. Missing source metadata is unavailable. Exposure, active and callable are separate fields; they do not establish provider-visible declarations or connection health. Schemas and source copies expand only on request.

MCP Servers uses public `getMcpServers()`, which returns extension registrations only. Names appear even when there are no tools; empty registrations do not prove that no configured servers exist. Connecting, failed, overridden and disconnected registrations cannot be distinguished through this API. Arbitrary tool namespaces do not create server records, and tool names are not used to infer ownership. No commands, URLs, environment variables, headers, OAuth fields or configuration errors are published. Registration API failures produce a fixed unavailable marker, not exception text.

Skills uses the metadata catalog captured from `getSystemPromptOptions().skills` when the inspector starts. Names, descriptions and paths are shown; duplicate names remain separate by their catalog position/path. A path is source evidence, not proof that a file still exists. Skill content is explicitly unavailable: no file read, symlink traversal or browser path endpoint is added. Advertised or read does not prove model compliance. Historical skill-read evidence remains in Event details, not in this catalog.

| Data | Observation and refresh | Missing or limited data |
| --- | --- | --- |
| Tool metadata, source and schema | Snapshot; reconciled through the existing one-second public metadata check | Missing source marked unavailable; metadata/schema truncation labeled |
| MCP registrations | Snapshot; names reconciled through the existing one-second check | At most 256 registrations; complete configured catalog, health and tool ownership unavailable |
| Skill catalog | Inspector startup; capture start time shown | At most 256 entries; content/current file availability unavailable; restart the inspector to recapture |

Disconnected browser status does not change MCP health to disconnected: it refers to the inspector transport, not an MCP connection.

## Bounds and security

- The server binds only `127.0.0.1` on a random port, checks Host and Origin, rejects cross-site requests and non-GET methods, and authenticates every data/SSE request with a random per-session token and generation.
- The URL is at most 68 characters and printed on its own line for manual opening. Its fragment contains the full 256-bit token in Base64url, not a query or HTTP request; the data-free HTML supplies the public generation identifier. Legacy token/generation fragments remain supported. The browser keeps credentials in memory, attempts to save them in origin-scoped sessionStorage, and removes the fragment from history. Storage getter/read/write failures do not block a valid compact or legacy private URL; without saved credentials, fragment-free reloads require reopening from Pi. The launch notification necessarily contains the private credential URL for manual opening; it is not an API key. No request logs are written.
- A data-free shell and bundled JS/CSS are publicly readable on that loopback port; session data requires authentication. There is no CORS grant, arbitrary file read, attachment path endpoint, or upstream credential/header/environment API.
- Structured previews use native object/array disclosure. JSON text and plain content are literal with line numbers and bounded coloring (at most 1,000 lines and 512 colored tokens); long lines fall back to plain text. Copy acts only on the bounded redacted display value, including lines omitted by the local preview cap, and reports success or denial. Pending clipboard feedback is ignored after its owning component is disposed; the browser clipboard API itself cannot be aborted.
- CSP blocks remote scripts, connections, frames, fonts, and images. Tool text and Markdown are shown literally, not as executable HTML. Only small validated PNG/JPEG/GIF/WebP base64 blocks can render as images; SVG, remote images, opaque signatures, and large image payloads are omitted. No filesystem attachment is opened automatically.
- The collector keeps 128 records, at most 8 KiB-equivalent argument characters and 32 KiB-equivalent result characters per call, with depth/node limits. Tool schemas are bounded, inventory limits are 256 tools/skills/MCP registrations, tool source copies use 2 KiB-equivalent characters, names/descriptions/namespaces use 512 characters and skill paths use 2,048 characters, prompt/raw/projection fields are bounded to 64 KiB-equivalent characters, and evictions/truncation are visible. These are character/traversal budgets, not exact serialized-byte caps; JSON overhead adds size.
- Context observation is consent-gated, in-memory and discarded on stop/replacement/reload/shutdown. It captures at most 4,000 segments, up to 8 KiB-equivalent characters per message and approximately 1 MiB-equivalent serialized message characters overall; provider-payload copies use 64 KiB-equivalent characters. Bounds, omitted content and source-stage limitations are labeled. Expanded Raw/Copy can reveal all captured text beyond local preview limits, but cannot recover content excluded by capture bounds.
- SSE sends revision invalidations rather than result payloads. Notifications and intermediate partial copies are sampled/coalesced to at most one per 250 ms; final results are always captured. Live-only requests reuse the static snapshot rather than re-summarizing history. An owned 1-second reconciliation checks the public SDK entry count (when exposed), leaf/name, bounded public tool/source metadata and MCP registration names for actions without extension change events, without rebuilding context projections or copying idle history on SDK managers. Readonly adapters lacking that public count method fall back to getEntries. At most eight stream clients and 32 sockets are accepted. A backpressured stream is closed, and reconnect triggers a fresh snapshot. Viewer-capacity responses (429), transient server failures and offline transport retry after one second. Snapshot request failures also retry with an owned one-second timer even when SSE is quiet; authentication/closed-session/generation responses do not retry. Authentication, closed-session, generation and protocol failures stop the connection and require reopening from Pi. Pending fetches, readers, reconnect timers, reconciliation/coalescing timers, browser-launch tasks, and session resources are cancelled on their owning boundary.
- Tokens do not defend against a malicious process running as your local user. Redaction is not a secrecy guarantee. Do not expose the port remotely or assume this is a multi-user service.

Entry IDs, parent IDs, timestamps and tool-result IDs over 512 characters are rejected, not truncated; malformed labels, session names, assistant/tool-call envelopes and context-edit replacements remain bounded raw diagnostics. Missing or empty session names preserve Pi’s title-clear semantics. Ordinary message entries retain Pi’s legacy missing/null-content normalization; checkpoint-owned system messages use strict replay validation. Historical current/previous system replay is preflighted against a cumulative 1 MiB input-character budget before native concatenation; over-budget previews are explicitly unavailable while bounded Raw evidence remains accessible. Tool replay also allows at most 2,048 cumulative additions/removals, with declaration traversal bounded to 16,384 nodes and depth 32 inside the shared character budget. Live own IDs and tool-name inputs must be non-empty strings of at most 512 characters; rejected events are counted as omitted with unavailable correlation, not rewritten. Invalid parent IDs and anchors are omitted with explicit unavailable relationships. When a bounded refresh omits a selected entry, the preview falls back to a represented entry without moving Pi’s leaf. Credential classification includes camelCase/acronym suffixes without masking metric keys. Search queries strip terminal controls before a 512-character bound and normalize once per filter update. Responsive mode depends only on viewport width, with side widths constrained to preserve the central workspace. Explicit filter resets start at the source top even when the matching IDs do not change.
