Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
050db56
Plan: Bot chat activity slice C2 (Bots sidebar and side panel)
sambitcreate Oct 14, 2026
7c1cd95
Bots: route helpers for the Bots sidebar and panel shortcut
sambitcreate Oct 14, 2026
f67a617
Bots: per-device pin and last-viewed preferences for the Bots sidebar
sambitcreate Oct 14, 2026
a68beab
Bots: list summaries carry Needs you and the last reply time; bots:at…
sambitcreate Oct 14, 2026
22a8a7d
Bots: guard sidebar preference pruning and cover the preference hook
sambitcreate Oct 14, 2026
cd3a90c
Bots: testable attention signal and fresher needsYou
sambitcreate Oct 14, 2026
027a90d
Bots: Bot panel state apart from the workspace panel; route the toggl…
sambitcreate Oct 14, 2026
fbb60ec
Bots: one live subscription per Bot, shared by the chat and the panel
sambitcreate Oct 14, 2026
8e502d5
Bots: scope Bot panel file requests to their Bot and keep actions stable
sambitcreate Oct 14, 2026
c148e94
Bots: keep the shared live store registered across remounts and keep …
sambitcreate Oct 14, 2026
a03e70f
Bots: the Activity log, every step grouped by reply
sambitcreate Oct 14, 2026
9c9a413
Bots: the Activity log treats only an open reply as live
sambitcreate Oct 14, 2026
f850226
Bots: the Bot side panel with Activity, Files, Memory and Routines
sambitcreate Oct 14, 2026
33e0312
Bots: file chips, Files and Memory open the side panel; the Files dia…
sambitcreate Oct 14, 2026
196081b
Bots: one ••• menu with Routines and Pin to top on every Bot surface
sambitcreate Oct 14, 2026
31bc590
Bots: the Bot panel shows its failures and re-renders only what changed
sambitcreate Oct 14, 2026
de5d913
Bots: a Bots-only sidebar with pinned Bot, working faces, Needs you a…
sambitcreate Oct 14, 2026
2181c98
Bots: /bots opens the most recently used Bot; the list page is removed
sambitcreate Oct 14, 2026
87d6c24
Bots: the sidebar reads only the Bot that changed; tests prove real n…
sambitcreate Oct 14, 2026
78a0751
Bots: e2e for the sidebar and panel; onboarding tile, spec rulings, m…
sambitcreate Oct 14, 2026
7c8996a
Bots: unread survives a mid-stream leave; delete, Files focus and liv…
sambitcreate Oct 14, 2026
24cdafd
Bots: the panel sits beside the chat; menu icons, inline markers, one…
sambitcreate Oct 14, 2026
392d8b7
Bots: C2 docs list what stays parked after the final fix wave
sambitcreate Oct 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion .memory/bot-chat-activity.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ Spec: docs/superpowers/specs/2026-10-14-bot-chat-activity-design.md (§17 lists
- Queue/Steer: `pi.inbox` projected as `snapshot.queued` — the person's own inputs only, classified by durable submission requestId (`routine:`/`intro:`) plus the hidden flag, failing closed. Follow-ups show faded with Remove (`bots:withdraw` → `Harness.abortSubmission`: withdrawn | started | gone); steers are normal bubbles.
- Advanced → Chat activity (`?page=chat-activity`, `bot-chat-activity-page.tsx`): saves on pick through a serial queue, per-level Reduce Motion still frames, Reduce Motion read live.
- `BotAvatar` faces glance via `data-working` with a shared off-screen observer; list's green dot removed. Under Reduce Motion (setting or OS) a working face holds the glance's look-aside pose (`translateX(-2.6px)` on the eyes, no animation), so it still differs from idle.
- Onboarding: the Bots gallery tile (`BotsArt`: name pill, one exchange, routine chip) shows neither the list nor the Updates line, so it needed no redraw.
- Onboarding: the Bots gallery tile (`BotsArt`: name pill, one exchange, routine chip) shows neither the list nor the Updates line. C2 changed only its header (panel toggle instead of a back chevron).

## For slice D (phones)
- Remote wire is unchanged by B. D adds `activity` (phase, app ≤ 40, startedAt only) to the session stream and `chatActivity` to detail/PATCH behind `bot-activity-v1`; reuse `botActivityDisplay`, `botElapsedLabel` and `nextBotAnnouncement` semantics and the §5 table as the shared fixture.
Expand All @@ -30,3 +30,18 @@ Spec: docs/superpowers/specs/2026-10-14-bot-chat-activity-design.md (§17 lists
- Queue: Remove focus moves before the withdraw resolves; the fade couples to `MessageBubble`'s private `bg-control` class (prefer a variant prop); the hidden flag is not durable (latent); a missing-id streak is never logged; no backoff on permanent inspect failure (1/s per live feed); an `openFeed` failure could orphan a retry timer.
- Runtime: `deps.state` rejection at run end leaves `{preparing}` stuck (pre-existing fallback); interrupted-with-run and approval-flip lack projection-level tests; `onResumed` → projection wire (`bot-session-main.ts`) is untested end to end and unguarded inside serialize.
- Chat activity page: `useReducedMotion` subscribe closure re-subscribes every render (hoist); Summary still-frame folded state not asserted; Pause under Reduce Motion freezes mid-loop.

## Slice C2 (desktop) — what shipped
- Bots sidebar: `renderer/main/bots/bot-sidebar.tsx`, rendered by `ChatSidebar` on any Bots path (`isBotsPath`, `renderer/lib/bot-routes.ts`). Rows from `useBotRows` (`bot-sidebar-rows.ts`): `bots:live:summary` now carries `needsYou` and `lastReplyAt`; `bots:attention` (questions/approvals) and `bots:changed` re-read only the affected Bot's row, synced once per window by `useBotNotificationsSync` (`renderer/lib/bot-notifications-sync.ts`, mounted in `root-view.tsx`). Back to Aiden, Search and New Bot sit at the top of the sidebar body, not the title bar (window controls overlap it). Back goes to `lastNonBotRoute()` (recorded by `ChatLayout`).
- Pin, last-viewed and seen reply: `aiden-agent.bots-sidebar.v1` (`renderer/lib/bot-sidebar-preferences.ts`, hook `use-bot-sidebar-preferences.ts`, selector `useIsBotPinned`), with `since` so old replies are never unread. Unread is `lastReplyAt > (seenReplyAt[bot] ?? since)`: `seenReplyAt` is `botLastReplyAt` of the entries the chat showed (main's clock; a reply streaming when the person leaves is not in them yet), `lastViewed` (this Mac's clock) only picks the Bot `/bots` opens. Pruned only after a settled successful load. Not synced to phones.
- `/bots` (`BotsIndex` in `bots-view.tsx`) redirects (replace) to the last viewed Bot's chat, else the newest-activity Bot (summaries awaited up to 800 ms, then `bot.updatedAt`), showing `BotPageSkeleton` meanwhile; the starter carousel when there are none; the create flow suppresses the redirect. `bot-list.tsx` is gone.
- Bot panel: `BotPanelProvider` (`bot-panel-context.tsx`, keys `aiden-agent.bot-panel.*`) at the root; `BotWorkbench` (`bot-workbench.tsx`) on the C1 `WorkbenchPanel` (`id="bot-panel"`, optional `surface`, `tabMenu` and `widthBounds` props added to the shell; `BOT_PANEL_WIDTH_BOUNDS` 400–720, default 440, in `environment-panel-layout.ts`, so it sits inline beside the chat where the chat keeps 560px and floats only where the workspace panel would too), fixed tabs Activity (`bot-panel-activity.tsx`, `bot-activity-log.ts`; always the full log, independent of the Chat activity level), Files (`bot-panel-files.tsx`, replaced `bot-files-dialog.tsx`), Memory (`BotMemoryContent` shared with Profile → Memory), Routines (`BotRoutines`). Chat route only.
- `environment.toggle` → `environmentToggleTarget(pathname)`: Bot panel in a Bot chat, nothing elsewhere in Bots, workspace otherwise. `EnvironmentWorkbench` is suppressed (state kept, `data-state="covered"`) on Bots paths.
- One live subscription per Bot per window: a refcounted store behind `useBotLive` (`renderer/lib/use-bot-live.ts`); the chat holds it while shown and the Activity tab (memoised on entries/running) shares it. A consumer joining a failed store keeps the last view (flagged failed) while it retries.
- Turns: `walkBotTurns` (`renderer/main/bots/bot-turns.ts`) is the one stop/interrupted/carried-on state machine; `botTranscriptRows` and `botActivityLog` both build on it.
- One ••• menu: `bot-menu.tsx` (`BotMenuItems` with leading icons, `MemoryCardIcon` for Memory; `useBotMenuActions` reads the open chat from `router.state` when an item is chosen, so a Bot switch hands rows no new callback); closing it returns focus to the trigger unless it opened the panel. Deleting a Bot navigates to `/bots` only when that Bot's page is on screen.
- Bots on paired Macs: `RemoteBotsSection` rows in the sidebar ("On {Mac}"), filtered by `remoteBotGroupsMatching` before they are passed in. Search (`renderer/lib/search-fold.ts`) folds only Latin accents.
- "All Bots" stays only where no Bot chat exists to go back to: Profile's loading and not-found shells and the chat's not-found shell.
- Test harness: Bot renderer suites run with `NODE_ENV=test` (`renderer/main/bots/test-env.ts`, first import of `test-dom.ts`, which throws if the router's server build loaded anyway) so TanStack Router behaves as on a client and routed pages re-render; `mountWithBotRouter` takes `sidebar` and mounts inside `act`. Wrap test navigations, sleeps and emitted notifications in `act` (bot-sidebar, bot-chat-pane, bots-home and bot-panel print no act warnings).
- e2e: `tests/e2e/bots-sidebar-panel.spec.ts` (1280×800: the panel leaves the composer uncovered; the workspace surface is inert; `bots:attention` reaches a row through the app's own sync).
- Parked: workspace sidebar remount loses its local state after a Bots visit; batched `bots:live:summaries` IPC (N reads on mount); Profile→Chat pushes history (pre-existing); remote-only shows "Meet your first Bot"; the device mini player reconnects while suppressed (pre-existing); panel close returns focus to the toggle, not the composer (briefed); chip names are basenames (pre-existing); test debt: `lastNonBotRoute` module-state seam and edge cases, dead `deferred()` lines in the Files keep-on-refresh test, the render probe never uninstalls, Task 2 review minors 3, 4 and 6.
6 changes: 3 additions & 3 deletions .memory/bot-durable-runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ Plan: `docs/superpowers/plans/2026-10-07-bots-rework.md`. Spec: `docs/superpower
- `main/services/bot-runtime/live-projection.ts`: one `conv.watch` feed per Bot, `(epoch, seq)` events, >100 pending collapses to a snapshot, `[SILENT]` turns hidden, connect cards re-resolved on every subscribe. IPC: `bots:live:subscribe|unsubscribe|summary` (`main/handlers/bot-live.ts`), push `bots:live:event`.
- `renderer/lib/use-bot-live.ts` applies events and re-subscribes on a gap or epoch change.
- Chat route is `/bots/$botId/chat` (no chat id). Tool approvals show as a card in the chat (`bots:pendingApprovals`, `bots:approval`, `bots:approval-settled`, answered with `bots:approve`). `renderer/main/bots/bot-chat-pane.tsx` renders the transcript, the interrupted card (Resume/Dismiss, access-changed copy) and the composer (`Composer` with a `placeholder` override). Sends use `bots:send` with a UUID per message; Steer/Queue map to `whenBusy`; Stop maps to `bots:stop`. `bots:openChat` is gone. A legacy `/chat/<botChatId>` redirects to the Bot route; `ChatPane` renders nothing while it redirects, and its former Bot-mode branches are removed (`MessageList` has no `botPresentation` prop).
- List previews and state come from `bots:live:summary`, invalidated on `bots:changed`.
- Sidebar previews and state come from `bots:live:summary`, re-read per Bot on `bots:changed` and `bots:attention` (`useBotNotificationsSync`).
- Create flow: two steps (name and help, then `ConnectionChips`; Skip creates). The self-intro (`bots:introduce`) runs only for a Bot made here with a model. `sendBotIntro` itself refuses unless the Bot is idle and its conversation has no `pi.user`/`pi.assistant` entries, and its fixed `intro:<botId>` request id dedupes repeats.
- Start Chat has one process-wide starter (`botStarter()`, keys in `bot-creation-keys.json`). Remote `POST /bots/from-preset` reaches it through `createBotPresetCreatorFor` (`main/services/bot-preset-store.ts`), passing only its own `createBot` (Remote audience), so Mac and phone taps share keys, the in-flight lane and the one self-intro. Crash-safe: the starter mints the Bot id and saves `preset:<id>` → id before creating the Bot with exactly that id (`botApplicationService.createBot({ botId })`), so a crash before or after the create never makes a second Bot (a key whose Bot is missing is forgotten).
- `bots:live:subscribe` tracks the newest wanted subscribe per document and Bot; one that finishes after an unsubscribe (chat left while opening) releases itself. Deleting a Bot closes its projection (subscribers marked closed). `useBotLive` exposes `failed`; the chat shows Try again instead of loading forever.
- `bots:connections:setup` is handled by `BotConnectionSetupHost`, mounted in `root-view.tsx`, so a phone's request opens setup from any page. It is sent to one window only: the focused one, else the most recently focused (`bot-connection-setup-core.ts`).
- First run: `renderer/main/bots/bot-starter-carousel.tsx` is the empty state of the Bots list and the onboarding step's body (`renderer/components/onboarding-bots-step.tsx`, `bots:createFromPreset`). Onboarding progress is the persisted `lastSatisfiedStep` enum; `"bots"` sits between `provider` and `tour`. The step is offered only with the Bots capability and zero Bots (decided when onboarding loads); Start Chat and Skip both persist `"bots"`. Playwright e2e: `tests/e2e/bots-starter.spec.ts`; flows that only need the tour call `skipBotsOnboardingStep` in `tests/e2e/fixtures.ts`.
- First run: `renderer/main/bots/bot-starter-carousel.tsx` is the empty state of `/bots` (no Bots yet) and the onboarding step's body (`renderer/components/onboarding-bots-step.tsx`, `bots:createFromPreset`). Onboarding progress is the persisted `lastSatisfiedStep` enum; `"bots"` sits between `provider` and `tour`. The step is offered only with the Bots capability and zero Bots (decided when onboarding loads); Start Chat and Skip both persist `"bots"`. Playwright e2e: `tests/e2e/bots-starter.spec.ts`; flows that only need the tour call `skipBotsOnboardingStep` in `tests/e2e/fixtures.ts`.
- Profile: `bot-routines.tsx` and `bot-routine-editor.tsx` (`bots:routines:*`, labels from the host), photo menu (`bots:photo:set|remove`).
- Connections: `use-connection-setup.tsx` opens `PresetSetupDialog`; `main/services/bot-connection-setup.ts` `openConnectionSetup(pluginId)` broadcasts `bots:connections:setup` (for Remote "Finish on your Mac" once that route calls it). `bots:connections:dismiss` persists Not now.
- Tests: `bot-chat-pane.test.tsx`, `bot-routines.test.tsx`, `bots-home.test.tsx`, `bot-create-flow.test.tsx`; the test harness mounts the chat route through `test-providers.tsx` (and `installBotTestIpc` can push notifications with `emitBotTestNotification`).
Expand Down Expand Up @@ -77,7 +77,7 @@ Plan: `docs/superpowers/plans/2026-10-07-bots-rework.md`. Spec: `docs/superpower
## Chat follow-ups (PR #377 gap pass, 2026-10-07)
- Failed turns: `projectBotTranscript` turns an `error` reply into a `failed_turn` entry (id `<assistantId>:failed`, after any text it wrote) with `retryText` = the turn's typed message (null for routines, the self-intro and photo-only turns) and the Mac-only `errorMessage`. Remote revision 25 carries `{type:"failed_turn", id, createdAt?, retryText?}` (fixture `botSessionFailedTurns`). Every client shows "I couldn't finish that reply."; only the newest entry offers Retry (`botRetryableFailedTurn`), which sends `retryText` as a new submission with a new request id / Idempotency-Key.
- File chips: a confirmed `write_file`/`edit_file` result (`details.producedFile`) with no `location` argument becomes a `file` entry; `aiden.bot-shared-image` becomes a `shared_image` entry. Desktop only: Remote entries carry no file metadata, so phones show no chips.
- Desktop Files: `bots:files:list` / `bots:files:read` (`main/handlers/bot-files.ts`) read the Bot's folder (`resolveManagedWorkspace(botId).homePath`) read-only; `BotFilesDialog` opens from ••• Files or a file chip. Phones use `/bot-conversations/:chatId/files` (chatId from the Bot's canonical conversation).
- Desktop Files: `bots:files:list` / `bots:files:read` (`main/handlers/bot-files.ts`) read the Bot's folder (`resolveManagedWorkspace(botId).homePath`) read-only; the panel's Files tab (`bot-panel-files.tsx`, which replaced `BotFilesDialog` in C2) opens from ••• Files or a file chip. Phones use `/bot-conversations/:chatId/files` (chatId from the Bot's canonical conversation).
- Desktop transcript rows come from `botTranscriptRows` (`renderer/main/bots/bot-transcript-rows.ts`): one Updates line per reply, chips, failed cards. Messages have Copy and Reply (right-click or hover/focus •••); Reply quotes into the composer. A Stop button sits under the question card.
- Onboarding Start Chat opens `/bots/$botId/chat` once onboarding finishes (`OnboardingFlow onOpenBotChat`).
- Bot store: no legacy avatar compatibility; a stored avatar that is not a current recipe reads as `DEFAULT_BOT_AVATAR`. The companion `bot-avatar-appearances.json` is no longer read or written.
Expand Down
Loading
Loading