@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 +71 -5
- package/package.json +1 -1
- package/prompts/global.md +18 -0
- package/prompts/master.md +71 -2
- package/prompts/worker.md +7 -2
- package/src/ask/dialog.ts +11 -1
- package/src/index.ts +1 -1
- package/src/lobby/feed.ts +1 -1
- package/src/lobby/keys.ts +0 -1
- package/src/lobby/runtime.ts +2 -22
- package/src/lobby/tabs/home.ts +6 -42
- package/src/lobby/tabs/tasks.ts +1 -1
- package/src/lobby/view.ts +9 -23
- package/src/pi/activity.ts +1 -33
- package/src/pi/events.ts +1 -16
- package/src/pi/plan-checklist.ts +305 -0
- package/src/pi/run-summary.ts +12 -3
- package/src/pi/settings-ui.ts +2 -2
- package/src/pi/tools.ts +5 -4
- package/src/pi/ui.ts +18 -284
- package/src/roles/worker.ts +1 -1
- package/src/schemas/configuration.ts +8 -6
- package/src/workflow/brief.ts +59 -0
- package/src/workflow/workflow.ts +3 -0
- package/src/pi/expressions.ts +0 -169
- package/src/pi/kaomoji.ts +0 -227
- package/src/pi/mascot-art.ts +0 -359
- package/src/pi/zen-large.ts +0 -699
- package/src/pi/zen-metrics.ts +0 -130
- package/src/pi/zen.ts +0 -659
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
|
-

|
|
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.
|
|
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** |
|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
|
+

|
|
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
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,
|
|
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
|
-
-
|
|
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 =
|
|
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
|
|
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
|
|
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" },
|
package/src/lobby/runtime.ts
CHANGED
|
@@ -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,
|
|
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 =
|
|
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();
|
package/src/lobby/tabs/home.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The Lobby tab: the
|
|
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
|
|
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
|
|
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(
|
|
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 =
|
|
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([...
|
|
543
|
+
return fill([...fill(body, main), ...think], height, width);
|
|
580
544
|
}
|
package/src/lobby/tabs/tasks.ts
CHANGED
|
@@ -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/
|
|
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> = {
|
|
58
|
-
const PANEL_ACTIONS: Record<LobbyPanel, LobbyAction> = {
|
|
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
|
|
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
|
|
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:
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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),
|