Pass modelRuntime to createAgentSessionServices instead of authStorage +
modelRegistry; carry ModelRuntime on PiAgentSession; make modelRuntime a
required PiSessionService dependency (sessiond already injects auth.runtime).
Switch anthropicSubscriptionWarning to readStoredCredential, and rederive
model reads (availableModels/setModel/syncCurrentModelAuthWarning) via the
runtime (getAvailableSnapshot/getModel/hasConfiguredAuth). sessiond.ts and
piSessionService.ts now typecheck; only slice-5 test/support files remain.
Show a pinned banner at the top of the session view with resource and
runtime diagnostics (skills, prompts, themes, extension load errors) plus
the Anthropic subscription-auth billing notice, recomputed live from the
current runtime so they stay accurate across browser reloads.
Warnings carry an optional dismiss capability; the Anthropic notice is
dismissable and durably suppressed through pi's own anthropicExtraUsage
warning setting. Also fixes the testing-guide skill frontmatter so it
loads.
Seed the in-flight partial assistant message (text, thinking, and
in-progress tool calls) when opening or reconnecting to a session that is
mid-stream, then continue streaming live deltas on top of it. Replaces the
blocking "Catching up..." placeholder and the end-of-turn transcript reload.
Server stamps every per-session UI event with a monotonic seq at the
SessionEventHub publish choke point and exposes
GET /sessions/:sessionId/stream-snapshot returning { seq, partial }. The
client fetches the snapshot on join, seeds the normalized partial into the
in-memory transcript (never the history cache), and applies buffered/live
events using the seq watermark for exactly-once delivery.
The snapshot is a progressive enhancement: a 404 from an older remote
pi-web or a not-yet-restarted session daemon falls back to an empty seed
(seq 0, drops nothing), so sessions still open and stream normally. The
stream-snapshot route is registered in the federation allowlist for
remote-machine proxying.
Rewrite sessionNameGenerator.ts to consume a StreamFn-shaped dependency
(sourced from PiAgentSession.agent.streamFn, wired in leg B) instead of
ModelRegistry plus a dynamic @earendil-works/pi-ai/compat import.
streamFn resolves auth/headers/retry internally, so the explicit
modelRegistry.getApiKeyAndHeaders(model) call and apiKey/headers stream
options are no longer needed.
Deletes now-dead compat-loading machinery: getPiAiProviderRegistryModule,
loadPiAiProviderRegistryModule, importOptionalPiAiModule,
isModuleUnavailableError, hasGetApiProvider, PI_AI_COMPAT_MODULE,
ModuleImporter, SessionNameApiProvider, PiAiProviderRegistryModule, and
the module-level provider registry cache.
Updates the maybeGenerateSessionName call site in piSessionService.ts to
pass session.agent.streamFn instead of this.modelRegistry, and updates
both test files: sessionNameGenerator.test.ts gains coverage for the new
streamFn-driven generateShortSessionName signature (success and error
paths), and piSessionService.test.ts gains an end-to-end test proving a
first prompt generates a session name through the wired agent.streamFn
fake.
sessionNameGenerator.ts no longer imports @earendil-works/pi-ai/compat
or references getApiProvider anywhere.
Add a narrow `agent: { streamFn: StreamFn }` field to the PiAgentSession
interface, sourced structurally from the real AgentSession.agent (Agent,
from the stable @earendil-works/pi-agent-core package). This is the
resolved-auth/headers/retry "call this model" function pi's own
compaction/branch-summarization code uses internally, and will let
sessionNameGenerator.ts drop its dependency on the deprecated pi-ai
/compat provider registry in a follow-up leg.
No adapter object was needed: AgentSession's real .agent field already
structurally satisfies the new narrow shape, so defaultCreateAgentRuntime
needed no changes.
Adds a unit test proving the field is wired end-to-end via the injected
runtime creator. sessionNameGenerator.ts itself is untouched (leg C).
Split subsession inspection into two tools: check_subsession gives a quick
glance (status + latest assistant output), while read_subsession reads through
a child's transcript with role/content filters, full-content substring search
(including tool-call args), optional per-value maxChars truncation that flags
clipped parts, includeToolArgs, and pagination.
Filtering and search run on full untruncated content; truncation is an explicit,
caller-owned final projection (no default) so a narrow read never silently hides
a match. Empty page-windows are distinguished from zero-match results.
Add spawn_subsession / list_subsessions / read_subsession tools that let an
agent start child sessions it stays attached to: the child records its parent
in the session tree, the parent is notified (as a system-authored custom
message that wakes an idle parent and queues behind in-flight work) when the
child stops working, and the parent can inspect children's status and result.
Gated behind a beta flag, off by default, mirroring spawnSessions: enable via
PI_WEB_SUBSESSIONS, the subsessions config key, or the Settings toggle. Also
requires spawnSessions.
Also fix the release skill so the version step resyncs package-lock.json
(npm install --package-lock-only) and the commit step refuses a release where
package.json and package-lock.json versions disagree.
A slash command sent as the first (or any idle) message showed the raw
command text twice until reload: once from the client's optimistic insert
and once from the server's message.append echo, neither of which converges
with the agent's canonical expanded message (e.g. a /skill:* block).
Make commands obey the same source-of-truth contract as prompts:
- client no longer inserts the raw command text optimistically; it shows
the existing per-session sending indicator instead
- forwarded runtime/skill commands return a bare done result rather than a
synthetic "Accepted ..." line
- server suppresses the raw message.append echo for command-forwarded
prompts (threaded through the compaction queue too)
Result: pre-reload state matches reload, with no transient duplicate.
Add a project-scoped spawn_session tool so agents can dispatch new,
independent sessions (ralph loops, long-plan chaining). Spawned sessions
are constrained to a workspace/worktree of the same registered project,
appear in the session list immediately via a new session.created event,
and the capability is on by default with a Settings -> Session daemon
toggle (spawnSessions / PI_WEB_SPAWN_SESSIONS).
Note: adds a session daemon code path, so pi-web-sessiond.service must be
restarted manually for the server side to take effect.
Build on the original Reload action with the fixes raised in review:
- Server reload() now refuses to run on archived (read-only) sessions
and when the session has work in progress, mirroring archive(), so a
reload can no longer silently abort an in-flight agent run.
- Add a sessions.reload runtime capability; the client gates both the
reloadSession call and the Reload menu entry on it so the action only
appears for machines whose Pi-Web runtime supports it.
- reloadSession ignores cached-new and archived sessions.
- Add server (PiSessionService + routes) and client (SessionController)
tests covering reload success, the active-work guard, archived
rejection, route forwarding, capability gating, and error mapping.
- Restore alphabetical parser import ordering in clients.ts.
- Add a changeset documenting the feature and the sessiond restart note.
Note: touches a session daemon code path, so pi-web-sessiond.service
must be restarted manually for the server side to take effect.
Sessiond caches the in-memory SessionManager and never re-reads the
session file. When the same session is also being edited by another
process (e.g. the pi CLI), new entries on disk are invisible to the
web UI \u2014 the tail of the conversation gets cut.
Add a manual Reload action in the session three-dot menu:
- Server: PiSessionService.reload(sessionId) closes the active
session and re-opens it from disk, then publishes a fresh status.
Exposed as POST /api/.../sessions/:sessionId/reload.
- Client: api.reloadSession, SessionController.reloadSession which
discards the cached transcript and re-runs selectSession so the
history page is re-fetched.
- ChatTranscriptStore gains discard(sessionId) and the history
cache adapter gains optional remove(sessionId).
- SessionList shows a Reload entry for non-archived, non-cached
sessions; plumbed through AppNavigationPanel and PiWebApp.
Note: pi-web-sessiond.service must be restarted manually after this
change since the session daemon code path is affected.
Depend on @earendil-works/pi-agent-core so the ThinkingLevel union has a single source of truth (re-exported via shared/thinkingLevels). Wire/data fields use string and the parser is lenient, so an unknown level from a newer pi runtime is still listed, selectable, and rendered gracefully instead of throwing. The composer gauge now derives its bar count from the levels available for the current model and fills by rank. Adds compile-time drift guards (satisfies + Exclude check) and unit tests so a changed pi level set fails fast in development.
Support pasting (Ctrl/Cmd+V), drag-and-drop, and an Attach button to add
PNG/JPEG/GIF/WebP images to a message, with thumbnail previews and
multi-image support.
Attachments are delivered to the session using pi's native ImageContent
format and are run through pi's own resizeImage so they match pi's inline
image limits exactly. Image content now renders inline in the transcript.
A per-message delivery toggle also lets users save attachments into the
workspace `.pi-web/paste` folder and reference them so the agent reads
them with its own tools.
The accepted HTTP upload size is configurable via PI_WEB_MAX_UPLOAD_BYTES
or the maxUploadBytes config value (default 64 MB).
Closes#13
Sessions outside the server's launch directory were invisible and returned
404 on open, leaving the model picker empty. List without the SDK's
process-cwd filter and normalize working directories at the API boundary and
when reading stored session data, tolerating separator/normalization
differences (including Windows backslash vs forward slash). Requires Pi
coding agent SDK 0.78.0 or newer.