@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.
Files changed (38) hide show
  1. package/README.md +15 -4
  2. package/package.json +1 -6
  3. package/src/execution/agent-runner.ts +0 -2
  4. package/src/execution/delegation.ts +1 -2
  5. package/src/execution/pi-runner.ts +2 -6
  6. package/src/execution/task-cancellation.ts +27 -0
  7. package/src/index.ts +0 -3
  8. package/src/lobby/host.ts +5 -12
  9. package/src/lobby/planner.ts +1 -6
  10. package/src/lobby/prompts.ts +4 -7
  11. package/src/lobby/quickfix.ts +0 -2
  12. package/src/lobby/runtime.ts +1 -5
  13. package/src/lobby/service.ts +77 -7
  14. package/src/lobby/topics.ts +0 -1
  15. package/src/pi/tools.ts +2 -2
  16. package/src/state/persistence.ts +26 -1
  17. package/src/webui/api/index.ts +13 -45
  18. package/src/webui/api/tasks.ts +39 -2
  19. package/src/webui/dev/fake-service.ts +0 -57
  20. package/src/webui/dev/fixtures.ts +0 -6
  21. package/src/webui/palette-core.ts +233 -2
  22. package/src/webui/protocol.ts +6 -103
  23. package/src/workflow/workflow.ts +16 -20
  24. package/webui/dist/assets/index-BJ1fEle9.js +110 -0
  25. package/webui/dist/assets/index-BdtiIB9L.css +2 -0
  26. package/webui/dist/build.json +39 -107
  27. package/webui/dist/index.html +2 -2
  28. package/src/excalidraw/check.ts +0 -31
  29. package/src/excalidraw/client.ts +0 -334
  30. package/src/excalidraw/room.ts +0 -118
  31. package/src/excalidraw/scene.ts +0 -746
  32. package/src/excalidraw/sessions.ts +0 -302
  33. package/src/excalidraw/tools.ts +0 -223
  34. package/src/lobby/models/metrics.ts +0 -14
  35. package/src/webui/api/excalidraw.ts +0 -105
  36. package/src/webui/api/metrics.ts +0 -96
  37. package/webui/dist/assets/index-C0T8aK16.js +0 -110
  38. package/webui/dist/assets/index-XtSiJAAR.css +0 -2
@@ -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
- /** whiteboard: name for the new Excalidraw session; script: the script's file name (`seed_public_holidays.py`). */
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
- return withBlockingRequest(identity, run);
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
  }