Repository navigation
M4 (3/3): timelines, failed deliveries, tasks and the taskbar mark - #7
Merged
Merged
Conversation
The app's forms need the daemon's length limits and color palette; the generated file now carries them so the UI never restates them.
Commands for what the page cannot do (spec 15.2): daemon_status checks /health on the port from <home>\config.toml, daemon_start launches the botloftd.exe next to the app detached so it outlives the window, read_owner_token reads the owner token, and open_path opens folders only. The window has no decorations (the app draws its own title bar), and the CSP allows ws://127.0.0.1 on any port so a dev daemon with its own port is reachable.
rpc.ts speaks spec 11 over WebSocket: session.hello first, requests queued until it is accepted, answers matched by id, typed RpcError codes, and reconnection with backoff (0.5 s doubling to 5 s). Requests already sent when the socket drops fail instead of being replayed, since they may have run. A refused hello (token or protocol) stops for good. BotloftApi is the contract components use; client.ts implements it over rpc.ts and FakeBotloft in memory, typed from the generated RpcMethods so every method is covered. Host is the same idea for the Tauri commands and the window.
- Onboarding: finds the daemon, offers to start it, reads the owner token and connects; a taken port, another protocol version or a refused hello each get their own message. The first run lists what is ready (daemon, Claude Code) and explains the one-time folder trust prompt per bot. - Store: crews, bots and system.status load on every (re)connection, then follow crew.changed, bot.changed and bot.state; archived records leave. - Screens: crews and their bots in the sidebar, a crew overview with bot cards, and a bot view with pause, restart (fresh or not), edit, open folder and archive. State reads as color, icon and label; paused bots read as paused. Daemon validation errors stay in the form. - Own title bar with Windows controls, light and dark themes following Windows, IBM Plex type, bot avatars drawn from the mascot. - `pnpm dev` in a plain browser runs the UI against FakeBotloft, for UI work without Tauri or the daemon. The preview is not in release builds.
The docs say a parent folder's trust does not cover a project's permissions.allow rules, and the generated settings allow mcp__botloft, so every new bot asks once; trusting the workspaces root would not help. The app explains the prompt instead, and Botloft leaves ~/.claude.json alone. Spec 15 gains daemon_start (M4, replaced by the service in M5) and how the app finds the daemon's port. The dev config now lives inside the dev BOTLOFT_HOME so the daemon and the app share it.
The replay carries the queries a terminal answers (cursor position, XTVERSION, keyboard flags). A client that answers them again sends stale replies into the bot as keystrokes; with Claude Code 2.1.284 a stray character showed up in the prompt after attaching. The attach result now has liveOffset, where live output starts, so the client can leave the queries before it unanswered (spec 8).
- TerminalSession follows spec 8 without depending on xterm: attach and replay, a newer generation is a new screen, a gap attaches again from the last byte, duplicates and old generations are ignored, and a new connection resumes where the old one stopped. Replayed bytes are marked as history; the view drops what xterm "types" while parsing them. Keys typed while disconnected are dropped instead of sent late. - TerminalView: xterm.js with fit and WebGL, a dark screen in both themes, Ctrl+C copies a selection, and size changes reach the PTY. - The bot view has Terminal and Details tabs; the terminal stays mounted across tabs. A start that takes more than 4 s explains the one-time folder trust prompt; a bot that never started says how to start it. - FakeBotloft gains terminals: buffers, replay after the attach answer, live output, input and sizes. The dev preview shows sample screens, and `?live=<port>` connects it to a real dev daemon. Checked with Claude Code 2.1.284 through the preview connected to a dev daemon: the trust prompt showed in the terminal and was accepted with Down and Enter, the bot went idle, a typed prompt made it busy and back to idle with the answer on screen, and a reload replayed the screen without stray input.
The store now keeps deliveries by message (the 500 most recently updated and every dead one, then each delivery.changed) and every task (then each task.changed); loading moves to store/sync.ts. Messages stay out of the store: each timeline pages its own. FakeBotloft's messages, deliveries and tasks move into FakeConversation, with helpers to play the bots and the courier in tests (say, deliver, task); terminal handlers move into FakeTerminals. The fake's clock is the real one, so deadlines read as they will in the app.
- Timelines: a crew's messages and a bot's (to or from it), oldest first, following new ones while the owner reads the end, with older pages on demand. Each message shows sender, recipient, task or result tag with the task's state, and where its delivery is: waiting, retrying with the next try and last error, delivering, delivered, or not delivered with Retry. - The owner writes from a composer: to any bot of the crew from the crew timeline, to the bot itself from its Messages tab. Ctrl+Enter sends. - Failed deliveries: a title-bar button while any message to an active bot gave up, opening the list with each message, its error and Retry (or Retry all). - Tasks tab: open tasks by default, every task on demand, with status, requester and assignee, hop, deadline or when it settled, and result. - Taskbar overlay (spec 15.2): a dot on the app icon while a bot waits for approval or sign-in, or a message to an active bot was not delivered. It uses the window's setOverlayIcon; no command of our own. - Spec 9.3 records what the real test showed: Claude Code presents inbox messages, the owner's included, as coming from another session, so a timeline message cannot approve a prompt; that happens in the terminal. Checked with Claude Code 2.1.284 through the dev preview on a real daemon: a message from the bot's composer was delivered by the courier, the bot went busy and back to idle, and its answer showed in the terminal.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last of three slices of M4: messages, deliveries and tasks in the app. This PR is built on #6, which is built on #5. Merge them in order (merge commits); this diff then shrinks to the two commits below. With it, M4 is complete: crews, bots, terminals, the timeline and failed deliveries all work from the app, without opening a terminal.
What changes
delivery.changed.task.changed.store/sync.ts.setOverlayIcon(new permissioncore:window:allow-set-overlay-icon), with no command of our own.FakeConversationplays the bots and the courier in tests (say,deliver,task), andsrc/test/app.tsxrenders the whole app. The fake's clock is now the real one.Found in the real test (spec 9.3)
Claude Code 2.1.284 presents everything from the inbox as coming from another session and not typed by the user, including the owner's message. It also adds a note that a peer cannot grant permissions or approve a pending prompt.
So a timeline message has a colleague's authority. To approve a prompt or change settings as the bot's user, the owner types in the bot's terminal. The "Needs approval" notice already points there.
Tests
messages.test.tsx:needs_approval.pnpm check,pnpm build, clippy (including the Tauri capability check) and fmt pass. Each commit passes on its own.Checked with real Claude Code 2.1.284
The test used the dev preview connected to a real dev daemon.
[botloft] from the owner · crew …envelope.Manual test (Tauri window)