projectinator 0.3.1 → 0.4.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/README.md +7 -6
- package/bin/projectinator.mjs +22 -15
- package/dist/bakeoff.js +163 -0
- package/dist/bakeoff.js.map +1 -0
- package/dist/build-state.js +26 -0
- package/dist/build-state.js.map +1 -0
- package/dist/burndown.js +19 -0
- package/dist/burndown.js.map +1 -0
- package/dist/calibration.js +86 -0
- package/dist/calibration.js.map +1 -0
- package/dist/cli.js +289 -0
- package/dist/cli.js.map +1 -0
- package/dist/cost.js +35 -0
- package/dist/cost.js.map +1 -0
- package/dist/council.js +147 -0
- package/dist/council.js.map +1 -0
- package/dist/demo.js +93 -0
- package/dist/demo.js.map +1 -0
- package/dist/estimate.js +98 -0
- package/dist/estimate.js.map +1 -0
- package/dist/executor.js +118 -0
- package/dist/executor.js.map +1 -0
- package/dist/git.js +82 -0
- package/dist/git.js.map +1 -0
- package/dist/intake.js +98 -0
- package/dist/intake.js.map +1 -0
- package/dist/models.js +171 -0
- package/dist/models.js.map +1 -0
- package/dist/narrate.js +76 -0
- package/dist/narrate.js.map +1 -0
- package/dist/openrouter.js +112 -0
- package/dist/openrouter.js.map +1 -0
- package/dist/orchestrator.js +273 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/pm.js +245 -0
- package/dist/pm.js.map +1 -0
- package/dist/preview.js +194 -0
- package/dist/preview.js.map +1 -0
- package/dist/registry-store.js +38 -0
- package/dist/registry-store.js.map +1 -0
- package/dist/registry.js +115 -0
- package/dist/registry.js.map +1 -0
- package/dist/research.js +98 -0
- package/dist/research.js.map +1 -0
- package/dist/retro.js +83 -0
- package/dist/retro.js.map +1 -0
- package/dist/roles.js +357 -0
- package/dist/roles.js.map +1 -0
- package/dist/router.js +95 -0
- package/dist/router.js.map +1 -0
- package/dist/run-bakeoff.js +68 -0
- package/dist/run-bakeoff.js.map +1 -0
- package/dist/run-build.js +193 -0
- package/dist/run-build.js.map +1 -0
- package/{src/run-dev.ts → dist/run-dev.js} +23 -39
- package/dist/run-dev.js.map +1 -0
- package/{src/run-pm.ts → dist/run-pm.js} +34 -46
- package/dist/run-pm.js.map +1 -0
- package/{src/run-research.ts → dist/run-research.js} +19 -30
- package/dist/run-research.js.map +1 -0
- package/{src/run-scout.ts → dist/run-scout.js} +17 -27
- package/dist/run-scout.js.map +1 -0
- package/dist/run-web.js +93 -0
- package/dist/run-web.js.map +1 -0
- package/dist/scout.js +81 -0
- package/dist/scout.js.map +1 -0
- package/dist/session-cost.js +15 -0
- package/dist/session-cost.js.map +1 -0
- package/dist/stack.js +34 -0
- package/dist/stack.js.map +1 -0
- package/dist/stuck.js +21 -0
- package/dist/stuck.js.map +1 -0
- package/dist/tui/App.js +1269 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/BakeOff.js +95 -0
- package/dist/tui/BakeOff.js.map +1 -0
- package/dist/tui/BoardEditor.js +183 -0
- package/dist/tui/BoardEditor.js.map +1 -0
- package/dist/tui/EditableBoard.js +140 -0
- package/dist/tui/EditableBoard.js.map +1 -0
- package/dist/tui/Frame.js +78 -0
- package/dist/tui/Frame.js.map +1 -0
- package/dist/tui/Intake.js +59 -0
- package/dist/tui/Intake.js.map +1 -0
- package/dist/tui/Kanban.js +75 -0
- package/dist/tui/Kanban.js.map +1 -0
- package/dist/tui/Settings.js +299 -0
- package/dist/tui/Settings.js.map +1 -0
- package/dist/tui/StackPick.js +44 -0
- package/dist/tui/StackPick.js.map +1 -0
- package/dist/tui/WebAccounts.js +131 -0
- package/dist/tui/WebAccounts.js.map +1 -0
- package/dist/tui/components.js +178 -0
- package/dist/tui/components.js.map +1 -0
- package/dist/tui/config.js +129 -0
- package/dist/tui/config.js.map +1 -0
- package/dist/tui/deploy.js +114 -0
- package/dist/tui/deploy.js.map +1 -0
- package/dist/tui/engine.js +710 -0
- package/dist/tui/engine.js.map +1 -0
- package/dist/tui/notify.js +47 -0
- package/dist/tui/notify.js.map +1 -0
- package/dist/tui/panels.js +42 -0
- package/dist/tui/panels.js.map +1 -0
- package/dist/tui/templates.js +106 -0
- package/dist/tui/templates.js.map +1 -0
- package/dist/tui/theme.js +42 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/validate.js +51 -0
- package/dist/tui/validate.js.map +1 -0
- package/{src/tui.tsx → dist/tui.js} +23 -33
- package/dist/tui.js.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/web/oauth-anthropic.js +179 -0
- package/dist/web/oauth-anthropic.js.map +1 -0
- package/dist/web/session.js +274 -0
- package/dist/web/session.js.map +1 -0
- package/package.json +5 -4
- package/src/bakeoff.ts +0 -214
- package/src/build-state.ts +0 -47
- package/src/burndown.ts +0 -35
- package/src/calibration.ts +0 -88
- package/src/cli.ts +0 -263
- package/src/cost.ts +0 -43
- package/src/council.ts +0 -175
- package/src/demo.ts +0 -106
- package/src/estimate.ts +0 -111
- package/src/executor.ts +0 -169
- package/src/git.ts +0 -72
- package/src/intake.ts +0 -125
- package/src/models.ts +0 -175
- package/src/narrate.ts +0 -87
- package/src/openrouter.ts +0 -119
- package/src/orchestrator.ts +0 -310
- package/src/pm.ts +0 -302
- package/src/preview.ts +0 -206
- package/src/registry-store.ts +0 -41
- package/src/registry.ts +0 -132
- package/src/research.ts +0 -123
- package/src/retro.ts +0 -99
- package/src/roles.ts +0 -401
- package/src/router.ts +0 -120
- package/src/run-bakeoff.ts +0 -77
- package/src/run-build.ts +0 -203
- package/src/run-web.ts +0 -87
- package/src/scout.ts +0 -121
- package/src/session-cost.ts +0 -17
- package/src/stack.ts +0 -46
- package/src/tui/App.tsx +0 -1760
- package/src/tui/BakeOff.tsx +0 -190
- package/src/tui/BoardEditor.tsx +0 -260
- package/src/tui/EditableBoard.tsx +0 -179
- package/src/tui/Frame.tsx +0 -142
- package/src/tui/Intake.tsx +0 -111
- package/src/tui/Kanban.tsx +0 -158
- package/src/tui/Settings.tsx +0 -513
- package/src/tui/StackPick.tsx +0 -79
- package/src/tui/WebAccounts.tsx +0 -197
- package/src/tui/components.tsx +0 -340
- package/src/tui/config.ts +0 -150
- package/src/tui/deploy.ts +0 -137
- package/src/tui/engine.ts +0 -749
- package/src/tui/notify.ts +0 -21
- package/src/tui/panels.tsx +0 -89
- package/src/tui/templates.ts +0 -119
- package/src/tui/theme.ts +0 -44
- package/src/tui/validate.ts +0 -53
- package/src/types.ts +0 -208
- package/src/web/oauth-anthropic.ts +0 -206
- package/src/web/session.ts +0 -299
package/dist/narrate.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// AI narrative for a build retro. Feeds the data-driven RetroReport to a model
|
|
2
|
+
// and gets back a short "what went well / what to improve" write-up. On-demand
|
|
3
|
+
// (costs a small call) and cached on the build state by the caller.
|
|
4
|
+
import { createAgentSession, } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { findEntry } from "./registry.js";
|
|
6
|
+
import { piRuntime, resolvePiModel } from "./executor.js";
|
|
7
|
+
import { addSessionCost } from "./session-cost.js";
|
|
8
|
+
function lastAssistantText(session) {
|
|
9
|
+
const msgs = session.messages;
|
|
10
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
11
|
+
const m = msgs[i];
|
|
12
|
+
if (m?.role !== "assistant")
|
|
13
|
+
continue;
|
|
14
|
+
const c = m.content;
|
|
15
|
+
if (typeof c === "string")
|
|
16
|
+
return c;
|
|
17
|
+
if (Array.isArray(c)) {
|
|
18
|
+
return c
|
|
19
|
+
.map((p) => (typeof p === "string" ? p : p && typeof p === "object" && "text" in p ? String(p.text) : ""))
|
|
20
|
+
.join("")
|
|
21
|
+
.trim();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
/** Compact fact sheet the model reasons over — numbers, not prose. */
|
|
27
|
+
function facts(r) {
|
|
28
|
+
const lines = [
|
|
29
|
+
`Idea: ${r.idea}`,
|
|
30
|
+
`Status: ${r.status}. Tasks done: ${r.doneCount}/${r.taskCount}.`,
|
|
31
|
+
`Cost: predicted $${r.estCost.toFixed(2)}, actual $${r.totalCost.toFixed(2)}.`,
|
|
32
|
+
`Tests: ${r.tests.passed} passed, ${r.tests.failed} failed.`,
|
|
33
|
+
r.retries.length ? `Rebuilds: ${r.retries.map((x) => `${x.taskId}×${x.rounds}`).join(", ")}.` : "Rebuilds: none.",
|
|
34
|
+
`Cost by epic: ${r.byEpic.map((e) => `${e.epic} $${e.cost}`).join(", ") || "n/a"}.`,
|
|
35
|
+
`Cost by model: ${r.byModel.map((m) => `${m.model} $${m.cost} (${m.tasks})`).join(", ") || "n/a"}.`,
|
|
36
|
+
`Priciest tasks: ${r.topCost.map((t) => `${t.taskId} $${t.cost}`).join(", ") || "n/a"}.`,
|
|
37
|
+
r.bugs.length
|
|
38
|
+
? `Tester flags: ${r.bugs.map((b) => `[${b.severity}] ${b.description}`).slice(0, 6).join("; ")}.`
|
|
39
|
+
: "Tester flags: none.",
|
|
40
|
+
];
|
|
41
|
+
return lines.join("\n");
|
|
42
|
+
}
|
|
43
|
+
const PROMPT = [
|
|
44
|
+
"You are a pragmatic engineering lead writing a SHORT retro for this build.",
|
|
45
|
+
"Use the facts below — reference the actual numbers. Do not invent anything.",
|
|
46
|
+
"Write exactly three sections, terse bullets, under 140 words total:",
|
|
47
|
+
"**What went well** (2-3 bullets)",
|
|
48
|
+
"**What to improve** (2-3 bullets)",
|
|
49
|
+
"**Next time** (one line)",
|
|
50
|
+
"No preamble, no closing remarks.",
|
|
51
|
+
].join("\n");
|
|
52
|
+
/** Generate the narrative. Throws on failure (caller shows the error). */
|
|
53
|
+
export async function narrateRetro(report, opts) {
|
|
54
|
+
const runtime = await piRuntime();
|
|
55
|
+
const { entry } = findEntry("plan", "mid");
|
|
56
|
+
const pick = opts.modelOverride ?? entry.byBackend[opts.backend];
|
|
57
|
+
const model = resolvePiModel(runtime, pick.provider, pick.model);
|
|
58
|
+
const { session } = await createAgentSession({
|
|
59
|
+
model,
|
|
60
|
+
modelRuntime: runtime,
|
|
61
|
+
thinkingLevel: "low",
|
|
62
|
+
noTools: "all",
|
|
63
|
+
});
|
|
64
|
+
try {
|
|
65
|
+
await session.prompt(`${PROMPT}\n\n--- FACTS ---\n${facts(report)}`);
|
|
66
|
+
const text = lastAssistantText(session);
|
|
67
|
+
if (!text)
|
|
68
|
+
throw new Error("The model returned no narrative.");
|
|
69
|
+
return text;
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
addSessionCost(session.getSessionStats().cost);
|
|
73
|
+
session.dispose();
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=narrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrate.js","sourceRoot":"","sources":["../src/narrate.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// The OpenRouter model catalog — so users can pick ANY OpenRouter model by name
|
|
2
|
+
// (Kimi, DeepSeek, Qwen, …) instead of typing slugs.
|
|
3
|
+
//
|
|
4
|
+
// - refreshOpenRouterModels(): live-fetch openrouter.ai/api/v1/models (freshest
|
|
5
|
+
// names + pricing), cache to disk. Falls back to Pi's built-in OR catalog.
|
|
6
|
+
// - openRouterModels() / findOpenRouterModel(): SYNC reads (disk cache, else Pi's
|
|
7
|
+
// built-in list) so cost estimation (getModel) can price any picked model.
|
|
8
|
+
import { piRuntime } from "./executor.js";
|
|
9
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
const CACHE = join(homedir(), ".projectinator", "openrouter-models.json");
|
|
13
|
+
let builtinMemo = null;
|
|
14
|
+
let diskMemo; // undefined = not read yet, null = no cache
|
|
15
|
+
/** Pi's built-in OpenRouter catalog — offline, names + pricing. Sync read of the memo;
|
|
16
|
+
* call warmBuiltinOpenRouterModels() once at startup to fill it (Pi's runtime is async). */
|
|
17
|
+
export function builtinOpenRouterModels() {
|
|
18
|
+
return builtinMemo ?? [];
|
|
19
|
+
}
|
|
20
|
+
/** Load Pi's built-in OpenRouter list into the memo. Safe to call repeatedly. */
|
|
21
|
+
export async function warmBuiltinOpenRouterModels() {
|
|
22
|
+
if (builtinMemo)
|
|
23
|
+
return builtinMemo;
|
|
24
|
+
try {
|
|
25
|
+
const runtime = await piRuntime();
|
|
26
|
+
builtinMemo = runtime
|
|
27
|
+
.getModels("openrouter")
|
|
28
|
+
.filter((m) => !!m.cost)
|
|
29
|
+
.map((m) => ({
|
|
30
|
+
id: m.id,
|
|
31
|
+
name: m.name ?? m.id,
|
|
32
|
+
contextWindow: m.contextWindow ?? 200_000,
|
|
33
|
+
cost: m.cost,
|
|
34
|
+
}))
|
|
35
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
builtinMemo = [];
|
|
39
|
+
}
|
|
40
|
+
return builtinMemo;
|
|
41
|
+
}
|
|
42
|
+
function readDiskCache() {
|
|
43
|
+
if (diskMemo !== undefined)
|
|
44
|
+
return diskMemo;
|
|
45
|
+
try {
|
|
46
|
+
if (existsSync(CACHE)) {
|
|
47
|
+
const parsed = JSON.parse(readFileSync(CACHE, "utf8"));
|
|
48
|
+
diskMemo = Array.isArray(parsed.models) && parsed.models.length ? parsed.models : null;
|
|
49
|
+
}
|
|
50
|
+
else
|
|
51
|
+
diskMemo = null;
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
diskMemo = null;
|
|
55
|
+
}
|
|
56
|
+
return diskMemo;
|
|
57
|
+
}
|
|
58
|
+
/** The list to show / price against: live-fetched disk cache if present, else Pi's built-in. */
|
|
59
|
+
export function openRouterModels() {
|
|
60
|
+
const cached = readDiskCache();
|
|
61
|
+
return cached && cached.length ? cached : builtinOpenRouterModels();
|
|
62
|
+
}
|
|
63
|
+
/** Look up one model's pricing/name by slug (cache first, then built-in). Sync. */
|
|
64
|
+
export function findOpenRouterModel(id) {
|
|
65
|
+
return openRouterModels().find((m) => m.id === id) ?? builtinOpenRouterModels().find((m) => m.id === id);
|
|
66
|
+
}
|
|
67
|
+
/** Map OpenRouter's API pricing (USD per token, strings) to our per-1M-token cost. */
|
|
68
|
+
function mapApiModel(m) {
|
|
69
|
+
const p = m.pricing ?? {};
|
|
70
|
+
const input = Number(p.prompt) * 1e6;
|
|
71
|
+
const output = Number(p.completion) * 1e6;
|
|
72
|
+
if (!Number.isFinite(input) || !Number.isFinite(output))
|
|
73
|
+
return null;
|
|
74
|
+
const cacheRead = p.input_cache_read != null ? Number(p.input_cache_read) * 1e6 : input * 0.1;
|
|
75
|
+
const cacheWrite = p.input_cache_write != null ? Number(p.input_cache_write) * 1e6 : input * 1.25;
|
|
76
|
+
return {
|
|
77
|
+
id: m.id,
|
|
78
|
+
name: m.name ?? m.id,
|
|
79
|
+
contextWindow: m.context_length ?? 200_000,
|
|
80
|
+
cost: { input, output, cacheRead, cacheWrite },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Live-fetch the catalog and cache it. Falls back to the current list on any failure. */
|
|
84
|
+
export async function refreshOpenRouterModels(timeoutMs = 8000) {
|
|
85
|
+
const ctrl = new AbortController();
|
|
86
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
87
|
+
try {
|
|
88
|
+
const res = await fetch("https://openrouter.ai/api/v1/models", { signal: ctrl.signal });
|
|
89
|
+
if (!res.ok)
|
|
90
|
+
return openRouterModels();
|
|
91
|
+
const json = (await res.json());
|
|
92
|
+
const list = (json.data ?? []).map(mapApiModel).filter((m) => m !== null)
|
|
93
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
94
|
+
if (list.length) {
|
|
95
|
+
try {
|
|
96
|
+
mkdirSync(join(homedir(), ".projectinator"), { recursive: true });
|
|
97
|
+
writeFileSync(CACHE, JSON.stringify({ fetchedAt: Date.now(), models: list }));
|
|
98
|
+
diskMemo = list;
|
|
99
|
+
}
|
|
100
|
+
catch { /* cache write is best-effort */ }
|
|
101
|
+
return list;
|
|
102
|
+
}
|
|
103
|
+
return openRouterModels();
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return openRouterModels();
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../src/openrouter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
// Phase 4 — the Orchestrator. Runs a whole backlog end to end.
|
|
2
|
+
//
|
|
3
|
+
// - Topologically orders tasks by dependsOn (respects design->code->test->deploy).
|
|
4
|
+
// - Threads handoff context: a task sees the final output of its dependencies.
|
|
5
|
+
// - Runs a Tester -> Developer feedback loop, bounded by maxFeedbackRounds.
|
|
6
|
+
// - Tracks cost and halts on the budget cap.
|
|
7
|
+
//
|
|
8
|
+
// The executor is INJECTED (RoleExecutor), so this entire control flow is testable
|
|
9
|
+
// offline with a fake — no model, no spend. The real Pi executor lives in roles.ts.
|
|
10
|
+
import { TaskLimitError, } from "./types.js";
|
|
11
|
+
import { route } from "./router.js";
|
|
12
|
+
import { REGISTRY } from "./registry.js";
|
|
13
|
+
/** Order tasks so every task comes after its dependencies. Throws on a cycle. */
|
|
14
|
+
export function toposort(tasks) {
|
|
15
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
16
|
+
const state = new Map();
|
|
17
|
+
const out = [];
|
|
18
|
+
const visit = (t, trail) => {
|
|
19
|
+
const s = state.get(t.id);
|
|
20
|
+
if (s === "done")
|
|
21
|
+
return;
|
|
22
|
+
if (s === "visiting") {
|
|
23
|
+
throw new Error(`Dependency cycle: ${[...trail, t.id].join(" -> ")}`);
|
|
24
|
+
}
|
|
25
|
+
state.set(t.id, "visiting");
|
|
26
|
+
for (const dep of t.dependsOn ?? []) {
|
|
27
|
+
const d = byId.get(dep);
|
|
28
|
+
if (d)
|
|
29
|
+
visit(d, [...trail, t.id]); // unknown deps already stripped by normalize
|
|
30
|
+
}
|
|
31
|
+
state.set(t.id, "done");
|
|
32
|
+
out.push(t);
|
|
33
|
+
};
|
|
34
|
+
for (const t of tasks)
|
|
35
|
+
visit(t, []);
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
/** Build handoff text from a task's dependency outcomes. */
|
|
39
|
+
function gatherContext(task, outcomes) {
|
|
40
|
+
const deps = task.dependsOn ?? [];
|
|
41
|
+
if (deps.length === 0)
|
|
42
|
+
return "";
|
|
43
|
+
const parts = [];
|
|
44
|
+
for (const depId of deps) {
|
|
45
|
+
const o = outcomes.get(depId);
|
|
46
|
+
if (!o)
|
|
47
|
+
continue;
|
|
48
|
+
const snippet = o.finalText.trim();
|
|
49
|
+
if (snippet)
|
|
50
|
+
parts.push(`### From ${depId} (${o.capability}):\n${snippet}`);
|
|
51
|
+
if (o.files.length)
|
|
52
|
+
parts.push(`### Files from ${depId}: ${o.files.join(", ")}`);
|
|
53
|
+
}
|
|
54
|
+
return parts.length ? `Context from upstream work:\n\n${parts.join("\n\n")}` : "";
|
|
55
|
+
}
|
|
56
|
+
function bugReport(bugs) {
|
|
57
|
+
return [
|
|
58
|
+
"The tester found these issues. Fix them, then stop:",
|
|
59
|
+
...bugs.map((b) => `- [${b.severity}]${b.file ? ` (${b.file})` : ""} ${b.description}`),
|
|
60
|
+
].join("\n");
|
|
61
|
+
}
|
|
62
|
+
export async function runBacklog(tasks, opts) {
|
|
63
|
+
const { policy, execute } = opts;
|
|
64
|
+
const registry = opts.registry ?? REGISTRY;
|
|
65
|
+
const byId = new Map(tasks.map((t) => [t.id, t]));
|
|
66
|
+
const outcomes = new Map();
|
|
67
|
+
const record = [];
|
|
68
|
+
let running = 0;
|
|
69
|
+
// Resume: replay prior outcomes so finished tasks are skipped and cost is restored.
|
|
70
|
+
// A failed attempt is billed but never "done" — it is rebuilt.
|
|
71
|
+
const seed = opts.seedOutcomes ?? [];
|
|
72
|
+
for (const o of seed) {
|
|
73
|
+
record.push(o);
|
|
74
|
+
if (o.error)
|
|
75
|
+
outcomes.delete(o.taskId); // last wins: a later failure voids an earlier pass
|
|
76
|
+
else
|
|
77
|
+
outcomes.set(o.taskId, o);
|
|
78
|
+
running += o.cost;
|
|
79
|
+
}
|
|
80
|
+
running = round2(running);
|
|
81
|
+
const wasDone = new Set(outcomes.keys());
|
|
82
|
+
let halted = false;
|
|
83
|
+
let haltReason;
|
|
84
|
+
const emit = opts.onProgress ?? (() => { });
|
|
85
|
+
const checkpoint = () => opts.onCheckpoint?.(record, round2(running));
|
|
86
|
+
let ordered;
|
|
87
|
+
try {
|
|
88
|
+
ordered = toposort(tasks);
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
92
|
+
emit({ type: "cycle_or_error", message });
|
|
93
|
+
return { outcomes: [], totalCost: 0, halted: true, haltReason: message };
|
|
94
|
+
}
|
|
95
|
+
const runOne = async (task, round, contextOverride, tierBump = 0) => {
|
|
96
|
+
const decision = route(task, { policy, registry, runningTotalBefore: running, tierBump });
|
|
97
|
+
emit({ type: "task_start", task, round, provider: decision.provider, modelId: decision.model.id });
|
|
98
|
+
const contextText = contextOverride ?? gatherContext(task, outcomes);
|
|
99
|
+
const meta = { taskId: task.id, capability: task.capability, provider: decision.provider, modelId: decision.model.id, round };
|
|
100
|
+
let outcome;
|
|
101
|
+
try {
|
|
102
|
+
outcome = { ...(await execute({ task, decision, contextText, round, limits: policy.taskLimits })), ...meta };
|
|
103
|
+
}
|
|
104
|
+
catch (e) {
|
|
105
|
+
if (!(e instanceof TaskLimitError))
|
|
106
|
+
throw e;
|
|
107
|
+
// Limit breach: bill what was spent, record the failure, halt the build.
|
|
108
|
+
outcome = { finalText: "", files: [], cost: e.costSoFar, error: e.message, ...meta };
|
|
109
|
+
running += outcome.cost;
|
|
110
|
+
record.push(outcome);
|
|
111
|
+
halted = true;
|
|
112
|
+
haltReason = `${task.id} aborted: ${e.message}`;
|
|
113
|
+
emit({ type: "task_failed", outcome, runningTotal: round2(running) });
|
|
114
|
+
return outcome;
|
|
115
|
+
}
|
|
116
|
+
running += outcome.cost;
|
|
117
|
+
outcomes.set(task.id, outcome);
|
|
118
|
+
record.push(outcome);
|
|
119
|
+
emit({ type: "task_done", outcome, runningTotal: round2(running) });
|
|
120
|
+
return outcome;
|
|
121
|
+
};
|
|
122
|
+
// One task's full lifecycle: run it, then its Reviewer/Tester -> Developer feedback loop.
|
|
123
|
+
const runTaskUnit = async (task) => {
|
|
124
|
+
let outcome = await runOne(task, 0);
|
|
125
|
+
const judges = task.capability === "test" || task.capability === "review";
|
|
126
|
+
if (!outcome.error && judges && outcome.verdict && !outcome.verdict.passed) {
|
|
127
|
+
// The code to fix: direct code deps, plus code deps reached through a review
|
|
128
|
+
// (a test depends on the review, which depends on the code).
|
|
129
|
+
const codeDeps = [];
|
|
130
|
+
for (const id of task.dependsOn ?? []) {
|
|
131
|
+
const dep = byId.get(id);
|
|
132
|
+
if (!dep)
|
|
133
|
+
continue;
|
|
134
|
+
if (dep.capability === "code")
|
|
135
|
+
codeDeps.push(dep);
|
|
136
|
+
else if (dep.capability === "review") {
|
|
137
|
+
for (const id2 of dep.dependsOn ?? []) {
|
|
138
|
+
const d2 = byId.get(id2);
|
|
139
|
+
if (d2?.capability === "code" && !codeDeps.includes(d2))
|
|
140
|
+
codeDeps.push(d2);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
let round = 1;
|
|
145
|
+
fix: while (outcome.verdict && !outcome.verdict.passed && round <= policy.maxFeedbackRounds) {
|
|
146
|
+
emit({ type: "test_failed", taskId: task.id, bugs: outcome.verdict.bugs.length, round });
|
|
147
|
+
const fixContext = bugReport(outcome.verdict.bugs);
|
|
148
|
+
for (const dep of codeDeps) {
|
|
149
|
+
emit({ type: "retry_dev", taskId: dep.id, forTest: task.id, round });
|
|
150
|
+
// Escalation: the developer that just failed review/test retries one tier up.
|
|
151
|
+
// The judge (review/test) stays on its routed model.
|
|
152
|
+
if ((await runOne(dep, round, fixContext, 1)).error)
|
|
153
|
+
break fix;
|
|
154
|
+
}
|
|
155
|
+
outcome = await runOne(task, round); // re-test
|
|
156
|
+
if (outcome.error)
|
|
157
|
+
break;
|
|
158
|
+
round++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
checkpoint();
|
|
162
|
+
};
|
|
163
|
+
// Human gate: pause once before the first development (code) task begins.
|
|
164
|
+
let gateDone = false;
|
|
165
|
+
const passGate = async () => {
|
|
166
|
+
if (gateDone || !opts.onGate)
|
|
167
|
+
return true;
|
|
168
|
+
gateDone = true;
|
|
169
|
+
emit({ type: "gate", stage: "design→dev" });
|
|
170
|
+
const decision = await opts.onGate({ stage: "design→dev" });
|
|
171
|
+
return decision !== "stop";
|
|
172
|
+
};
|
|
173
|
+
const concurrency = Math.max(1, Math.floor(opts.concurrency ?? 1));
|
|
174
|
+
// ---- sequential path (concurrency 1) — unchanged behavior ----
|
|
175
|
+
if (concurrency === 1) {
|
|
176
|
+
for (const task of ordered) {
|
|
177
|
+
if (wasDone.has(task.id)) {
|
|
178
|
+
emit({ type: "task_skipped", taskId: task.id });
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
if (task.capability === "code" && !(await passGate())) {
|
|
182
|
+
checkpoint();
|
|
183
|
+
return { outcomes: record, totalCost: round2(running), halted: true, haltReason: "stopped at review gate" };
|
|
184
|
+
}
|
|
185
|
+
const est = route(task, { policy, registry, runningTotalBefore: running });
|
|
186
|
+
if (est.overCap) {
|
|
187
|
+
emit({ type: "budget_halt", runningTotal: round2(running + est.cost), cap: policy.budgetCapUSD });
|
|
188
|
+
checkpoint();
|
|
189
|
+
return { outcomes: record, totalCost: round2(running), halted: true, haltReason: "budget cap" };
|
|
190
|
+
}
|
|
191
|
+
await runTaskUnit(task);
|
|
192
|
+
if (halted)
|
|
193
|
+
return { outcomes: record, totalCost: round2(running), halted, haltReason };
|
|
194
|
+
}
|
|
195
|
+
return { outcomes: record, totalCost: round2(running), halted: false };
|
|
196
|
+
}
|
|
197
|
+
// ---- parallel path (concurrency > 1) — ready-set scheduler ----
|
|
198
|
+
// JS is single-threaded, so mutations between awaits are atomic (no locks needed).
|
|
199
|
+
// Independent tasks (deps satisfied) run concurrently up to `concurrency`. A budget
|
|
200
|
+
// reservation on in-flight estimates prevents launches that could cross the cap.
|
|
201
|
+
const remaining = new Set(ordered.filter((t) => !wasDone.has(t.id)).map((t) => t.id));
|
|
202
|
+
for (const id of wasDone)
|
|
203
|
+
emit({ type: "task_skipped", taskId: id });
|
|
204
|
+
const inFlight = new Map();
|
|
205
|
+
let reserved = 0;
|
|
206
|
+
let codeInFlight = 0; // code tasks are serialized (they share files) even in parallel mode
|
|
207
|
+
let failure; // first task error; rethrown after in-flight work drains
|
|
208
|
+
const depsSatisfied = (t) => (t.dependsOn ?? []).every((d) => !remaining.has(d));
|
|
209
|
+
const readyTasks = () => ordered.filter((t) => remaining.has(t.id) && !inFlight.has(t.id) && depsSatisfied(t));
|
|
210
|
+
while (remaining.size > 0 && !halted) {
|
|
211
|
+
// Gate before any development task launches.
|
|
212
|
+
if (!gateDone && opts.onGate && readyTasks().some((t) => t.capability === "code")) {
|
|
213
|
+
if (!(await passGate())) {
|
|
214
|
+
halted = true;
|
|
215
|
+
haltReason = "stopped at review gate";
|
|
216
|
+
break;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
for (const task of readyTasks()) {
|
|
220
|
+
if (inFlight.size >= concurrency)
|
|
221
|
+
break;
|
|
222
|
+
// Only one code task builds at a time — they write to the shared workspace.
|
|
223
|
+
if (task.capability === "code" && codeInFlight >= 1)
|
|
224
|
+
continue;
|
|
225
|
+
// Reservations keep full precision: rounding each one to cents drops sub-cent
|
|
226
|
+
// estimates entirely, so a wide backlog of cheap tasks would under-reserve.
|
|
227
|
+
const est = route(task, { policy, registry, runningTotalBefore: running + reserved });
|
|
228
|
+
if (running + reserved + est.cost > policy.budgetCapUSD) {
|
|
229
|
+
if (inFlight.size === 0) {
|
|
230
|
+
emit({ type: "budget_halt", runningTotal: round2(running + est.cost), cap: policy.budgetCapUSD });
|
|
231
|
+
halted = true;
|
|
232
|
+
haltReason = "budget cap";
|
|
233
|
+
}
|
|
234
|
+
break; // wait for in-flight tasks to free budget/capacity
|
|
235
|
+
}
|
|
236
|
+
reserved += est.cost;
|
|
237
|
+
const cost = est.cost;
|
|
238
|
+
const isCode = task.capability === "code";
|
|
239
|
+
if (isCode)
|
|
240
|
+
codeInFlight++;
|
|
241
|
+
const settle = () => {
|
|
242
|
+
if (isCode)
|
|
243
|
+
codeInFlight--;
|
|
244
|
+
reserved -= cost;
|
|
245
|
+
remaining.delete(task.id);
|
|
246
|
+
inFlight.delete(task.id);
|
|
247
|
+
};
|
|
248
|
+
// A rejection must NOT escape through Promise.race below: that abandons the
|
|
249
|
+
// sibling promises, and their later rejections would have no handler attached
|
|
250
|
+
// (unhandled rejection -> the host process dies mid-build). Capture the first
|
|
251
|
+
// failure, stop launching, drain what's running, checkpoint, then rethrow.
|
|
252
|
+
const p = runTaskUnit(task).then(settle, (e) => {
|
|
253
|
+
settle();
|
|
254
|
+
halted = true;
|
|
255
|
+
haltReason ??= e instanceof Error ? e.message : String(e);
|
|
256
|
+
failure ??= e;
|
|
257
|
+
});
|
|
258
|
+
inFlight.set(task.id, p);
|
|
259
|
+
}
|
|
260
|
+
if (inFlight.size === 0)
|
|
261
|
+
break; // nothing running and nothing launchable -> done or halted
|
|
262
|
+
await Promise.race(inFlight.values());
|
|
263
|
+
}
|
|
264
|
+
await Promise.all(inFlight.values());
|
|
265
|
+
checkpoint();
|
|
266
|
+
if (failure)
|
|
267
|
+
throw failure;
|
|
268
|
+
return { outcomes: record, totalCost: round2(running), halted, haltReason };
|
|
269
|
+
}
|
|
270
|
+
function round2(n) {
|
|
271
|
+
return Math.round(n * 100) / 100;
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=orchestrator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orchestrator.js","sourceRoot":"","sources":["../src/orchestrator.ts"],"names":[],"mappings":"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"}
|