Commit Graph
339 Commits
Author SHA1 Message Date
Federico Jaramillo Martinez 69b125b001 feat(sessions): surface cross-worktree parent and child sessions
A session spawned into another worktree recorded a parent that no
listing contained, so the row showed only "parent unavailable" and its
parent's row looked childless. Both facts were accurate and useless:
neither said where the related session actually was.

Report both directions from the session store instead. A missing
parent's cwd and id come from its own file header, so one 4 KB read per
distinct missing parent resolves it without listing other workspaces;
children are counted by listing sibling workspaces and matching the
parent path they already recorded, needing no header reads. Reads are
memoized per path because Pi writes headers once, and the cache is
released on dispose. Both directions are best-effort: an unreadable
header or an unlistable worktree leaves a session unannotated rather
than failing the listing.

In the browser, an orphan child keeps the same child marker as a nested
one, dimmed, so it no longer renders as a root; whereabouts are stated
once on the meta line ("parent in feature/foo", "2 children
elsewhere"), where a clamped title cannot hide them. A "Go to parent
session" action switches to the owning workspace and selects the
parent. Live session.created events keep child counts current instead
of leaving them stale until the next listing.

Session and workspace paths reach the browser from two producers: store
enumeration for a listing, and the live runtime for a broadcast. They
are now compared through one normalizing helper, so tree nesting and
child counts cannot silently miss a link when only a trailing separator
differs.

Extract the shared "workspaces of the project containing this cwd"
lookup out of ProjectScopedSpawnTargetResolver so spawn targeting and
child counting share one implementation, and register it regardless of
whether spawning is enabled: children can predate a config change, and
the tree should stay honest about them either way.
2026-07-28 11:56:21 +02:00
Federico Jaramillo Martinez 4a5150309d feat(ui): add copy buttons to workspace menu details 2026-07-27 19:31:15 +02:00
Federico Jaramillo Martinez 8517800a24 feat(plugins): turn Info panel into a PI WEB status view
Rework the bundled Info plugin from a demo into an always-available
status view: running and installed versions, installation details,
release state, and per-service health rendered from host-provided
state, plus machine and workspace details. Replace the window.alert
demo action with a Copy PI WEB Diagnostics action that copies a
plain-text summary for bug reports.

Add state.selectedMachine to the stable plugin runtime state so
actions and other runtime callbacks can read the selected machine's
identity, and split the plugin into a copy-paste-friendly skeleton
(pi-web-plugin.ts) and replaceable internals (infoInternals.ts).
2026-07-27 18:03:25 +02:00
Federico Jaramillo Martinez 0a35748478 Merge pull request #105 from jmfederico/feat/unread-ux
feat(ui): improve unread-messages experience
2026-07-27 15:58:55 +02:00
Federico Jaramillo Martinez f76a9fabc8 feat(ui): unify row indicators into one mark with an unread ring
Unread is no longer a competing ActivityIndicatorKind or a separate
name-adjacent dot: every row renders a single indicator. An accent ring
wraps the still-pulsing work dot when a row is both busy and unread, a
filled accent dot shows while idle and unread, and activity kinds keep
their existing precedence (sending > session > terminal). Session rows
surface unread state even while busy, so the unread header count and
mobile Sessions badge now count busy unread sessions too.
2026-07-27 15:55:06 +02:00
Federico Jaramillo Martinez 8af637b00e feat(ui): bubble unread presence up to workspace, project, and machine rows
Wire the derived UnreadPresence into dot indicators (no counts) across the
navigation panel: workspace, project, and machine rows (machine-switcher and
machine-list) now show a static accent dot whenever a session beneath them is
unread, including offline machines with stale-but-present state. Presence
flows PiWebApp -> AppNavigationPanel -> leaf id sets, mirroring the
unreadSessionIds chain, and is covered by happy-dom component tests per list
plus panel- and app-level wiring tests. Adds changesets for the mark-as-read
actions and the bubble-up indicators.
2026-07-27 15:08:31 +02:00
Federico Jaramillo Martinez 76f292cfde fix(ui): scroll session/workspace lists only on positive reveal triggers
Live data refreshes (messageCount churn from session status publication,
workspace topology refreshes) replace the sessions/workspaces arrays and
the selected object with same-id copies, and each replacement re-scrolled
the selected row into view.

Replace the broad updated() scroll triggers with positive reveal triggers:
a different row becoming selected (first render included), the archived
reveal path, a restore moving the selected row from archived back to
current (same id, archived flag cleared), and section expansion. The
sessions/workspaces array triggers are removed, not guarded.
2026-07-27 14:46:25 +02:00
Federico Jaramillo Martinez 80494dd6d8 fix(ui): prevent iOS zoom on custom answers 2026-07-27 12:42:19 +02:00
Federico Jaramillo Martinez e567d43042 feat(settings): add Ask Questions toggle 2026-07-27 12:39:40 +02:00
Federico Jaramillo Martinez 0e83146315 fix(ui): streamline ask_user question forms 2026-07-27 10:55:49 +02:00
Federico Jaramillo Martinez 4e47c23f23 Merge remote-tracking branch 'origin/main' into feat/model-questions-ux
# Conflicts:
#	src/server/sessiond.ts
#	src/server/sessions/sessionRoutes.test.ts
2026-07-27 09:44:40 +02:00
Federico Jaramillo Martinez ce4b469727 feat(ui): add model and thinking selector actions 2026-07-27 09:40:24 +02:00
Federico Jaramillo Martinez 9191f59146 docs: document ask_user question forms 2026-07-27 01:17:48 +02:00
Federico Jaramillo Martinez 8a24a7c4a5 feat(workspaces): refresh worktrees on browser resume
Call WorkspaceController.refreshSelectedProjectTopology() from the existing
browser-resume refresh and the plugin-facing refreshAppData path, so worktrees
created or removed outside PI WEB become visible with no user action. No new
timer, watcher, process, or push channel; the resume path is already debounced
per animation frame and collapses concurrent requests.

Document the resume-scoped detection and the hiding of gone checkouts in the
FAQ, and add the changeset for the user-visible behavior.
2026-07-26 22:25:59 +02:00
Federico Jaramillo Martinez 49e7c390f3 feat(sessions): tell the user what a session start is waiting on
Creating or opening a session could stall for reasons the daemon knew
about and never shared. The browser invented the whole message it showed
while waiting -- "Creating session: Waiting for the backend session to be
ready" -- which says that we are waiting but never what for. A shared
ModelRuntime read during startup can be handed a network refresh that is
already in flight, and extensions may do their own network I/O while
loading, so the wait is real and previously unattributable.

The pre-session gap turned out to be a missing shared key rather than a
missing channel: publishActivity needs the PiAgentSession being built, but
the session id and cwd are both known before the first await. So create()
now publishes a new global session.startup event carrying an ordinary
SessionActivity, routed by cwd -- the one identity a browser row waiting
for a session id can match, since the client-invented pending id is
unknown to the daemon and the daemon's id is unknown to the browser.

Two phases are reported, each published before the await it describes so
the label changes during the wait rather than after it: "Starting the Pi
session" and "Loading session extensions". Both are facts, because the
service awaits exactly one call for each. A concurrent background catalog
refresh is appended as a note ("provider model lists are refreshing"),
never as the cause: the refresher can prove a refresh is running but not
that this startup joined it. ModelCatalogRefresher gains only a read-only
isRefreshInFlight() getter; cadence, timeout, and coalescing are untouched.

Reporting is event-only and synchronous. It writes no activities entry, no
workspace activity, and no unread state, so a failed creation leaves
nothing stranded, no await is added, and creation ordering and semantics
are unchanged. The window-ending idle report is skipped when a real
activity was published during startup, so an extension error survives.

The browser applies startup progress only when it can prove the target:
one non-discarded pending start in that cwd on the selected machine, or a
session whose id it already knows. A foreign workspace, another machine,
or two concurrent starts in one workspace keep today's generic wording
rather than showing one row the phase of another. An idle report restores
that generic wording, including the queued-messages variant.

docs/config.md said nothing a request triggers waits on a catalog fetch.
That is not strictly true for a refresh already in flight, so both it and
the generated docs/config.html now state the exception and say PI WEB
reports it while it happens.
2026-07-26 16:19:05 +02:00
Federico Jaramillo Martinez 531ccf7b2a feat(sessions): let a known provider refresh its own model list
The global provider bootstrap froze all three ModelRuntime mutation
methods after startup, so a provider extension that fetched an updated
model catalog had that work silently discarded.

registerProvider is now applied when the provider ID is already in the
frozen baseline and the incoming config equals the recorded baseline in
every field except `models`. Refreshing extensions re-send a complete
provider config rather than a models-only delta, so the test is
"equal except models", not "contains only models".

Everything else stays a logged no-op: unknown provider IDs, any change
to name/baseUrl/apiKey/api/streamSimple/headers/authHeader/oauth/
refreshModels, native registration, and unregistration. Function-valued
fields compare by reference and so always read as a mismatch, which is
the intended conservative direction.

An accepted update rebases the stored baseline from Pi's merged record,
so repeat refreshes work and an unchanged replay is correctly ignored
rather than re-applied on every session start. The accept path stays
synchronous and never awaits or networks; Pi's own trailing
fire-and-forget local refresh is untouched.
2026-07-26 14:14:07 +02:00
Federico Jaramillo Martinez 111db63f4a fix(chat): scroll wide markdown tables horizontally
Wrap chat markdown tables in a focusable scroll region and let the table
keep its natural width so narrow screens can scroll instead of squeezing
columns into the chat width.
2026-07-26 10:32:42 +02:00
Federico Jaramillo Martinez 2fb626204c chore(release): v1.202607.2 2026-07-25 23:51:01 +02:00
Federico Jaramillo Martinez 92854489cf docs: require pi 0.82.1 in user-facing requirements 2026-07-25 22:10:42 +02:00
Federico Jaramillo Martinez 90dd7ce401 docs(sessions): document background catalog refresh and contain offline env window
Finding 6: serialize createOfflineModelRuntime so overlapping calls cannot
interleave their PI_OFFLINE save/restore pairs and leave the process offline,
and name the process-wide visibility of that window in the docblock.

Finding 7: assert the offline construction through the public refresh seam via
reloadConfig() — the request path that regressed — instead of reading upstream's
private modelNetworkEnabled field.

Finding 8.4/8.5: document the background provider-catalog refresh in
docs/config.md and docs/config.html (cadence, timeout, single retry, offline
opt-out via PI_WEB_OFFLINE / PI_OFFLINE only), and update the changeset to
match the behavior after the earlier fixes.
2026-07-25 13:07:02 +02:00
Federico Jaramillo Martinez ed9c2f65bb fix(sessions): move provider catalog network refreshes off request paths
The shared ModelRuntime was constructed with network refreshes enabled, so
reloadConfig()/login()/logout() — called on the model picker, session model
changes, and auth dialogs — performed unbounded provider-catalog fetches.
A single stalled fetch blocked those requests for minutes and, through pi's
coalesced per-provider refresh, dragged session creation along with it.

Construct the runtime with PI_OFFLINE forced so every runtime-driven refresh
stays local, and add ModelCatalogRefresher as the single deliberate network
path: bounded by an abort timeout, serialized through one in-flight run,
scheduled in the background, and triggered after provider auth changes.
2026-07-24 23:44:35 +02:00
Federico Jaramillo Martinez 63b85fc246 Merge branch 'pr-92-review' into test/pr-92 2026-07-24 10:21:29 +02:00
Federico Jaramillo Martinez 165928a543 docs(changeset): describe in-pane chevron in quiet-session-warnings
Update the single quiet-session-warnings changeset entry to cover the
final shipped behavior: status-bar expand/collapse toggle, the in-pane
minimise chevron restored in 2bdfd48, per-session remembered state, and
SVG warning icons. Frontmatter (patch) unchanged. No new changeset.

Relay restore-warning-chevron leg 2 (final).
2026-07-23 12:03:28 +02:00
Federico Jaramillo Martinez e8d418a71a docs: distinguish Pi extensions from PI WEB plugins 2026-07-22 23:53:30 +02:00
Federico Jaramillo Martinez ee8d9e5359 docs: document immutable provider bootstrap 2026-07-22 20:46:41 +02:00
Federico Jaramillo Martinez c2bf595999 feat(sessions): allow global-extension providers, require Pi 0.81
Relaxes the provider policy from 'global config only' to 'global sources':
providers registered by agent-dir (global) extensions are learned once at
daemon startup and allowed on the shared runtime; project-extension
registrations are still rejected with a session warning. Global extensions
load identically for every session, so their providers are daemon-consistent
and cannot leak project state (#76).

- Shim now allows allowlisted ids through and also covers Pi 0.81's native
  provider path (registerNativeProvider), closing a bypass.
- Startup learning step loads only global extensions against a scratch cwd
  and diffs the runtime's registered provider ids.
- Bumps @earendil-works/* dev/peer ranges to >=0.81.1 <0.82; adapts to the
  Agent.streamFn -> streamFunction rename.
- Docs, changeset, unit and acceptance tests updated (global-extension allow
  path, late re-registration a la pi-tensorx, native provider rule).
2026-07-22 17:13:57 +02:00
lzr dd435cb1b0 feat(git): view submodule working-tree changes in the Git panel
Recurse into dirty submodules when building the Git status so their
modified and untracked files appear as full-path entries, and add a
commit-pointer entry (with short SHAs) only when the recorded commit
actually moved. Route diffs whose path falls inside a submodule to run
in that submodule's working tree so real per-file diffs are shown.

The changed-file list groups these under the submodule: tree view keeps
the nested structure and marks the submodule root with a badge, list
view flattens them into one expandable group pinned above the ordinary
files. Depth 1 only; ignored files are excluded; the panel stays
read-only.

Covered by client tree/list-grouping tests, parser tests, and a
server test that drives a real temporary repository and submodule.
2026-07-22 21:35:06 +08:00
lzr 8a5aaf93a1 feat(git): add list/tree toggle for changed files in the Git panel
Extract the Git panel into a dedicated `workspace-git-panel` Lit component
(mirroring the Files panel) and add a segmented List/Tree toggle next to
Refresh.

Tree view builds an in-memory, collapsible directory tree from the changed
files, starts fully collapsed, and offers a single expand-all/collapse-all
button (visible only in tree view). List view keeps the existing flat,
full-path rows. The selected view mode persists in localStorage under
`pi-web.gitFileView`; per-directory expand state is intentionally ephemeral.

- gitFileViewPreference.ts: localStorage-backed view preference (+ test)
- gitFileTree.ts: pure flat-paths -> nested-tree builder (+ test)
- WorkspaceGitPanel.ts: the panel component with toggle + tree state
2026-07-22 21:34:54 +08:00
Federico Jaramillo Martinez 242911331a docs: document global provider policy and extension registration rejection 2026-07-22 09:36:32 +02:00
Federico Jaramillo Martinez 24a3d3611e fix(ui): compact session tree indentation and allow default navigation after invalid custom input
- Only increment visual branch depth after forks so long linear session
  histories stay in one lane instead of scrolling off-screen; lower the
  max visual depth cap to match.
- Reset to the no-summary default when leaving an invalid custom summary
  choice so Navigate is never permanently disabled by a stale invalid entry.
2026-07-21 11:11:10 +02:00
Federico Jaramillo Martinez a13778c97b fix(ui): keep warning toggle visible 2026-07-21 10:43:13 +02:00
Federico Jaramillo Martinez 115d74e79a feat(sessions): persist shared unread state 2026-07-20 19:36:16 +02:00
Federico Jaramillo Martinez a20a8c8c09 fix: preserve minimised warnings across session navigation 2026-07-20 19:35:45 +02:00
Federico Jaramillo Martinez a884773357 fix(sessions): provide plain-text extension theme 2026-07-20 14:01:47 +02:00
Federico Jaramillo Martinez b48b147b5b fix(install): allow node-pty scripts with npm 12 2026-07-20 11:57:39 +02:00
Federico Jaramillo Martinez 4ca4a1d096 feat: add hierarchical session tree navigator 2026-07-20 10:40:17 +02:00
Federico Jaramillo Martinez a77c83b309 fix(sessions): clarify delegation tool guidance 2026-07-20 00:48:08 +02:00
Federico Jaramillo Martinez 2c777b4959 feat: add collapsible session warnings 2026-07-19 22:39:35 +02:00
Federico Jaramillo Martinez d5650aef5b fix: scope notifications to the selected chat 2026-07-19 10:37:36 +02:00
Federico Jaramillo Martinez df8cf5a250 Merge pull request #77 from jmfederico/agent/session-notification-inbox
feat(sessions): add ephemeral notification inbox
2026-07-19 06:15:05 +02:00
Federico Jaramillo Martinez b85e1b9ebe fix: show project activity for external worktrees 2026-07-19 06:13:09 +02:00
Federico Jaramillo Martinez 503c2c743d fix(sessions): harden notification inbox reconnects 2026-07-19 01:49:06 +02:00
Federico Jaramillo Martinez d77ca66f28 chore(release): v1.202607.1 2026-07-19 00:37:48 +02:00
Federico Jaramillo Martinez 008c83bf72 Merge pull request #73 from jmfederico/agent/pr-64-audit
fix(auth): preserve browser auth-flow ownership
2026-07-18 22:14:44 +02:00
Federico Jaramillo Martinez d29fbb98b2 Merge pull request #70 from jmfederico/agent/issue-67
fix(terminals): load interactive login profiles
2026-07-18 21:46:57 +02:00
Federico Jaramillo Martinez 62396c3165 fix(auth): bind flows to their originating machine
Retain client-owned machine affinity for each interactive auth flow and use it for prompt responses, polling, cancellation, and completion refreshes. This prevents a later machine selection from forwarding secrets to a different remote.\n\nRefs #74
2026-07-18 21:09:51 +02:00
Federico Jaramillo Martinez 67f673b227 fix(auth): cancel stale started flows
Best-effort cancel a running auth flow when its start response arrives after the browser operation was closed or superseded, so sessiond does not retain orphaned provider polling or callback listeners.\n\nRefs #72
2026-07-18 20:55:28 +02:00
Federico Jaramillo Martinez d72a0012c7 fix(sessions): surface extension command notifications 2026-07-18 20:49:41 +02:00
Federico Jaramillo Martinez 2b1507ba35 fix(terminals): load interactive login profiles 2026-07-18 20:47:07 +02:00
Federico Jaramillo Martinez 5ebcd34690 docs: clarify supported Pi range 2026-07-18 08:40:12 +02:00