Commit Graph
320 Commits
Author SHA1 Message Date
Federico Jaramillo Martinez e77ab98361 fix(sessions): stop duplicating user message for slash commands
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.
2026-06-17 00:02:04 +02:00
Federico Jaramillo Martinez dd23b3e054 fix(sessions): dedupe started session when created broadcast races
The session.created global event (added with spawn_session) could arrive
in the initiating tab before the start request's HTTP response resolved.
applyCreatedSession inserted a plain session row (archive/reload actions)
and then startSession unconditionally prepended the cached version (delete
action), leaving two badges with the same id.

Make the optimistic insert idempotent by filtering out any existing entry
with the same id before prepending the cached session, so the locally
cached session always wins regardless of event ordering.
2026-06-16 23:40:47 +02:00
Federico Jaramillo Martinez 95c151233a feat: let agents start new sessions via spawn_session tool
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.
2026-06-16 14:53:37 +02:00
Federico Jaramillo Martinez 61f0b79d63 fix: move session reload action last 2026-06-15 14:15:56 +02:00
Federico Jaramillo Martinez 3742bcc962 refactor(plugin-api): trim plugin API scope to grounded capabilities
Builds on marcus's plugin-api-completeness work. Narrows the new plugin
surface to capabilities that expose real, otherwise-unreachable pi-web
functionality, and drops invented/duplicative surfaces:

Kept:
- files.writeFile / deleteFile / moveFile (genuine workspace mutation,
  federated, path-safe)
- prompt.insertText / getText / getSelection (editor state access)

Dropped:
- attachments.* (insertFileReference/getAttachedFiles/removeFileReference):
  getAttachedFiles invented a structured-attachment notion pi-web does not
  have and duplicated prompt.getText() + a regex with a false email-safety
  claim; insert/removeFileReference were thin sugar over readFile +
  insertText that plugins can compose themselves.
- prompt.onPaste / onKeyDown: an incomplete two-event hook system shaped
  around a single use case, overlapping the editor's native image-paste
  handling. Deferred until a real editor event/hook surface is designed.
- prompt.focus: redundant and buggier duplicate of the existing
  focusPrompt() (silently no-ops when not on the chat view). Focus stays
  as focusPrompt().

Security fix:
- deleteWorkspaceFile now resolves the parent via realpath + ensureInside
  before lstat/unlink, closing a symlinked-parent-directory escape that
  allowed deleting files outside the workspace (write/move already did
  this). Final path component is still not resolved, so deleting a symlink
  removes the link, not its target. Adds a regression test.

Docs and the registry test mock updated to match the trimmed surface.
2026-06-14 23:16:37 +02:00
marcus dde8675454 fix(plugin-api): federate workspace file mutations and trim PR scope 2026-06-14 15:03:23 +02:00
marcus fd6c01067b fix(plugin-api): position cursor after insertions and removals in prompt/attachments API
Ensure prompt.insertText, attachments.insertFileReference, and
attachments.removeFileReference move the cursor to the correct
position after modifying the prompt editor. Previously the cursor
stayed at the start of inserted text, which broke the natural
flow for plugin-driven file attachments like screenshot-paste.
2026-06-14 15:03:23 +02:00
marcus 27a3b2b5ed feat: Plugin API Completeness — file mutations, prompt editor, and attachment APIs
- WorkspaceFiles: writeFile, deleteFile, moveFile with path safety
  - writeFile: text/binary, auto-create dirs, overwrite option
  - deleteFile: idempotent, uses lstat (removes symlinks not targets)
  - moveFile: unix mv semantics, overwrite defaults to false
  - All mutations auto-refreshFiles() in File Explorer
  - Symlink escape prevention via realpath(dirname) check

- PluginPromptEditor: insertText, getText, getSelection, onPaste, onKeyDown, focus
  - Uses CM6 EditorView.domEventHandlers() via Compartment (not raw DOM)
  - Handlers registered before mount are preserved and applied on mount
  - First-to-consume-wins ordering for multi-plugin scenarios
  - insertText replaces selection (not inserts after)

- PluginAttachments: insertFileReference, getAttachedFiles, removeFileReference
  - insertFileReference validates file exists before inserting @path
  - Does not auto-focus editor (unlike prompt.insertText)
  - @file regex requires file extension to avoid matching emails

- Server endpoints: PUT /file, DELETE /file, POST /file/move
  - All work for local and federated machines

- Tests: 31 unit tests, 9 integration tests, 5 client tests
- Docs: 3 new sections in plugins.md
2026-06-14 15:03:23 +02:00
Federico Jaramillo MartinezandClaude 9159da9353 feat(sessions): expose reload as a command-palette action and disable when busy
Add a "Reload Session" core action so reload is keyboard-accessible and can be
assigned a custom shortcut, gated by the same guards as the menu item (writable
session, sessions.reload capability, not currently busy). Disable the Reload
menu entry while the session has active work, mirroring the server guard and the
archived-delete control, so users get a clear reason instead of an error toast.

