@a-t-h-i/bot-lobby 0.6.5 → 0.6.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A [Pi](https://pi.dev) extension that turns Pi into a multi-agent software team.
4
4
 
5
- ![The bot-lobby status scene: the oracle orchestrating DEV, DESIGN, RESEARCH and QA through a task's plan](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/gallery.png)
5
+ ![The Lobby tab: your conversation with the oracle, the activity log of every agent, and their latest thoughts](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/gallery.png)
6
6
 
7
7
  `/bot-lobby <request>` (or a request typed in the lobby) starts a task, unless
8
8
  one agent can simply do it: then it goes to the [quick-fix agent](#quick-fix-or-the-team).
@@ -16,6 +16,10 @@ The rule: **LLMs decide, the engine enforces.** Agents propose; the
16
16
  extension validates every state change, permission and approval through one
17
17
  `orchestrate` tool.
18
18
 
19
+ The oracle is meant to be your most capable model; the agents can be smaller
20
+ and cheaper ones. So it never assumes they are as capable as it is: it makes
21
+ every decision itself and [briefs each agent in full](#briefing-the-agents).
22
+
19
23
  ## Install
20
24
 
21
25
  ```bash
@@ -35,7 +39,7 @@ tool names.
35
39
 
36
40
  1. `/bot-lobby add a login page` — starts a task; the lobby opens.
37
41
  2. Answer the Master's questions, then approve its proposal.
38
- 3. Watch the agents work in the lobby (`alt+l` shows or hides it).
42
+ 3. Follow the agents in the lobby (`alt+l` shows or hides it): what they do, and what they think.
39
43
 
40
44
  `/bot-lobby settings` sets each agent's model, thinking level, time limit and
41
45
  extra instructions.
@@ -129,6 +133,30 @@ passwords, payments, migrations, production, personal data), or **unclear**
129
133
  - The track shows in the lobby's activity log, the task's details on the
130
134
  Tasks tab, and `/bot-lobby status`.
131
135
 
136
+ ### Briefing the agents
137
+
138
+ Every agent may run on a smaller, cheaper model than the oracle's, one that
139
+ follows instructions well but does not infer intent. So the oracle writes
140
+ each delegation (`implement`, `scout`, `research`) as a self-contained brief
141
+ and settles every design and architecture decision itself first:
142
+
143
+ - **Goal**, the exact **Files**, numbered **What to do** with names, shapes
144
+ and values, the **Contracts** shared with other agents (repeated in full in
145
+ each brief), **Constraints**, **Done when** (checkable criteria and the
146
+ commands to run) and **If stuck**.
147
+ - Plan steps are written to the same standard, so a brief is the plan step
148
+ made explicit, never a new decision.
149
+ - Every agent is told to follow its brief and the approved plan exactly, use
150
+ the given names letter for letter, and report what does not match instead of
151
+ guessing. Workers end their report with a **Brief Check**: each "Done when"
152
+ item, met or not, with evidence.
153
+ - The oracle holds each report to its brief. Drift, a skipped criterion or a
154
+ guessed choice comes back as a fix step with a more explicit brief.
155
+ - The engine backs it up: a delegation that names nothing concrete (or a long
156
+ one with no done criteria) is sent back to the oracle once before any agent
157
+ starts. Sending the same text again goes through, so a short task that is
158
+ complete as written is never stuck. `workflow.briefCheck: false` turns it off.
159
+
132
160
  ### The full workflow
133
161
 
134
162
  - **Scouts** (read-only) investigate the domains the request touches.
@@ -188,16 +216,54 @@ everything. `workflow.freshContext: false` in the config turns this off.
188
216
  ## The lobby
189
217
 
190
218
  A full-screen view with a prompt at the bottom that talks to whatever tab is
191
- open. `alt+h` lists every key.
219
+ open. `alt+h` lists every key. It is text only: no animations, just the
220
+ conversation, the activity log and the thoughts, and a one-line status in Pi's
221
+ footer.
192
222
 
193
223
  | Tab | What it is |
194
224
  | --- | --- |
195
- | **1 Lobby** | The task's status, your conversation with the oracle, an activity log of every tool call, and each agent's latest thought |
225
+ | **1 Lobby** | Your conversation with the oracle, an activity log of every agent's steps, and each agent's latest thought |
196
226
  | **2 Tasks** | Every task and saved plan as a checklist. `s` starts a plan in a new session, `h` here; `c` comments on a plan; `a` archives, `d` deletes |
197
227
  | **3 Plan** | Plan a task with a panel of agents before building it (below) |
198
228
  | **4 Quick fix** | One agent makes a change right away, beside any running task; requests the oracle [routes here](#quick-fix-or-the-team) show up too |
199
229
  | **5 Metrics** | Run time, success rate, tokens and cost per model and agent |
200
230
 
231
+ ### Lobby
232
+
233
+ ![The Lobby tab](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/gallery.png)
234
+
235
+ Your conversation with the oracle on the left, the activity log of every
236
+ agent's steps on the right, and the agents' latest thoughts below. `alt+c`,
237
+ `alt+a` and `alt+k` hide any of the three; the prompt steers the running turn.
238
+
239
+ ### Tasks
240
+
241
+ ![The Tasks tab](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/lobby-tasks.png)
242
+
243
+ Every task as a checklist, with its track, plan progress, the approved plan
244
+ and your comments on it.
245
+
246
+ ### Plan
247
+
248
+ ![The Plan tab](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/lobby-plan.png)
249
+
250
+ The panel's questions, with recommended options, on the left; the draft plan
251
+ on the right. See [Planning](#planning).
252
+
253
+ ### Quick fix
254
+
255
+ ![The Quick fix tab](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/lobby-quickfix.png)
256
+
257
+ One agent's jobs, each with its live steps, the files it edited and its
258
+ report. See [Quick fix or the team](#quick-fix-or-the-team).
259
+
260
+ ### Metrics
261
+
262
+ ![The Metrics tab](https://raw.githubusercontent.com/a-t-h-i/bot-lobby/main/docs/lobby-metrics.png)
263
+
264
+ Run time, success rate, cost and tokens per model and agent, so you can see
265
+ which cheaper models hold up.
266
+
201
267
  Common keys: `tab` switches tabs, `esc` browses (arrows, single-key
202
268
  commands), `ctrl+f` searches, `ctrl+s` saves the plan, `alt+o` browses
203
269
  sessions, `alt+n` starts a task in a new session, `alt+s` opens settings.
@@ -336,7 +402,7 @@ the result.
336
402
  "scout": { "model": "anthropic/claude-haiku-4-5-20251001", "timeoutMs": 480000 },
337
403
  "planner": { "thinking": "high", "timeoutMs": 300000 },
338
404
  "lobby": { "planningPanel": ["backend", "designer", "qa", "researcher"], "maxPlanningRounds": 5 },
339
- "workflow": { "maxReviewIterations": 2, "maxParallelWorkers": 3, "stallTimeoutMs": 300000, "wrapUpAt": 0.75, "taskBudgetMinutes": 0, "fastTrack": true, "routeQuickFixes": true },
405
+ "workflow": { "maxReviewIterations": 2, "maxParallelWorkers": 3, "stallTimeoutMs": 300000, "wrapUpAt": 0.75, "taskBudgetMinutes": 0, "fastTrack": true, "briefCheck": true, "routeQuickFixes": true },
340
406
  "classifier": { "enabled": false, "provider": "auto", "effort": { "cheapModel": "inherit" } }
341
407
  }
342
408
  ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a-t-h-i/bot-lobby",
3
- "version": "0.6.5",
3
+ "version": "0.6.6",
4
4
  "description": "Structured multi-agent software engineering orchestrator for Pi",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
package/prompts/global.md CHANGED
@@ -15,6 +15,24 @@ Perform your assigned responsibility precisely and remain within your domain.
15
15
  - Do not invent requirements; ask when they are genuinely ambiguous.
16
16
  - Report conclusions, evidence, decisions, findings and blockers concisely.
17
17
 
18
+ ## Following your brief
19
+
20
+ The Master that instructs you plans the work and knows the whole task; you may
21
+ be a smaller model that sees only your part. Your brief and the approved plan
22
+ are your authority, so:
23
+
24
+ - Read the whole brief and the plan before acting, and do exactly what it
25
+ says, in its order. Do not add features, refactors or "improvements", and do
26
+ not skip steps because they look unnecessary.
27
+ - Use the names, paths, shapes and wording the brief gives you, letter for
28
+ letter. Where it leaves a detail open, choose the simplest option that
29
+ follows the existing code, and say what you chose in your report.
30
+ - Never guess at something that matters (a contract, a file that is not there,
31
+ a conflict between the brief and the code). Stop that part, finish the rest,
32
+ and report it under Blockers or Pushback with what you found.
33
+ - Check your own work against the brief's "Done when" list before you report,
34
+ criterion by criterion, and say honestly which are met and which are not.
35
+
18
36
  ## Hard rules
19
37
 
20
38
  - Do not add dependencies without approval.
package/prompts/master.md CHANGED
@@ -155,12 +155,81 @@ each `implement` task with its step number (`Step 3: ...`, or `Steps 3-4: ...`
155
155
  when one delegation covers several) so the user's checklist tracks progress
156
156
  exactly.
157
157
 
158
+ ## Briefing the agents
159
+
160
+ You are usually a far more capable model than the agents you delegate to. Scouts,
161
+ workers, the researcher and the reviewer may run on smaller, cheaper models
162
+ that follow instructions well but do not infer intent, fill gaps sensibly or
163
+ know what you know. Never assume they are as capable as you. Whatever you leave
164
+ unsaid, they will guess, and a wrong guess costs a whole agent run. Your plan
165
+ and every brief are how your goal reaches the code, so write them for a
166
+ capable but literal reader who has read nothing but the brief and the
167
+ repository.
168
+
169
+ Every `implement` task (each assignment in a parallel batch), `scout` and
170
+ `research` instruction is a self-contained brief with these parts, in this order:
171
+
172
+ 1. **Goal** — the outcome this step must produce and how it serves the user's
173
+ request and the approved plan, in one or two sentences. Name the step
174
+ number(s).
175
+ 2. **Files** — the exact paths to create or change, and the ones to leave
176
+ alone. When you do not know a path, say what to search for and where.
177
+ 3. **What to do** — numbered, concrete actions in the order to do them: names
178
+ of functions, components, endpoints, fields, types, CSS classes, strings,
179
+ values. Give the exact signature, shape or wording wherever it matters.
180
+ Write "use X", not "use a suitable library"; when a choice is left to the
181
+ agent, say which options are allowed and how to pick.
182
+ 4. **Contracts** — everything this step shares with another domain or step:
183
+ API shapes, status codes, error format, event names, shared types, data
184
+ formats, file locations. State them in full in every brief that touches
185
+ them; an agent never sees another agent's brief.
186
+ 5. **Constraints** — what it must not do: no new dependencies, no other files,
187
+ no refactors, no changed behavior outside the step, no restyling of code
188
+ it does not own. Repeat the user's explicit requirements that apply.
189
+ 6. **Done when** — a checklist of observable, checkable criteria (behaviors,
190
+ exact commands to run and what they should print, files that must exist),
191
+ including what to verify and how, with `timeout`. The agent must be able to
192
+ tell for itself whether it has finished.
193
+ 7. **If stuck** — what to do when something does not match the brief (a file is
194
+ missing, a name differs, two instructions conflict): stop that part, do not
195
+ invent a workaround, and report it under Blockers or Pushback with what it
196
+ found. Ask nothing you can answer yourself: settle it in the brief.
197
+
198
+ Rules for the brief:
199
+
200
+ - Decide first, delegate second. Every design, architecture and product
201
+ decision belongs to you; make it and write down the result. A brief must not
202
+ contain "consider", "as appropriate", "if needed", "etc.", "similar to",
203
+ "handle edge cases" or "make it look good" without the specifics. List the
204
+ edge cases; describe the look in concrete terms (layout, sizes, colors,
205
+ states).
206
+ - Say the obvious. Repeat what you already told an earlier agent, include the
207
+ conventions to follow and point to an existing file to imitate by path.
208
+ - One step, one purpose, small enough to hold in mind: a handful of files and
209
+ a few actions. Split anything larger into consecutive steps in the same
210
+ `implement` call rather than leaving the agent to sequence it. Prefer more
211
+ explicit detail to fewer, larger chunks.
212
+ - The plan's steps are written to the same standard: each step names its
213
+ files, its actions and its done criteria, so the brief is the step made
214
+ explicit, never a new decision.
215
+ - Scout and research instructions ask specific questions with the answer
216
+ format you want (paths, names, versions, yes/no plus evidence), and say what
217
+ you will do with the answer.
218
+
219
+ When a report comes back, hold it to the brief: check each **Done when**
220
+ criterion against the report's `## Brief Check`, the diff and the repository.
221
+ Drift, a skipped criterion or a guessed choice is a fix step with a corrected,
222
+ even more explicit brief that quotes the exact gap — not a reason to accept the
223
+ work, and not a reason to redo it yourself. Keep every agent on your plan: if
224
+ the code no longer matches it, say which step it deviates from and restore it.
225
+
158
226
  ## Speed
159
227
 
160
228
  Every delegation costs a full agent run, so keep the loop short:
161
229
 
162
- - Delegate fewer, larger chunks: one `implement` per domain covering its
163
- consecutive steps (`Steps 2-4: ...`) rather than one call per step.
230
+ - Delegate fewer calls, not vaguer ones: one `implement` per domain covering
231
+ its consecutive steps (`Steps 2-4: ...`) rather than one call per step, each
232
+ step still briefed in full (see Briefing the agents).
164
233
  - When steps for different domains are independent, run them together with
165
234
  `implement` `assignments` (one entry per domain). Workers then share files
166
235
  through the file desk: they claim files, queue for busy ones, and hand them
package/prompts/worker.md CHANGED
@@ -19,7 +19,9 @@ them.
19
19
 
20
20
  ## Implementation
21
21
 
22
- - Follow the approved plan.
22
+ - Follow the approved plan and your brief exactly: they are the Master's
23
+ decisions. Do not substitute your own design, rename things, or widen the
24
+ step. If the code contradicts the brief, do not improvise: report it.
23
25
  - Follow domain boundaries.
24
26
 
25
27
  If you need a new dependency, or you believe a significant architectural
@@ -73,7 +75,10 @@ repository at the same time, and files are checked out like physical documents:
73
75
 
74
76
  ## Before handoff
75
77
 
76
- - Inspect the actual diff.
78
+ - Go through the brief's "Done when" list one criterion at a time and record
79
+ each in `## Brief Check` as `- criterion — met|not met — evidence`.
80
+ - Inspect the actual diff: it must contain what the brief asked for and
81
+ nothing else.
77
82
  - Verify tests.
78
83
  - Update the temporary task scratchpad.
79
84
  - Report concise results.
package/src/ask/dialog.ts CHANGED
@@ -83,7 +83,17 @@ export class AskDialog implements Component {
83
83
  * cannot draw the questionnaire. Without any UI nobody can answer: the
84
84
  * result says the questions were put away.
85
85
  */
86
- export const askUser: Asker = async (questions, ctx, signal, from) => {
86
+ export const askUser: Asker = (questions, ctx, signal, from) => {
87
+ // A model may call the tool several times in one turn; pi runs them in parallel, and overlays opened together hide each other. One at a time.
88
+ const next = asking.then(() => (signal?.aborted ? { answers: [], cancelled: true } : askNow(questions, ctx, signal, from)));
89
+ asking = next.then(() => undefined, () => undefined);
90
+ return next;
91
+ };
92
+
93
+ /** Tail of the questions waiting their turn. */
94
+ let asking: Promise<void> = Promise.resolve();
95
+
96
+ const askNow: Asker = async (questions, ctx, signal, from) => {
87
97
  if (!ctx.hasUI || questions.length === 0) return { answers: [], cancelled: true };
88
98
  if ((ctx as { mode?: string }).mode === "rpc") return dialogAsker(questions, ctx, signal, from);
89
99
  // Option images are read before the questionnaire opens, so drawing it never waits on the disk.
package/src/index.ts CHANGED
@@ -21,7 +21,7 @@ export default function (pi: ExtensionAPI): void {
21
21
  // Before the lifecycle, so a task that ended while the oracle was idle is closed before it builds the next turn's prompt.
22
22
  registerFreshContext(pi, CONFIG_DIR_NAME);
23
23
  registerLifecycle(pi, CONFIG_DIR_NAME);
24
- // After the lifecycle, so the lobby opens over a task the widget state already knows.
24
+ // After the lifecycle, so the lobby opens over a task the status already knows.
25
25
  registerLobbyEvents(pi, CONFIG_DIR_NAME);
26
26
  registerOwner(pi, CONFIG_DIR_NAME);
27
27
  onTransition((task) => pingTransition(task));
package/src/lobby/feed.ts CHANGED
@@ -2,7 +2,7 @@
2
2
  * What the lobby narrates: an activity log of plain-words tool steps from the
3
3
  * Master and every subagent, a single place for thoughts, and the conversation
4
4
  * with the Master stripped of tool rows and thinking. Bounded ring buffers in
5
- * one module-level store (like the zen widget state), so every event source can
5
+ * one module-level store (like the task state in ui.ts), so every event source can
6
6
  * write without a reference to the lobby, and the lobby repaints on `version`.
7
7
  */
8
8
  import type { AgentRun } from "../schemas/findings.ts";
package/src/lobby/keys.ts CHANGED
@@ -17,7 +17,6 @@ export const LOBBY_ACTIONS = {
17
17
  toggleAuto: { key: "alt+g", help: "auto mode: the oracle drives the task to completion without asking" },
18
18
  nextTab: { key: "tab", help: "next tab" },
19
19
  prevTab: { key: "shift+tab", help: "previous tab" },
20
- toggleScene: { key: "alt+z", help: "show or hide the oracle and agent animations" },
21
20
  toggleConversation: { key: "alt+c", help: "show or hide the conversation" },
22
21
  toggleActivity: { key: "alt+a", help: "show or hide the activity log" },
23
22
  toggleThinking: { key: "alt+k", help: "show or hide thinking" },
@@ -20,8 +20,7 @@ import { archiveTask as archiveTaskOnDisk, deleteTask as deleteTaskOnDisk, listA
20
20
  import { discardPlannedTask, listPlannedTasks, type PlannedTask } from "../state/backlog.ts";
21
21
  import { appendMetrics, readClassifierMetrics, readMetrics, type MetricStatus } from "../state/metrics.ts";
22
22
  import { describeToolCall } from "../pi/activity.ts";
23
- import { applyStatus, currentZenTask, isMinimized, onMinimizeChange, onRunUpdates, persistedRuns, setMinimized, setWidgetSuppressor, ZenScene, zenSnapshot } from "../pi/ui.ts";
24
- import { panelLines } from "../pi/zen.ts";
23
+ import { applyStatus, currentZenTask, isMinimized, onMinimizeChange, onRunUpdates, setMinimized, taskSnapshot } from "../pi/ui.ts";
25
24
  import { shortTitle } from "../text.ts";
26
25
  import { isSubagentProcess } from "../pi/quiet.ts";
27
26
  import { modelRef, resolveLobbyProfile, resolvePanelProfile } from "../pi/model-support.ts";
@@ -43,7 +42,6 @@ import { LobbyView, type LiveSession, type LobbyHost, type SwitchTarget, type Ta
43
42
  import { lobbyTheme } from "./theme.ts";
44
43
  import { deliverComments, onOwnerEvent, setAuto } from "../pi/owner.ts";
45
44
  import { openEntrySettings, openSettings } from "../pi/settings-ui.ts";
46
- import { budgetClock } from "../state/budget.ts";
47
45
 
48
46
  export const ANCHOR_KEY = "bot-lobby-anchor";
49
47
  export { deliverComments };
@@ -59,7 +57,6 @@ interface Runtime {
59
57
  visible: boolean;
60
58
  /** Stepped aside while pi shows a dialog. */
61
59
  asideForPrompt: boolean;
62
- scene: ZenScene;
63
60
  quickfix: QuickFixQueue;
64
61
  planner?: PlanningSession;
65
62
  issues: IssuesState;
@@ -426,13 +423,6 @@ function deleteTask(state: Runtime, taskId: string, where: "list" | "archive"):
426
423
  return `deleted ${taskId} for good`;
427
424
  }
428
425
 
429
- /** A task's status box without animations, for a session other than this window's. */
430
- function taskScene(state: Runtime, task: Task, width: number, height: number): string[] {
431
- const now = Date.now();
432
- const time = budgetClock(state.root, state.configDir, task.id, now);
433
- return panelLines(task, persistedRuns(task), now, false, { width, rows: Math.floor(height / 0.75), still: true, theme: state.ctx.ui.theme, ...(time ? { time } : {}) });
434
- }
435
-
436
426
  function seatProfile(state: Runtime, member: PanelMember) {
437
427
  return resolvePanelProfile(loadConfig(), member, {
438
428
  lookup: modelLookup(state.ctx),
@@ -518,15 +508,9 @@ function host(state: Runtime, tui: TUI): LobbyHost {
518
508
  theme: () => lobbyTheme(state.ctx.ui.theme),
519
509
  sessionId: () => state.ctx.sessionManager.getSessionId(),
520
510
  zen: () => {
521
- const snapshot = zenSnapshot();
511
+ const snapshot = taskSnapshot();
522
512
  return { ...(snapshot.task ? { task: snapshot.task } : {}), runs: snapshot.runs };
523
513
  },
524
- // The scene's height budget is 3/4 of the rows it is given.
525
- scene: (width, height, animated) => state.scene.lines(width, Math.floor(height / 0.75), state.ctx.ui.theme, Date.now(), !animated),
526
- advanceScene: (now) => {
527
- state.scene.advance(now);
528
- return state.scene.delay(now);
529
- },
530
514
  feed: lobbyFeed,
531
515
  masterBusy: () => !state.ctx.isIdle(),
532
516
  tasks: () => peekTasks(state.root, state.configDir),
@@ -577,7 +561,6 @@ function host(state: Runtime, tui: TUI): LobbyHost {
577
561
  sessionChat: (sessionId) => chats.chat(sessionId),
578
562
  hasOlderChat: (sessionId) => chats.hasOlder(sessionId),
579
563
  chatHistory: (sessionId) => chatHistory(state, sessionId),
580
- taskScene: (task, width, height) => taskScene(state, task, width, height),
581
564
  requestRender: () => tui.requestRender(),
582
565
  };
583
566
  }
@@ -716,7 +699,6 @@ function shutdown(): void {
716
699
  state.planner?.cancel();
717
700
  state.view?.dispose();
718
701
  state.handle?.hide();
719
- setWidgetSuppressor(() => false);
720
702
  onRunUpdates(undefined);
721
703
  onMinimizeChange(undefined);
722
704
  }
@@ -755,7 +737,6 @@ export function initLobby(pi: ExtensionAPI, ctx: ExtensionContext, configDir: st
755
737
  asking: false,
756
738
  mouse: false,
757
739
  inSettings: false,
758
- scene: new ZenScene(),
759
740
  quickfix: undefined as unknown as QuickFixQueue,
760
741
  issues: new IssuesState(execCommand, ctx.cwd, rerender),
761
742
  };
@@ -781,7 +762,6 @@ export function initLobby(pi: ExtensionAPI, ctx: ExtensionContext, configDir: st
781
762
  // Only the newest messages are kept; the feed learns whether earlier ones exist, and loads them when scrolled to.
782
763
  lobbyFeed.seedChat(chatFromEntries(ctx.sessionManager.getBranch(), Number.POSITIVE_INFINITY));
783
764
  state.unsubscribeFeed = lobbyFeed.onChange(rerender);
784
- setWidgetSuppressor(() => runtime?.visible === true);
785
765
  onRunUpdates((runs) => lobbyFeed.runs(runs));
786
766
  onMinimizeChange((value) => {
787
767
  if (value) hideLobby();
@@ -1,9 +1,8 @@
1
1
  /**
2
- * The Lobby tab: the zen scene of this session's task on top (the oracle
3
- * and agent animations, or just their status when animations are off), then the
2
+ * The Lobby tab: the status of this session's task on top, then the
4
3
  * conversation with the oracle (text only, no tool rows, no thinking), the
5
4
  * activity log of plain-words steps from every agent, and the one place where
6
- * thoughts show up. Pure: the scene arrives as a callback, the clock as `now`.
5
+ * thoughts show up. Pure: the clock arrives as `now`.
7
6
  */
8
7
  import { textWidth } from "../../width.ts";
9
8
  import type { Task } from "../../schemas/task.ts";
@@ -18,8 +17,6 @@ export type HomePane = (typeof HOME_PANES)[number];
18
17
 
19
18
  export interface HomeInput {
20
19
  task?: Task;
21
- /** Draws the zen scene into at most `height` lines, animated or still; absent when no task is active. */
22
- scene?: (width: number, height: number, animated: boolean) => string[];
23
20
  chat: readonly ChatEntry[];
24
21
  /** The oracle's reply while it streams. */
25
22
  liveReply?: string;
@@ -48,8 +45,6 @@ export interface HomeInput {
48
45
  keys?: Record<LobbyPanel, string>;
49
46
  /** Another session is shown: its name titles the conversation. */
50
47
  title?: string;
51
- /** The scene is another session's still status (no animations to toggle). */
52
- stillScene?: boolean;
53
48
  /** What the empty conversation says instead of the default (a session starting, say). */
54
49
  emptyNote?: string;
55
50
  /** What the empty activity log says instead of "No activity yet.". */
@@ -60,11 +55,6 @@ export interface HomeInput {
60
55
 
61
56
  /** Wide terminals put the conversation and the activity log side by side. */
62
57
  export const HOME_COLUMNS_MIN = 100;
63
- /** Most lines the scene may take, and its share of the body; the still status needs far fewer. */
64
- const SCENE_SHARE = 0.45;
65
- const MAX_SCENE = 36;
66
- const MAX_STILL = 10;
67
-
68
58
  const SOURCE_COLORS: Record<string, LobbyColor> = {
69
59
  MASTER: "accent",
70
60
  DEV: "success",
@@ -490,31 +480,6 @@ function thinkingContent(input: HomeInput, thoughts: readonly ThoughtEntry[], wi
490
480
  return whole([paint(theme, "dim", input.query ? `No thought matches "${input.query}".` : "Thoughts from the oracle and every agent appear here, and only here.")]);
491
481
  }
492
482
 
493
- /**
494
- * The scene: with animations off (the `animations` panel, off by default)
495
- * only the task's status box, what the agents are doing and the checklist;
496
- * with them on, the animated oracle and agents too. Its first line names the
497
- * key that toggles the animations.
498
- */
499
- function sceneLines(input: HomeInput, width: number, height: number, theme?: LobbyTheme): string[] {
500
- if (!input.task || !input.scene) return [];
501
- const animated = input.panels.animations && !input.stillScene;
502
- const budget = Math.min(animated ? MAX_SCENE : MAX_STILL, Math.floor(height * SCENE_SHARE));
503
- if (budget < (animated ? 6 : 4)) return [];
504
- const lines = input.scene(width, budget, animated).slice(0, budget);
505
- const note = input.keys && !input.stillScene ? `${input.keys.animations} ${animated ? "hides" : "shows"} animations` : undefined;
506
- return keyNote(lines, width, note, theme);
507
- }
508
-
509
- /** `note` at the right end of the first line, dimmed, when it fits beside what is there. */
510
- function keyNote(lines: readonly string[], width: number, note: string | undefined, theme?: LobbyTheme): string[] {
511
- const first = lines[0];
512
- if (!note || first === undefined) return [...lines];
513
- const gap = width - textWidth(first) - textWidth(note) - 1;
514
- if (gap < 2) return [...lines];
515
- return [`${first}${" ".repeat(gap)}${paint(theme, "dim", note)}`, ...lines.slice(1)];
516
- }
517
-
518
483
  function hiddenHint(input: HomeInput, width: number, height: number, theme?: LobbyTheme): string[] {
519
484
  const keys = input.keys;
520
485
  const text = keys
@@ -536,12 +501,11 @@ export function renderHome(input: HomeInput, width: number, height: number, them
536
501
  if (height <= 0) return [];
537
502
  const { panels } = input;
538
503
  const feed = filterFeed(input);
539
- const scene = sceneLines(input, width, height, theme);
540
- const rest = height - scene.length;
504
+ const rest = height;
541
505
  const showMain = panels.conversation || panels.activity;
542
506
  const thinkHeight = !panels.thinking ? 0 : !showMain ? rest : rest >= 18 ? Math.max(5, Math.floor(rest * 0.25)) : rest >= 10 ? 4 : 0;
543
507
  const main = rest - thinkHeight;
544
- if (!showMain && !panels.thinking) return fill([...scene, ...hiddenHint(input, width, rest, theme)], height, width);
508
+ if (!showMain && !panels.thinking) return fill(hiddenHint(input, width, rest, theme), height, width);
545
509
  const matches = (count: number) => `${count} match${count === 1 ? "" : "es"}`;
546
510
  /**
547
511
  * One scrollable pane: only the lines it shows are drawn — its newest, or
@@ -563,7 +527,7 @@ export function renderHome(input: HomeInput, width: number, height: number, them
563
527
  const thought = currentThought(feed.thoughts);
564
528
  const thinkingNote = thought ? `${thought.source} · ${thought.live ? "thinking" : since(input.now - thought.at)}` : input.keys?.thinking;
565
529
  const thinkingBox = pane("thinking", "Thinking", thinkingNote, (inner, need, anchor) => thinkingContent(input, feed.thoughts, inner, need, anchor, theme));
566
- const top = scene.length;
530
+ const top = 0;
567
531
  let body: string[] = [];
568
532
  if (main > 0 && panels.conversation && panels.activity) {
569
533
  if (width >= HOME_COLUMNS_MIN) {
@@ -576,5 +540,5 @@ export function renderHome(input: HomeInput, width: number, height: number, them
576
540
  } else if (main > 0 && panels.conversation) body = chatBox(top, 0, width, main);
577
541
  else if (main > 0 && panels.activity) body = activityBox(top, 0, width, main);
578
542
  const think = thinkHeight >= 3 ? thinkingBox(top + main, 0, width, thinkHeight) : [];
579
- return fill([...scene, ...fill(body, main), ...think], height, width);
543
+ return fill([...fill(body, main), ...think], height, width);
580
544
  }
@@ -7,7 +7,7 @@
7
7
  import { TERMINAL_STATES, taskRequest, type Task } from "../../schemas/task.ts";
8
8
  import type { PlannedTask } from "../../state/backlog.ts";
9
9
  import type { PlanComment } from "../../state/comments.ts";
10
- import { planChecklist } from "../../pi/zen.ts";
10
+ import { planChecklist } from "../../pi/plan-checklist.ts";
11
11
  import { persistedRuns } from "../../pi/ui.ts";
12
12
  import { describeRun, runFromLog } from "../../pi/run-summary.ts";
13
13
  import { pendingApprovals } from "../../workflow/approvals.ts";
package/src/lobby/view.ts CHANGED
@@ -54,8 +54,8 @@ const PROMPT_FIRST: ReadonlySet<TabId> = new Set(["lobby", "plan", "quickfix"]);
54
54
  export type LobbyMode = "type" | "browse";
55
55
 
56
56
  /** What each pane toggle is called in notices and help. */
57
- const PANEL_NAMES: Record<LobbyPanel, string> = { animations: "oracle and agent animations", conversation: "conversation", activity: "activity log", thinking: "thinking" };
58
- const PANEL_ACTIONS: Record<LobbyPanel, LobbyAction> = { animations: "toggleScene", conversation: "toggleConversation", activity: "toggleActivity", thinking: "toggleThinking" };
57
+ const PANEL_NAMES: Record<LobbyPanel, string> = { conversation: "conversation", activity: "activity log", thinking: "thinking" };
58
+ const PANEL_ACTIONS: Record<LobbyPanel, LobbyAction> = { conversation: "toggleConversation", activity: "toggleActivity", thinking: "toggleThinking" };
59
59
 
60
60
  /**
61
61
  * Which session the Lobby tab shows and talks to: this window, one it started
@@ -125,12 +125,8 @@ export interface LobbyHost {
125
125
  rows(): number;
126
126
  theme(): LobbyTheme;
127
127
  sessionId(): string | undefined;
128
- /** This session's active task and its runs, as the zen widget sees them. */
128
+ /** This session's active task and its runs, as the lobby reads them. */
129
129
  zen(): { task?: Task; runs: readonly AgentRun[] };
130
- /** Draw the zen scene into at most `height` lines: animated, or only the task's status when `animated` is false. */
131
- scene(width: number, height: number, animated: boolean): string[];
132
- /** Advance the scene's clock; returns the delay it wants until the next step. */
133
- advanceScene(now: number): number;
134
130
  feed: LobbyFeed;
135
131
  masterBusy(): boolean;
136
132
  tasks(): Task[];
@@ -199,14 +195,12 @@ export interface LobbyHost {
199
195
  hasOlderChat(sessionId: string): boolean;
200
196
  /** A session's whole conversation, oldest first: this window's (no id) or another's; loaded only while scrolled back to it. */
201
197
  chatHistory(sessionId?: string): readonly ChatEntry[];
202
- /** A task's status without animations, for a session other than this window's. */
203
- taskScene(task: Task, width: number, height: number): string[];
204
198
  profileLabel(kind: LobbyAgentKind): string;
205
199
  requestRender(): void;
206
200
  now?(): number;
207
201
  }
208
202
 
209
- /** Live work speeds the clock up so spinners and the scene move. */
203
+ /** Live work speeds the clock up so spinners move. */
210
204
  export const LIVE_MS = 250;
211
205
  export const IDLE_MS = 1000;
212
206
  /** How often task and metrics data is reread from disk while the lobby is open. */
@@ -424,16 +418,13 @@ export class LobbyView implements Component, Focusable {
424
418
  || Boolean(zen.task && !zen.task.paused && !TERMINAL_STATES.includes(zen.task.state));
425
419
  }
426
420
 
427
- /** One clock step: advance the scene, reread data when due, repaint. */
421
+ /** One clock step: reread data when due, repaint. */
428
422
  private step(): void {
429
423
  if (!this.running) return;
430
- const now = this.now();
431
424
  this.tick += 1;
432
- let delay = this.isLive() ? LIVE_MS : IDLE_MS;
433
- if (this.tab === "lobby" && this.panels.animations && this.host.zen().task) delay = Math.min(delay, this.host.advanceScene(now));
434
425
  this.refreshData(false);
435
426
  this.host.requestRender();
436
- this.schedule(delay);
427
+ this.schedule(this.isLive() ? LIVE_MS : IDLE_MS);
437
428
  }
438
429
 
439
430
  /** Reread tasks, plans, comments and metrics from disk (throttled unless forced). */
@@ -675,8 +666,6 @@ export class LobbyView implements Component, Focusable {
675
666
  return this.cycleTab(1);
676
667
  case "prevTab":
677
668
  return this.cycleTab(-1);
678
- case "toggleScene":
679
- return this.togglePanel("animations");
680
669
  case "toggleConversation":
681
670
  return this.togglePanel("conversation");
682
671
  case "toggleActivity":
@@ -2295,7 +2284,7 @@ export class LobbyView implements Component, Focusable {
2295
2284
  if (entry.view.kind !== "here") return this.otherSessionBody(entry, width, height, theme, now);
2296
2285
  const talk = this.conversationFor(entry);
2297
2286
  return renderHome({
2298
- ...(zen.task ? { task: zen.task, scene: (w: number, h: number, animated: boolean) => this.host.scene(w, h, animated) } : {}),
2287
+ ...(zen.task ? { task: zen.task } : {}),
2299
2288
  chat: talk.chat,
2300
2289
  ...(talk.older ? { olderNote: OLDER_NOTE } : {}),
2301
2290
  ...(feed.reply ? { liveReply: feed.reply } : {}),
@@ -2313,7 +2302,6 @@ export class LobbyView implements Component, Focusable {
2313
2302
  panels: this.panels,
2314
2303
  ...(query ? { query } : {}),
2315
2304
  keys: {
2316
- animations: keyLabel(this.keys.toggleScene),
2317
2305
  conversation: keyLabel(this.keys.toggleConversation),
2318
2306
  activity: keyLabel(this.keys.toggleActivity),
2319
2307
  thinking: keyLabel(this.keys.toggleThinking),
@@ -2323,8 +2311,7 @@ export class LobbyView implements Component, Focusable {
2323
2311
 
2324
2312
  /**
2325
2313
  * Another session on the Lobby tab: a background session's live feed, or an
2326
- * other terminal's conversation read from its session file; the task's
2327
- * status sits on top without animations.
2314
+ * other terminal's conversation read from its session file.
2328
2315
  */
2329
2316
  private otherSessionBody(entry: SessionEntry, width: number, height: number, theme: LobbyTheme, now: number): string[] {
2330
2317
  const session = this.viewedSession();
@@ -2332,7 +2319,7 @@ export class LobbyView implements Component, Focusable {
2332
2319
  const task = entry.task;
2333
2320
  const query = this.query();
2334
2321
  const common = {
2335
- ...(task ? { task, scene: (w: number, h: number) => this.host.taskScene(task, w, h), stillScene: true } : {}),
2322
+ ...(task ? { task } : {}),
2336
2323
  others: 0,
2337
2324
  pending: 0,
2338
2325
  offsets: this.homeOffsets,
@@ -2345,7 +2332,6 @@ export class LobbyView implements Component, Focusable {
2345
2332
  ...(query ? { query } : {}),
2346
2333
  title: entry.name,
2347
2334
  keys: {
2348
- animations: keyLabel(this.keys.toggleScene),
2349
2335
  conversation: keyLabel(this.keys.toggleConversation),
2350
2336
  activity: keyLabel(this.keys.toggleActivity),
2351
2337
  thinking: keyLabel(this.keys.toggleThinking),