@a-t-h-i/bot-lobby 0.6.44 → 0.6.45
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 +15 -4
- package/package.json +1 -6
- package/src/execution/agent-runner.ts +0 -2
- package/src/execution/delegation.ts +1 -2
- package/src/execution/pi-runner.ts +2 -6
- package/src/index.ts +0 -3
- package/src/lobby/host.ts +1 -12
- package/src/lobby/planner.ts +1 -6
- package/src/lobby/prompts.ts +4 -6
- package/src/lobby/quickfix.ts +0 -2
- package/src/lobby/runtime.ts +1 -5
- package/src/lobby/service.ts +0 -6
- package/src/lobby/topics.ts +0 -1
- package/src/pi/tools.ts +2 -2
- package/src/webui/api/index.ts +0 -39
- package/src/webui/dev/fake-service.ts +0 -57
- package/src/webui/dev/fixtures.ts +0 -6
- package/src/webui/protocol.ts +0 -29
- package/src/workflow/workflow.ts +1 -17
- package/webui/dist/assets/index-ClwTDiyo.css +2 -0
- package/webui/dist/assets/index-Ct47Kp7Q.js +110 -0
- package/webui/dist/build.json +18 -58
- package/webui/dist/index.html +2 -2
- package/src/excalidraw/check.ts +0 -31
- package/src/excalidraw/client.ts +0 -334
- package/src/excalidraw/room.ts +0 -118
- package/src/excalidraw/scene.ts +0 -746
- package/src/excalidraw/sessions.ts +0 -302
- package/src/excalidraw/tools.ts +0 -223
- package/src/webui/api/excalidraw.ts +0 -105
- package/webui/dist/assets/index-C0T8aK16.js +0 -110
- package/webui/dist/assets/index-XtSiJAAR.css +0 -2
package/README.md
CHANGED
|
@@ -33,14 +33,25 @@ files the task changed, after the project's linter and a QA pass sized to the ch
|
|
|
33
33
|
- **Plan**: plan with a panel of agents. **View plan** (`V`) opens the draft in a slide-out, where
|
|
34
34
|
any line takes a comment.
|
|
35
35
|
- **Tasks**: every task across sessions as a list or a board, with its steps, timings and **Resume**.
|
|
36
|
-
- **
|
|
37
|
-
|
|
36
|
+
- **Git**: use the **Pull requests / Issues** switch to review pull requests or browse open issues
|
|
37
|
+
through `gh`. Issues retain descriptions, existing comments, **File issue** and **Plan it**;
|
|
38
|
+
pull-request reviews remain read-only and post nothing.
|
|
38
39
|
- **Knowledge**: what each agent has learned about the project, which you can edit, annotate or ask
|
|
39
40
|
about.
|
|
40
41
|
|
|
41
42
|
The rail on the left holds settings, the key list, the theme and sounds. The project switcher
|
|
42
|
-
(`P`)
|
|
43
|
-
in
|
|
43
|
+
(`P`) opens any folder on the machine running bot-lobby. Background sessions still run tasks;
|
|
44
|
+
follow their progress and use **Resume** in **Tasks**, without a separate Sessions conversation page.
|
|
45
|
+
|
|
46
|
+
Git opens pull requests at `#/git` and `#/git/<number>`, and issues at `#/git/issues` and
|
|
47
|
+
`#/git/issues/<number>`. Old `#/issues` bookmarks redirect to Git. Former Sessions and Excalidraw
|
|
48
|
+
addresses fall back to Lobby; Excalidraw integration is removed without deleting saved room links
|
|
49
|
+
or external boards.
|
|
50
|
+
|
|
51
|
+
The composer appears only on **Lobby**, **Tasks**, **Plan**, **Quick fix** and **Knowledge**, including
|
|
52
|
+
supported detail routes and Knowledge's Docs/Chat views. On Git, Metrics and Settings it stays mounted
|
|
53
|
+
but hidden and inert, preserving drafts and attachments (including uploads that finish while hidden)
|
|
54
|
+
without taking space. Phone navigation remains available.
|
|
44
55
|
|
|
45
56
|
| Agent | Does |
|
|
46
57
|
| --- | --- |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@a-t-h-i/bot-lobby",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.45",
|
|
4
4
|
"description": "Structured multi-agent software engineering orchestrator for Pi",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -72,16 +72,11 @@
|
|
|
72
72
|
"react-dom": "19.3.0",
|
|
73
73
|
"react-markdown": "10.1.0",
|
|
74
74
|
"remark-gfm": "4.0.1",
|
|
75
|
-
"socket.io": "4.8.4",
|
|
76
75
|
"tailwind-merge": "3.7.0",
|
|
77
76
|
"tailwindcss": "4.3.3",
|
|
78
77
|
"tw-animate-css": "1.4.0",
|
|
79
78
|
"typebox": "1.3.27",
|
|
80
79
|
"typescript": "^5.7.0",
|
|
81
80
|
"vite": "8.3.2"
|
|
82
|
-
},
|
|
83
|
-
"dependencies": {
|
|
84
|
-
"https-proxy-agent": "^9.1.0",
|
|
85
|
-
"socket.io-client": "4.8.4"
|
|
86
81
|
}
|
|
87
82
|
}
|
|
@@ -11,7 +11,6 @@ import { ASK_ENV } from "../ask/relay.ts";
|
|
|
11
11
|
import { STEP_LIMIT_MS } from "../state/step-limits.ts";
|
|
12
12
|
import type { EffortRouter, RunProfileLike } from "../classifier/effort.ts";
|
|
13
13
|
import { ASK_TOOL } from "../ask/types.ts";
|
|
14
|
-
import { agentOfRun, grantOption } from "../excalidraw/sessions.ts";
|
|
15
14
|
|
|
16
15
|
export interface AgentContext {
|
|
17
16
|
task: string;
|
|
@@ -289,7 +288,6 @@ async function runAgentOnce(request: AgentRequest, run: ProcessRunner, attempt:
|
|
|
289
288
|
} : {}),
|
|
290
289
|
signal,
|
|
291
290
|
...(request.onAsk ? { onAsk: request.onAsk } : {}),
|
|
292
|
-
...grantOption(agentOfRun(request.domain, request.role)),
|
|
293
291
|
env: request.onAsk ? { ...request.env, [ASK_ENV]: "1" } : request.env,
|
|
294
292
|
stallTimeoutMs: request.stallTimeoutMs,
|
|
295
293
|
toolStallTimeoutMs: request.toolStallTimeoutMs,
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { toolsOf } from "../excalidraw/sessions.ts";
|
|
2
1
|
import { appendMetrics, metricFromRun } from "../state/metrics.ts";
|
|
3
2
|
import { randomUUID } from "node:crypto";
|
|
4
3
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
@@ -73,7 +72,7 @@ export async function delegationHost(options: PiRunOptions, run: ProcessRunner)
|
|
|
73
72
|
const agent = options.agent ?? { runId: id, taskId: id, domain: "backend" as const, role: editable ? "worker" as const : "reviewer" as const };
|
|
74
73
|
const depth = agent.depth ?? 1;
|
|
75
74
|
const quota = options.delegationQuota ?? { used: 0 };
|
|
76
|
-
const tools = [...new Set([...baseTools,
|
|
75
|
+
const tools = [...new Set([...baseTools, "codemode", "tool_search", ...(canDelegate(agent, quota.used) ? [DELEGATE_TOOL] : [])])];
|
|
77
76
|
const mcpTools = options.mcpTools ?? agentProfile(config, agent.domain, agent.role).mcpTools ?? [];
|
|
78
77
|
const policy: AgentPolicy = { tools, mcpTools, readOnly: !editable, depth };
|
|
79
78
|
if (depth > 1) return { tools, env: { ...options.env, [AGENT_ENV]: JSON.stringify(policy), [DELEGATION_ENV]: "" }, note: "You are a child agent. Finish only your assigned brief and report evidence to your parent; you cannot delegate further.", close: async () => {}, onStart: options.onStart, children: [] as AgentRun[] };
|
|
@@ -10,7 +10,6 @@ import { activityWord } from "../pi/activity.ts";
|
|
|
10
10
|
import { shortDuration } from "../text.ts";
|
|
11
11
|
import { readRelayRequest } from "../ask/relay.ts";
|
|
12
12
|
import type { AskQuestion, AskResult } from "../ask/types.ts";
|
|
13
|
-
import { grantEnv, grantNote, type Grant } from "../excalidraw/sessions.ts";
|
|
14
13
|
import type { EffortRouter, RunProfileLike } from "../classifier/effort.ts";
|
|
15
14
|
|
|
16
15
|
/** Live control over one running subagent: queue a steering message for its next turn. */
|
|
@@ -57,8 +56,6 @@ export interface PiRunOptions {
|
|
|
57
56
|
time?: TimeLimit;
|
|
58
57
|
/** Answers the agent's relayed questions; without it they are cancelled like any other dialog. */
|
|
59
58
|
onAsk?: RelayAsk;
|
|
60
|
-
/** The Excalidraw sessions assigned to this agent: its tools, the room links in its environment, and a note on them in its task. */
|
|
61
|
-
excalidraw?: Grant;
|
|
62
59
|
}
|
|
63
60
|
|
|
64
61
|
/**
|
|
@@ -771,7 +768,6 @@ function writePromptFile(prompt: string): { file: string; cleanup: () => void }
|
|
|
771
768
|
export async function runPiAgent(options: PiRunOptions, run: ProcessRunner = spawnPiProcess): Promise<PiRunResult> {
|
|
772
769
|
const host = await delegationHost(options, run);
|
|
773
770
|
let prompt: ReturnType<typeof writePromptFile> | undefined;
|
|
774
|
-
const grant = options.excalidraw;
|
|
775
771
|
try {
|
|
776
772
|
prompt = writePromptFile([options.systemPrompt, host.note].filter(Boolean).join("\n\n"));
|
|
777
773
|
if (options.signal?.aborted) return { status: "cancelled", output: "", usage: { input: 0, output: 0, cost: 0, turns: 0 } };
|
|
@@ -780,8 +776,8 @@ export async function runPiAgent(options: PiRunOptions, run: ProcessRunner = spa
|
|
|
780
776
|
cwd: options.cwd,
|
|
781
777
|
signal: options.signal,
|
|
782
778
|
timeoutMs: options.timeoutMs,
|
|
783
|
-
prompt: `Task: ${options.task}
|
|
784
|
-
env:
|
|
779
|
+
prompt: `Task: ${options.task}`,
|
|
780
|
+
env: host.env,
|
|
785
781
|
stallTimeoutMs: options.stallTimeoutMs,
|
|
786
782
|
toolStallTimeoutMs: options.toolStallTimeoutMs,
|
|
787
783
|
wrapUpAtMs: options.wrapUpAtMs,
|
package/src/index.ts
CHANGED
|
@@ -19,7 +19,6 @@ import { registerClassifierTools } from "./classifier/tools.ts";
|
|
|
19
19
|
import { registerFreshContext } from "./pi/fresh-context.ts";
|
|
20
20
|
import { registerAskTool } from "./ask/tool.ts";
|
|
21
21
|
import { registerWebTools } from "./web/tools.ts";
|
|
22
|
-
import { registerExcalidrawTools } from "./excalidraw/tools.ts";
|
|
23
22
|
import { registerToolBudget } from "./pi/tool-budget.ts";
|
|
24
23
|
|
|
25
24
|
export default function (pi: ExtensionAPI): void {
|
|
@@ -49,8 +48,6 @@ export default function (pi: ExtensionAPI): void {
|
|
|
49
48
|
registerAskTool(pi, undefined, CONFIG_DIR_NAME);
|
|
50
49
|
// The web tools, for the researcher and for pi without a task (the oracle leaves them to the researcher).
|
|
51
50
|
registerWebTools(pi);
|
|
52
|
-
// The shared Excalidraw sessions the user assigned to agents: the oracle here, every other agent through its own grant.
|
|
53
|
-
registerExcalidrawTools(pi, CONFIG_DIR_NAME);
|
|
54
51
|
// Parallel workers share files through the master's file desk.
|
|
55
52
|
if (isSubagentProcess()) registerDeskClient(pi);
|
|
56
53
|
// Subagents look files up with the classifier (the engine allows the tool only while file hints are on).
|
package/src/lobby/host.ts
CHANGED
|
@@ -18,18 +18,11 @@ import type { IssuesState } from "./issues.ts";
|
|
|
18
18
|
import type { PullsState } from "./pulls.ts";
|
|
19
19
|
import type { PullReviews } from "./pr-review.ts";
|
|
20
20
|
import type { KnowledgeBook } from "./knowledge.ts";
|
|
21
|
-
import type { ExcalidrawBook, ExcalidrawSession } from "../excalidraw/sessions.ts";
|
|
22
21
|
import type { WorkspaceInfo } from "../execution/workspace.ts";
|
|
23
22
|
|
|
24
23
|
export { TAB_IDS, TAB_LABELS, visibleTabs } from "./prompts.ts";
|
|
25
24
|
export type { TabId } from "./prompts.ts";
|
|
26
25
|
|
|
27
|
-
/** What a checked Excalidraw session reported. */
|
|
28
|
-
export interface SessionCheck {
|
|
29
|
-
ok: boolean;
|
|
30
|
-
text: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
26
|
/**
|
|
34
27
|
* Which session the Lobby tab shows and talks to: this window, one it started
|
|
35
28
|
* in the background, one running in another terminal, or a task whose
|
|
@@ -147,10 +140,6 @@ export interface LobbyService {
|
|
|
147
140
|
reviews: PullReviews;
|
|
148
141
|
/** Every agent's knowledge files: read, edited, commented on. */
|
|
149
142
|
knowledge: KnowledgeBook;
|
|
150
|
-
/** The shared Excalidraw sessions (five at most) and which agents each is assigned to. */
|
|
151
|
-
excalidraw: ExcalidrawBook;
|
|
152
|
-
/** Join a session's room for a moment and report what is there. */
|
|
153
|
-
checkExcalidraw(session: ExcalidrawSession): Promise<SessionCheck>;
|
|
154
143
|
/** The effective config, never a secret; the web settings page reads it. */
|
|
155
144
|
config?(): BotLobbyConfig;
|
|
156
145
|
/** Save a whole config, the way the settings menu does; the web settings page writes it. */
|
|
@@ -163,7 +152,7 @@ export interface LobbyService {
|
|
|
163
152
|
classifierModels?(): Array<{ id: string; label: string }>;
|
|
164
153
|
/** The model this window's session runs on (`provider/id`), when it has one. */
|
|
165
154
|
sessionModel?(): string | undefined;
|
|
166
|
-
/**
|
|
155
|
+
/** Git's Issues view is switched on (`lobby.issues`). */
|
|
167
156
|
issuesEnabled(): boolean;
|
|
168
157
|
/** What this window carried on from one that stopped unexpectedly, when it did. */
|
|
169
158
|
recovered?(): { at: number; text: string } | undefined;
|
package/src/lobby/planner.ts
CHANGED
|
@@ -22,7 +22,6 @@ import { randomUUID } from "node:crypto";
|
|
|
22
22
|
import { loadPrompt } from "../prompts/loader.ts";
|
|
23
23
|
import { compilePrompt } from "../prompts/compiler.ts";
|
|
24
24
|
import { runPiAgent, spawnPiProcess, type PiStreamEvent, type ProcessRunner } from "../execution/pi-runner.ts";
|
|
25
|
-
import { grantOption, type Grant } from "../excalidraw/sessions.ts";
|
|
26
25
|
import { describeToolCall } from "../pi/activity.ts";
|
|
27
26
|
import { appendMetrics, type MetricRecord } from "../state/metrics.ts";
|
|
28
27
|
import { savePlannedTask, type IssueRef, type PlannedTask } from "../state/backlog.ts";
|
|
@@ -904,7 +903,6 @@ export class PlanningSession {
|
|
|
904
903
|
task: splitRequest({ title, plan, steps, ...(feedback ? { revision: feedback } : {}), ...(rejected.length > 0 ? { rejected } : {}) }),
|
|
905
904
|
systemPrompt: splitPrompt(profile.instructions),
|
|
906
905
|
tools: this.tools(PLANNER_TOOLS),
|
|
907
|
-
...grantOption("planner"),
|
|
908
906
|
}, signal, (step) => (this.step = step));
|
|
909
907
|
if (signal.aborted) throw new Error("stopped");
|
|
910
908
|
if (lead.status !== "success") return lead.error ?? lead.status;
|
|
@@ -997,7 +995,7 @@ export class PlanningSession {
|
|
|
997
995
|
this.deps.onChange?.();
|
|
998
996
|
}
|
|
999
997
|
|
|
1000
|
-
private async run(label: string, kind: "planner" | "panel", profile: QuickFixProfile, request: { task: string; systemPrompt: string; tools: readonly string[]
|
|
998
|
+
private async run(label: string, kind: "planner" | "panel", profile: QuickFixProfile, request: { task: string; systemPrompt: string; tools: readonly string[] }, signal: AbortSignal, setStep: (step: string) => void, routedFrom?: string): Promise<RunOutcome> {
|
|
1001
999
|
const startedAt = Date.now();
|
|
1002
1000
|
const runId = randomUUID();
|
|
1003
1001
|
let outcome: RunOutcome;
|
|
@@ -1069,8 +1067,6 @@ export class PlanningSession {
|
|
|
1069
1067
|
task: `${transcript}\n\nYou are ${label} on the planning panel${roundNote(this.turns, this.limit)}: ask your seat's open questions, or declare READY.`,
|
|
1070
1068
|
systemPrompt: memberPrompt(member, profile.instructions),
|
|
1071
1069
|
tools: this.tools(member === "researcher" ? RESEARCH_PANEL_TOOLS : PLANNER_TOOLS),
|
|
1072
|
-
// A panel seat is the agent of its own kind: DEV is Backend, DESIGN the Designer.
|
|
1073
|
-
...grantOption(member),
|
|
1074
1070
|
};
|
|
1075
1071
|
const route = effort ? this.deps.effort?.plan(effort, { ...(profile.model ? { model: profile.model } : {}), thinking: profile.thinking }) : undefined;
|
|
1076
1072
|
let outcome = await this.run(label, "panel", route ? { ...profile, model: route.model, thinking: route.thinking } : profile, request, signal, (step) => (state.step = step), route ? profileLabel(route.from) : undefined);
|
|
@@ -1128,7 +1124,6 @@ export class PlanningSession {
|
|
|
1128
1124
|
task: `${transcript}\n\n${panelSection(outcomes)}\n\n${oracleClosing(mode, this.turns, limit)}`,
|
|
1129
1125
|
systemPrompt: plannerPrompt(profile.instructions),
|
|
1130
1126
|
tools: this.tools(PLANNER_TOOLS),
|
|
1131
|
-
...grantOption("planner"),
|
|
1132
1127
|
}, controller.signal, (step) => (this.step = step));
|
|
1133
1128
|
if (controller.signal.aborted) throw new Error("stopped");
|
|
1134
1129
|
this.finishRound(outcomes, lead, mode);
|
package/src/lobby/prompts.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `node:*`): the web page imports this file directly.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
export const TAB_IDS = ["lobby", "tasks", "plan", "quickfix", "
|
|
7
|
+
export const TAB_IDS = ["lobby", "tasks", "plan", "quickfix", "git", "knowledge", "metrics"] as const;
|
|
8
8
|
export type TabId = (typeof TAB_IDS)[number];
|
|
9
9
|
|
|
10
10
|
export const TAB_LABELS: Record<TabId, string> = {
|
|
@@ -12,16 +12,14 @@ export const TAB_LABELS: Record<TabId, string> = {
|
|
|
12
12
|
tasks: "Tasks",
|
|
13
13
|
plan: "Plan",
|
|
14
14
|
quickfix: "Quick fix",
|
|
15
|
-
issues: "Issues",
|
|
16
15
|
metrics: "Metrics",
|
|
17
16
|
git: "Git",
|
|
18
17
|
knowledge: "Knowledge",
|
|
19
|
-
excalidraw: "Excalidraw",
|
|
20
18
|
};
|
|
21
19
|
|
|
22
|
-
/** The tabs on show
|
|
23
|
-
export function visibleTabs(
|
|
24
|
-
return TAB_IDS
|
|
20
|
+
/** The tabs on show; issues are available inside Git. */
|
|
21
|
+
export function visibleTabs(_issues: boolean): TabId[] {
|
|
22
|
+
return [...TAB_IDS];
|
|
25
23
|
}
|
|
26
24
|
|
|
27
25
|
/** A question answered in another window of the page (or put away) before this one's answer arrived. */
|
package/src/lobby/quickfix.ts
CHANGED
|
@@ -9,7 +9,6 @@ import type { AgentRun } from "../schemas/findings.ts";
|
|
|
9
9
|
import { loadPrompt } from "../prompts/loader.ts";
|
|
10
10
|
import { runPiAgent, spawnPiProcess, type PiStreamEvent, type ProcessRunner } from "../execution/pi-runner.ts";
|
|
11
11
|
import { MAX_CHILDREN } from "../execution/delegation.ts";
|
|
12
|
-
import { grantOption } from "../excalidraw/sessions.ts";
|
|
13
12
|
import { describeToolCall } from "../pi/activity.ts";
|
|
14
13
|
import { appendMetrics, type MetricRecord } from "../state/metrics.ts";
|
|
15
14
|
import { appendChange, EditLog } from "../state/changes.ts";
|
|
@@ -338,7 +337,6 @@ export class QuickFixQueue {
|
|
|
338
337
|
task: [job.prompt, job.interrupted ? INTERRUPTED_NOTE : "", likely, scripts ? `## Scripts\n\n${scripts}` : ""].filter(Boolean).join("\n\n"),
|
|
339
338
|
systemPrompt: quickFixPrompt(profile.instructions),
|
|
340
339
|
tools: [...QUICK_FIX_TOOLS, ...(this.deps.hints?.tools() ?? [])],
|
|
341
|
-
...grantOption("quickfix"),
|
|
342
340
|
model,
|
|
343
341
|
thinking,
|
|
344
342
|
mcpTools: profile.mcpTools,
|
package/src/lobby/runtime.ts
CHANGED
|
@@ -26,7 +26,6 @@ import { execCommand, IssuesState } from "./issues.ts";
|
|
|
26
26
|
import { PullsState } from "./pulls.ts";
|
|
27
27
|
import { PullReviews } from "./pr-review.ts";
|
|
28
28
|
import { KnowledgeBook } from "./knowledge.ts";
|
|
29
|
-
import { ExcalidrawBook } from "../excalidraw/sessions.ts";
|
|
30
29
|
import type { LobbyService } from "./host.ts";
|
|
31
30
|
import { backgroundSessions, createLobbyService, lobbyNotify, lobbyProfile, plannerDeps, resumeSession, reviewProfile, seatProfile, setServiceState, savePlan as serviceSavePlan, answerPanel as serviceAnswerPanel } from "./service.ts";
|
|
32
31
|
import { lobbyTopics } from "./topics.ts";
|
|
@@ -49,7 +48,6 @@ export interface Runtime {
|
|
|
49
48
|
pulls: PullsState;
|
|
50
49
|
reviews: PullReviews;
|
|
51
50
|
knowledge: KnowledgeBook;
|
|
52
|
-
excalidraw: ExcalidrawBook;
|
|
53
51
|
unsubscribeFeed?: () => void;
|
|
54
52
|
/** Puts the panel's questions to the user: the web page's questionnaire unless a test sets another. */
|
|
55
53
|
asker?: Asker;
|
|
@@ -163,7 +161,6 @@ export function startLobbyService(pi: ExtensionAPI, ctx: ExtensionContext, confi
|
|
|
163
161
|
backups: () => loadConfig().knowledge.backupCount,
|
|
164
162
|
sessionId: () => ctx.sessionManager.getSessionId(),
|
|
165
163
|
}),
|
|
166
|
-
excalidraw: new ExcalidrawBook({ root }),
|
|
167
164
|
};
|
|
168
165
|
state.reviews = new PullReviews({
|
|
169
166
|
cwd: ctx.cwd,
|
|
@@ -204,8 +201,7 @@ export function startLobbyService(pi: ExtensionAPI, ctx: ExtensionContext, confi
|
|
|
204
201
|
// changes and the web page rereads it. lobbyFeed (lobby), run updates (tasks),
|
|
205
202
|
// quick fix (quickfix), planner (planner), background sessions (sessions),
|
|
206
203
|
// metrics (metrics), pulls/reviews (git), issues (issues), the prompt hub
|
|
207
|
-
// (prompts), owner/notify events (notices), busy/workspace (status)
|
|
208
|
-
// checks (excalidraw).
|
|
204
|
+
// (prompts), owner/notify events (notices), busy/workspace (status).
|
|
209
205
|
setQuickFixHandoff((request, builder, reason) => (runtime === state ? handToQuickFix(state, request, builder, reason) : undefined));
|
|
210
206
|
lobbyFeed.clear();
|
|
211
207
|
// Only the newest messages are kept; the feed learns whether earlier ones exist, and loads them when scrolled to.
|
package/src/lobby/service.ts
CHANGED
|
@@ -45,7 +45,6 @@ import { carryOnMessage, resumeRoute, RESUMED_DECISION, type Drivers } from "./r
|
|
|
45
45
|
import { archivePlan as archivePlanOnDisk, deletePlan as deletePlanOnDisk, isPlanId, previousPlans, readPlan, type PlanRecord, type PlanSummary } from "../state/plan-history.ts";
|
|
46
46
|
import { applyMasterModel } from "../pi/model-settings.ts";
|
|
47
47
|
import { lobbyTopics } from "./topics.ts";
|
|
48
|
-
import { checkSession } from "../excalidraw/check.ts";
|
|
49
48
|
import type { LobbyAgentKind, PanelMember } from "../schemas/configuration.ts";
|
|
50
49
|
import type { LiveSession, LobbyService, SwitchTarget } from "./host.ts";
|
|
51
50
|
import { pushNotice, type NoticeLevel } from "../webui/notices.ts";
|
|
@@ -814,11 +813,6 @@ export function createLobbyService(state: Runtime): LobbyService {
|
|
|
814
813
|
pulls: state.pulls,
|
|
815
814
|
reviews: state.reviews,
|
|
816
815
|
knowledge: state.knowledge,
|
|
817
|
-
excalidraw: state.excalidraw,
|
|
818
|
-
checkExcalidraw: (session) => {
|
|
819
|
-
lobbyTopics.bump("excalidraw");
|
|
820
|
-
return checkSession(session.link, session.name);
|
|
821
|
-
},
|
|
822
816
|
profileLabel: (kind) => {
|
|
823
817
|
const profile = lobbyProfile(state, kind);
|
|
824
818
|
return `${profile.model ?? "session model"} · ${profile.thinking}`;
|
package/src/lobby/topics.ts
CHANGED
package/src/pi/tools.ts
CHANGED
|
@@ -62,7 +62,7 @@ const OrchestrateSchema = Type.Object({
|
|
|
62
62
|
text: Type.Optional(Type.String({ description: "decide/complete/knowledge/script: the decision, completion summary, knowledge text, or the script itself" })),
|
|
63
63
|
message: Type.Optional(Type.String({ description: "complete: the commit message for the agents' changes, which the engine commits (only their files): a first line of 72 characters or fewer saying what changed, at most three short lines below it" })),
|
|
64
64
|
file: Type.Optional(Type.String({ description: "compact: the knowledge file to rewrite, e.g. knowledge.md; script: a script a worker drafted, to save instead of text" })),
|
|
65
|
-
name: Type.Optional(Type.String({ description: "
|
|
65
|
+
name: Type.Optional(Type.String({ description: "script: the file name, what it does in snake_case with its language's extension (seed_public_holidays.py, run_changed_tests.sh)" })),
|
|
66
66
|
purpose: Type.Optional(Type.String({ description: "script: one line on what the script does" })),
|
|
67
67
|
usage: Type.Optional(Type.String({ description: "script: the command that runs it, with its arguments" })),
|
|
68
68
|
kind: Type.Optional(
|
|
@@ -79,7 +79,7 @@ const DESCRIPTION = [
|
|
|
79
79
|
"dependency), propose (record the proposal and request approval), plan (record the internal",
|
|
80
80
|
"plan, or amend it later with the full revised plan), implement (delegate a step to a domain worker, or several domains in parallel with assignments), qa (final quality gate and the only",
|
|
81
81
|
"review), knowledge (record approved project knowledge or a decision; works without a task),",
|
|
82
|
-
"compact (replace a knowledge file with a rewritten version, archiving the old one; works without a task; disperse domain-relevant facts to action=knowledge (domain=designer|backend|qa) first), script (save a script to the shared scripts library, or replace one, for a job that comes back; works without a task),
|
|
82
|
+
"compact (replace a knowledge file with a rewritten version, archiving the old one; works without a task; disperse domain-relevant facts to action=knowledge (domain=designer|backend|qa) first), script (save a script to the shared scripts library, or replace one, for a job that comes back; works without a task),",
|
|
83
83
|
"resolve_approval (approve or reject a request), complete (declare the task done after the gates",
|
|
84
84
|
"pass, then ask the user to confirm merging and pushing main and removing its worktree), block/resume (escalate or continue), budget (under a time budget: where it stands, or ask the",
|
|
85
85
|
"user for more minutes with a reason), track (the task's path and who takes part: show it, or correct it with",
|
package/src/webui/api/index.ts
CHANGED
|
@@ -18,7 +18,6 @@ import { quickfixCancel, quickfixList, quickfixMovedToTask, quickfixRunAnyway, q
|
|
|
18
18
|
import { tasksArchive, tasksArchived, tasksAuto, tasksComment, tasksComments, tasksDelete, tasksEditComment, tasksGet, tasksList, tasksMessage, tasksRestore, tasksOpen, tasksResume, tasksDeliveryReview, tasksDeliveryDefer, tasksDeliver } from "./tasks.ts";
|
|
19
19
|
import { metricsGet } from "./metrics.ts";
|
|
20
20
|
import { knowledgeAdd, knowledgeAsk, knowledgeComment, knowledgeEdit, knowledgeFiles, knowledgeOpen, knowledgeRemove, knowledgeReplaceFile, knowledgeUnnote } from "./knowledge.ts";
|
|
21
|
-
import { excalidrawAdd, excalidrawCheck, excalidrawCreate, excalidrawList, excalidrawRemove, excalidrawRename, excalidrawReveal, excalidrawToggleAgent, excalidrawToggleAll, excalidrawToggleContribute } from "./excalidraw.ts";
|
|
22
21
|
import { plansDiscard, plansGet, plansStart } from "./plans.ts";
|
|
23
22
|
import { gitCancelReview, gitJev, gitPull, gitPulls, gitReview } from "./git.ts";
|
|
24
23
|
import { issuesCreate, issuesGet, issuesList } from "./issues.ts";
|
|
@@ -93,7 +92,6 @@ const KnowledgeAgent = Type.Union([Type.Literal("master"), Type.Literal("designe
|
|
|
93
92
|
const KnowledgeFile = Type.String({ minLength: 1, maxLength: 200 });
|
|
94
93
|
const KnowledgeText = Type.String({ minLength: 1, maxLength: 100_000 });
|
|
95
94
|
const EntryRef = Type.Object({ text: Type.String({ minLength: 1, maxLength: 100_000 }), occurrence: Type.Integer({ minimum: 0, maximum: 100000 }) }, { additionalProperties: false });
|
|
96
|
-
const ExcalidrawAgent = Type.Union([Type.Literal("master"), Type.Literal("designer"), Type.Literal("backend"), Type.Literal("qa"), Type.Literal("scout"), Type.Literal("researcher"), Type.Literal("quickfix"), Type.Literal("planner")]);
|
|
97
95
|
const PlannerSeed = Type.Object({
|
|
98
96
|
issue: Type.Object({ number: Type.Integer({ minimum: 1 }), title: Type.String({ minLength: 1, maxLength: 500 }), url: Type.Optional(Type.String({ maxLength: 2000 })) }, { additionalProperties: false }),
|
|
99
97
|
body: Type.String({ maxLength: 20_000 }),
|
|
@@ -307,43 +305,6 @@ function buildRoutes(): Record<string, Route> {
|
|
|
307
305
|
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
308
306
|
run: (body, ctx) => knowledgeUnnote(body as { id: string }, ctx),
|
|
309
307
|
};
|
|
310
|
-
routes["excalidraw.list"] = { schema: Empty, run: (_body, ctx) => excalidrawList(ctx) };
|
|
311
|
-
routes["excalidraw.add"] = {
|
|
312
|
-
schema: Type.Object({ link: Type.String({ minLength: 1, maxLength: 2000 }), name: Type.Optional(Type.String({ maxLength: 40 })) }, { additionalProperties: false }),
|
|
313
|
-
run: (body, ctx) => excalidrawAdd(body as { link: string; name?: string }, ctx),
|
|
314
|
-
};
|
|
315
|
-
routes["excalidraw.create"] = {
|
|
316
|
-
schema: Type.Object({ name: Type.Optional(Type.String({ maxLength: 40 })) }, { additionalProperties: false }),
|
|
317
|
-
run: (body, ctx) => excalidrawCreate(body as { name?: string }, ctx),
|
|
318
|
-
};
|
|
319
|
-
routes["excalidraw.remove"] = {
|
|
320
|
-
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
321
|
-
run: (body, ctx) => excalidrawRemove(body as { id: string }, ctx),
|
|
322
|
-
};
|
|
323
|
-
routes["excalidraw.rename"] = {
|
|
324
|
-
schema: Type.Object({ id: TaskId, name: Type.String({ maxLength: 40 }) }, { additionalProperties: false }),
|
|
325
|
-
run: (body, ctx) => excalidrawRename(body as { id: string; name: string }, ctx),
|
|
326
|
-
};
|
|
327
|
-
routes["excalidraw.toggleAgent"] = {
|
|
328
|
-
schema: Type.Object({ id: TaskId, agent: ExcalidrawAgent }, { additionalProperties: false }),
|
|
329
|
-
run: (body, ctx) => excalidrawToggleAgent(body as { id: string; agent: "master" | "designer" | "backend" | "qa" | "scout" | "researcher" | "quickfix" | "planner" }, ctx),
|
|
330
|
-
};
|
|
331
|
-
routes["excalidraw.toggleAll"] = {
|
|
332
|
-
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
333
|
-
run: (body, ctx) => excalidrawToggleAll(body as { id: string }, ctx),
|
|
334
|
-
};
|
|
335
|
-
routes["excalidraw.toggleContribute"] = {
|
|
336
|
-
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
337
|
-
run: (body, ctx) => excalidrawToggleContribute(body as { id: string }, ctx),
|
|
338
|
-
};
|
|
339
|
-
routes["excalidraw.check"] = {
|
|
340
|
-
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
341
|
-
run: (body, ctx) => excalidrawCheck(body as { id: string }, ctx),
|
|
342
|
-
};
|
|
343
|
-
routes["excalidraw.reveal"] = {
|
|
344
|
-
schema: Type.Object({ id: TaskId }, { additionalProperties: false }),
|
|
345
|
-
run: (body, ctx) => excalidrawReveal(body as { id: string }, ctx),
|
|
346
|
-
};
|
|
347
308
|
routes["git.pulls"] = {
|
|
348
309
|
schema: Type.Object({ refresh: Type.Optional(Type.Boolean()) }, { additionalProperties: false }),
|
|
349
310
|
run: (body, ctx) => gitPulls(body as { refresh?: boolean }, ctx),
|
|
@@ -342,10 +342,6 @@ export function createFixtureService(name: string, feed = new LobbyFeed()): Lobb
|
|
|
342
342
|
const classifierMetrics = (fixture.mockClassifierMetrics ?? []).map((record) => ({ ...record }));
|
|
343
343
|
const knowledgeFiles = (fixture.mockKnowledge ?? []).map((file) => ({ ...file }));
|
|
344
344
|
const knowledgeView = fixture.mockKnowledgeView ? { ...fixture.mockKnowledgeView, entries: [...fixture.mockKnowledgeView.entries], attached: [...fixture.mockKnowledgeView.attached], detached: [...fixture.mockKnowledgeView.detached] } : undefined;
|
|
345
|
-
const xSessions = (fixture.mockExcalidraw ?? []).map((session) => ({ ...session, agents: [...session.agents] }));
|
|
346
|
-
const xLinks = new Map<string, string>();
|
|
347
|
-
if (xSessions[0] && fixture.mockExcalidrawLink) xLinks.set(xSessions[0].id, fixture.mockExcalidrawLink);
|
|
348
|
-
const xLinkOf = (id: string): string => xLinks.get(id) ?? `https://whiteboard.example/#room=${id},AAAAAAAAAAAAAAAAAAAAAA`;
|
|
349
345
|
const knowledge = {
|
|
350
346
|
files: () => knowledgeFiles.map((file) => ({ ...file })),
|
|
351
347
|
open: (agent: string, file: string) => {
|
|
@@ -359,57 +355,6 @@ export function createFixtureService(name: string, feed = new LobbyFeed()): Lobb
|
|
|
359
355
|
comment: () => "note saved — every agent reads it under this entry",
|
|
360
356
|
unnote: () => "note removed",
|
|
361
357
|
};
|
|
362
|
-
const excalidraw = {
|
|
363
|
-
list: () => xSessions.map((session) => ({ id: session.id, name: session.name, link: xLinkOf(session.id), agents: [...session.agents], contribute: session.contribute, addedAt: session.addedAt })),
|
|
364
|
-
add: (link: string, name?: string) => {
|
|
365
|
-
if (!link.includes("#room=")) return { notice: "that is not an Excalidraw room link — it looks like https://excalidraw.com/#room=<id>,<key>" };
|
|
366
|
-
const session = { id: `x${xSessions.length + 1}`, name: name || `Session ${xSessions.length + 1}`, link, agents: [] as string[], contribute: true, addedAt: new Date().toISOString() };
|
|
367
|
-
xSessions.push({ id: session.id, name: session.name, masked: "", agents: [], contribute: true, addedAt: session.addedAt });
|
|
368
|
-
xLinks.set(session.id, link);
|
|
369
|
-
return { notice: `added “${session.name}” — assign it to agents with enter`, session };
|
|
370
|
-
},
|
|
371
|
-
create: (name?: string) => {
|
|
372
|
-
const id = `x${xSessions.length + 1}`;
|
|
373
|
-
const link = `https://whiteboard.example/#room=mock${xSessions.length + 1},AAAAAAAAAAAAAAAAAAAAAA`;
|
|
374
|
-
const session = { id, name: name || `Session ${xSessions.length + 1}`, link, agents: [] as string[], contribute: true, addedAt: new Date().toISOString() };
|
|
375
|
-
xSessions.push({ id, name: session.name, masked: "", agents: [], contribute: true, addedAt: session.addedAt });
|
|
376
|
-
xLinks.set(id, link);
|
|
377
|
-
return { notice: `added “${session.name}” — assign it to agents with enter`, session };
|
|
378
|
-
},
|
|
379
|
-
remove: (id: string) => {
|
|
380
|
-
const index = xSessions.findIndex((session) => session.id === id);
|
|
381
|
-
if (index < 0) return "no such session";
|
|
382
|
-
const [gone] = xSessions.splice(index, 1);
|
|
383
|
-
return `removed “${gone!.name}”`;
|
|
384
|
-
},
|
|
385
|
-
rename: (id: string, name: string) => {
|
|
386
|
-
const session = xSessions.find((entry) => entry.id === id);
|
|
387
|
-
if (!session) return "no such session";
|
|
388
|
-
if (!name.trim()) return "a session needs a name";
|
|
389
|
-
session.name = name.trim().slice(0, 40);
|
|
390
|
-
return `renamed to “${session.name}”`;
|
|
391
|
-
},
|
|
392
|
-
toggleAgent: (id: string, agent: string) => {
|
|
393
|
-
const session = xSessions.find((entry) => entry.id === id);
|
|
394
|
-
if (!session) return "no such session";
|
|
395
|
-
const at = session.agents.indexOf(agent);
|
|
396
|
-
if (at >= 0) session.agents.splice(at, 1);
|
|
397
|
-
else session.agents.push(agent);
|
|
398
|
-
return at >= 0 ? `${agent} no longer has this session` : `${agent} has this session now`;
|
|
399
|
-
},
|
|
400
|
-
toggleAll: (id: string) => {
|
|
401
|
-
const session = xSessions.find((entry) => entry.id === id);
|
|
402
|
-
if (!session) return "no such session";
|
|
403
|
-
session.agents = session.agents.length > 0 ? [] : ["master"];
|
|
404
|
-
return session.agents.length > 0 ? "assigned to every agent" : "taken back from every agent";
|
|
405
|
-
},
|
|
406
|
-
toggleContribute: (id: string) => {
|
|
407
|
-
const session = xSessions.find((entry) => entry.id === id);
|
|
408
|
-
if (!session) return "no such session";
|
|
409
|
-
session.contribute = !session.contribute;
|
|
410
|
-
return session.contribute ? "agents may draw in this session" : "agents may only look at this session";
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
358
|
const qfJobs = fakeQuickFixJobs(fixture.mockQuickfix ?? []);
|
|
414
359
|
let qfCounter = qfJobs.length;
|
|
415
360
|
const quickfix = {
|
|
@@ -485,8 +430,6 @@ export function createFixtureService(name: string, feed = new LobbyFeed()): Lobb
|
|
|
485
430
|
metrics: () => metrics.map((record) => ({ ...record })),
|
|
486
431
|
classifierMetrics: () => classifierMetrics.map((record) => ({ ...record })),
|
|
487
432
|
knowledge,
|
|
488
|
-
excalidraw,
|
|
489
|
-
checkExcalidraw: async () => ({ ok: true, text: "reached the server; nobody has this session open yet — open the link in Excalidraw, and agents can read and draw" }),
|
|
490
433
|
comments: (taskId: string) => [...(comments.get(taskId) ?? [])],
|
|
491
434
|
comment: (taskId: string, text: string) => {
|
|
492
435
|
const entry = { id: `C-mock-${(commentSeq += 1)}`, taskId, text, by: fixture.status.sessionId, createdAt: new Date().toISOString(), status: "open" };
|
|
@@ -101,10 +101,6 @@ export interface ScenarioFixture {
|
|
|
101
101
|
mockKnowledge?: Array<{ agent: string; file: string; label: string; chars: number; over: boolean; notes: number }>;
|
|
102
102
|
/** One open knowledge file for `knowledge.open` (absent means an empty file). */
|
|
103
103
|
mockKnowledgeView?: { agent: string; file: string; label: string; chars: number; over: boolean; notes: number; content: string; entries: Array<Record<string, unknown>>; attached: Array<Record<string, unknown>>; detached: Array<Record<string, unknown>> };
|
|
104
|
-
/** Excalidraw sessions for `excalidraw.list` (masked links only; absent means none). */
|
|
105
|
-
mockExcalidraw?: Array<{ id: string; name: string; masked: string; agents: string[]; contribute: boolean; addedAt: string }>;
|
|
106
|
-
/** The full link `excalidraw.reveal` returns (absent means none). */
|
|
107
|
-
mockExcalidrawLink?: string;
|
|
108
104
|
/** Open pull requests for the `git.*` calls, each with its detail fields (absent means none). */
|
|
109
105
|
mockPulls?: Array<Record<string, unknown>>;
|
|
110
106
|
/** Saved reviews of those pull requests, by number. */
|
|
@@ -173,8 +169,6 @@ export function loadScenario(name: string | undefined): ScenarioFixture {
|
|
|
173
169
|
mockClassifierMetrics: Array.isArray(parsed.mockClassifierMetrics) ? (parsed.mockClassifierMetrics as ScenarioFixture["mockClassifierMetrics"]) : [],
|
|
174
170
|
mockKnowledge: Array.isArray(parsed.mockKnowledge) ? (parsed.mockKnowledge as ScenarioFixture["mockKnowledge"]) : [],
|
|
175
171
|
mockKnowledgeView: isRecord(parsed.mockKnowledgeView) ? (parsed.mockKnowledgeView as unknown as ScenarioFixture["mockKnowledgeView"]) : undefined,
|
|
176
|
-
mockExcalidraw: Array.isArray(parsed.mockExcalidraw) ? (parsed.mockExcalidraw as ScenarioFixture["mockExcalidraw"]) : [],
|
|
177
|
-
mockExcalidrawLink: typeof parsed.mockExcalidrawLink === "string" ? (parsed.mockExcalidrawLink as string) : undefined,
|
|
178
172
|
mockPulls: Array.isArray(parsed.mockPulls) ? (parsed.mockPulls as ScenarioFixture["mockPulls"]) : [],
|
|
179
173
|
mockReviews: Array.isArray(parsed.mockReviews) ? (parsed.mockReviews as ScenarioFixture["mockReviews"]) : [],
|
|
180
174
|
mockReads: Array.isArray(parsed.mockReads) ? (parsed.mockReads as ScenarioFixture["mockReads"]) : [],
|
package/src/webui/protocol.ts
CHANGED
|
@@ -393,25 +393,6 @@ export interface EntryRefInfo {
|
|
|
393
393
|
occurrence: number;
|
|
394
394
|
}
|
|
395
395
|
|
|
396
|
-
/** Who an Excalidraw session can be assigned to. */
|
|
397
|
-
export type ExcalidrawAgentName = "master" | "designer" | "backend" | "qa" | "scout" | "researcher" | "quickfix" | "planner";
|
|
398
|
-
|
|
399
|
-
/** One shared session with its key hidden (`room <id>`). */
|
|
400
|
-
export interface ExcalidrawSessionInfo {
|
|
401
|
-
id: string;
|
|
402
|
-
name: string;
|
|
403
|
-
masked: string;
|
|
404
|
-
agents: ExcalidrawAgentName[];
|
|
405
|
-
contribute: boolean;
|
|
406
|
-
addedAt: string;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/** What the last check of a session found. */
|
|
410
|
-
export interface ExcalidrawCheck {
|
|
411
|
-
ok: boolean;
|
|
412
|
-
text: string;
|
|
413
|
-
}
|
|
414
|
-
|
|
415
396
|
/** `tasks.get`: one task read whole, in the terminal's wording. */
|
|
416
397
|
export interface TaskDetail {
|
|
417
398
|
timing?: TimingProjection;
|
|
@@ -655,16 +636,6 @@ export interface Api {
|
|
|
655
636
|
"knowledge.replaceFile": { request: { agent: KnowledgeAgentName; file: string; text: string }; result: { notice: string } };
|
|
656
637
|
"knowledge.comment": { request: { agent: KnowledgeAgentName; file: string; ref: EntryRefInfo; text: string }; result: { notice: string } };
|
|
657
638
|
"knowledge.unnote": { request: { id: string }; result: { notice: string } };
|
|
658
|
-
"excalidraw.list": { request: Record<string, never>; result: { sessions: ExcalidrawSessionInfo[] } };
|
|
659
|
-
"excalidraw.add": { request: { link: string; name?: string }; result: { notice: string; id?: string } };
|
|
660
|
-
"excalidraw.create": { request: { name?: string }; result: { notice: string; id?: string } };
|
|
661
|
-
"excalidraw.remove": { request: { id: string }; result: { notice: string } };
|
|
662
|
-
"excalidraw.rename": { request: { id: string; name: string }; result: { notice: string } };
|
|
663
|
-
"excalidraw.toggleAgent": { request: { id: string; agent: ExcalidrawAgentName }; result: { notice: string } };
|
|
664
|
-
"excalidraw.toggleAll": { request: { id: string }; result: { notice: string } };
|
|
665
|
-
"excalidraw.toggleContribute": { request: { id: string }; result: { notice: string } };
|
|
666
|
-
"excalidraw.check": { request: { id: string }; result: ExcalidrawCheck };
|
|
667
|
-
"excalidraw.reveal": { request: { id: string }; result: { link: string } };
|
|
668
639
|
"git.pulls": { request: { refresh?: boolean }; result: GitPulls };
|
|
669
640
|
"git.pull": { request: { number: number }; result: GitPull };
|
|
670
641
|
"git.review": { request: { number: number; focus?: string }; result: { notice: string } };
|
package/src/workflow/workflow.ts
CHANGED
|
@@ -20,7 +20,6 @@ import { loadTask, ownerlessTask, readTaskArtifact, removeTaskScratchpads, saveT
|
|
|
20
20
|
import { dataRoot, readDataRoots } from "../state/project.ts";
|
|
21
21
|
import { appendCompletedTask, appendDecision, applyKnowledge, readFileOr, writeFileEnsured, type KnowledgeKind } from "../knowledge/store.ts";
|
|
22
22
|
import { compactKnowledgeFile, overThreshold } from "../knowledge/compactor.ts";
|
|
23
|
-
import { ExcalidrawBook } from "../excalidraw/sessions.ts";
|
|
24
23
|
import { knowledgeDir, type KnowledgeAgent } from "../knowledge/paths.ts";
|
|
25
24
|
import { lobbyTopics } from "../lobby/topics.ts";
|
|
26
25
|
import { writeScratchpad } from "../state/persistence.ts";
|
|
@@ -92,7 +91,6 @@ export const ORCHESTRATE_ACTIONS = [
|
|
|
92
91
|
"knowledge",
|
|
93
92
|
"compact",
|
|
94
93
|
"script",
|
|
95
|
-
"whiteboard",
|
|
96
94
|
"complete",
|
|
97
95
|
"block",
|
|
98
96
|
"resume",
|
|
@@ -130,7 +128,7 @@ export interface OrchestrateParams {
|
|
|
130
128
|
kind?: KnowledgeKind;
|
|
131
129
|
/** compact: the knowledge file being rewritten; script: a drafted script to take instead of `text`. */
|
|
132
130
|
file?: string;
|
|
133
|
-
/**
|
|
131
|
+
/** script: the script's file name (`seed_public_holidays.py`). */
|
|
134
132
|
name?: string;
|
|
135
133
|
/** script: one line on what the script does. */
|
|
136
134
|
purpose?: string;
|
|
@@ -1542,19 +1540,6 @@ function handleCompact(task: Task | undefined, params: OrchestrateParams, deps:
|
|
|
1542
1540
|
return `Compacted ${agent}/${file}: ${outcome.before} -> ${outcome.after} chars. Previous version archived at ${outcome.archive}.`;
|
|
1543
1541
|
}
|
|
1544
1542
|
|
|
1545
|
-
/**
|
|
1546
|
-
* The Master creates its own whiteboard: a room on the configured server,
|
|
1547
|
-
* assigned to itself so its seat keeps the room alive for this pi session.
|
|
1548
|
-
*/
|
|
1549
|
-
function handleWhiteboard(task: Task, params: OrchestrateParams, deps: WorkflowDeps): string {
|
|
1550
|
-
const book = new ExcalidrawBook({ root: deps.root });
|
|
1551
|
-
const made = book.create(params.name?.trim() || undefined);
|
|
1552
|
-
if (!made.session) throw new Error(made.notice);
|
|
1553
|
-
book.toggleAgent(made.session.id, "master");
|
|
1554
|
-
recordDecision(task, `Created Excalidraw session "${made.session.name}" and assigned it to Master.`);
|
|
1555
|
-
return `Created Excalidraw session "${made.session.name}" and assigned it to you. Join link: ${made.session.link}. Call excalidraw_read to open your seat; the connection stays open for this pi session and keeps the room alive without anyone else in it. It ends when the pi session ends.`;
|
|
1556
|
-
}
|
|
1557
|
-
|
|
1558
1543
|
/** §63: record history, drop scratchpads, then mark the task completed. */
|
|
1559
1544
|
async function handleComplete(task: Task, params: OrchestrateParams, deps: WorkflowDeps): Promise<string> {
|
|
1560
1545
|
// The fast track (and a plan QA sat out) completes straight from its work; the full workflow from review.
|
|
@@ -2068,7 +2053,6 @@ const HANDLERS: Record<OrchestrateAction, (task: Task, params: OrchestrateParams
|
|
|
2068
2053
|
knowledge: handleKnowledge,
|
|
2069
2054
|
script: handleScript,
|
|
2070
2055
|
compact: handleCompact,
|
|
2071
|
-
whiteboard: handleWhiteboard,
|
|
2072
2056
|
complete: handleComplete,
|
|
2073
2057
|
block: handleBlock,
|
|
2074
2058
|
resume: handleResume,
|