Co-authored-by: Claude <[email protected]>
2026-06-14 14:49:21 +02:00
Federico Jaramillo Martinez 82db15f894 fix(sessions): guard, gate, and test session reload
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.
2026-06-14 14:17:51 +02:00
Slava Iumin ea1ec1b595 feat(sessions): add Reload action to refresh session from disk
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.
2026-06-14 14:11:34 +02:00
Federico Jaramillo Martinez ca30c970a7 refactor: source thinking levels from pi and make the gauge dynamic
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.
2026-06-14 13:47:44 +02:00
Federico Jaramillo Martinez 411e61ac75 feat: use icon actions in chat composer bar
Replace Send/Queue/Steer/Stop text buttons with compact icons, move Attach into the message box, and show the thinking level as a fill gauge. Keeps mobile layouts uncrowded while preserving accessible labels and the readable model selector.
2026-06-14 11:03:56 +02:00
Federico Jaramillo Martinez edae57b836 Clean up remaining dead code and dependencies
- Replace unused 'codemirror' barrel dep with explicit @codemirror/{state,
  view,commands,language} sub-packages that were imported but unlisted
- Remove unused exports: writeNamespacedQuery, targetWorkspacePathForRun,
  isWorkspaceDeletionRun, piWebConfigDir, duplicate parsePiWebRuntimeResponse
- Remove unused types: WorkspacePanelFiles, WorkspacePanelHost,
  GetActiveSession, SaveAttachmentsResponse, QueryValues
- Drop now-unused re-exports/imports cascading from the above
- Add knip config + 'npm run knip' script for ongoing dead-code detection
2026-06-13 21:57:51 +02:00
Federico Jaramillo Martinez 5d335a7e72 Remove dead/unused code
Delete orphaned files and unreferenced exports flagged by knip/tsc:
- Remove Composer.ts (chat-composer element superseded by prompt-editor)
- Remove plugins/example/index.ts (never registered)
- Drop unused exports: enabledActions, gitDiffUrl, machineWorkspaceKey,
  GlobalSessionSocket, shouldShowMachineSwitcher, renderWorkspaceLabel,
  renderWorkspaceLabelItems, composerStyles alias
- Fix unused parseValue field in PersistentValueMap
2026-06-13 21:48:34 +02:00
Federico Jaramillo Martinez cfb7493384 Improve dark theme user/assistant message distinction
Add per-role left accent stripes and color-coded header labels across
all themes. Lighten the dark theme user-message background, decouple it
from the generic hover color, and brighten the user border so user and
assistant turns are clearly distinguishable.
2026-06-13 21:26:08 +02:00
Federico Jaramillo Martinez a369b1e550 feat: give the sending indicator a distinct color in session lists
Add a dedicated "sending" activity indicator kind (warning color) so a
session uploading attachments is visually distinct from server activity.
Server activity propagates up to workspace/machine rows; client-side
sending does not, and the different color signals that to users.

Also extract sessionRowActivityKind as a pure, exported helper so the
"sending takes precedence, archived/cached-new never show" logic is unit
tested without rendering.
2026-06-13 21:20:46 +02:00
Federico Jaramillo Martinez 886393a31e fix: scope the attachment sending indicator per session
isSendingPrompt was a single global flag, so an in-flight upload showed
the "Sending…" dock on whatever session/machine the user switched to.

Replace it with sendingPrompts, a Record<sessionId, true> keyed like
sessionStatuses/sessionActivities. The controller sets/clears the entry
for the originating session (captured before the await), the chat dock
reads only the selected session's entry, and the session list now shows
the activity dot for a session that is uploading so progress is visible
after switching away. Machine switches clear the record like other
per-session state; deselecting a session no longer cancels its indicator.
2026-06-13 21:09:31 +02:00
Federico Jaramillo Martinez 970c0bf1d2 refactor: extract testable image attachment capture from the composer
The paste/drop/file capture logic (supported-type filtering, unnamed-file
extension fallback, per-file error collection) lived inside PromptEditor,
mixing browser side effects with branching that had no tests.

Move it into a pure promptAttachmentCapture module with the byte reader
injected, so the FileReader side effect stays at the component boundary
and the orchestration is unit-tested. Also removes a duplicated mime->ext
fallback in favour of the shared extensionForImageMimeType helper.
2026-06-13 20:45:16 +02:00
Federico Jaramillo Martinez ecede3ac7c refactor: drive attachment sending state through the chat activity dock
Replace the composer-local "Sending…" indicator with a single, consistent
state surfaced by the existing chat activity dock. The session controller
now owns the full attachment send lifecycle (including the folder-mode
upload + reference rewrite) and toggles an isSendingPrompt flag around it,
which the chat dock renders as "Sending your message…" until the real
server activity supersedes it.

