@a-t-h-i/bot-lobby 0.6.44 → 0.6.46
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/execution/task-cancellation.ts +27 -0
- package/src/index.ts +0 -3
- package/src/lobby/host.ts +5 -12
- package/src/lobby/planner.ts +1 -6
- package/src/lobby/prompts.ts +4 -7
- package/src/lobby/quickfix.ts +0 -2
- package/src/lobby/runtime.ts +1 -5
- package/src/lobby/service.ts +77 -7
- package/src/lobby/topics.ts +0 -1
- package/src/pi/tools.ts +2 -2
- package/src/state/persistence.ts +26 -1
- package/src/webui/api/index.ts +13 -45
- package/src/webui/api/tasks.ts +39 -2
- package/src/webui/dev/fake-service.ts +0 -57
- package/src/webui/dev/fixtures.ts +0 -6
- package/src/webui/palette-core.ts +233 -2
- package/src/webui/protocol.ts +6 -103
- package/src/workflow/workflow.ts +16 -20
- package/webui/dist/assets/index-BJ1fEle9.js +110 -0
- package/webui/dist/assets/index-BdtiIB9L.css +2 -0
- package/webui/dist/build.json +39 -107
- 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/lobby/models/metrics.ts +0 -14
- package/src/webui/api/excalidraw.ts +0 -105
- package/src/webui/api/metrics.ts +0 -96
- package/webui/dist/assets/index-C0T8aK16.js +0 -110
- package/webui/dist/assets/index-XtSiJAAR.css +0 -2
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";
|
|
@@ -29,6 +28,7 @@ import { spawnPiProcess, type ProcessRunner, type RelayAsk } from "../execution/
|
|
|
29
28
|
import { previewDir } from "../ask/relay.ts";
|
|
30
29
|
import type { AskQuestion, AskResult } from "../ask/types.ts";
|
|
31
30
|
import { mapConcurrent } from "../execution/agent-runner.ts";
|
|
31
|
+
import { taskExecution } from "../execution/task-cancellation.ts";
|
|
32
32
|
import { parseWorkerResult } from "../roles/worker.ts";
|
|
33
33
|
import { autoNote, DESK_TOOLS, DeskSession } from "../desk/session.ts";
|
|
34
34
|
import type { Handover } from "../desk/desk.ts";
|
|
@@ -92,7 +92,6 @@ export const ORCHESTRATE_ACTIONS = [
|
|
|
92
92
|
"knowledge",
|
|
93
93
|
"compact",
|
|
94
94
|
"script",
|
|
95
|
-
"whiteboard",
|
|
96
95
|
"complete",
|
|
97
96
|
"block",
|
|
98
97
|
"resume",
|
|
@@ -130,7 +129,7 @@ export interface OrchestrateParams {
|
|
|
130
129
|
kind?: KnowledgeKind;
|
|
131
130
|
/** compact: the knowledge file being rewritten; script: a drafted script to take instead of `text`. */
|
|
132
131
|
file?: string;
|
|
133
|
-
/**
|
|
132
|
+
/** script: the script's file name (`seed_public_holidays.py`). */
|
|
134
133
|
name?: string;
|
|
135
134
|
/** script: one line on what the script does. */
|
|
136
135
|
purpose?: string;
|
|
@@ -1173,7 +1172,7 @@ function handoverLog(handovers: readonly Handover[]): string {
|
|
|
1173
1172
|
return `File handovers:\n${lines.join("\n")}`;
|
|
1174
1173
|
}
|
|
1175
1174
|
|
|
1176
|
-
function handleResolveApproval(task: Task, params: OrchestrateParams): string {
|
|
1175
|
+
export function handleResolveApproval(task: Task, params: OrchestrateParams): string {
|
|
1177
1176
|
const id = params.approvalId?.trim();
|
|
1178
1177
|
const decision = params.decision;
|
|
1179
1178
|
if (!id || (decision !== "approved" && decision !== "rejected")) {
|
|
@@ -1542,19 +1541,6 @@ function handleCompact(task: Task | undefined, params: OrchestrateParams, deps:
|
|
|
1542
1541
|
return `Compacted ${agent}/${file}: ${outcome.before} -> ${outcome.after} chars. Previous version archived at ${outcome.archive}.`;
|
|
1543
1542
|
}
|
|
1544
1543
|
|
|
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
1544
|
/** §63: record history, drop scratchpads, then mark the task completed. */
|
|
1559
1545
|
async function handleComplete(task: Task, params: OrchestrateParams, deps: WorkflowDeps): Promise<string> {
|
|
1560
1546
|
// The fast track (and a plan QA sat out) completes straight from its work; the full workflow from review.
|
|
@@ -1572,7 +1558,9 @@ async function handleComplete(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1572
1558
|
// The gate refuses before any side effect, so a refusal leaves the task untouched.
|
|
1573
1559
|
const oversized = overThreshold(readDataRoots(deps.root, deps.configDir), deps.config.knowledge.compactionThreshold);
|
|
1574
1560
|
if (oversized.length > 0) throw new Error(`cannot complete: knowledge files over the compaction threshold: ${oversized.map((entry) => `${entry.agent}/${entry.file} (${entry.chars})`).join(", ")}. First record domain-relevant facts where they belong with action=knowledge (domain=designer|backend|qa), then rewrite each oversized file with action=compact, then call complete again.`);
|
|
1561
|
+
deps.signal?.throwIfAborted();
|
|
1575
1562
|
const committed = await commitTaskWork(task, params, deps);
|
|
1563
|
+
deps.signal?.throwIfAborted();
|
|
1576
1564
|
const summary = params.text?.trim() || task.proposal || task.title;
|
|
1577
1565
|
recordCompletion(deps, task, summary);
|
|
1578
1566
|
removeTaskScratchpads(deps.root, deps.configDir, task.id);
|
|
@@ -1613,6 +1601,7 @@ async function commitTaskWork(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1613
1601
|
}
|
|
1614
1602
|
let result: Awaited<ReturnType<typeof commitFiles>>;
|
|
1615
1603
|
try {
|
|
1604
|
+
deps.signal?.throwIfAborted();
|
|
1616
1605
|
result = await commitFiles(state.top, dirty, message);
|
|
1617
1606
|
} catch (error) {
|
|
1618
1607
|
throw new Error(`cannot complete: git refused the commit: ${(error as Error).message}`);
|
|
@@ -2068,7 +2057,6 @@ const HANDLERS: Record<OrchestrateAction, (task: Task, params: OrchestrateParams
|
|
|
2068
2057
|
knowledge: handleKnowledge,
|
|
2069
2058
|
script: handleScript,
|
|
2070
2059
|
compact: handleCompact,
|
|
2071
|
-
whiteboard: handleWhiteboard,
|
|
2072
2060
|
complete: handleComplete,
|
|
2073
2061
|
block: handleBlock,
|
|
2074
2062
|
resume: handleResume,
|
|
@@ -2114,14 +2102,19 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2114
2102
|
}
|
|
2115
2103
|
const gone = missingWorktree(task);
|
|
2116
2104
|
if (gone && params.action !== "status") return { ok: false, taskId: task.id, state: task.state, message: `Rejected: ${gone}` };
|
|
2105
|
+
const execution = taskExecution(deps.root, deps.configDir, task.id, deps.signal);
|
|
2117
2106
|
const finished = new Map<string, AgentRun>();
|
|
2118
2107
|
const identity = { root: deps.root, configDir: deps.configDir, taskId: task.id, sessionId: task.ownerSessionId };
|
|
2119
|
-
const wait = <T>(run: () => Promise<T>) => {
|
|
2108
|
+
const wait = async <T>(run: () => Promise<T>) => {
|
|
2109
|
+
execution.signal.throwIfAborted();
|
|
2120
2110
|
saveTask(deps.root, deps.configDir, task);
|
|
2121
|
-
|
|
2111
|
+
const result = await withBlockingRequest(identity, run);
|
|
2112
|
+
execution.signal.throwIfAborted();
|
|
2113
|
+
return result;
|
|
2122
2114
|
};
|
|
2123
2115
|
const tracked: WorkflowDeps = {
|
|
2124
2116
|
...deps,
|
|
2117
|
+
signal: execution.signal,
|
|
2125
2118
|
choose: (...args) => wait(() => deps.choose(...args)),
|
|
2126
2119
|
ask: (...args) => wait(() => deps.ask(...args)),
|
|
2127
2120
|
...(deps.askQuestions ? { askQuestions: (...args: Parameters<NonNullable<WorkflowDeps["askQuestions"]>>) => wait(() => deps.askQuestions!(...args)) } : {}),
|
|
@@ -2129,6 +2122,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2129
2122
|
cwd: taskCwd(task, deps.cwd),
|
|
2130
2123
|
// An agent can work for an hour: what the action has done so far (implementing, reviewing) is saved before it starts, so every window reads it.
|
|
2131
2124
|
runProcess: (args, options) => {
|
|
2125
|
+
execution.signal.throwIfAborted();
|
|
2132
2126
|
saveTask(deps.root, deps.configDir, task);
|
|
2133
2127
|
return (deps.runProcess ?? spawnPiProcess)(args, options);
|
|
2134
2128
|
},
|
|
@@ -2150,6 +2144,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2150
2144
|
const working = new Set<string>();
|
|
2151
2145
|
try {
|
|
2152
2146
|
const message = await handler(task, params, tracked);
|
|
2147
|
+
execution.signal.throwIfAborted();
|
|
2153
2148
|
const runs = recordRunLog(task, finished, deps);
|
|
2154
2149
|
saveTask(deps.root, deps.configDir, task);
|
|
2155
2150
|
return { ok: true, taskId: task.id, state: task.state, message: `${message}${runsFooter(runs)}${budgetFooter(task, deps)}`, runs };
|
|
@@ -2158,6 +2153,7 @@ export async function runWorkflowAction(params: OrchestrateParams, deps: Workflo
|
|
|
2158
2153
|
saveTask(deps.root, deps.configDir, task);
|
|
2159
2154
|
return { ok: false, taskId: task.id, state: task.state, message: `Rejected: ${(error as Error).message}${budgetFooter(task, deps)}`, runs };
|
|
2160
2155
|
} finally {
|
|
2156
|
+
execution.release();
|
|
2161
2157
|
for (const runId of working) noteWorkerRun(deps.root, deps.configDir, task.id, { runId, startedAt: "", status: "cancelled" });
|
|
2162
2158
|
}
|
|
2163
2159
|
}
|