diff --git a/AGENTS.md b/AGENTS.md index 080bd2a..9acb834 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -44,7 +44,7 @@ When the owner tells the lead "deploy byre", or anything that means the same, th - Exactly two panes, side by side, each exactly half the width, at every window size. Never three or more columns, never a sidebar, never a resizable or collapsible split. - No navbar. The panes fill the window, below only the identity banner when it shows. -- The left pane has six tabs: Lead (the user's lead agent), Notes (what the lead has noted about the user), Workflows (the saved workflows), Status, Server (the byre service on this Mac) and Settings (the user's and this client's preferences), with the connection dot and the user's name at the end of its tab strip. Nothing else goes in it. The right pane always shows one task, through tabs of that task's views; the lead sets the task, which tabs come first and which one is selected, and the human can switch the task (⌘K) and tabs too. A new task view is a new tab in the right pane, never a new column or region. +- The left pane has six tabs: Lead (the user's lead agent), Notes (what the lead has noted about the user), Workflows (the saved workflows), Status, Server (the byre service on this Mac) and Settings (the user's and this client's preferences), with the connection dot and the user's name at the end of its tab strip. Nothing else goes in it. The right pane always shows one task, through tabs of that task's views; the lead sets the task and which tab is selected, never the tabs' order or look, and the human can switch the task (⌘K) and tabs too. A new task view is a new tab in the right pane, never a new column or region. - Overlays (the task switcher, confirmation dialogs) are allowed; they sit above the panes and don't change the layout. ## UI look diff --git a/api/openapi.yaml b/api/openapi.yaml index cc3809b..0834c11 100644 --- a/api/openapi.yaml +++ b/api/openapi.yaml @@ -1750,14 +1750,10 @@ components: Focus: type: object - required: [tabs, tab] + required: [tab] properties: project_id: { type: string, description: The project the right pane shows; the task's project when task_id is set, absent for none. } task_id: { type: string, description: The task the right pane shows; absent for none. } - tabs: - type: array - description: The tabs the lead put first, in order. The other task tabs follow them. - items: { $ref: "#/components/schemas/TaskTab" } tab: { $ref: "#/components/schemas/TaskTab" } LeadNote: diff --git a/docs/macos.md b/docs/macos.md index 85f88e0..21df60d 100644 --- a/docs/macos.md +++ b/docs/macos.md @@ -87,7 +87,7 @@ One window (a `Window` scene, id `main`) shows one saved server at a time: `Serv At launch the window shows the oldest saved server. A CloudKit import that brings a server saved on another Mac starts its connection, and the window takes it while it shows an empty connection screen. A CloudKit import that removes the current server switches to the first saved one. SwiftUI's own window restoration is off (`restorationBehavior(.disabled)`); the scene keeps the id `main` that earlier builds used, because AppKit restores their saved window by that id and SwiftUI then opens no launch window at all when the id names no scene. - **Left pane:** tabs Lead, Status, Server, Settings, with "reconnecting…", the connection dot and the user's name at the end of the strip. -- **Right pane:** the task bar (task picker ` ▸ ▾` and the preview links), then the task's tabs in the server's focus order, hiding Questions, Reviews and Workflow when they have nothing, with a **More ▾** menu for tabs that don't fit, carrying their badges. The pane's accent is the selected task's colour. +- **Right pane:** the task bar (task picker `<project> ▸ <title> ▾` and the preview links), then the task's tabs in their fixed order, hiding Questions, Reviews and Workflow when they have nothing, with a **More ▾** menu for tabs that don't fit, carrying their badges. The pane's accent is the selected task's colour. Overlays (the switcher, alerts, confirmations, the merge dialog, keyboard help) are SwiftUI views drawn above the panes inside the window, not system sheets or alerts. The switcher and the Discard confirmation sit just below their anchor; the merge dialog is centred in the right pane. diff --git a/docs/operations.md b/docs/operations.md index 1e62696..5d53335 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -271,8 +271,8 @@ Each user has one long-running lead agent, shown in the left pane. It is not tie - **Scrollback.** `GET /api/me/lead/events` returns a page of the caller's lead events in whole turns, `turns` of them (default 50): those after the `turns`+1'th `turn.ended` before `before` (or before the latest event, so the page also holds a turn still running), with `more` saying whether earlier events exist. The lead pane loads only the last page (see "Events"); when its top scrolls into view (an empty row kept first in the transcript), it fetches the page before its first event (`loadOlderLead` in `mac/Sources/Byre/Store.swift`), puts its rows in front (`TranscriptBuilder.prepend`) and scrolls back to the row that was first. A page starts just after a `turn.ended`, so rendering it from scratch matches how it drew as part of the whole history. Live events still append without limit until the app is relaunched. - **Signals.** Every published event goes through `Manager.Observe`. These become signals in `lead_inputs` for every user who has a lead session: `inbox.created` (a question with its choices and recommendation), `session.error` and `session.rate_limited` on a task, `inbox.updated` when a human answered a question (the signal also carries its text and the agent's recommendation, so the lead sees where the human departed from it), `task.created` (naming the TODO item it was started from, or a subtask's parent, if any), `task.merge_queued` ("<task>'s merge was requested by <login> and is held until the merge in progress in its project is done."), `task.merge_after_review` ("<task> was set by <login> to merge once the review of snapshot <n> finishes with no findings."), `task.merge_after_review_cancelled` ("<task>'s merge after review was cancelled by <actor>."), `task.merge_after_review_dropped` ("<task> didn't merge after the review: <reason>"), `task.merge_started` ("<task> is merging, started by <login>."), `task.merged`, `task.discarded`, `turn.ended` with `summary: true` ("<task> finished a turn:" with its `result` cut to 1500 characters), `task.workflow_stage` ("<task> started stage "<stage>" of workflow "<workflow>".") and `task.workflow_finished` ("<task> finished workflow "<workflow>":" with its output cut to 1500 characters), `review.started` ("<task> started a review of snapshot <n>."), `review.finished` ("<task> review of snapshot <n>:" with the review's summary, `change` and findings, or why it failed or was skipped; `reviewLine` in `internal/agents/review.go`), `user.message` on a task from a human (see "Pane lock"), and `task.state` ("<task> state: <from> → <to>.", or "<task> state: <to>." for a new task; see "Task state in the store"). The store hands `Observe` all of one transaction's events together, and they become one signal, its `task.state` lines first, so a turn's summary always arrives with the state its turn end left the task in. The human switching the right pane is not a signal. A signal caused by the lead itself (actor `<login> (lead)`) is skipped, as is the update from an answer it gave. A task's `task.created` event carries the actor that created it, separately from its `created_by` login: tasks the lead creates with `create_task` or `start_todo` keep the human's login as `created_by` but have actor `<login> (lead)`. Whenever the lead is idle, byre sends the oldest held message from the human as its own turn, or else every held signal as one `byre` message headed "Signals since your last turn (n)", followed by "Tasks now:" with the `list_tasks` text and a line saying what the lead's right pane shows (task or project, and selected tab) and whether the human is working in it (`leadStateText` in `internal/agents/lead.go`). If collecting that fails, for example because a project's repository is gone, the batch is still sent, ending instead with "The task list and right pane state are unavailable:" and the error, which is also logged as `lead state`. Its guidance tells it not to respond to the human's own actions, and to make its final message exactly `NO_REPLY` when a batch needs nothing from it. A transcript (`quietReply` in `mac/Sources/Byre/TranscriptBuilder.swift`) never draws an `agent.message` whose last line is `NO_REPLY` (the model sometimes writes a sentence before it), and hides the live partial message while its text is still a prefix of it, so an empty one never takes up space. A lead turn that byre starts without a handoff (a signal batch or a handoff continuation) is held (`ctx.held`): its `byre` message is never drawn, the turn isn't opened, so the scanner stays off, its partial message stays hidden, and its tool calls, subagents and thinking are built into a held row list instead of the transcript. The turn is released, inserting those rows where the turn belongs, when it draws anything else: a real message, a note, or its own `turn.ended` when it ends without `NO_REPLY`. When it ends in `NO_REPLY` while still held, the held rows are dropped, so nothing of the turn ever appears; a turn that draws a real message anywhere in it keeps all of it, even when a later message is `NO_REPLY`. - **Tools.** `list_tasks` (each project's active tasks with their state, each subtask noting its parent, a held merge (`merge_queued_at`) and a merge after review (`merge_after_review`) each noted, then a line listing the titles of its `TODO.md` items on the default branch that no active task was started from), `read_transcript` (a task's events in brief, the last `limit` or those after `after_seq`; closed tasks too), `search_closed_tasks` (see "Closing a task"), `read_inbox`, `read_status` (the `GET /api/admin/status` body as JSON, built by the same `Server.status`, which the server hands `agents.New`), `answer_question` (an `option` or `other` response recorded as the user, through the same checks as `POST /api/inbox/responses`), `send_message` (starts the task's agent if it has none; while it is mid-turn the message is queued through `Manager.Message` like the human's, see "Agent sessions", and the tool result says it was queued; on a team task it posts to the board, see "Teams"), `create_task` and `start_todo` (each with an optional `workflow` to freeze and run; `create_task` also takes `agents` to start a team), `create_project` (`name` and `repo_path`, created as the user by the same `Server.createProject` as `POST /api/projects`, which the server hands `agents.New`, so it publishes the same `project.created` event, and returned as JSON), `interrupt`, `compact_task`, `take_snapshot` (see "Snapshots"), `merge_task` (optional `target`: `main`, or `parent` for a subtask, see "Subtasks"), `discard_task`, `show`, `compact_self`, the note tools (see "Notes") and the workflow tools (see "Workflows"). The lead also has Claude Code's own tools and reads worktrees by path. Messages it sends task agents have actor `<login> (lead)`, so they don't make the user a participant; tasks, merges, discards and answers are done as the user. Its guidance says to answer a question only when the answer is clear or the human gave it, and to merge or discard only when the human asks. -- **Focus.** `users.focus` holds what the user's right pane shows: `project_id`, `task_id`, `tabs` (the tabs put first, in order) and `tab` (the selected one). With a `task_id`, `project_id` is always that task's project, filled in by the server; with only a `project_id`, the pane shows that project with no task, which only the Project tab has anything for. `GET /api/me/focus` reads it; `PUT /api/me/focus` and the lead's `show` set it, refusing an unknown tab, a task that isn't active or an unknown project, and record a `focus.updated` event owned by the user. The app applies the focus at startup and on every `focus.updated` it didn't cause itself; the human's own changes (⌘K, the right pane's task picker, clicking a tab, a task link, a notification) are sent with `PUT /api/me/focus`. -- **Pane lock.** A `show` of what the pane already shows (the same task, or the same project with no task, on the same selected tab; `sameView`) tells the lead so and applies only its `tabs` order, whether or not the human is in the pane, so the human's selected tab stays selected. Otherwise, while the human is working in the right pane (the app reports `focused` or `drafting`, below), the lead's `show` is refused with an error telling it to say in chat what it would have shown, unless its selected tab is `project` (the fallback the lead shows when nothing needs the human, so the human being there doesn't hold it) or the pane is finished (`Manager.paneFinished`): its task is merged, discarded or merging (`merging_by` or `finalizing` set), or its selected tab is `questions` and the task has no open items, or the human is waiting on the task's agent (`Manager.paneWaiting`): the selected tab is `transcript`, the task's implementer session is running, the app reports no unsent text in the pane, and either the `user.message` that started the running turn came from the user after the pane last moved, or the pane last moved 10 seconds ago or more (`watchHold`). The pane's last move is the last `PUT /api/me/focus` or `show` that set it (`Manager.paneMoved`, in memory, so a restart counts as long ago). For the user's own message, unsent text reported before the turn started doesn't count, since the app's report of the composer emptying on send can arrive after the message does. A `user.message` on a task from a human (not `byre`, an agent or a lead) signals every lead "<actor> sent <task> a message; its agent is working on it.", and the sender's own lead, when its pane shows that task and is waiting that way, also gets "Your right pane is free: the human is waiting on <task>'s agent." Each `PUT /api/me/focus` from the user starts a 10-second timer (`endWatchHold`); when it fires, if the pane hasn't moved since, the app still reports the human in it, the user hasn't messaged the task since the move (that already freed it, above), and the pane is waiting that way, the lead gets the signal "Your right pane is free: the human has watched <task>'s agent work for 10 seconds without typing." The lead's own `show` starts no timer. When an `inbox.updated`, `task.merge_started`, `task.merged` or `task.discarded` event leaves a user's focused task finished that way, that user's signal for it gets a line "Your right pane is free: <task> has nothing left in its inbox." (or "is merging."/"is merged."/"is discarded."), even when the event itself makes no signal, such as a withdrawn question; `inbox.updated` on a task that is no longer active adds none. The lead's guidance tells it to show the next thing that needs the human on that line, or, when nothing does, the project's Project tab with a short status in chat: one line per active task, then up to three TODO items it would start next, each with a reason, starting one only when the human picks it. It writes each item as a link to `#/todo/<project_id>/<URL-encoded title>`; Markdown views draw that (`todo` in `mac/Sources/Byre/MarkdownView.swift`) as the title followed by `start` and `discuss` actions, as accent link text so the line stays selectable, that call `POST /api/projects/{id}/todo/tasks` with the user's default model, as the Project tab's do, or, once an active task was started from that item, the task's colour, dot and state, linking to it. When the lead suggests new work that isn't in TODO.md, its guidance tells it to write a link to `#/suggest/<project_id>/<URL-encoded title>/<URL-encoded brief>`, drawn by the same code with `start` and `discuss` buttons: `start` calls `POST /api/projects/{id}/tasks` with the title, the brief as `prompt` and the user's default model, which checks the user's git identity, creates the task and starts its agent in one request, with no lead turn in between; `discuss` sends the lead `POST /api/me/lead/messages` asking to discuss the suggestion, quoting its title and brief (with the user's default model only when the user has no live lead session). Once an active task in that project has the suggestion's title, it shows that task's state the same way. A chat view refills them whenever its transcript grows or tasks or sessions change. When that status says nothing new since its last one, it replies `NO_REPLY` instead. The app sends `PUT /api/me/pane-focus` with `focused` and `drafting` whenever either changes (`PaneFocus` in `mac/Sources/Byre/PaneFocus.swift`): `focused` is true while the window is key, the app is active and the keyboard focus or the last click is inside the right pane, so a click anywhere in it counts, not only on inputs and buttons; `drafting` while any text field in the right pane other than the terminal holds non-blank text. The window reports to the server it shows. It sends false on deactivating, switching to another server, Forget Server, closing the window and quit, and resends true when its event stream reconnects. byre keeps both flags and the time of the last report in memory per user (`Manager.paneFocus`, `Manager.paneReported`), so they start false after a restart until the app resends them. +- **Focus.** `users.focus` holds what the user's right pane shows: `project_id`, `task_id` and `tab` (the selected one). The tabs' order is fixed; neither the lead nor the human can change it. With a `task_id`, `project_id` is always that task's project, filled in by the server; with only a `project_id`, the pane shows that project with no task, which only the Project tab has anything for. `GET /api/me/focus` reads it; `PUT /api/me/focus` and the lead's `show` set it, refusing an unknown tab, a task that isn't active or an unknown project, and record a `focus.updated` event owned by the user. The app applies the focus at startup and on every `focus.updated` it didn't cause itself; the human's own changes (⌘K, the right pane's task picker, clicking a tab, a task link, a notification) are sent with `PUT /api/me/focus`. +- **Pane lock.** A `show` of what the pane already shows (the same task, or the same project with no task, on the same selected tab; `sameView`) tells the lead so and changes nothing. Otherwise, while the human is working in the right pane (the app reports `focused` or `drafting`, below), the lead's `show` is refused with an error telling it to say in chat what it would have shown, unless its selected tab is `project` (the fallback the lead shows when nothing needs the human, so the human being there doesn't hold it) or the pane is finished (`Manager.paneFinished`): its task is merged, discarded or merging (`merging_by` or `finalizing` set), or its selected tab is `questions` and the task has no open items, or the human is waiting on the task's agent (`Manager.paneWaiting`): the selected tab is `transcript`, the task's implementer session is running, the app reports no unsent text in the pane, and either the `user.message` that started the running turn came from the user after the pane last moved, or the pane last moved 10 seconds ago or more (`watchHold`). The pane's last move is the last `PUT /api/me/focus` or `show` that set it (`Manager.paneMoved`, in memory, so a restart counts as long ago). For the user's own message, unsent text reported before the turn started doesn't count, since the app's report of the composer emptying on send can arrive after the message does. A `user.message` on a task from a human (not `byre`, an agent or a lead) signals every lead "<actor> sent <task> a message; its agent is working on it.", and the sender's own lead, when its pane shows that task and is waiting that way, also gets "Your right pane is free: the human is waiting on <task>'s agent." Each `PUT /api/me/focus` from the user starts a 10-second timer (`endWatchHold`); when it fires, if the pane hasn't moved since, the app still reports the human in it, the user hasn't messaged the task since the move (that already freed it, above), and the pane is waiting that way, the lead gets the signal "Your right pane is free: the human has watched <task>'s agent work for 10 seconds without typing." The lead's own `show` starts no timer. When an `inbox.updated`, `task.merge_started`, `task.merged` or `task.discarded` event leaves a user's focused task finished that way, that user's signal for it gets a line "Your right pane is free: <task> has nothing left in its inbox." (or "is merging."/"is merged."/"is discarded."), even when the event itself makes no signal, such as a withdrawn question; `inbox.updated` on a task that is no longer active adds none. The lead's guidance tells it to show the next thing that needs the human on that line, or, when nothing does, the project's Project tab with a short status in chat: one line per active task, then up to three TODO items it would start next, each with a reason, starting one only when the human picks it. It writes each item as a link to `#/todo/<project_id>/<URL-encoded title>`; Markdown views draw that (`todo` in `mac/Sources/Byre/MarkdownView.swift`) as the title followed by `start` and `discuss` actions, as accent link text so the line stays selectable, that call `POST /api/projects/{id}/todo/tasks` with the user's default model, as the Project tab's do, or, once an active task was started from that item, the task's colour, dot and state, linking to it. When the lead suggests new work that isn't in TODO.md, its guidance tells it to write a link to `#/suggest/<project_id>/<URL-encoded title>/<URL-encoded brief>`, drawn by the same code with `start` and `discuss` buttons: `start` calls `POST /api/projects/{id}/tasks` with the title, the brief as `prompt` and the user's default model, which checks the user's git identity, creates the task and starts its agent in one request, with no lead turn in between; `discuss` sends the lead `POST /api/me/lead/messages` asking to discuss the suggestion, quoting its title and brief (with the user's default model only when the user has no live lead session). Once an active task in that project has the suggestion's title, it shows that task's state the same way. A chat view refills them whenever its transcript grows or tasks or sessions change. When that status says nothing new since its last one, it replies `NO_REPLY` instead. The app sends `PUT /api/me/pane-focus` with `focused` and `drafting` whenever either changes (`PaneFocus` in `mac/Sources/Byre/PaneFocus.swift`): `focused` is true while the window is key, the app is active and the keyboard focus or the last click is inside the right pane, so a click anywhere in it counts, not only on inputs and buttons; `drafting` while any text field in the right pane other than the terminal holds non-blank text. The window reports to the server it shows. It sends false on deactivating, switching to another server, Forget Server, closing the window and quit, and resends true when its event stream reconnects. byre keeps both flags and the time of the last report in memory per user (`Manager.paneFocus`, `Manager.paneReported`), so they start false after a restart until the app resends them. - **Move hold.** Each `PUT /api/me/focus` from the user (switching task or tab, by any means: the switcher, a tab, a link, a notification, or the app's own switch to Questions when a question arrives) records the time in `Manager.humanMoved`, and for 10 seconds after it (`moveHold`) every `show` that would change the view is refused, whatever the pane lock above would allow, with an error giving how long ago the move was. A `show` of what the pane already shows still applies its tab order. A refusal also marks the user in `Manager.moveRefused`, which each move by the user clears. When the 10 seconds pass without another move by the user (`endMoveHold`, on a timer), byre clears both and, only if a `show` was refused since that move, sends the lead the signal "The move hold is over: …"; its guidance tells it to show what it held back if the pane's other rules allow, or reply `NO_REPLY`. Because only a refusal arranges the signal, the lead's system prompt and the `show` tool's description tell it to call `show` at once whenever the user asks to see something or it wants to show something, never holding back in expectation of a refusal. It is in memory, so a restart clears it. - **Start hold.** Start on a TODO item in the Project tab leaves the pane where it is. When the user starts one themselves (`StartTodo` with the user as actor, not the lead's `start_todo`), byre records the time in `Manager.startedTodo` for them, and for 10 seconds after it (`startHold`), while the pane's selected tab is `project`, every `show` that would change the view is refused, focused or not, with an error telling the lead the human is likely to start more. A refusal also marks the user in `Manager.startRefused`. The user's own `PUT /api/me/focus` (switching task or tab) or a message to the lead clears both, and another Start restarts the hold and clears the mark. The refusal and the `show` tool's description both tell the lead the hold lasts 10 seconds. When the 10 seconds pass without it being cleared or restarted (`endStartHold`, on a timer), byre clears both and, only if a `show` was refused since that Start and the pane's selected tab is still `project`, sends the lead the signal "The start hold is over: …" so it can show what it held back; its guidance tells it to reply `NO_REPLY` when that no longer matters. It is in memory, so a restart clears it. - **Compaction.** The pane's **Compact** button asks the lead for a handoff (`leadHandoffPrompt`). The lead can't see its own context size, so the first time one of its turns ends (other than a handoff or a `compact_self` turn) with the latest `session.context` `used` at half of its `window` or more, byre holds a signal for it giving the percentage and asking it to call `compact_self` at the next logical point (`warnLeadContext` in `internal/agents/lead.go`); it is sent once per live process, so a byre restart can send it again once the window is known. The lead calls `compact_self`; when that turn ends normally, byre sends the same request. The handoff turn ends the session, and a new lead session starts from `leadHandoffPreamble` plus the handoff; held input follows once that turn ends. @@ -281,7 +281,7 @@ Each user has one long-running lead agent, shown in the left pane. It is not tie Task agents get notes too. An implementer session's instructions end with `taskNotesGuidance` and `notesText` for the task's creator, limited to the every-project notes and the task's project's (`startLive` in `internal/agents/manager.go`), fixed when the agent process starts like the lead's. Reviewers get none. Implementers have a `suggest_note` tool (`text`, `evidence`) that queues a lead signal for the task's creator naming the task, the suggested text, the evidence and the project id, and refuses when the creator has no live lead session. The lead's guidance tells it to save a suggestion with `remember`, choosing its project, only when it holds and isn't already covered, and to tell the human nothing either way. A suggestion leaves no record of its own beyond the signal; a saved one shows as the lead's `lead.note_saved`. - **Restarts.** On shutdown, byre marks each running lead session idle with "byre stopped mid-turn" and holds a restart notice for it, so a lead is never left `running` for a build without lead support to find. After a crash, startup resumes a running lead like a task session. Startup then delivers any held input. -The UI is two panes filling the window, with no navbar. The left has six tabs: Lead, the lead's transcript and composer (the same view as a task transcript, with only the Compact button in its header), Notes, the lead's notes (see "Notes" under the lead), Workflows, Status, Server and Settings, and at the end of its tab strip the connection dot and the user's name. Workflows (`mac/Sources/Byre/Workflows.swift`) lists `GET /api/workflows` by name with each description, refetching on `workflow.updated` and `workflow.deleted`; a workflow's run button doesn't start anything itself, since a run needs a project and a brief: it appends `Run the "<name>" workflow: ` to the lead's composer draft, selects the Lead tab and focuses the composer, for the human to finish and send. Server is about the byre service on the Mac the app runs on, whatever server the app is connected to (`mac/Sources/Byre/Server.swift`): it is On when launchd has the service loaded (`launchctl print gui/<uid>/run.fc.byre` succeeds), showing the address from `tailscale serve status --json` and Turn off (`launchctl bootout`), and Off otherwise, showing Turn on, which runs the bundle's `byre install` as the connection screen's Run byre on this Mac does. Below that, Tools on this Mac lists every check from the bundle's `byre check` (see "Running as a service"), each Found or Missing, with how to install a missing one; Missing is in the danger colour only when it blocks the server. Run byre on this Mac runs `byre check` first, and when something blocks it lists the missing checks in the same form instead of installing. Both Turn on and Run byre on this Mac use the running service's address instead of reinstalling when it is already on, since a reinstall restarts it and the old service could answer the first status check. The app runs both helpers through the user's login shell, so they see the `PATH` the service is given. It reads the state and checks again each time it shows and when the app becomes active. While the service is on, Agents' permissions has Check permissions, which calls `POST /api/admin/permissions` on the service at that address (see "Permissions" under "Screen and app control") and lists each grant as Granted, Missing (with Open System Settings, which opens the grant's pane) or Not checked. Settings holds the user's and this client's preferences; its This Mac's permissions has the same Check permissions for the app's own grants, the ones `client_hands` uses, running the bundle's `byre-hands check` from a copy in the temporary directory (`checkLocalPermissions` in `Server.swift`). Which left tab is selected is kept only in memory (`Overlays`); the Lead view stays mounted while another tab shows, and Notes, Workflows, Status, Server or Settings mounts each time its tab is selected. The right shows the focused task, or project with no task: a top row holding the task picker, centred, and the task's preview links at its right end (see "Previews"), then the tab strip, then the selected tab's view with its own header. The tabs are Transcript, Board, Changes, Files, Terminal, Questions, Reviews, Workflow and Project, the lead's `tabs` first. Board shows only on a team task (see "Teams"), Reviews only once it has a `review.*` event, Inbox only while the task has open items, and Workflow only on a task with a `workflow_run` (see "Workflows"); the selected tab always shows. Tabs that don't fit in the strip move, from the end, into a **More ▾** menu (`MoreButton` in `mac/Sources/Byre/Panes.swift`); the selected tab never moves there, and More carries the Questions highlight and count when Questions is in it. The **Go** menu holds the shortcuts, each also on its Ctrl key outside the terminal. ⌘K opens the task switcher. ⌘P selects the Project tab, keeping the right pane's task. ⌘L selects the Lead tab and puts the cursor in the lead's composer. ⌘S opens the right pane's task's Merge confirmation, from any tab, when the Merge button would be enabled. ⌘/ opens a dialog listing every keyboard shortcut; the list is `shortcuts` in `mac/Sources/Byre/Switcher.swift`, kept in step with the menu by hand. The shortcuts do nothing while a dialog is open. +The UI is two panes filling the window, with no navbar. The left has six tabs: Lead, the lead's transcript and composer (the same view as a task transcript, with only the Compact button in its header), Notes, the lead's notes (see "Notes" under the lead), Workflows, Status, Server and Settings, and at the end of its tab strip the connection dot and the user's name. Workflows (`mac/Sources/Byre/Workflows.swift`) lists `GET /api/workflows` by name with each description, refetching on `workflow.updated` and `workflow.deleted`; a workflow's run button doesn't start anything itself, since a run needs a project and a brief: it appends `Run the "<name>" workflow: ` to the lead's composer draft, selects the Lead tab and focuses the composer, for the human to finish and send. Server is about the byre service on the Mac the app runs on, whatever server the app is connected to (`mac/Sources/Byre/Server.swift`): it is On when launchd has the service loaded (`launchctl print gui/<uid>/run.fc.byre` succeeds), showing the address from `tailscale serve status --json` and Turn off (`launchctl bootout`), and Off otherwise, showing Turn on, which runs the bundle's `byre install` as the connection screen's Run byre on this Mac does. Below that, Tools on this Mac lists every check from the bundle's `byre check` (see "Running as a service"), each Found or Missing, with how to install a missing one; Missing is in the danger colour only when it blocks the server. Run byre on this Mac runs `byre check` first, and when something blocks it lists the missing checks in the same form instead of installing. Both Turn on and Run byre on this Mac use the running service's address instead of reinstalling when it is already on, since a reinstall restarts it and the old service could answer the first status check. The app runs both helpers through the user's login shell, so they see the `PATH` the service is given. It reads the state and checks again each time it shows and when the app becomes active. While the service is on, Agents' permissions has Check permissions, which calls `POST /api/admin/permissions` on the service at that address (see "Permissions" under "Screen and app control") and lists each grant as Granted, Missing (with Open System Settings, which opens the grant's pane) or Not checked. Settings holds the user's and this client's preferences; its This Mac's permissions has the same Check permissions for the app's own grants, the ones `client_hands` uses, running the bundle's `byre-hands check` from a copy in the temporary directory (`checkLocalPermissions` in `Server.swift`). Which left tab is selected is kept only in memory (`Overlays`); the Lead view stays mounted while another tab shows, and Notes, Workflows, Status, Server or Settings mounts each time its tab is selected. The right shows the focused task, or project with no task: a top row holding the task picker, centred, and the task's preview links at its right end (see "Previews"), then the tab strip, then the selected tab's view with its own header. The tabs are Transcript, Board, Changes, Files, Terminal, Questions, Reviews, Workflow and Project, always in that order. Board shows only on a team task (see "Teams"), Reviews only once it has a `review.*` event, Inbox only while the task has open items, and Workflow only on a task with a `workflow_run` (see "Workflows"); the selected tab always shows. Tabs that don't fit in the strip move, from the end, into a **More ▾** menu (`MoreButton` in `mac/Sources/Byre/Panes.swift`); the selected tab never moves there, and More carries the Questions highlight and count when Questions is in it. The **Go** menu holds the shortcuts, each also on its Ctrl key outside the terminal. ⌘K opens the task switcher. ⌘P selects the Project tab, keeping the right pane's task. ⌘L selects the Lead tab and puts the cursor in the lead's composer. ⌘S opens the right pane's task's Merge confirmation, from any tab, when the Merge button would be enabled. ⌘/ opens a dialog listing every keyboard shortcut; the list is `shortcuts` in `mac/Sources/Byre/Switcher.swift`, kept in step with the menu by hand. The shortcuts do nothing while a dialog is open. ## TODO diff --git a/internal/agents/lead.go b/internal/agents/lead.go index d25d31f..fc158a2 100644 --- a/internal/agents/lead.go +++ b/internal/agents/lead.go @@ -19,7 +19,7 @@ const leadGuidance = `You are the human's lead agent in byre, shown in the left - Don't respond to what the human just did. A signal about their own action (a question they answered, a task they created, merged, started merging or discarded) tells them nothing new: don't report, confirm or comment on it. Act on it only if it changes what you are doing, and then end the turn with one short line at most. When a batch of signals needs nothing from you to the human, make your final message exactly NO_REPLY and nothing else; byre folds that turn away so the human doesn't see it. - "Your right pane is free" means the human finished what was in the right pane. Show them the next thing that needs them, most urgent first (questions, then errors on the task's transcript tab, then finished turns, then tasks ready to merge), with one line saying what it is. If nothing needs them, show that project's project tab, then post a short status: one line per active task (what it's doing or waiting on), then up to three TODO items you'd start next (from list_tasks), each with a one-line reason. Write each item as a link to #/todo/<project_id>/<title, URL-encoded with parentheses as %28 and %29>, for example [Fix login redirect](#/todo/3f2a9c1b7d4e/Fix%20login%20redirect): byre draws it with a Start button the human can click, and once the task exists, its state. Start one with start_todo only when the human picks it. If that says nothing new since your last status, make your final message NO_REPLY. - Whenever you suggest new work that isn't in TODO.md (a task you think is worth doing, an improvement to byre or to your own role), write it as a link to #/suggest/<project_id>/<title>/<brief>, both URL-encoded with parentheses as %28 and %29, for example [Lead tool for admin status](#/suggest/3f2a9c1b7d4e/Lead%20tool%20for%20admin%20status/Add%20a%20lead%20tool%20that%20reads%20%2Fapi%2Fadmin%2Fstatus%20...). The brief is the new task agent's whole first message, so make it a complete brief, as for create_task. byre draws the link with two buttons: Start creates the task with that title and brief at once, without you, and Discuss sends you a message asking to talk the suggestion over. Once an active task has that title, it shows the task's state instead. Don't ask whether to start a suggestion; the buttons do that. -- Steering the right pane is a core part of your job, as important as what you write. The right pane shows one task, or a project with no task; point it with show (the task or project, the tabs to put first, the tab to select). By default, keep it on whatever you and the human are discussing, so they can see it while they read your reply; don't make them go looking for it. Call show in the same turn you bring something up: +- Steering the right pane is a core part of your job, as important as what you write. The right pane shows one task, or a project with no task; point it with show (the task or project, and the tab to select). By default, keep it on whatever you and the human are discussing, so they can see it while they read your reply; don't make them go looking for it. Call show in the same turn you bring something up: - Discussing, picking or starting TODO items, or a project's tasks as a whole: the project's project tab (show with project_id when no single task of that project is involved). - A question that needs the human: that task's questions tab, with one line naming it and what you recommend. - A finished turn that needs the human, discussing a task's work, what its agent did or is doing, or whether it is ready to merge: its transcript tab, which ends with the agent's final message. A specific file in its worktree: its files tab. diff --git a/internal/agents/leadtools.go b/internal/agents/leadtools.go index fb8649a..583c201 100644 --- a/internal/agents/leadtools.go +++ b/internal/agents/leadtools.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "log/slog" - "slices" "strings" "time" @@ -456,11 +455,11 @@ func (m *Manager) leadTools(l *live) []mcp.Tool { }), tool("show", - "Point the human's right pane at a task, or at a project with no task: tabs are the tabs to put first, in order (the rest follow), and tab is the one to select. "+ + "Point the human's right pane at a task, or at a project with no task, and select one of its tabs. The tabs' order is fixed. "+ "Whenever the human asks you to show something (\"show me that when you can\") or you want to show something, call show right away; never hold back because you expect a refusal, since only a refused show arranges byre's signal to try again. "+ "Tabs: transcript, board (a team task's board: its agents' posts, the human's and yours), changes, files, terminal (a shell in the task's worktree), questions (the task's oldest open question), reviews (the task's reviews by reviewer agents), workflow (a workflow stage task's run: its graph and each stage's state, attempts and outputs), project (the project's dashboard: its active tasks and its TODO.md items); with only project_id, only project shows anything. "+ "Omit both task_id and project_id to clear the pane. "+ - "If the pane already shows that task (or project) on that tab, only the tab order changes and you are told so, even while the human is working in the pane. "+ + "If the pane already shows that task (or project) on that tab, nothing changes and you are told so. "+ "Otherwise refused for 10 seconds after the human last moved the pane themselves (switching task or tab), whatever else holds; if a show was refused for it, byre signals you \"The move hold is over\" when those 10 seconds pass without another move, and sends no signal otherwise. "+ "Otherwise refused while the human is working in the right pane (focused in it, or with unsent text in it), unless it is on the project tab, or what it shows is finished (a merged or discarded task, or a questions tab with nothing left open),"+ "or it is on the transcript tab of a task whose agent is mid-turn, the human has no unsent text there, and either the human sent the message that started that turn since the pane last moved or the pane moved there 10 seconds ago or more; byre signals you \"Your right pane is free\" when either happens. "+ @@ -468,9 +467,8 @@ func (m *Manager) leadTools(l *live) []mcp.Tool { `{"type": "object", "properties": { "task_id": {"type": "string"}, "project_id": {"type": "string", "description": "Ignored when task_id is given."}, - "tabs": {"type": "array", "items": {"type": "string", "enum": ["transcript", "board", "changes", "files", "terminal", "questions", "reviews", "workflow", "project"]}}, "tab": {"type": "string", "enum": ["transcript", "board", "changes", "files", "terminal", "questions", "reviews", "workflow", "project"]} - }, "required": ["tabs", "tab"]}`, + }, "required": ["tab"]}`, func(ctx context.Context, user api.User, raw json.RawMessage) (string, error) { f := api.Focus{} err := json.Unmarshal(raw, &f) @@ -482,14 +480,7 @@ func (m *Manager) leadTools(l *live) []mcp.Tool { return "", err } if sameView(current, f) { - if slices.Equal(current.Tabs, f.Tabs) { - return "Already shown: the right pane shows that now, so nothing changed.", nil - } - err = m.SetFocus(ctx, user.Login, f, leadActor(user.Login)) - if err != nil { - return "", err - } - return "Already shown on that tab; the tab order was updated.", nil + return "Already shown: the right pane shows that now, so nothing changed.", nil } m.mu.Lock() working := m.paneFocus[user.Login].Focused || m.paneFocus[user.Login].Drafting @@ -546,10 +537,8 @@ func (m *Manager) leadTools(l *live) []mcp.Tool { } func (m *Manager) SetFocus(ctx context.Context, login string, f api.Focus, actor string) error { - for _, t := range append(slices.Clone(f.Tabs), f.Tab) { - if !t.Valid() { - return fmt.Errorf("%w: unknown tab %q", ErrInvalid, t) - } + if !f.Tab.Valid() { + return fmt.Errorf("%w: unknown tab %q", ErrInvalid, f.Tab) } if f.TaskId != nil && *f.TaskId == "" { f.TaskId = nil diff --git a/internal/api/api.gen.go b/internal/api/api.gen.go index 6ec29b0..10d8813 100644 --- a/internal/api/api.gen.go +++ b/internal/api/api.gen.go @@ -868,9 +868,6 @@ type Focus struct { ProjectId *string `json:"project_id,omitempty"` Tab TaskTab `json:"tab"` - // Tabs The tabs the lead put first, in order. The other task tabs follow them. - Tabs []TaskTab `json:"tabs"` - // TaskId The task the right pane shows; absent for none. TaskId *string `json:"task_id,omitempty"` } @@ -3880,266 +3877,265 @@ func HandlerWithOptions(si ServerInterface, options StdHTTPServerOptions) http.H // const string: with thousands of chunks the chained `+` fold is several // times slower for the Go compiler than parsing a slice literal. var swaggerSpec = []string{ - "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", } // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, diff --git a/internal/store/migrations/060-drop-focus-tabs.sql b/internal/store/migrations/060-drop-focus-tabs.sql new file mode 100644 index 0000000..4774542 --- /dev/null +++ b/internal/store/migrations/060-drop-focus-tabs.sql @@ -0,0 +1,17 @@ +CREATE TABLE users_next ( + login TEXT PRIMARY KEY, + display_name TEXT NOT NULL, + git_name TEXT NOT NULL DEFAULT '', + git_email TEXT NOT NULL DEFAULT '', + created_at TEXT NOT NULL, + notification_prefs TEXT NOT NULL DEFAULT '{"questions":true,"reports":false,"errors":false,"rate_limits":false}', + focus TEXT NOT NULL DEFAULT '{"tab":"transcript"}', + default_model TEXT NOT NULL DEFAULT 'claude-opus-5-5', + review_model TEXT NOT NULL DEFAULT 'gpt-6.1-sol', + second_review_model TEXT NOT NULL DEFAULT 'claude-opus-5-5' +); +INSERT INTO users_next (login, display_name, git_name, git_email, created_at, notification_prefs, focus, default_model, review_model, second_review_model) + SELECT login, display_name, git_name, git_email, created_at, notification_prefs, json_remove(focus, '$.tabs'), default_model, review_model, second_review_model + FROM users; +DROP TABLE users; +ALTER TABLE users_next RENAME TO users; diff --git a/mac/Sources/Byre/Panes.swift b/mac/Sources/Byre/Panes.swift index 91b013a..e2ba139 100644 --- a/mac/Sources/Byre/Panes.swift +++ b/mac/Sources/Byre/Panes.swift @@ -395,10 +395,6 @@ private struct TabStrip: View { } private func info() -> Info { - var first: [TaskTab] = [] - for t in store.focus.tabs where taskTabs.contains(t) && !first.contains(t) { - first.append(t) - } let tab = store.focus.tab let waiting = store.inbox.filter { $0.taskId == store.selectedTaskId }.count let transcript = store.transcript @@ -408,7 +404,7 @@ private struct TabStrip: View { let hasReviews = transcript.contains(where: isReviewEvent) let workflow = store.selectedTaskId.flatMap { store.findTask($0) }?.workflowRun != nil let team = store.selectedTaskId.map { !store.team($0).isEmpty } ?? false - let order = (first + taskTabs.filter { !first.contains($0) }).filter { t in + let order = taskTabs.filter { t in if t == tab { return true } diff --git a/mac/Sources/Byre/Store.swift b/mac/Sources/Byre/Store.swift index 71d34de..c178e48 100644 --- a/mac/Sources/Byre/Store.swift +++ b/mac/Sources/Byre/Store.swift @@ -39,7 +39,7 @@ final class Store { var reviewed: Int? var question: (taskId: String, itemId: String)? var connected = false - private(set) var focus = Focus(tabs: [.transcript], tab: .transcript) + private(set) var focus = Focus(tab: .transcript) var leadSessions: [AgentSession] = [] let leadFeed: Feed var jump: (taskId: String, seq: Int64)? @@ -343,7 +343,7 @@ final class Store { func setFocus(taskId: String?? = .none, projectId: String?? = .none, tab: TaskTab? = nil) async throws { let task = taskId ?? focus.taskId let project = task != nil ? findTask(task!)?.projectId : (projectId ?? focus.projectId) - var next = Focus(tabs: focus.tabs, tab: tab ?? focus.tab) + var next = Focus(tab: tab ?? focus.tab) if let task, !task.isEmpty { next.taskId = task }