@loompad/cli 0.1.0
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/ARCHITECTURE.md +211 -0
- package/CHANGELOG.md +470 -0
- package/LICENSE +21 -0
- package/README.md +630 -0
- package/dist/adapters/base.d.ts +71 -0
- package/dist/adapters/base.js +187 -0
- package/dist/adapters/base.js.map +1 -0
- package/dist/adapters/bridges/antigravity.d.ts +53 -0
- package/dist/adapters/bridges/antigravity.js +106 -0
- package/dist/adapters/bridges/antigravity.js.map +1 -0
- package/dist/adapters/bridges/cdp.d.ts +82 -0
- package/dist/adapters/bridges/cdp.js +222 -0
- package/dist/adapters/bridges/cdp.js.map +1 -0
- package/dist/adapters/bridges/gui-chat.d.ts +113 -0
- package/dist/adapters/bridges/gui-chat.js +498 -0
- package/dist/adapters/bridges/gui-chat.js.map +1 -0
- package/dist/adapters/bridges/kiro.d.ts +19 -0
- package/dist/adapters/bridges/kiro.js +22 -0
- package/dist/adapters/bridges/kiro.js.map +1 -0
- package/dist/adapters/bridges/profiles.d.ts +82 -0
- package/dist/adapters/bridges/profiles.js +125 -0
- package/dist/adapters/bridges/profiles.js.map +1 -0
- package/dist/adapters/claude-code.d.ts +26 -0
- package/dist/adapters/claude-code.js +209 -0
- package/dist/adapters/claude-code.js.map +1 -0
- package/dist/adapters/codex.d.ts +51 -0
- package/dist/adapters/codex.js +291 -0
- package/dist/adapters/codex.js.map +1 -0
- package/dist/adapters/echo.d.ts +20 -0
- package/dist/adapters/echo.js +79 -0
- package/dist/adapters/echo.js.map +1 -0
- package/dist/adapters/grok.d.ts +61 -0
- package/dist/adapters/grok.js +217 -0
- package/dist/adapters/grok.js.map +1 -0
- package/dist/adapters/index.d.ts +9 -0
- package/dist/adapters/index.js +33 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/opencode.d.ts +64 -0
- package/dist/adapters/opencode.js +426 -0
- package/dist/adapters/opencode.js.map +1 -0
- package/dist/cli/common.d.ts +11 -0
- package/dist/cli/common.js +40 -0
- package/dist/cli/common.js.map +1 -0
- package/dist/cli/doctor.d.ts +12 -0
- package/dist/cli/doctor.js +181 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/index.d.ts +8 -0
- package/dist/cli/index.js +641 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/tui-model.d.ts +73 -0
- package/dist/cli/tui-model.js +312 -0
- package/dist/cli/tui-model.js.map +1 -0
- package/dist/cli/tui.d.ts +15 -0
- package/dist/cli/tui.js +451 -0
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli/ui.d.ts +10 -0
- package/dist/cli/ui.js +135 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/core/ades.d.ts +79 -0
- package/dist/core/ades.js +130 -0
- package/dist/core/ades.js.map +1 -0
- package/dist/core/baton.d.ts +37 -0
- package/dist/core/baton.js +92 -0
- package/dist/core/baton.js.map +1 -0
- package/dist/core/brain-extract.d.ts +111 -0
- package/dist/core/brain-extract.js +248 -0
- package/dist/core/brain-extract.js.map +1 -0
- package/dist/core/brain-index.d.ts +128 -0
- package/dist/core/brain-index.js +338 -0
- package/dist/core/brain-index.js.map +1 -0
- package/dist/core/brain.d.ts +214 -0
- package/dist/core/brain.js +425 -0
- package/dist/core/brain.js.map +1 -0
- package/dist/core/claude-cli.d.ts +12 -0
- package/dist/core/claude-cli.js +47 -0
- package/dist/core/claude-cli.js.map +1 -0
- package/dist/core/distill.d.ts +19 -0
- package/dist/core/distill.js +88 -0
- package/dist/core/distill.js.map +1 -0
- package/dist/core/eventlog.d.ts +43 -0
- package/dist/core/eventlog.js +211 -0
- package/dist/core/eventlog.js.map +1 -0
- package/dist/core/git.d.ts +203 -0
- package/dist/core/git.js +460 -0
- package/dist/core/git.js.map +1 -0
- package/dist/core/logbook.d.ts +59 -0
- package/dist/core/logbook.js +118 -0
- package/dist/core/logbook.js.map +1 -0
- package/dist/core/memory.d.ts +34 -0
- package/dist/core/memory.js +124 -0
- package/dist/core/memory.js.map +1 -0
- package/dist/core/notify.d.ts +11 -0
- package/dist/core/notify.js +30 -0
- package/dist/core/notify.js.map +1 -0
- package/dist/core/projection.d.ts +20 -0
- package/dist/core/projection.js +96 -0
- package/dist/core/projection.js.map +1 -0
- package/dist/core/registry.d.ts +76 -0
- package/dist/core/registry.js +147 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/router.d.ts +42 -0
- package/dist/core/router.js +99 -0
- package/dist/core/router.js.map +1 -0
- package/dist/core/routes.d.ts +93 -0
- package/dist/core/routes.js +399 -0
- package/dist/core/routes.js.map +1 -0
- package/dist/core/search.d.ts +63 -0
- package/dist/core/search.js +0 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/setup.d.ts +70 -0
- package/dist/core/setup.js +250 -0
- package/dist/core/setup.js.map +1 -0
- package/dist/core/suggestions.d.ts +15 -0
- package/dist/core/suggestions.js +38 -0
- package/dist/core/suggestions.js.map +1 -0
- package/dist/core/worktree.d.ts +32 -0
- package/dist/core/worktree.js +83 -0
- package/dist/core/worktree.js.map +1 -0
- package/dist/daemon/app-page.d.ts +32 -0
- package/dist/daemon/app-page.js +6363 -0
- package/dist/daemon/app-page.js.map +1 -0
- package/dist/daemon/auth.d.ts +51 -0
- package/dist/daemon/auth.js +135 -0
- package/dist/daemon/auth.js.map +1 -0
- package/dist/daemon/board.d.ts +117 -0
- package/dist/daemon/board.js +202 -0
- package/dist/daemon/board.js.map +1 -0
- package/dist/daemon/brand-icons.d.ts +29 -0
- package/dist/daemon/brand-icons.js +44 -0
- package/dist/daemon/brand-icons.js.map +1 -0
- package/dist/daemon/client.d.ts +127 -0
- package/dist/daemon/client.js +237 -0
- package/dist/daemon/client.js.map +1 -0
- package/dist/daemon/geist-font.d.ts +10 -0
- package/dist/daemon/geist-font.js +783 -0
- package/dist/daemon/geist-font.js.map +1 -0
- package/dist/daemon/guards.d.ts +18 -0
- package/dist/daemon/guards.js +40 -0
- package/dist/daemon/guards.js.map +1 -0
- package/dist/daemon/linear.d.ts +59 -0
- package/dist/daemon/linear.js +117 -0
- package/dist/daemon/linear.js.map +1 -0
- package/dist/daemon/push.d.ts +22 -0
- package/dist/daemon/push.js +58 -0
- package/dist/daemon/push.js.map +1 -0
- package/dist/daemon/runtime.d.ts +299 -0
- package/dist/daemon/runtime.js +1016 -0
- package/dist/daemon/runtime.js.map +1 -0
- package/dist/daemon/server.d.ts +125 -0
- package/dist/daemon/server.js +1728 -0
- package/dist/daemon/server.js.map +1 -0
- package/dist/daemon/tasks.d.ts +153 -0
- package/dist/daemon/tasks.js +264 -0
- package/dist/daemon/tasks.js.map +1 -0
- package/dist/daemon/terminals.d.ts +82 -0
- package/dist/daemon/terminals.js +372 -0
- package/dist/daemon/terminals.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/sdk.d.ts +17 -0
- package/dist/sdk.js +17 -0
- package/dist/sdk.js.map +1 -0
- package/dist/types.d.ts +236 -0
- package/dist/types.js +18 -0
- package/dist/types.js.map +1 -0
- package/docs/SETUP.md +265 -0
- package/docs/adapters.md +132 -0
- package/docs/design-system.md +245 -0
- package/docs/img/board-linear.png +0 -0
- package/docs/img/board-projects.png +0 -0
- package/docs/img/brain.png +0 -0
- package/docs/img/settings.png +0 -0
- package/docs/img/workspace.png +0 -0
- package/docs/integration-notes.md +76 -0
- package/docs/proposals/brain.md +262 -0
- package/package.json +84 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hop routers for dynamic routes — after each step, decide who works next.
|
|
3
|
+
*
|
|
4
|
+
* Two brains, one interface:
|
|
5
|
+
* - rulesRouter: deterministic role rules (free, offline, always available)
|
|
6
|
+
* - llmRouter: asks Claude (headless, JSON out) to pick the next hop,
|
|
7
|
+
* falling back to the rules on any failure
|
|
8
|
+
*
|
|
9
|
+
* Loom never lets a router run away: dynamic routes carry a hop budget and
|
|
10
|
+
* every decision lands in the event log with its reason.
|
|
11
|
+
*/
|
|
12
|
+
import { claudeText } from "./claude-cli.js";
|
|
13
|
+
const NEEDS_MORE_WORK = /\b(must fix|needs? (?:a )?fix|found (?:an? |some )?(?:issues?|bugs?)|reject(?:ed)?|fail(?:ed|ing)|not approved|request(?:ing)? changes|does not (?:pass|work))\b/i;
|
|
14
|
+
function byRole(agents, role) {
|
|
15
|
+
return agents.find((a) => a.role === role);
|
|
16
|
+
}
|
|
17
|
+
function lastTextFrom(ctx, agentId) {
|
|
18
|
+
for (let i = ctx.recent.length - 1; i >= 0; i--) {
|
|
19
|
+
if (ctx.recent[i].author === agentId)
|
|
20
|
+
return ctx.recent[i].text;
|
|
21
|
+
}
|
|
22
|
+
return "";
|
|
23
|
+
}
|
|
24
|
+
/** Deterministic plan → execute → review → (fix loop) → done. */
|
|
25
|
+
export function rulesRouter(ctx) {
|
|
26
|
+
const { hops, agents } = ctx;
|
|
27
|
+
const done = (reason) => ({ next: "done", reason, by: "rules" });
|
|
28
|
+
if (!agents.length)
|
|
29
|
+
return done("no adapters available");
|
|
30
|
+
if (!hops.length) {
|
|
31
|
+
const first = byRole(agents, "planner") ?? byRole(agents, "executor") ?? agents[0];
|
|
32
|
+
return { next: first.id, reason: `start with the ${first.role}`, by: "rules" };
|
|
33
|
+
}
|
|
34
|
+
const last = hops[hops.length - 1];
|
|
35
|
+
const lastRole = agents.find((a) => a.id === last)?.role ?? "general";
|
|
36
|
+
if (lastRole === "planner") {
|
|
37
|
+
const exec = byRole(agents, "executor");
|
|
38
|
+
if (exec)
|
|
39
|
+
return { next: exec.id, reason: "plan ready — execute it", by: "rules" };
|
|
40
|
+
return done("planned, and no executor exists");
|
|
41
|
+
}
|
|
42
|
+
if (lastRole === "executor") {
|
|
43
|
+
const reviewer = byRole(agents, "reviewer");
|
|
44
|
+
if (reviewer)
|
|
45
|
+
return { next: reviewer.id, reason: "work done — review it", by: "rules" };
|
|
46
|
+
return done("executed, and no reviewer exists");
|
|
47
|
+
}
|
|
48
|
+
if (lastRole === "reviewer") {
|
|
49
|
+
const verdict = lastTextFrom(ctx, last);
|
|
50
|
+
const exec = byRole(agents, "executor");
|
|
51
|
+
const reviewRounds = hops.filter((h) => agents.find((a) => a.id === h)?.role === "reviewer").length;
|
|
52
|
+
if (exec && reviewRounds < 3 && NEEDS_MORE_WORK.test(verdict)) {
|
|
53
|
+
return { next: exec.id, reason: "review found issues — back to the executor", by: "rules" };
|
|
54
|
+
}
|
|
55
|
+
return done("review passed");
|
|
56
|
+
}
|
|
57
|
+
return done("general agent finished the task");
|
|
58
|
+
}
|
|
59
|
+
function routerPrompt(ctx) {
|
|
60
|
+
const agents = ctx.agents.map((a) => `- ${a.id} (${a.role})`).join("\n");
|
|
61
|
+
const hops = ctx.hops.length ? ctx.hops.join(" → ") : "(none yet)";
|
|
62
|
+
const recent = ctx.recent
|
|
63
|
+
.slice(-10)
|
|
64
|
+
.map((m) => `${m.author}: ${m.text.replace(/\s+/g, " ").slice(0, 240)}`)
|
|
65
|
+
.join("\n");
|
|
66
|
+
return [
|
|
67
|
+
"You are the routing brain of Loom, a control plane that passes a baton between coding agents.",
|
|
68
|
+
"Decide which agent should take the NEXT turn on this task, or whether the task is complete.",
|
|
69
|
+
"",
|
|
70
|
+
`Task: ${ctx.task}`,
|
|
71
|
+
`Hops so far: ${hops}`,
|
|
72
|
+
"Available agents:",
|
|
73
|
+
agents,
|
|
74
|
+
"Recent conversation:",
|
|
75
|
+
recent || "(empty)",
|
|
76
|
+
"",
|
|
77
|
+
'Reply with ONLY a JSON object, no prose: {"next":"<agent-id or done>","reason":"<under 15 words>"}',
|
|
78
|
+
"Prefer finishing (\"done\") once the task is genuinely complete; avoid pointless extra hops.",
|
|
79
|
+
].join("\n");
|
|
80
|
+
}
|
|
81
|
+
export async function llmRouter(ctx, opts = {}) {
|
|
82
|
+
try {
|
|
83
|
+
const raw = await claudeText(routerPrompt(ctx), opts);
|
|
84
|
+
const match = raw.match(/\{[\s\S]*\}/);
|
|
85
|
+
if (!match)
|
|
86
|
+
throw new Error("no JSON in router reply");
|
|
87
|
+
const parsed = JSON.parse(match[0]);
|
|
88
|
+
const next = String(parsed.next ?? "").trim();
|
|
89
|
+
const valid = next === "done" || ctx.agents.some((a) => a.id === next);
|
|
90
|
+
if (!valid)
|
|
91
|
+
throw new Error(`router chose unknown agent "${next}"`);
|
|
92
|
+
return { next, reason: String(parsed.reason ?? "llm decision").slice(0, 140), by: "llm" };
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
const fallback = rulesRouter(ctx);
|
|
96
|
+
return { ...fallback, reason: `${fallback.reason} (llm unavailable — rules fallback)` };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../src/core/router.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routes — automated multi-hop handoffs. "Claude plans, OpenCode executes,
|
|
3
|
+
* Claude reviews" as one command instead of three manual baton passes.
|
|
4
|
+
*
|
|
5
|
+
* The engine reuses the exact same machinery as manual handoffs (interrupt →
|
|
6
|
+
* projection → briefing → baton), it just drives the sequence:
|
|
7
|
+
*
|
|
8
|
+
* start(steps, task)
|
|
9
|
+
* └─ step i: handoff(agent_i) → send(instruction_i)
|
|
10
|
+
* run_complete, no question → advance to step i+1
|
|
11
|
+
* run_complete, agent asked → pause (waiting_human) + notify
|
|
12
|
+
* user answers in chat → resume, next run_complete advances
|
|
13
|
+
* error / timeout → route fails
|
|
14
|
+
* last step completes → route_completed + notify
|
|
15
|
+
*
|
|
16
|
+
* The human always outranks the route: a manual handoff or interrupt cancels
|
|
17
|
+
* it. State persists in .loom/state.json; a daemon restart mid-route marks it
|
|
18
|
+
* failed rather than pretending nothing happened.
|
|
19
|
+
*/
|
|
20
|
+
import type { LoomEvent, ProjectConfig, RouteState, RouteStepSpec, RouterKind } from "../types.js";
|
|
21
|
+
import type { EventLog } from "./eventlog.js";
|
|
22
|
+
export declare class RouteActiveError extends Error {
|
|
23
|
+
constructor();
|
|
24
|
+
}
|
|
25
|
+
/** What the engine needs from the project runtime (avoids a circular import). */
|
|
26
|
+
export interface RouteHost {
|
|
27
|
+
projectName: string;
|
|
28
|
+
projectDir: string;
|
|
29
|
+
config: ProjectConfig;
|
|
30
|
+
log: EventLog;
|
|
31
|
+
handoff(to: string): Promise<unknown>;
|
|
32
|
+
send(text: string, agentId: string): Promise<unknown>;
|
|
33
|
+
interrupt(): Promise<unknown>;
|
|
34
|
+
isAdapterId(id: string): boolean;
|
|
35
|
+
/** Lifetime project spend (USD) — used to attribute cost to routes. */
|
|
36
|
+
costTotal(): number;
|
|
37
|
+
}
|
|
38
|
+
export interface ResolvedSteps {
|
|
39
|
+
ids: string[];
|
|
40
|
+
/** Parallel to ids; null when the step carries no custom instruction. */
|
|
41
|
+
instructions: Array<string | null>;
|
|
42
|
+
/**
|
|
43
|
+
* Parallel to ids; the job the step assigns, or null to inherit the agent's
|
|
44
|
+
* own role. This is what lets a task say "claude-code plans, opencode
|
|
45
|
+
* executes" without permanently changing either agent's default role.
|
|
46
|
+
*/
|
|
47
|
+
roles: Array<string | null>;
|
|
48
|
+
}
|
|
49
|
+
export declare function stepName(spec: RouteStepSpec): string;
|
|
50
|
+
/** Resolve step specs (ids/roles, optionally with instructions) to adapter ids. */
|
|
51
|
+
export declare function resolveSteps(spec: RouteStepSpec[], config: ProjectConfig, isAdapterId: (id: string) => boolean): ResolvedSteps;
|
|
52
|
+
export declare class RouteEngine {
|
|
53
|
+
private host;
|
|
54
|
+
private sawNeedsInput;
|
|
55
|
+
private lastQuestion;
|
|
56
|
+
private stepTimer;
|
|
57
|
+
constructor(host: RouteHost);
|
|
58
|
+
private read;
|
|
59
|
+
private write;
|
|
60
|
+
state(): RouteState | null;
|
|
61
|
+
isActive(): boolean;
|
|
62
|
+
private failStaleOnBoot;
|
|
63
|
+
start(spec: RouteStepSpec[], task: string, name?: string): Promise<RouteState>;
|
|
64
|
+
/** Dynamic route: a router (LLM or rules) picks every next hop. */
|
|
65
|
+
startDynamic(task: string, opts?: {
|
|
66
|
+
router?: RouterKind;
|
|
67
|
+
maxHops?: number;
|
|
68
|
+
}): Promise<RouteState>;
|
|
69
|
+
private routerContext;
|
|
70
|
+
private decide;
|
|
71
|
+
private pushHop;
|
|
72
|
+
abort(reason?: string): Promise<RouteState>;
|
|
73
|
+
/** A manual baton pass while a route is active cancels the route. */
|
|
74
|
+
onManualHandoff(): void;
|
|
75
|
+
/** A manual interrupt while a route is active cancels the route. */
|
|
76
|
+
onManualInterrupt(): void;
|
|
77
|
+
/**
|
|
78
|
+
* A user message addressed to the current step's agent while the route is
|
|
79
|
+
* paused = the answer to the agent's question. Resume; the next
|
|
80
|
+
* run_complete advances the route.
|
|
81
|
+
*/
|
|
82
|
+
onUserMessage(targetAgentId: string): void;
|
|
83
|
+
/** Fed every adapter event by the runtime; drives the state machine. */
|
|
84
|
+
handleAgentEvent(event: LoomEvent): void;
|
|
85
|
+
private routeCost;
|
|
86
|
+
private complete;
|
|
87
|
+
private advance;
|
|
88
|
+
private beginStep;
|
|
89
|
+
private instruction;
|
|
90
|
+
private finish;
|
|
91
|
+
private armTimer;
|
|
92
|
+
private clearTimer;
|
|
93
|
+
}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Routes — automated multi-hop handoffs. "Claude plans, OpenCode executes,
|
|
3
|
+
* Claude reviews" as one command instead of three manual baton passes.
|
|
4
|
+
*
|
|
5
|
+
* The engine reuses the exact same machinery as manual handoffs (interrupt →
|
|
6
|
+
* projection → briefing → baton), it just drives the sequence:
|
|
7
|
+
*
|
|
8
|
+
* start(steps, task)
|
|
9
|
+
* └─ step i: handoff(agent_i) → send(instruction_i)
|
|
10
|
+
* run_complete, no question → advance to step i+1
|
|
11
|
+
* run_complete, agent asked → pause (waiting_human) + notify
|
|
12
|
+
* user answers in chat → resume, next run_complete advances
|
|
13
|
+
* error / timeout → route fails
|
|
14
|
+
* last step completes → route_completed + notify
|
|
15
|
+
*
|
|
16
|
+
* The human always outranks the route: a manual handoff or interrupt cancels
|
|
17
|
+
* it. State persists in .loom/state.json; a daemon restart mid-route marks it
|
|
18
|
+
* failed rather than pretending nothing happened.
|
|
19
|
+
*/
|
|
20
|
+
import { notify } from "./notify.js";
|
|
21
|
+
import { newId, readProjectState, writeProjectState } from "./registry.js";
|
|
22
|
+
import { llmRouter, rulesRouter } from "./router.js";
|
|
23
|
+
const DEFAULT_STEP_TIMEOUT_MS = 45 * 60 * 1000;
|
|
24
|
+
const DEFAULT_MAX_HOPS = 8;
|
|
25
|
+
export class RouteActiveError extends Error {
|
|
26
|
+
constructor() {
|
|
27
|
+
super("a route is already active in this project — finish or abort it first");
|
|
28
|
+
this.name = "RouteActiveError";
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
export function stepName(spec) {
|
|
32
|
+
return typeof spec === "string" ? spec : spec.step;
|
|
33
|
+
}
|
|
34
|
+
/** Resolve step specs (ids/roles, optionally with instructions) to adapter ids. */
|
|
35
|
+
export function resolveSteps(spec, config, isAdapterId) {
|
|
36
|
+
if (!spec.length)
|
|
37
|
+
throw new Error("a route needs at least one step");
|
|
38
|
+
const ids = [];
|
|
39
|
+
const instructions = [];
|
|
40
|
+
const roles = [];
|
|
41
|
+
for (const entry of spec) {
|
|
42
|
+
const step = stepName(entry);
|
|
43
|
+
const byId = config.agents.find((a) => a.id === step);
|
|
44
|
+
const byRole = config.agents.find((a) => a.role === step && isAdapterId(a.id));
|
|
45
|
+
const cfg = byId ?? byRole;
|
|
46
|
+
if (!cfg) {
|
|
47
|
+
throw new Error(`route step "${step}" matches no agent id or role in this project`);
|
|
48
|
+
}
|
|
49
|
+
if (!isAdapterId(cfg.id)) {
|
|
50
|
+
throw new Error(`route step "${step}" resolves to "${cfg.id}", a bridge — bridges never hold the baton`);
|
|
51
|
+
}
|
|
52
|
+
ids.push(cfg.id);
|
|
53
|
+
instructions.push(typeof entry === "object" && entry.instruction?.trim() ? entry.instruction.trim() : null);
|
|
54
|
+
roles.push(typeof entry === "object" && entry.role?.trim() ? entry.role.trim().slice(0, 40) : null);
|
|
55
|
+
}
|
|
56
|
+
return { ids, instructions, roles };
|
|
57
|
+
}
|
|
58
|
+
const ROLE_INSTRUCTIONS = {
|
|
59
|
+
planner: "Produce a concrete, actionable plan for the task. Be specific about files and steps, and state clearly when the plan is complete.",
|
|
60
|
+
executor: "Execute the work using the shared context handed to you. State clearly when the implementation is complete.",
|
|
61
|
+
reviewer: "Review the preceding work for correctness, gaps and risks. Give a clear verdict and list any required fixes.",
|
|
62
|
+
general: "Continue the task using the shared context handed to you.",
|
|
63
|
+
};
|
|
64
|
+
export class RouteEngine {
|
|
65
|
+
host;
|
|
66
|
+
sawNeedsInput = false;
|
|
67
|
+
lastQuestion;
|
|
68
|
+
stepTimer = null;
|
|
69
|
+
constructor(host) {
|
|
70
|
+
this.host = host;
|
|
71
|
+
this.failStaleOnBoot();
|
|
72
|
+
}
|
|
73
|
+
// ---------------------------------------------------------------------
|
|
74
|
+
// State persistence (.loom/state.json — same pattern as the baton)
|
|
75
|
+
// ---------------------------------------------------------------------
|
|
76
|
+
read() {
|
|
77
|
+
return readProjectState(this.host.projectDir).route;
|
|
78
|
+
}
|
|
79
|
+
write(route) {
|
|
80
|
+
const state = readProjectState(this.host.projectDir);
|
|
81
|
+
route.updatedAt = Date.now();
|
|
82
|
+
state.route = route;
|
|
83
|
+
writeProjectState(this.host.projectDir, state);
|
|
84
|
+
}
|
|
85
|
+
state() {
|
|
86
|
+
return this.read() ?? null;
|
|
87
|
+
}
|
|
88
|
+
isActive() {
|
|
89
|
+
const r = this.read();
|
|
90
|
+
return Boolean(r && (r.status === "running" || r.status === "waiting_human"));
|
|
91
|
+
}
|
|
92
|
+
failStaleOnBoot() {
|
|
93
|
+
const r = this.read();
|
|
94
|
+
if (r && (r.status === "running" || r.status === "waiting_human")) {
|
|
95
|
+
r.status = "failed";
|
|
96
|
+
r.reason = "daemon restarted mid-route";
|
|
97
|
+
this.write(r);
|
|
98
|
+
this.host.log.append({
|
|
99
|
+
kind: "route_failed",
|
|
100
|
+
payload: { routeId: r.id, reason: r.reason },
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// ---------------------------------------------------------------------
|
|
105
|
+
// Lifecycle
|
|
106
|
+
// ---------------------------------------------------------------------
|
|
107
|
+
async start(spec, task, name) {
|
|
108
|
+
if (this.isActive())
|
|
109
|
+
throw new RouteActiveError();
|
|
110
|
+
const resolved = resolveSteps(spec, this.host.config, this.host.isAdapterId);
|
|
111
|
+
// The step's assigned role wins; fall back to the agent's own default role
|
|
112
|
+
// when the task didn't say. This is the seam that makes roles per-task.
|
|
113
|
+
const stepRoles = resolved.ids.map((id, i) => resolved.roles[i] ?? this.host.config.agents.find((a) => a.id === id).role);
|
|
114
|
+
const route = {
|
|
115
|
+
id: newId(4),
|
|
116
|
+
...(name ? { name } : {}),
|
|
117
|
+
task,
|
|
118
|
+
steps: resolved.ids,
|
|
119
|
+
stepRoles,
|
|
120
|
+
stepInstructions: resolved.instructions,
|
|
121
|
+
current: 0,
|
|
122
|
+
status: "running",
|
|
123
|
+
mode: "static",
|
|
124
|
+
costStartUsd: this.host.costTotal(),
|
|
125
|
+
startedAt: Date.now(),
|
|
126
|
+
updatedAt: Date.now(),
|
|
127
|
+
};
|
|
128
|
+
this.write(route);
|
|
129
|
+
this.host.log.append({
|
|
130
|
+
kind: "route_started",
|
|
131
|
+
payload: { routeId: route.id, name: name ?? null, mode: "static", steps: resolved.ids, task },
|
|
132
|
+
});
|
|
133
|
+
await this.beginStep(route);
|
|
134
|
+
return this.read();
|
|
135
|
+
}
|
|
136
|
+
/** Dynamic route: a router (LLM or rules) picks every next hop. */
|
|
137
|
+
async startDynamic(task, opts = {}) {
|
|
138
|
+
if (this.isActive())
|
|
139
|
+
throw new RouteActiveError();
|
|
140
|
+
const router = opts.router ?? "llm";
|
|
141
|
+
const route = {
|
|
142
|
+
id: newId(4),
|
|
143
|
+
name: "auto",
|
|
144
|
+
task,
|
|
145
|
+
steps: [],
|
|
146
|
+
stepRoles: [],
|
|
147
|
+
current: -1,
|
|
148
|
+
status: "running",
|
|
149
|
+
mode: "dynamic",
|
|
150
|
+
router,
|
|
151
|
+
maxHops: opts.maxHops ?? DEFAULT_MAX_HOPS,
|
|
152
|
+
costStartUsd: this.host.costTotal(),
|
|
153
|
+
startedAt: Date.now(),
|
|
154
|
+
updatedAt: Date.now(),
|
|
155
|
+
};
|
|
156
|
+
this.write(route);
|
|
157
|
+
this.host.log.append({
|
|
158
|
+
kind: "route_started",
|
|
159
|
+
payload: { routeId: route.id, name: "auto", mode: "dynamic", router, task },
|
|
160
|
+
});
|
|
161
|
+
const decision = await this.decide(route);
|
|
162
|
+
if (decision.next === "done") {
|
|
163
|
+
this.finish(route, "failed", `router declined to start: ${decision.reason}`);
|
|
164
|
+
return this.read();
|
|
165
|
+
}
|
|
166
|
+
this.pushHop(route, decision);
|
|
167
|
+
await this.beginStep(route);
|
|
168
|
+
return this.read();
|
|
169
|
+
}
|
|
170
|
+
routerContext(r) {
|
|
171
|
+
const agents = this.host.config.agents
|
|
172
|
+
.filter((a) => this.host.isAdapterId(a.id))
|
|
173
|
+
.map((a) => ({ id: a.id, role: a.role }));
|
|
174
|
+
const recent = this.host.log
|
|
175
|
+
.list({ kinds: ["message"], limit: 12 })
|
|
176
|
+
.map((e) => ({
|
|
177
|
+
author: e.agentId ?? String(e.payload.author ?? "user"),
|
|
178
|
+
text: String(e.payload.text ?? ""),
|
|
179
|
+
}));
|
|
180
|
+
return { task: r.task, hops: [...r.steps], agents, recent };
|
|
181
|
+
}
|
|
182
|
+
async decide(r) {
|
|
183
|
+
const ctx = this.routerContext(r);
|
|
184
|
+
if (r.router === "llm")
|
|
185
|
+
return llmRouter(ctx);
|
|
186
|
+
return rulesRouter(ctx);
|
|
187
|
+
}
|
|
188
|
+
pushHop(r, decision) {
|
|
189
|
+
const role = this.host.config.agents.find((a) => a.id === decision.next)?.role ?? "general";
|
|
190
|
+
r.steps.push(decision.next);
|
|
191
|
+
r.stepRoles.push(role);
|
|
192
|
+
r.stepInstructions = [...(r.stepInstructions ?? []), null];
|
|
193
|
+
r.current = r.steps.length - 1;
|
|
194
|
+
r.reason = decision.reason;
|
|
195
|
+
this.write(r);
|
|
196
|
+
}
|
|
197
|
+
async abort(reason = "aborted by user") {
|
|
198
|
+
const r = this.read();
|
|
199
|
+
if (!r || !this.isActive())
|
|
200
|
+
throw new Error("no active route to abort");
|
|
201
|
+
this.finish(r, "aborted", reason);
|
|
202
|
+
await this.host.interrupt().catch(() => { });
|
|
203
|
+
return this.read();
|
|
204
|
+
}
|
|
205
|
+
/** A manual baton pass while a route is active cancels the route. */
|
|
206
|
+
onManualHandoff() {
|
|
207
|
+
const r = this.read();
|
|
208
|
+
if (r && this.isActive())
|
|
209
|
+
this.finish(r, "aborted", "manual handoff — route cancelled");
|
|
210
|
+
}
|
|
211
|
+
/** A manual interrupt while a route is active cancels the route. */
|
|
212
|
+
onManualInterrupt() {
|
|
213
|
+
const r = this.read();
|
|
214
|
+
if (r && this.isActive())
|
|
215
|
+
this.finish(r, "aborted", "manual interrupt — route cancelled");
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* A user message addressed to the current step's agent while the route is
|
|
219
|
+
* paused = the answer to the agent's question. Resume; the next
|
|
220
|
+
* run_complete advances the route.
|
|
221
|
+
*/
|
|
222
|
+
onUserMessage(targetAgentId) {
|
|
223
|
+
const r = this.read();
|
|
224
|
+
if (!r || r.status !== "waiting_human")
|
|
225
|
+
return;
|
|
226
|
+
if (targetAgentId !== r.steps[r.current])
|
|
227
|
+
return;
|
|
228
|
+
this.sawNeedsInput = false;
|
|
229
|
+
delete r.pendingQuestion;
|
|
230
|
+
r.status = "running";
|
|
231
|
+
this.write(r);
|
|
232
|
+
this.host.log.append({
|
|
233
|
+
kind: "route_resumed",
|
|
234
|
+
payload: { routeId: r.id, step: r.current, agent: targetAgentId },
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/** Fed every adapter event by the runtime; drives the state machine. */
|
|
238
|
+
handleAgentEvent(event) {
|
|
239
|
+
const r = this.read();
|
|
240
|
+
if (!r || (r.status !== "running" && r.status !== "waiting_human"))
|
|
241
|
+
return;
|
|
242
|
+
const currentAgent = r.steps[r.current];
|
|
243
|
+
if (!event.agentId || event.agentId !== currentAgent)
|
|
244
|
+
return;
|
|
245
|
+
if (event.kind === "needs_input") {
|
|
246
|
+
this.sawNeedsInput = true;
|
|
247
|
+
this.lastQuestion = String(event.payload.question ?? "");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
if (event.kind === "error" && r.status === "running") {
|
|
251
|
+
this.finish(r, "failed", `step ${r.current + 1} (${currentAgent}) errored: ${String(event.payload.message ?? "unknown")}`);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
if (event.kind === "run_complete" && r.status === "running") {
|
|
255
|
+
if (this.sawNeedsInput) {
|
|
256
|
+
r.status = "waiting_human";
|
|
257
|
+
r.pendingQuestion = this.lastQuestion ?? "";
|
|
258
|
+
this.write(r);
|
|
259
|
+
this.host.log.append({
|
|
260
|
+
kind: "route_paused",
|
|
261
|
+
payload: {
|
|
262
|
+
routeId: r.id,
|
|
263
|
+
step: r.current,
|
|
264
|
+
agent: currentAgent,
|
|
265
|
+
question: r.pendingQuestion,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
notify({
|
|
269
|
+
title: `Loom · ${this.host.projectName}`,
|
|
270
|
+
body: `route paused — ${currentAgent} asks: ${r.pendingQuestion}`,
|
|
271
|
+
});
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
void this.advance(r).catch((err) => this.finish(r, "failed", String(err instanceof Error ? err.message : err)));
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
// ---------------------------------------------------------------------
|
|
278
|
+
// Step mechanics
|
|
279
|
+
// ---------------------------------------------------------------------
|
|
280
|
+
routeCost(r) {
|
|
281
|
+
return Math.max(0, this.host.costTotal() - (r.costStartUsd ?? 0));
|
|
282
|
+
}
|
|
283
|
+
complete(r, note) {
|
|
284
|
+
r.status = "completed";
|
|
285
|
+
if (note)
|
|
286
|
+
r.reason = note;
|
|
287
|
+
r.costUsd = this.routeCost(r);
|
|
288
|
+
this.write(r);
|
|
289
|
+
this.host.log.append({
|
|
290
|
+
kind: "route_completed",
|
|
291
|
+
payload: {
|
|
292
|
+
routeId: r.id,
|
|
293
|
+
steps: r.steps.length,
|
|
294
|
+
task: r.task,
|
|
295
|
+
costUsd: r.costUsd,
|
|
296
|
+
...(note ? { note } : {}),
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
notify({
|
|
300
|
+
title: `Loom · ${this.host.projectName}`,
|
|
301
|
+
body: `route complete: ${r.steps.join(" → ")}`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
async advance(r) {
|
|
305
|
+
this.clearTimer();
|
|
306
|
+
if (r.mode === "dynamic") {
|
|
307
|
+
if (r.steps.length >= (r.maxHops ?? DEFAULT_MAX_HOPS)) {
|
|
308
|
+
this.complete(r, `hop budget (${r.maxHops ?? DEFAULT_MAX_HOPS}) reached`);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
const decision = await this.decide(r);
|
|
312
|
+
if (decision.next === "done") {
|
|
313
|
+
this.complete(r, decision.reason);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
this.pushHop(r, decision);
|
|
317
|
+
await this.beginStep(r);
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
r.current += 1;
|
|
321
|
+
if (r.current >= r.steps.length) {
|
|
322
|
+
this.complete(r);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
this.write(r);
|
|
326
|
+
await this.beginStep(r);
|
|
327
|
+
}
|
|
328
|
+
async beginStep(r) {
|
|
329
|
+
const agent = r.steps[r.current];
|
|
330
|
+
this.sawNeedsInput = false;
|
|
331
|
+
this.lastQuestion = undefined;
|
|
332
|
+
this.host.log.append({
|
|
333
|
+
kind: "route_step",
|
|
334
|
+
payload: {
|
|
335
|
+
routeId: r.id,
|
|
336
|
+
step: r.current,
|
|
337
|
+
of: r.mode === "static" ? r.steps.length : null,
|
|
338
|
+
agent,
|
|
339
|
+
...(r.mode === "dynamic" && r.reason ? { reason: r.reason } : {}),
|
|
340
|
+
},
|
|
341
|
+
});
|
|
342
|
+
this.armTimer(r.id, r.current);
|
|
343
|
+
try {
|
|
344
|
+
await this.host.handoff(agent);
|
|
345
|
+
await this.host.send(this.instruction(r), agent);
|
|
346
|
+
}
|
|
347
|
+
catch (err) {
|
|
348
|
+
const fresh = this.read();
|
|
349
|
+
if (fresh && fresh.id === r.id) {
|
|
350
|
+
this.finish(fresh, "failed", `step ${r.current + 1} (${agent}) could not start: ${String(err instanceof Error ? err.message : err)}`);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
instruction(r) {
|
|
355
|
+
const i = r.current;
|
|
356
|
+
const role = r.stepRoles[i] ?? "general";
|
|
357
|
+
const position = r.mode === "static" ? `step ${i + 1}/${r.steps.length}` : `hop ${i + 1}`;
|
|
358
|
+
const header = `[Loom route${r.name ? ` "${r.name}"` : ""} — ${position} (${role})]`;
|
|
359
|
+
const continuity = i === 0
|
|
360
|
+
? ""
|
|
361
|
+
: "The previous step has completed; its full context was handed to you via the Loom briefing and .loom/memory.\n";
|
|
362
|
+
const custom = r.stepInstructions?.[i];
|
|
363
|
+
const focus = custom ? `\nStep-specific instructions: ${custom}` : "";
|
|
364
|
+
return `${header}\nTask: ${r.task}\n${continuity}${ROLE_INSTRUCTIONS[role] ?? ROLE_INSTRUCTIONS.general}${focus}`;
|
|
365
|
+
}
|
|
366
|
+
finish(r, status, reason) {
|
|
367
|
+
this.clearTimer();
|
|
368
|
+
r.status = status;
|
|
369
|
+
r.reason = reason;
|
|
370
|
+
r.costUsd = this.routeCost(r);
|
|
371
|
+
delete r.pendingQuestion;
|
|
372
|
+
this.write(r);
|
|
373
|
+
this.host.log.append({
|
|
374
|
+
kind: "route_failed",
|
|
375
|
+
payload: { routeId: r.id, reason, aborted: status === "aborted" },
|
|
376
|
+
});
|
|
377
|
+
if (status === "failed") {
|
|
378
|
+
notify({ title: `Loom · ${this.host.projectName}`, body: `route failed: ${reason}` });
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
armTimer(routeId, stepIndex) {
|
|
382
|
+
this.clearTimer();
|
|
383
|
+
const timeoutMs = Number(process.env.LOOM_ROUTE_STEP_TIMEOUT_MS) || DEFAULT_STEP_TIMEOUT_MS;
|
|
384
|
+
this.stepTimer = setTimeout(() => {
|
|
385
|
+
const r = this.read();
|
|
386
|
+
if (r && r.id === routeId && r.current === stepIndex && r.status === "running") {
|
|
387
|
+
this.finish(r, "failed", `step ${stepIndex + 1} timed out`);
|
|
388
|
+
void this.host.interrupt().catch(() => { });
|
|
389
|
+
}
|
|
390
|
+
}, timeoutMs);
|
|
391
|
+
this.stepTimer.unref?.();
|
|
392
|
+
}
|
|
393
|
+
clearTimer() {
|
|
394
|
+
if (this.stepTimer)
|
|
395
|
+
clearTimeout(this.stepTimer);
|
|
396
|
+
this.stepTimer = null;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/core/routes.ts"],"names":[],"mappings":"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"}
|