/chat's session list, transcript load, delete, and rename now span both harnesses.
- opencode-sessions.ts — REST-backed reader (OpenCode's SQLite via its HTTP API, never
the DB): listOpenCodeSessions / loadOpenCodeSession / delete / rename, returning the
same shapes as the Claude reader, tagged harness:'opencode'. A serve is directory-
scoped, so listing a cwd = asking the serve rooted there. Transcript rebuild maps
user/assistant/tool parts and drops reasoning (parity with the delta filter).
- client.ts — adds listSessions/getMessages/deleteSession/renameSession over /session/*.
- chat.ts — /sessions merges both (newest first); /sessions/:id, DELETE, and
/title route by id shape (ses_ = OpenCode). ClaudeSessionSummary gains an optional
`harness` tag.
- send-opencode.ts — resuming from history: when the sessionKey is itself a ses_ id,
reuse that OpenCode session instead of creating a new one.
- SessionList.tsx — shows an "OpenCode" badge for OpenCode sessions.
Verified end-to-end against a live serve: list (5 sessions, tagged), load (transcript
rebuilt, reasoning filtered), and rename all work. Phases 1-3 complete; needs a restart.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
list-models.ts now merges the OpenCode catalog (from `opencode models`, cached;
ids are providerID/modelID) with the static Claude tiers, so /chat/models returns
both. invalidateModelCache clears the OpenCode cache for real now.
Adds the 'opencode' → 'OpenCode Zen' provider label in the /models response and the
ModelSelector's PROVIDER_DISPLAY. The existing useModels visibility gates already
pass non-claude-code providers through, so no gate changes are needed — Super Admin
sees all models. Selecting any non-claude-code model routes the turn to the OpenCode
harness (Phase 1).
Default model stays 'claude-code'. 58 OpenCode Zen models currently list; curating to
a flagship subset is an easy follow-up if the full catalog is unwieldy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Deletes all session persistence that isn't Claude's native transcript store, per
the "only harness-native session management survives" rule.
Backend: delete api/pi/storage.ts (meta.json+messages.json file store), the
api/saved-sessions router (+ unmount), the /pi/sessions REST endpoints, and the
storage.save/loadSession calls in the chat WS handler (in-memory session-manager
stays for live turns; no disk persistence — Claude's transcript is the record).
Also drops the Postgres saved_sessions layer: schema/chat.ts, queries/saved-sessions.ts,
its types and re-exports.
Frontend: delete state/useSavedSessions, ChatList, and the ChatHistory Widget
(all pure saved-session UI); slim ChatHeader to a label; strip the auto-load-latest
+ Save wiring from ChatPanelWrapper and ChatDetailPanel; drop the old resume path
from useChat and SessionListPage; remove the /chat/saved/:id route and the
useInitialData prefetch.
Behavior removed (intended): the Save-session button, email/project panels
auto-resuming the last chat, and /chat/saved/:id. /chat itself is unchanged —
already fully on Claude transcripts. The orphaned saved_sessions Postgres table
is dropped on the next `bun db:push`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frontend de-Pi (Stage 3). Renames the chat hook usePiChat → useChat (+ UsePiChatType
→ UseChatType, file moved to hooks/useChat.ts) across all consumers, and renames
the TaskRunnerModal agentic runner PiMonoInner → AgenticTaskRunner (dropping the
dead defaultProvider === 'pi' check → always the Claude default). Pure rename, no
behavior change. The WS route stays /api/pi/chat/ws until Stage 4.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A "Browse…" entry in the pwd popover opens a simplified file-browser modal
(breadcrumb nav, subfolder list, New folder) that returns an absolute path to use
as the /chat working directory. Built on useFilesAPI within the home root;
dirs outside home stay reachable via the auto-discovered list and free-text field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Sessions panel header gets a pwd selector: the default claude_sessions dir,
plus every directory that already has Claude sessions (auto-discovered by reading
the real cwd back from ~/.claude/projects), plus free-text entry. Switching pwd
refetches the list for that cwd's Claude project group and runs New Chat / resume
in it. Backend: GET /chat/pwds + a ?cwd= param on the session ops; the WS handler
honors a chosen cwd for /chat (default claude_sessions). Browse-modal picker next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rename appends a {"type":"summary",...} entry to the session's JSONL transcript
(Claude's own format, so the title lives in .claude); the reader takes the last
summary as the title, without a timestamp so it doesn't reorder the list.
PATCH /chat/sessions/:id/title backs it. The list gets inline rename (pencil ->
edit in place) and a two-step delete confirm so a stray click can't nuke a
transcript. Rename verified against a synthetic store.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
DELETE /chat/sessions/:id removes Claude's transcript file; the list gets a
per-row delete button. The list also invalidates on turn-complete so new and
continued sessions surface without a manual refresh.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Clicking a session in the list loads its transcript (GET /chat/sessions/:id,
parsed from Claude's JSONL into display messages) and continues the actual Claude
session: a resumeSessionId is threaded chat handler -> send-claude-code -> sidecar
-> claude-manager, which passes --resume <uuid> (in-memory session mapping still
takes precedence for live turns). Parser verified against real transcripts.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The /chat detail (ChatDetailPanel) now passes context 'chat' to usePiChat, so the
backend actually runs the session from claude_sessions (the earlier tag was on the
wrong component). The left panel (SessionList) now reads GET /chat/sessions —
Claude's own transcripts — instead of the old saved-sessions model. List-only:
rows display title/time/count; click-to-resume comes next.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The standalone /chat route now tags its context as 'chat'; the backend runs those
sessions with cwd = DATA_PATH/<email>/claude_sessions (created on demand), so their
transcripts form an isolated Claude project group instead of polluting the home-dir
history. Other chat contexts (email/project panels) keep their own cwd.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Hybrid grouping via a new thread_id column: new mail threads exactly on
References/In-Reply-To (id is sha1(Message-Id), so a referenced id hashes to
the ancestor's own id); already-synced mail is backfilled with a
normalized-subject + counterpart key. Folder views collapse to one row per
thread with a count badge; the reader shows the thread as a collapsible stack.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/download-video now delegates to the ReClip service (its own yt-dlp) and runs as
a background job: POST returns a jobId immediately and the client polls
GET /download-video/:jobId, so a large download no longer holds one long request
open (which was 504-ing behind the reverse proxy). the finished file is streamed
into the user's folder with a title-based name.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
when a folder has mixed audio/subtitle layouts, the mismatch banner now offers a
toggle: convert the matching group (pick tracks, skip the rest) OR convert every
video keeping all audio + all subtitles (no picking, nothing skipped). the second
mode hides the pickers and clears the track-selection inputs so run.sh keeps
everything. only shown for tasks with track pickers (Convert Video).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
selecting several files/folders and running a task now runs it on the whole
selection instead of just the right-clicked item. reuses the folder + include
mechanism: the run targets the current folder scoped to the selected files
(and everything under selected folders). track-picker tasks scope their probe
to the selection too.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
the video used max-w/max-h so a low-res source stayed at native size surrounded
by black. switch to w-full h-full object-contain so it scales up to fill the
player (windowed and fullscreen) while preserving aspect ratio.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
renders each subtitle as a keep checkbox + editable label field, seeded from the
file's current tracks. reuses the existing probe / folder-grouping / include
plumbing, so it works on a single file or a whole season.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
converting a folder now recursively probes every video and groups episodes by
track layout (audio language+channels, subtitle language). the pickers are
driven off the largest matching group; episodes with a different layout are
listed as skipped and converted separately. adds a /probe-folder endpoint and
passes the majority file list to run.sh as INPUT_INCLUDE.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
convert video's task modal now probes a single file on open, lists its audio
and subtitle tracks as inline checkboxes, and passes the selection to the
script. audio defaults to all kept, subtitles to none; a "none" sentinel keeps
empty (folder mode) meaning keep-all.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add /file-browser/audio-tracks (list) and raw?audio=N (serve the video with
a chosen audio track via a cached -c copy remux, byte-range seekable). The
player shows an always-visible selector outside the controls; switching
reloads the source and restores the playhead. Works in Chromium, which
doesn't expose the audioTracks API.
Temp remux file keeps the real extension (a .tmp suffix breaks ffmpeg's
muxer selection); concurrent requests for a track share one remux.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add /file-browser/subtitles (list text subtitle tracks) and /subtitle-vtt
(extract one as WebVTT on demand); the VideoRenderer fetches the list,
renders <track> elements, and shows a CC selector to switch/turn off subs.
Track labels prefer title, then handler_name (mp4), then a real language,
falling back to Track N — so untagged tracks aren't shown as "und".
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Navigation is now session-local state that always starts at home. The
unscoped browser still mirrors its folder into files/currentPath so the
Create Dashboard flow keeps defaulting a new dashboard's cwd to it.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the /transcode (and dead /transcode-audio) routes and the frontend
mkv/avi transcode wiring. Only browser-native formats (mp4, webm, mov,
m4v, ogv) are classified as video now; other containers fall through to
the generic file view.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Agentic steps now have a 5-minute inactivity timeout (non-Claude-Code
models only) so stalled local LLM requests fail with a clear error
instead of hanging forever. A "Waiting for model" indicator is emitted
every 10s and shown in the pipeline UI stats bar, clearing as soon as
the model starts responding.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix stop: abort poll now rejects the promise after killing the agent process
- Fix jobs link: close modal before navigating, use react-router navigate
- Fix seed parser: strip quotes from step input values (delete_source: "true")
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pipeline jobs now persist to DB with progress tracking and cost accumulation
- Jobs survive WebSocket disconnects with in-memory event buffer replay
- New /jobs list page with search, status badges, and cost display
- New /jobs/:id detail page with live WebSocket attachment and REST fallback
- Two-column layout using WorkspaceLayout for resizable steps/output panels
- Streaming messages tagged with stepIndex/iterationLabel for per-step output grouping
- TaskRunnerModal links to job detail page once job is created
- Dock entry added for Jobs page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Pipeline mode: new task mode that chains agentic tasks sequentially with
foreach/subdirectory iteration and skip_if conditions
- Pipeline executor backend (WebSocket at /api/tasks/pipeline/ws) with
support for both Pi and Claude Code models
- Frontend PipelineRunner component with step progress, streaming output,
and aggregate cost tracking
- New agentic tasks: prepare-discography, fetch-album-info, build-discography
(pipeline combining both)
- Seed parser extended to handle pipeline steps in frontmatter config
- CopyButton component added to assistant bubbles, error bubbles, and
tool input/output sections
- Removed obsolete SearXNG/Apify/browser relay code from pi-bridge
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- rename task to convert-audio, support mp3/flac/wav/ogg/aac/opus targets
- add options input type with selectable pill buttons in UI
- extend seed script parser to handle YAML list properties (options)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- fetch task input definitions from API and render configurable inputs
- boolean inputs render as No/Yes toggle (e.g. delete_source)
- auto-filled inputs (file_path) are hidden from the form
- wrap script output in dark pre/code block with copy button
- fix ffmpeg -nostdin for batch directory processing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tasks now live in the database (mode: script or agentic). Script-mode tasks
bypass the agent entirely — the implementation is materialized to a temp file
and executed directly, with stdout/stderr streamed to the UI via WebSocket.
Includes convert-to-mp3 as the first native script task.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add agent-agnostic tool registry (tool-registry.ts) that discovers tools from disk
- Embed tool-loader extension as platform infrastructure (ensure-tool-loader.ts)
- Inject tool context into Claude prompts on first message
- Add marketplace tool sync (sync-marketplace.ts)
- Fix model selector defaulting to claude-code when no model explicitly selected
- Exclude tool-loader-source.ts from tsconfig (Pi-specific deps)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The PUT endpoint was re-merging old DB messages (which already included
previous merges) with disk messages on every turn, causing duplication.
Now the client passes resumedMessageCount so the server always slices
only the original pre-resume messages before concatenating.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>