@a-t-h-i/bot-lobby 0.6.27 → 0.6.28
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 +30 -11
- package/package.json +1 -1
- package/src/lobby/host.ts +19 -0
- package/src/lobby/planner.ts +131 -0
- package/src/lobby/quickfix.ts +40 -1
- package/src/lobby/recovery.ts +415 -0
- package/src/lobby/resume.ts +66 -0
- package/src/lobby/runtime.ts +80 -7
- package/src/lobby/service.ts +196 -11
- package/src/lobby/sessions.ts +13 -3
- package/src/lobby/task-rows.ts +2 -0
- package/src/pi/commands.ts +38 -2
- package/src/pi/events.ts +6 -3
- package/src/pi/switch.ts +7 -2
- package/src/pi/ui.ts +9 -2
- package/src/state/plan-history.ts +142 -0
- package/src/state/recovery.ts +170 -0
- package/src/state/themes.ts +127 -0
- package/src/webui/api/index.ts +24 -2
- package/src/webui/api/planner.ts +47 -1
- package/src/webui/api/status.ts +2 -0
- package/src/webui/api/tasks.ts +35 -5
- package/src/webui/api/themes.ts +37 -0
- package/src/webui/command.ts +26 -1
- package/src/webui/dev/fake-service.ts +60 -0
- package/src/webui/dev/fixtures.ts +4 -1
- package/src/webui/palette-core.ts +260 -0
- package/src/webui/protocol.ts +56 -0
- package/src/webui/server.ts +10 -0
- package/webui/dist/assets/index-BzCxEueR.css +2 -0
- package/webui/dist/assets/index-qisGNoCh.js +108 -0
- package/webui/dist/build.json +36 -28
- package/webui/dist/index.html +2 -2
- package/webui/dist/assets/index-BUzOUrih.css +0 -2
- package/webui/dist/assets/index-myC4UiEg.js +0 -108
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Bot-Lobby
|
|
2
2
|
|
|
3
|
-
A [Pi](https://pi.dev) extension for multi-agent software development. Describe a change, agree a
|
|
4
|
-
with the oracle, and follow the agents in a local browser lobby. Approvals, domain boundaries and QA
|
|
3
|
+
A [Pi](https://pi.dev) extension for multi-agent software development. Describe a change, agree on a
|
|
4
|
+
plan with the oracle, and follow the agents in a local browser lobby. Approvals, domain boundaries and QA
|
|
5
5
|
are enforced before anything is called done.
|
|
6
6
|
|
|
7
7
|

|
|
@@ -18,12 +18,18 @@ The questionnaire and web tools are built in, so don't also install extensions w
|
|
|
18
18
|
## Use
|
|
19
19
|
|
|
20
20
|
1. In Pi, run `/bot-lobby add a login page`. Bot-lobby turns on and the lobby opens in your browser.
|
|
21
|
-
A new
|
|
22
|
-
2. Answer the oracle's questions and approve its plan, or agree one with the panel in **Plan** first.
|
|
21
|
+
A new Pi session starts with bot-lobby off, as plain Pi: `Ctrl+Shift+M` or `/bot-lobby on|off` switches it.
|
|
22
|
+
2. Answer the oracle's questions and approve its plan, or agree on one with the panel in **Plan** first.
|
|
23
23
|
A plan opens with a short list of steps; the detail of each follows for whoever wants it.
|
|
24
|
+
Plans you leave without saving them as a task wait under **Plan → Previous**. You can read, carry on,
|
|
25
|
+
archive or delete them there. A plan saved as a task leaves that list.
|
|
24
26
|
3. Follow the agents in **Lobby**: the step being worked on reads `active · 1m 12s`, and each task shows
|
|
25
27
|
how long its agents have worked on it (idle time left out). Models, effort and time limits are in **Settings**.
|
|
26
28
|
|
|
29
|
+
**Issues** lists the repository's open issues (with `lobby.issues` on), and **Plan it** starts a
|
|
30
|
+
planning session from one. **Git** lists open pull requests; an agent reviews one on request,
|
|
31
|
+
read-only, and nothing is posted to GitHub. Both read GitHub through `gh`.
|
|
32
|
+
|
|
27
33
|
Small changes go to **Quick fix**. `--task` forces a full task, `--fast` / `--full` picks the workflow,
|
|
28
34
|
and `--branch` / `--worktree` isolates it in git. A finished isolated task pushes its branch and tries
|
|
29
35
|
to open a PR through `gh`; nothing merges on its own.
|
|
@@ -37,7 +43,17 @@ QA is sized to what was built, not to the request. Once the work is in, Jev (the
|
|
|
37
43
|
reads the diff and sets the QA risk. A change with nothing that runs (docs, styles, comments) passes
|
|
38
44
|
on the engine's checks with no QA agent, and a small one gets a light check. One that touches
|
|
39
45
|
security, data, concurrency or orchestration gets a deep, adversarial review. The test budget is a
|
|
40
|
-
ceiling, never a quota, and the Tasks tab shows
|
|
46
|
+
ceiling, never a quota, and the Tasks tab shows Jev's read and why. With Jev off, the engine's rules decide.
|
|
47
|
+
|
|
48
|
+
If Pi crashes, is killed or loses its terminal, the lobby page keeps everything on screen and
|
|
49
|
+
reconnects when Pi is back. The next Pi started in the project carries on what was running. The
|
|
50
|
+
planning session returns as it was, and a round that was cut off runs again. Queued quick fixes
|
|
51
|
+
run, and every task's session starts again from its saved file, picking up where it stopped. A
|
|
52
|
+
quit on purpose stops this work, and `pi -c` brings back the planning session.
|
|
53
|
+
|
|
54
|
+
A task that is paused, or that no running session drives, shows **Resume** (`R`) in **Tasks**.
|
|
55
|
+
It carries the task on without moving your window: a paused task is unpaused where it runs, and a
|
|
56
|
+
stopped one carries on in a background session, which restarts its own session when its file is found.
|
|
41
57
|
|
|
42
58
|
| Agent | Does |
|
|
43
59
|
| --- | --- |
|
|
@@ -56,14 +72,17 @@ rebound with `lobby.keys`. In Pi, `Alt+G` toggles auto mode and `Ctrl+Shift+M` t
|
|
|
56
72
|
|
|
57
73
|
## Screenshots
|
|
58
74
|
|
|
59
|
-
|
|
|
75
|
+
| Planning | Tasks |
|
|
60
76
|
| --- | --- |
|
|
61
|
-
|  |  |
|
|
78
|
+
| **Thinking** | **Questionnaire** |
|
|
79
|
+
|  |  |
|
|
80
|
+
| **Issues** | **Git** |
|
|
81
|
+
|  |  |
|
|
64
82
|
|
|
65
83
|
## Configuration
|
|
66
84
|
|
|
67
85
|
Settings live in `~/.pi/bot-lobby/config.json` (`BOT_LOBBY_CONFIG_DIR` moves them), and
|
|
68
|
-
`/bot-lobby config` shows what is in effect.
|
|
69
|
-
|
|
86
|
+
`/bot-lobby config` shows what is in effect. Colour themes you import, under a name you choose,
|
|
87
|
+
are kept in `themes.json` beside it, so every Pi session and project shows them. Tasks and
|
|
88
|
+
project knowledge stay in `.pi/bot-lobby/` inside your project.
|
package/package.json
CHANGED
package/src/lobby/host.ts
CHANGED
|
@@ -13,6 +13,7 @@ import type { ChatEntry, LobbyFeed } from "./feed.ts";
|
|
|
13
13
|
import type { BackgroundSession } from "./sessions.ts";
|
|
14
14
|
import type { QuickFixQueue } from "./quickfix.ts";
|
|
15
15
|
import type { PlannerSeed, PlanningSession } from "./planner.ts";
|
|
16
|
+
import type { PlanRecord, PlanSummary } from "../state/plan-history.ts";
|
|
16
17
|
import type { IssuesState } from "./issues.ts";
|
|
17
18
|
import type { PullsState } from "./pulls.ts";
|
|
18
19
|
import type { PullReviews } from "./pr-review.ts";
|
|
@@ -115,6 +116,16 @@ export interface LobbyService {
|
|
|
115
116
|
abortMaster(): void;
|
|
116
117
|
quickfix: QuickFixQueue;
|
|
117
118
|
planner(): PlanningSession | undefined;
|
|
119
|
+
/** The project's previous plans (never saved as a task), newest first; the archived ones when asked. */
|
|
120
|
+
previousPlans(archived: boolean): PlanSummary[];
|
|
121
|
+
/** One previous plan, whole; throws when there is none (or it was saved as a task). */
|
|
122
|
+
previousPlan(id: string): PlanRecord;
|
|
123
|
+
/** Archive a previous plan, or bring it back; returns a notice (throws when it cannot). */
|
|
124
|
+
archivePlan(id: string, archived: boolean): string;
|
|
125
|
+
/** Delete a previous plan for good; returns a notice (throws when it cannot). */
|
|
126
|
+
deletePlan(id: string): string;
|
|
127
|
+
/** Carry a previous plan on as the plan on screen; returns a notice. */
|
|
128
|
+
reopenPlan(id: string): string;
|
|
118
129
|
/** Start a planning session (replacing any other) with these seats on the panel. */
|
|
119
130
|
newPlanner(seed?: PlannerSeed, seats?: readonly PanelMember[]): PlanningSession;
|
|
120
131
|
/** The oracle puts the panel's open questions to the user, one questionnaire at a time; returns a notice. */
|
|
@@ -150,6 +161,8 @@ export interface LobbyService {
|
|
|
150
161
|
sessionModel?(): string | undefined;
|
|
151
162
|
/** The Issues tab is switched on (`lobby.issues`). */
|
|
152
163
|
issuesEnabled(): boolean;
|
|
164
|
+
/** What this window carried on from one that stopped unexpectedly, when it did. */
|
|
165
|
+
recovered?(): { at: number; text: string } | undefined;
|
|
153
166
|
/** This window's pi session name, when it has one. */
|
|
154
167
|
sessionName(): string | undefined;
|
|
155
168
|
/** Background sessions this window started, oldest first. */
|
|
@@ -161,6 +174,12 @@ export interface LobbyService {
|
|
|
161
174
|
setAuto(taskId: string, on: boolean): void;
|
|
162
175
|
/** Leave a message for a task's oracle, delivered by whichever session drives it (now or once one resumes it); returns a notice. */
|
|
163
176
|
sendToTask(taskId: string, text: string): string;
|
|
177
|
+
/**
|
|
178
|
+
* Resume a task from the Tasks screen, leaving this window where it is: a
|
|
179
|
+
* paused task is unpaused where it runs, one nothing runs carries on in a
|
|
180
|
+
* background session. A notice, and the background session's key.
|
|
181
|
+
*/
|
|
182
|
+
resumeTask?(taskId: string): Promise<{ notice: string; key?: string }>;
|
|
164
183
|
/** Leave a message for a session running in another terminal; it delivers it within seconds. Returns a notice. */
|
|
165
184
|
sendToSession(sessionId: string, text: string): string;
|
|
166
185
|
/** Sessions running in this project, other than this window's, from their heartbeats. */
|
package/src/lobby/planner.ts
CHANGED
|
@@ -522,6 +522,49 @@ export function memberPrompt(member: PanelMember, instructions?: string): string
|
|
|
522
522
|
return [base, loadPrompt("panel.md"), `## Your seat\n\n${MEMBER_SEATS[member]}`].join("\n\n---\n\n");
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
+
/** A planning session as JSON: what `PlanningSession.snapshot` writes and `restore` reads. */
|
|
526
|
+
export interface PlanningSnapshot {
|
|
527
|
+
version: 1;
|
|
528
|
+
/** Absent on snapshots kept before sessions had ids: a restore gives them one. */
|
|
529
|
+
id?: string;
|
|
530
|
+
createdAt?: number;
|
|
531
|
+
messages: PlannerMessage[];
|
|
532
|
+
seed?: PlannerSeed;
|
|
533
|
+
reply?: PlannerReply;
|
|
534
|
+
questions: PanelQuestion[];
|
|
535
|
+
notes: PanelNote[];
|
|
536
|
+
members: MemberState[];
|
|
537
|
+
seats: PanelMember[];
|
|
538
|
+
pins: PanelMember[];
|
|
539
|
+
satOut: Array<[PanelMember, number]>;
|
|
540
|
+
lastStatus: Array<[PanelMember, "ready" | "open"]>;
|
|
541
|
+
sat: PanelMember[];
|
|
542
|
+
memberNotes: Array<[PanelMember, string[]]>;
|
|
543
|
+
turns: number;
|
|
544
|
+
mode: RoundMode;
|
|
545
|
+
answered: AskResult[];
|
|
546
|
+
lineComments: LineComment[];
|
|
547
|
+
saved?: PlannedTask;
|
|
548
|
+
savedParts: PlannedTask[];
|
|
549
|
+
error?: string;
|
|
550
|
+
autoContinued: boolean;
|
|
551
|
+
attempts: number;
|
|
552
|
+
/** A round was running when it was taken. */
|
|
553
|
+
running: boolean;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** What a seat that was thinking when pi stopped shows until its round runs again. */
|
|
557
|
+
export const STOPPED_WITH_PI = "pi stopped during the round";
|
|
558
|
+
|
|
559
|
+
/** Whether a parsed file is a snapshot this version reads; a torn or foreign file is not. */
|
|
560
|
+
export function isPlanningSnapshot(value: unknown): value is PlanningSnapshot {
|
|
561
|
+
const snapshot = value as PlanningSnapshot;
|
|
562
|
+
return Boolean(snapshot) && snapshot.version === 1 && Array.isArray(snapshot.messages) && Array.isArray(snapshot.questions) && Array.isArray(snapshot.seats)
|
|
563
|
+
&& Array.isArray(snapshot.members) && Array.isArray(snapshot.pins) && Array.isArray(snapshot.satOut) && Array.isArray(snapshot.lastStatus)
|
|
564
|
+
&& Array.isArray(snapshot.sat) && Array.isArray(snapshot.memberNotes) && Array.isArray(snapshot.answered) && Array.isArray(snapshot.lineComments)
|
|
565
|
+
&& Array.isArray(snapshot.savedParts) && Array.isArray(snapshot.notes) && typeof snapshot.turns === "number";
|
|
566
|
+
}
|
|
567
|
+
|
|
525
568
|
interface RunOutcome {
|
|
526
569
|
status: MetricRecord["status"];
|
|
527
570
|
output: string;
|
|
@@ -530,7 +573,15 @@ interface RunOutcome {
|
|
|
530
573
|
usage: { input: number; output: number; cost: number; turns: number };
|
|
531
574
|
}
|
|
532
575
|
|
|
576
|
+
/** A new planning session's id: `PS-` and the time, so ids sort by when they began. */
|
|
577
|
+
export function planningId(now = Date.now()): string {
|
|
578
|
+
return `PS-${now.toString(36)}${Math.random().toString(36).slice(2, 6)}`;
|
|
579
|
+
}
|
|
580
|
+
|
|
533
581
|
export class PlanningSession {
|
|
582
|
+
/** Names the session among the project's plans (src/state/plan-history.ts). */
|
|
583
|
+
id = planningId();
|
|
584
|
+
createdAt = Date.now();
|
|
534
585
|
messages: PlannerMessage[] = [];
|
|
535
586
|
seed?: PlannerSeed;
|
|
536
587
|
/** The oracle's latest reply; its plan is the current draft. */
|
|
@@ -718,6 +769,86 @@ export class PlanningSession {
|
|
|
718
769
|
return PANEL_MEMBERS.filter((member) => this.sat.has(member) && this.seats.has(member));
|
|
719
770
|
}
|
|
720
771
|
|
|
772
|
+
/** Everything the session needs to carry on in another pi process, as JSON. */
|
|
773
|
+
snapshot(): PlanningSnapshot {
|
|
774
|
+
return {
|
|
775
|
+
version: 1,
|
|
776
|
+
id: this.id,
|
|
777
|
+
createdAt: this.createdAt,
|
|
778
|
+
messages: this.messages,
|
|
779
|
+
...(this.seed ? { seed: this.seed } : {}),
|
|
780
|
+
...(this.reply ? { reply: this.reply } : {}),
|
|
781
|
+
questions: this.questions,
|
|
782
|
+
notes: this.notes,
|
|
783
|
+
members: this.members,
|
|
784
|
+
seats: [...this.seats],
|
|
785
|
+
pins: [...this.pins],
|
|
786
|
+
satOut: [...this.satOut],
|
|
787
|
+
lastStatus: [...this.lastStatus],
|
|
788
|
+
sat: [...this.sat],
|
|
789
|
+
memberNotes: [...this.memberNotes],
|
|
790
|
+
turns: this.turns,
|
|
791
|
+
mode: this.mode,
|
|
792
|
+
answered: this.answered,
|
|
793
|
+
lineComments: this.lineComments,
|
|
794
|
+
...(this.saved ? { saved: this.saved } : {}),
|
|
795
|
+
savedParts: this.savedParts,
|
|
796
|
+
...(this.error ? { error: this.error } : {}),
|
|
797
|
+
autoContinued: this.autoContinued,
|
|
798
|
+
attempts: this.attempts,
|
|
799
|
+
running: this.busy,
|
|
800
|
+
};
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
* A session as a snapshot left it. A round that was running when pi
|
|
805
|
+
* stopped is marked interrupted: `resume` runs it again.
|
|
806
|
+
*/
|
|
807
|
+
static restore(deps: PlannerDeps, snapshot: PlanningSnapshot): PlanningSession {
|
|
808
|
+
const session = new PlanningSession({ ...deps, panel: snapshot.seats }, snapshot.seed);
|
|
809
|
+
if (snapshot.id) session.id = snapshot.id;
|
|
810
|
+
if (typeof snapshot.createdAt === "number") session.createdAt = snapshot.createdAt;
|
|
811
|
+
session.messages = snapshot.messages;
|
|
812
|
+
if (snapshot.reply) session.reply = snapshot.reply;
|
|
813
|
+
session.questions = snapshot.questions;
|
|
814
|
+
session.notes = snapshot.notes;
|
|
815
|
+
session.members = snapshot.members.map((member) => member.status === "thinking" ? { ...member, status: "failed", error: STOPPED_WITH_PI, step: undefined } : member);
|
|
816
|
+
for (const member of snapshot.pins) session.pins.add(member);
|
|
817
|
+
session.satOut = new Map(snapshot.satOut);
|
|
818
|
+
for (const [member, status] of snapshot.lastStatus) session.lastStatus.set(member, status);
|
|
819
|
+
for (const member of snapshot.sat) session.sat.add(member);
|
|
820
|
+
for (const [member, notes] of snapshot.memberNotes) session.memberNotes.set(member, notes);
|
|
821
|
+
session.turns = snapshot.turns;
|
|
822
|
+
session.mode = snapshot.mode;
|
|
823
|
+
session.answered = snapshot.answered;
|
|
824
|
+
session.lineComments = snapshot.lineComments;
|
|
825
|
+
if (snapshot.saved) session.saved = snapshot.saved;
|
|
826
|
+
session.savedParts = snapshot.savedParts;
|
|
827
|
+
if (snapshot.error) session.error = snapshot.error;
|
|
828
|
+
session.autoContinued = snapshot.autoContinued;
|
|
829
|
+
session.attempts = snapshot.attempts;
|
|
830
|
+
session.interrupted = snapshot.running;
|
|
831
|
+
return session;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
/** A round was running when pi stopped, and has not run again yet. */
|
|
835
|
+
interrupted = false;
|
|
836
|
+
|
|
837
|
+
/** Run again the round pi stopped in the middle of; nothing when none was. */
|
|
838
|
+
async resume(): Promise<void> {
|
|
839
|
+
if (!this.interrupted || this.busy) return;
|
|
840
|
+
this.interrupted = false;
|
|
841
|
+
const last = this.messages.at(-1);
|
|
842
|
+
if (last?.role === "you") return this.retry();
|
|
843
|
+
// The seed alone had started the first round.
|
|
844
|
+
if (this.messages.length === 0 && this.seed) {
|
|
845
|
+
this.turns = Math.max(0, this.turns - 1);
|
|
846
|
+
return this.open();
|
|
847
|
+
}
|
|
848
|
+
// The round had already written its reply: only its last checks were cut off.
|
|
849
|
+
this.deps.onChange?.();
|
|
850
|
+
}
|
|
851
|
+
|
|
721
852
|
/** Save the latest draft as a pending task. */
|
|
722
853
|
save(now = new Date()): PlannedTask {
|
|
723
854
|
const plan = this.reply?.plan;
|
package/src/lobby/quickfix.ts
CHANGED
|
@@ -65,6 +65,8 @@ export interface QuickFixJob {
|
|
|
65
65
|
profile?: QuickFixProfile;
|
|
66
66
|
/** The oracle sent it here from a request typed to the lobby. */
|
|
67
67
|
routed?: boolean;
|
|
68
|
+
/** An earlier run was cut off when pi stopped: some of its edits may already be in the tree. */
|
|
69
|
+
interrupted?: boolean;
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
/** How a job enters the queue besides its prompt. */
|
|
@@ -111,6 +113,10 @@ export interface QuickFixDeps {
|
|
|
111
113
|
|
|
112
114
|
export const MAX_JOBS = 30;
|
|
113
115
|
export const MAX_STEPS = 200;
|
|
116
|
+
/** Steps a snapshot keeps per job: enough to read what it did. */
|
|
117
|
+
const MAX_KEPT_STEPS = 40;
|
|
118
|
+
/** What a job pi stopped in the middle of is told when it runs again. */
|
|
119
|
+
export const INTERRUPTED_NOTE = "An earlier run of this request was cut off when pi stopped, so some of its edits may already be in the tree: look at the files before you change them, and finish the request.";
|
|
114
120
|
|
|
115
121
|
function isActive(job: QuickFixJob): boolean {
|
|
116
122
|
return job.status === "queued" || job.status === "running";
|
|
@@ -202,6 +208,39 @@ export class QuickFixQueue {
|
|
|
202
208
|
return true;
|
|
203
209
|
}
|
|
204
210
|
|
|
211
|
+
/** The queue as JSON, for the next pi process to carry on from. */
|
|
212
|
+
snapshot(): QuickFixJob[] {
|
|
213
|
+
return this.jobs.map((job) => ({ ...job, steps: job.steps.slice(-MAX_KEPT_STEPS) }));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Take up a queue an earlier pi process left: finished jobs stay as they
|
|
218
|
+
* ended, and the one that was running when pi stopped goes back to the front
|
|
219
|
+
* of the queue, told that it was cut off. Returns the jobs that will run.
|
|
220
|
+
*/
|
|
221
|
+
restore(jobs: readonly QuickFixJob[]): QuickFixJob[] {
|
|
222
|
+
const kept = jobs.map((job): QuickFixJob => job.status !== "running" ? { ...job } : {
|
|
223
|
+
...job,
|
|
224
|
+
status: "queued",
|
|
225
|
+
interrupted: true,
|
|
226
|
+
note: "pi stopped while this ran; it runs again from where the tree is",
|
|
227
|
+
steps: job.steps.map((step) => ({ ...step, pending: false })),
|
|
228
|
+
});
|
|
229
|
+
// Queues from more than one stopped window may share ids: a repeated one is numbered on.
|
|
230
|
+
const number = (job: QuickFixJob) => Number(/^QF-(\d+)$/.exec(job.id)?.[1] ?? 0);
|
|
231
|
+
this.counter = Math.max(this.counter, ...this.jobs.map(number), ...kept.map(number));
|
|
232
|
+
const taken = new Set(this.jobs.map((job) => job.id));
|
|
233
|
+
for (const job of kept) {
|
|
234
|
+
if (taken.has(job.id)) job.id = `QF-${++this.counter}`;
|
|
235
|
+
taken.add(job.id);
|
|
236
|
+
}
|
|
237
|
+
const resumed = kept.filter((job) => job.interrupted && job.status === "queued");
|
|
238
|
+
this.jobs = [...resumed, ...kept.filter((job) => !resumed.includes(job)), ...this.jobs];
|
|
239
|
+
this.changed();
|
|
240
|
+
this.pump();
|
|
241
|
+
return this.jobs.filter(isActive);
|
|
242
|
+
}
|
|
243
|
+
|
|
205
244
|
/** Abort everything (session shutdown). */
|
|
206
245
|
cancelAll(): void {
|
|
207
246
|
for (const job of this.jobs) if (job.status === "queued") job.status = "cancelled";
|
|
@@ -259,7 +298,7 @@ export class QuickFixQueue {
|
|
|
259
298
|
const attempt = (model: string | undefined, thinking: string) => runPiAgent(
|
|
260
299
|
{
|
|
261
300
|
cwd: this.deps.cwd,
|
|
262
|
-
task:
|
|
301
|
+
task: [job.prompt, job.interrupted ? INTERRUPTED_NOTE : "", likely].filter(Boolean).join("\n\n"),
|
|
263
302
|
systemPrompt: quickFixPrompt(profile.instructions),
|
|
264
303
|
tools: [...QUICK_FIX_TOOLS, ...(this.deps.hints?.tools() ?? [])],
|
|
265
304
|
...grantOption("quickfix"),
|