This covers the pre-receipt dead zone (upload, server-side image resize,
first-session open) with one indicator instead of two, and keeps plain
text sends fire-and-forget.
2026-06-13 14:09:04 +02:00
Federico Jaramillo Martinez 53b00c47f9 fix: show sending indicator while uploading attachments
Messages with image attachments could take a moment to appear (large
base64 upload, server-side resize, first-session open) while the composer
cleared instantly, making it look like nothing happened.

Await the send for attachment messages and surface a "Sending…" button
label plus a "Sending your files…" / "Saving your files…" hint, disabling
the composer until the message lands. Plain text messages stay
fire-and-forget so the input frees up instantly.
2026-06-13 13:57:10 +02:00
Federico Jaramillo Martinez d17050e144 feat: add image attachments to the chat composer
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
2026-06-13 13:49:39 +02:00
Federico Jaramillo Martinez d66eccc5c0 fix: handle spaced file suggestions 2026-06-11 12:09:27 +02:00
Federico Jaramillo Martinez 9dd59c0f64 fix: show model response errors in chat 2026-06-11 11:51:51 +02:00
Federico Jaramillo Martinez 577594a622 fix: prevent sidebar action menus from being cropped
Use viewport-constrained positioning for sidebar action menus and keep the auth warning test isolated from local models.json config so pre-commit verification is deterministic.
2026-06-11 01:31:56 +02:00
Federico Jaramillo Martinez 5855c443ca Merge remote-tracking branch 'origin/main' 2026-06-10 23:27:23 +02:00
Federico Jaramillo Martinez ef22247d76 fix: preserve remote route during transient reconnects 2026-06-10 23:04:25 +02:00
Federico Jaramillo Martinez b99143f757 fix: preserve legacy session route lookups 2026-06-10 22:42:55 +02:00
Federico Jaramillo Martinez 71510444c4 Merge remote-tracking branch 'origin/main' into investigate/issue-12-session-dir
# Conflicts:
#	src/client/src/api.ts
#	src/client/src/api/clients.ts
#	src/client/src/api/federatedRouteContract.test.ts
#	src/server/sessions/piSessionService.ts
#	src/server/sessions/sessionRoutes.ts
2026-06-10 20:43:10 +02:00
Federico Jaramillo Martinez 06052ea5ec fix: respect Pi session directories by cwd 2026-06-10 20:26:51 +02:00
Federico Jaramillo Martinez 4bc390a33a fix: keep machine restore snappy 2026-06-09 17:06:18 +02:00
Federico Jaramillo Martinez 65b4c76513 fix: preserve selected chat text on copy 2026-06-09 15:41:48 +02:00
Federico Jaramillo Martinez c57f24dfa5 feat: support machine-specific plugins 2026-06-09 15:10:32 +02:00
Federico Jaramillo Martinez 0118e6ebe9 fix: keep archived parent sessions visible 2026-06-09 13:40:48 +02:00
Federico Jaramillo Martinez 313dbea34e chore: clarify archived delete capability hint 2026-06-09 12:29:56 +02:00
Federico Jaramillo Martinez fc20b95fed feat: gate session cleanup by runtime capabilities 2026-06-09 11:40:17 +02:00
Federico Jaramillo Martinez a3b5b722c9 feat: add safe session bulk cleanup actions 2026-06-08 17:20:00 +02:00
Federico Jaramillo Martinez b2a79750a3 fix: align machine badge status 2026-06-08 15:44:52 +02:00
Federico Jaramillo Martinez b6166842fe feat: add resizable side panels 2026-06-08 15:41:52 +02:00
Federico Jaramillo Martinez 351ed03bf8 feat: add editable keyboard shortcuts 2026-06-08 14:57:24 +02:00
Federico Jaramillo Martinez f3e19d1463 feat: add keyboard navigation ladder 2026-06-08 13:33:39 +02:00
Federico Jaramillo Martinez f501f9d757 fix: stabilize navigation activity indicators 2026-06-08 13:33:39 +02:00
Federico Jaramillo Martinez a73bcebbd6 feat: simplify desktop navigation 2026-06-06 00:12:38 +02:00
Federico Jaramillo Martinez 9a3f2ce64f feat: make desktop navigation sections collapsible 2026-06-06 00:12:38 +02:00
Federico Jaramillo Martinez f7eff88e00 feat: make refresh control reload the page 2026-06-05 23:40:59 +02:00
Federico Jaramillo Martinez ad963a2391 fix: simplify mobile breadcrumb machine context 2026-06-05 20:43:45 +02:00
Federico Jaramillo Martinez 66c5d0faca fix: right align status bar metrics 2026-06-05 20:35:18 +02:00
Federico Jaramillo Martinez 98cdc29208 fix: streamline status bar context 2026-06-05 20:33:58 +02:00
Federico Jaramillo Martinez b35ce1d3e0 fix: reduce repeated workspace context 2026-06-05 20:26:12 +02:00
Federico Jaramillo Martinez 0d9d124237 fix(plugins): preserve deprecated panel terminal alias 2026-06-05 14:26:35 +02:00