@a-t-h-i/bot-lobby 0.6.11 → 0.6.12
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 -0
- package/package.json +1 -1
- package/src/pi/tools.ts +2 -1
- package/src/roles/reviewer.ts +9 -1
- package/src/workflow/workflow.ts +22 -6
package/README.md
CHANGED
|
@@ -325,6 +325,14 @@ knowledge, standards, decisions and completed tasks — files on the left, the
|
|
|
325
325
|
open file's entries on the right (a heading, a bullet, a paragraph), one of
|
|
326
326
|
them picked. Files past the compaction threshold are marked.
|
|
327
327
|
|
|
328
|
+
Completion is gated on compaction: the engine refuses `complete` while any
|
|
329
|
+
knowledge file is over the threshold (default 20,000 chars,
|
|
330
|
+
`knowledge.compactionThreshold`). The Master first disperses domain-relevant
|
|
331
|
+
facts where they belong with `action=knowledge (domain=designer|backend|qa)`
|
|
332
|
+
— per-agent files fill up through that dispersal — then rewrites each
|
|
333
|
+
oversized file with `action=compact` (which archives the previous version)
|
|
334
|
+
before the task closes.
|
|
335
|
+
|
|
328
336
|
- `e` edits the picked entry: it comes into the prompt (Shift+Enter for a new
|
|
329
337
|
line, Enter saves). `n` adds an entry after it, `d d` deletes it, `E` edits
|
|
330
338
|
the whole file in pi's editor. Every write archives the version before
|
|
@@ -361,6 +369,12 @@ planner), and those agents can look at what you drew and add to it.
|
|
|
361
369
|
sessions are all look-only gets no drawing tool.
|
|
362
370
|
- `t` **checks** the session: joins the room for a moment and says whether the
|
|
363
371
|
server can be reached, who is in it, and how much is on the board. `r` renames it.
|
|
372
|
+
- `orchestrate action=whiteboard [name=...]` lets the Master create its own
|
|
373
|
+
session: a room on the configured collaboration server, assigned to itself,
|
|
374
|
+
with the join link returned. Its seat (opened by `excalidraw_read` /
|
|
375
|
+
`excalidraw_draw`) holds the room alive for the pi session's lifetime, so it
|
|
376
|
+
needs no one else in the room. The room is not guaranteed to persist after
|
|
377
|
+
the last connection leaves.
|
|
364
378
|
|
|
365
379
|
**What an assigned agent can do.** It gets two tools and the room link:
|
|
366
380
|
`excalidraw_read` describes the board in words (shapes with their labels, arrows
|
|
@@ -647,6 +661,7 @@ unavailable, it runs again on the fallback instead of failing the task.
|
|
|
647
661
|
| Scouts and the QA gate can't edit code | Scouts get read-only tools; the QA gate adds only `bash` for tests |
|
|
648
662
|
| New dependencies and architecture changes need approval | Parsed from worker reports; the domain is blocked until resolved |
|
|
649
663
|
| Only the Master writes knowledge | Agents can only propose it |
|
|
664
|
+
| Knowledge is compacted before done | `complete` is refused while any knowledge file is over the threshold; disperse with `action=knowledge (domain=designer\|backend\|qa)`, then `action=compact` |
|
|
650
665
|
| "Done" is earned | Needs a plan, a passing QA gate that ran checks (or your explicit acceptance), and no open blockers; on the fast track, a finished worker step, and QA's part only when the change needs tests |
|
|
651
666
|
| Parallel workers don't clobber files | Edits need a file-desk claim |
|
|
652
667
|
| A crash doesn't corrupt a task | State is on disk; tasks resume from their state |
|
package/package.json
CHANGED
package/src/pi/tools.ts
CHANGED
|
@@ -58,6 +58,7 @@ const OrchestrateSchema = Type.Object({
|
|
|
58
58
|
roster: Type.Optional(Type.Array(Type.String(), { description: "track: who takes part — any of designer, backend, qa, researcher" })),
|
|
59
59
|
text: Type.Optional(Type.String({ description: "decide/complete/knowledge: the decision, completion summary, or knowledge text" })),
|
|
60
60
|
file: Type.Optional(Type.String({ description: "compact: the knowledge file to rewrite, e.g. knowledge.md" })),
|
|
61
|
+
name: Type.Optional(Type.String({ description: "whiteboard: name for the new Excalidraw session" })),
|
|
61
62
|
kind: Type.Optional(
|
|
62
63
|
StringEnum(["knowledge", "standard", "decision", "completed"] as const, {
|
|
63
64
|
description: "knowledge: which persistent file the text belongs to",
|
|
@@ -72,7 +73,7 @@ const DESCRIPTION = [
|
|
|
72
73
|
"dependency), propose (record the proposal and request approval), plan (record the internal",
|
|
73
74
|
"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",
|
|
74
75
|
"review), knowledge (record approved knowledge or a decision),",
|
|
75
|
-
"compact (replace a knowledge file with a rewritten version, archiving the old one),",
|
|
76
|
+
"compact (replace a knowledge file with a rewritten version, archiving the old one, dispersing domain-relevant facts to action=knowledge (domain=designer|backend|qa) first), whiteboard (create your own Excalidraw session and assign it to yourself),",
|
|
76
77
|
"resolve_approval (approve or reject a request), complete (declare the task done after the gates",
|
|
77
78
|
"pass), block/resume (escalate or continue), budget (under a time budget: where it stands, or ask the",
|
|
78
79
|
"user for more minutes with a reason), track (the task's path and who takes part: show it, or correct it with",
|
package/src/roles/reviewer.ts
CHANGED
|
@@ -30,6 +30,14 @@ function parseVerdict(text: string | undefined): Verdict {
|
|
|
30
30
|
return "blocked";
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
/** The verdict token may sit in the heading itself (`## Verdict PASS`), leaving an empty body. */
|
|
34
|
+
function verdictHeading(sections: Map<string, string>): string | undefined {
|
|
35
|
+
for (const heading of sections.keys()) {
|
|
36
|
+
if (heading.includes("verdict")) return heading;
|
|
37
|
+
}
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
33
41
|
/** The severity a finding was tagged with, or undefined when it carries none. */
|
|
34
42
|
function taggedSeverity(text: string): Severity | undefined {
|
|
35
43
|
const match = /^\[([^\]]+)\]/.exec(text);
|
|
@@ -58,7 +66,7 @@ export function parseReviewResult(domain: Domain, raw: string): ReviewResult {
|
|
|
58
66
|
const verification = findSection(sections, "verification") ?? "";
|
|
59
67
|
const requiredChanges = bullets(findSection(sections, "required changes"));
|
|
60
68
|
const entries = bullets(findSection(sections, "findings"));
|
|
61
|
-
let verdict = parseVerdict(findSection(sections, "verdict"));
|
|
69
|
+
let verdict = parseVerdict(`${verdictHeading(sections) ?? ""}\n${findSection(sections, "verdict") ?? ""}`);
|
|
62
70
|
let downgraded: string | undefined;
|
|
63
71
|
let relaxed: string | undefined;
|
|
64
72
|
const checked = executedChecks(verification).length > 0;
|
package/src/workflow/workflow.ts
CHANGED
|
@@ -20,6 +20,7 @@ import { ownerlessTask, readTaskArtifact, removeTaskScratchpads, saveTask, selec
|
|
|
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";
|
|
23
24
|
import { knowledgeDir, type KnowledgeAgent } from "../knowledge/paths.ts";
|
|
24
25
|
import { writeScratchpad } from "../state/persistence.ts";
|
|
25
26
|
import { spawnPiProcess, type ProcessRunner, type RelayAsk } from "../execution/pi-runner.ts";
|
|
@@ -75,6 +76,7 @@ export const ORCHESTRATE_ACTIONS = [
|
|
|
75
76
|
"qa",
|
|
76
77
|
"knowledge",
|
|
77
78
|
"compact",
|
|
79
|
+
"whiteboard",
|
|
78
80
|
"complete",
|
|
79
81
|
"block",
|
|
80
82
|
"resume",
|
|
@@ -112,6 +114,8 @@ export interface OrchestrateParams {
|
|
|
112
114
|
kind?: KnowledgeKind;
|
|
113
115
|
/** compact: the knowledge file being rewritten. */
|
|
114
116
|
file?: string;
|
|
117
|
+
/** whiteboard: name for the new Excalidraw session. */
|
|
118
|
+
name?: string;
|
|
115
119
|
approvalId?: string;
|
|
116
120
|
decision?: "approved" | "rejected";
|
|
117
121
|
note?: string;
|
|
@@ -1270,6 +1274,19 @@ function handleCompact(task: Task, params: OrchestrateParams, deps: WorkflowDeps
|
|
|
1270
1274
|
return `Compacted ${agent}/${file}: ${outcome.before} -> ${outcome.after} chars. Previous version archived at ${outcome.archive}.`;
|
|
1271
1275
|
}
|
|
1272
1276
|
|
|
1277
|
+
/**
|
|
1278
|
+
* The Master creates its own whiteboard: a room on the configured server,
|
|
1279
|
+
* assigned to itself so its seat keeps the room alive for this pi session.
|
|
1280
|
+
*/
|
|
1281
|
+
function handleWhiteboard(task: Task, params: OrchestrateParams, deps: WorkflowDeps): string {
|
|
1282
|
+
const book = new ExcalidrawBook({ root: deps.root });
|
|
1283
|
+
const made = book.create(params.name?.trim() || undefined);
|
|
1284
|
+
if (!made.session) throw new Error(made.notice);
|
|
1285
|
+
book.toggleAgent(made.session.id, "master");
|
|
1286
|
+
recordDecision(task, `Created Excalidraw session "${made.session.name}" and assigned it to Master.`);
|
|
1287
|
+
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.`;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1273
1290
|
/** §63: record history, drop scratchpads, then mark the task completed. */
|
|
1274
1291
|
async function handleComplete(task: Task, params: OrchestrateParams, deps: WorkflowDeps): Promise<string> {
|
|
1275
1292
|
// The fast track completes straight from its work; the full workflow from review.
|
|
@@ -1279,6 +1296,9 @@ async function handleComplete(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1279
1296
|
if (task.state === "blocked") throw new Error("cannot complete: the task is blocked, and the user did not accept its work as it is");
|
|
1280
1297
|
const blockers = completionBlockers(task, pendingApprovals(task).length);
|
|
1281
1298
|
if (blockers.length > 0) throw new Error(`cannot complete: ${blockers.join("; ")}`);
|
|
1299
|
+
// The gate refuses before any side effect, so a refusal leaves the task untouched.
|
|
1300
|
+
const oversized = overThreshold(readDataRoots(deps.root, deps.configDir), deps.config.knowledge.compactionThreshold);
|
|
1301
|
+
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.`);
|
|
1282
1302
|
const summary = params.text?.trim() || task.proposal || task.title;
|
|
1283
1303
|
flushDecisions(deps, task);
|
|
1284
1304
|
recordCompletion(deps, task, summary);
|
|
@@ -1286,12 +1306,7 @@ async function handleComplete(task: Task, params: OrchestrateParams, deps: Workf
|
|
|
1286
1306
|
task.blockers = [];
|
|
1287
1307
|
if (task.state === "implementing") transition(task, "reviewing");
|
|
1288
1308
|
transition(task, "completed");
|
|
1289
|
-
|
|
1290
|
-
const advice =
|
|
1291
|
-
oversized.length > 0
|
|
1292
|
-
? `\nKnowledge files over the compaction threshold: ${oversized.map((entry) => `${entry.agent}/${entry.file} (${entry.chars})`).join(", ")}. Compact them with action=compact when convenient.`
|
|
1293
|
-
: "";
|
|
1294
|
-
return `Task ${task.id} completed. History recorded and temporary scratchpads removed.${advice}`;
|
|
1309
|
+
return `Task ${task.id} completed. History recorded and temporary scratchpads removed.`;
|
|
1295
1310
|
}
|
|
1296
1311
|
|
|
1297
1312
|
/** Asked when the oracle completes a task QA has not passed (the user told it to finish, say). */
|
|
@@ -1653,6 +1668,7 @@ const HANDLERS: Record<OrchestrateAction, (task: Task, params: OrchestrateParams
|
|
|
1653
1668
|
qa: handleQa,
|
|
1654
1669
|
knowledge: handleKnowledge,
|
|
1655
1670
|
compact: handleCompact,
|
|
1671
|
+
whiteboard: handleWhiteboard,
|
|
1656
1672
|
complete: handleComplete,
|
|
1657
1673
|
block: handleBlock,
|
|
1658
1674
|
resume: handleResume,
|