@orc-brain/core 1.0.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/LICENSE +21 -0
- package/dist/autoLoop.d.ts +70 -0
- package/dist/autoLoop.d.ts.map +1 -0
- package/dist/autoLoop.js +178 -0
- package/dist/autoLoop.js.map +1 -0
- package/dist/backpressure.d.ts +38 -0
- package/dist/backpressure.d.ts.map +1 -0
- package/dist/backpressure.js +103 -0
- package/dist/backpressure.js.map +1 -0
- package/dist/budgetTracker.d.ts +53 -0
- package/dist/budgetTracker.d.ts.map +1 -0
- package/dist/budgetTracker.js +93 -0
- package/dist/budgetTracker.js.map +1 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +107 -0
- package/dist/config.js.map +1 -0
- package/dist/escalation.d.ts +43 -0
- package/dist/escalation.d.ts.map +1 -0
- package/dist/escalation.js +81 -0
- package/dist/escalation.js.map +1 -0
- package/dist/eventBus.d.ts +30 -0
- package/dist/eventBus.d.ts.map +1 -0
- package/dist/eventBus.js +42 -0
- package/dist/eventBus.js.map +1 -0
- package/dist/goalJudge.d.ts +92 -0
- package/dist/goalJudge.d.ts.map +1 -0
- package/dist/goalJudge.js +254 -0
- package/dist/goalJudge.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +30 -0
- package/dist/index.js.map +1 -0
- package/dist/modelRouter.d.ts +40 -0
- package/dist/modelRouter.d.ts.map +1 -0
- package/dist/modelRouter.js +114 -0
- package/dist/modelRouter.js.map +1 -0
- package/dist/orchestrator.d.ts +242 -0
- package/dist/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator.js +975 -0
- package/dist/orchestrator.js.map +1 -0
- package/dist/pacing.d.ts +34 -0
- package/dist/pacing.d.ts.map +1 -0
- package/dist/pacing.js +49 -0
- package/dist/pacing.js.map +1 -0
- package/dist/planValidation.d.ts +24 -0
- package/dist/planValidation.d.ts.map +1 -0
- package/dist/planValidation.js +134 -0
- package/dist/planValidation.js.map +1 -0
- package/dist/planner.d.ts +70 -0
- package/dist/planner.d.ts.map +1 -0
- package/dist/planner.js +210 -0
- package/dist/planner.js.map +1 -0
- package/dist/plugins/host.d.ts +39 -0
- package/dist/plugins/host.d.ts.map +1 -0
- package/dist/plugins/host.js +109 -0
- package/dist/plugins/host.js.map +1 -0
- package/dist/plugins/registry.d.ts +71 -0
- package/dist/plugins/registry.d.ts.map +1 -0
- package/dist/plugins/registry.js +269 -0
- package/dist/plugins/registry.js.map +1 -0
- package/dist/plugins/secrets.d.ts +30 -0
- package/dist/plugins/secrets.d.ts.map +1 -0
- package/dist/plugins/secrets.js +99 -0
- package/dist/plugins/secrets.js.map +1 -0
- package/dist/preflight.d.ts +38 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +108 -0
- package/dist/preflight.js.map +1 -0
- package/dist/reporting.d.ts +41 -0
- package/dist/reporting.d.ts.map +1 -0
- package/dist/reporting.js +229 -0
- package/dist/reporting.js.map +1 -0
- package/dist/safety/denyRules.d.ts +57 -0
- package/dist/safety/denyRules.d.ts.map +1 -0
- package/dist/safety/denyRules.js +498 -0
- package/dist/safety/denyRules.js.map +1 -0
- package/dist/safety/envClassifier.d.ts +45 -0
- package/dist/safety/envClassifier.d.ts.map +1 -0
- package/dist/safety/envClassifier.js +99 -0
- package/dist/safety/envClassifier.js.map +1 -0
- package/dist/safety/index.d.ts +83 -0
- package/dist/safety/index.d.ts.map +1 -0
- package/dist/safety/index.js +184 -0
- package/dist/safety/index.js.map +1 -0
- package/dist/safety/limitSignals.d.ts +36 -0
- package/dist/safety/limitSignals.d.ts.map +1 -0
- package/dist/safety/limitSignals.js +79 -0
- package/dist/safety/limitSignals.js.map +1 -0
- package/dist/safety/paths.d.ts +32 -0
- package/dist/safety/paths.d.ts.map +1 -0
- package/dist/safety/paths.js +102 -0
- package/dist/safety/paths.js.map +1 -0
- package/dist/safety/redact.d.ts +17 -0
- package/dist/safety/redact.d.ts.map +1 -0
- package/dist/safety/redact.js +85 -0
- package/dist/safety/redact.js.map +1 -0
- package/dist/spawnEnv.d.ts +18 -0
- package/dist/spawnEnv.d.ts.map +1 -0
- package/dist/spawnEnv.js +43 -0
- package/dist/spawnEnv.js.map +1 -0
- package/dist/store/auditLog.d.ts +28 -0
- package/dist/store/auditLog.d.ts.map +1 -0
- package/dist/store/auditLog.js +48 -0
- package/dist/store/auditLog.js.map +1 -0
- package/dist/store/index.d.ts +140 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +648 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/schema.d.ts +17 -0
- package/dist/store/schema.d.ts.map +1 -0
- package/dist/store/schema.js +197 -0
- package/dist/store/schema.js.map +1 -0
- package/dist/system.d.ts +69 -0
- package/dist/system.d.ts.map +1 -0
- package/dist/system.js +135 -0
- package/dist/system.js.map +1 -0
- package/dist/workerManager.d.ts +80 -0
- package/dist/workerManager.d.ts.map +1 -0
- package/dist/workerManager.js +280 -0
- package/dist/workerManager.js.map +1 -0
- package/dist/worktrees.d.ts +74 -0
- package/dist/worktrees.d.ts.map +1 -0
- package/dist/worktrees.js +210 -0
- package/dist/worktrees.js.map +1 -0
- package/package.json +48 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid goal-satisfaction evaluator (.specs/active/autonomous-loop.md §3.4, G3;
|
|
3
|
+
* spec §4 success_criteria). Decides whether a run's success criteria are met so
|
|
4
|
+
* the autonomous controller can finish (or trigger another re-plan cycle),
|
|
5
|
+
* replacing the DAG-exhaustion-only check in `Orchestrator.maybeFinish`.
|
|
6
|
+
*
|
|
7
|
+
* Two passes:
|
|
8
|
+
* 1. Deterministic — criteria written as `` `$ <cmd>` `` are shell checks. Each
|
|
9
|
+
* is routed through the SAME {@link SafetyLayer} as a worker (Golden Rule 2,
|
|
10
|
+
* R3): a denied command is NEVER executed — it is recorded as unmet and
|
|
11
|
+
* audited. An allowed command runs; exit 0 = met.
|
|
12
|
+
* 2. Judge — remaining prose criteria go to an Opus plan-mode (read-only)
|
|
13
|
+
* session that inspects the repo and returns met/unmet + rationale.
|
|
14
|
+
*
|
|
15
|
+
* Deterministic failures are authoritative: the judge can never flip a failing
|
|
16
|
+
* command check to satisfied (AC4).
|
|
17
|
+
*/
|
|
18
|
+
import { spawnSync } from "node:child_process";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { SafetyLayer } from "./safety/index.js";
|
|
21
|
+
import { redactValue } from "./safety/redact.js";
|
|
22
|
+
import { buildSpawnEnv } from "./spawnEnv.js";
|
|
23
|
+
/**
|
|
24
|
+
* Parses an executable criterion (Open Decision 2): a description of the form
|
|
25
|
+
* `` `$ <cmd>` `` or `$ <cmd>` marks a deterministic shell check. Returns the
|
|
26
|
+
* command, or null when the criterion is prose for the judge.
|
|
27
|
+
*/
|
|
28
|
+
export function parseCommandCriterion(description) {
|
|
29
|
+
const m = description.trim().match(/^`?\s*\$\s+([\s\S]+?)\s*`?$/);
|
|
30
|
+
return m ? m[1].trim() : null;
|
|
31
|
+
}
|
|
32
|
+
/** Builds the judge prompt for prose criteria (read-only, plan-mode session). */
|
|
33
|
+
export function buildJudgePrompt(title, objective, criteria) {
|
|
34
|
+
const list = criteria.map((c, i) => `${i}. ${c}`).join("\n");
|
|
35
|
+
return [
|
|
36
|
+
"You are the acceptance judge of a local orchestrator. Decide, for each",
|
|
37
|
+
"success criterion below, whether it is MET by the CURRENT state of the",
|
|
38
|
+
"repository. You are in read-only mode: inspect with Read/Glob/Grep; do not",
|
|
39
|
+
"edit anything or run commands. Be strict — if you cannot verify a criterion,",
|
|
40
|
+
"mark it not met.",
|
|
41
|
+
"",
|
|
42
|
+
`# Goal: ${title}`,
|
|
43
|
+
"## Objective",
|
|
44
|
+
objective,
|
|
45
|
+
"",
|
|
46
|
+
"## Criteria (index. text)",
|
|
47
|
+
list,
|
|
48
|
+
"",
|
|
49
|
+
"Return JSON matching the schema: an object with a `results` array holding,",
|
|
50
|
+
"for each criterion index in order, `{ met: boolean, rationale: string }`.",
|
|
51
|
+
].join("\n");
|
|
52
|
+
}
|
|
53
|
+
/** JSON schema for the judge's structured output (hand-written, no validator dep). */
|
|
54
|
+
const JUDGE_SCHEMA = {
|
|
55
|
+
type: "object",
|
|
56
|
+
properties: {
|
|
57
|
+
results: {
|
|
58
|
+
type: "array",
|
|
59
|
+
items: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
met: { type: "boolean" },
|
|
63
|
+
rationale: { type: "string" },
|
|
64
|
+
},
|
|
65
|
+
required: ["met", "rationale"],
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
required: ["results"],
|
|
70
|
+
};
|
|
71
|
+
/** Default runner: real shell via spawnSync, output discarded, 2-min ceiling. */
|
|
72
|
+
const defaultRunCommand = (command, cwd) => {
|
|
73
|
+
const res = spawnSync(command, {
|
|
74
|
+
cwd,
|
|
75
|
+
shell: true,
|
|
76
|
+
stdio: "ignore",
|
|
77
|
+
timeout: 120_000,
|
|
78
|
+
});
|
|
79
|
+
return { exitCode: res.status ?? 1 };
|
|
80
|
+
};
|
|
81
|
+
/** The hybrid evaluator (autonomous-loop.md §3.4). */
|
|
82
|
+
export class GoalJudge {
|
|
83
|
+
config;
|
|
84
|
+
safety;
|
|
85
|
+
audit;
|
|
86
|
+
runCommand;
|
|
87
|
+
judgeQueryFn;
|
|
88
|
+
constructor(config, safety, audit, opts = {}) {
|
|
89
|
+
this.config = config;
|
|
90
|
+
this.safety = safety;
|
|
91
|
+
this.audit = audit;
|
|
92
|
+
this.runCommand = opts.runCommand ?? defaultRunCommand;
|
|
93
|
+
this.judgeQueryFn = opts.judgeQueryFn;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Evaluates a run's criteria (autonomous-loop.md §3.4). Deterministic command
|
|
97
|
+
* checks run first through the safety layer; remaining prose criteria go to
|
|
98
|
+
* the judge. `satisfied` is true only when EVERY criterion is met.
|
|
99
|
+
*/
|
|
100
|
+
async evaluate(input) {
|
|
101
|
+
const ctx = {
|
|
102
|
+
run_id: input.run_id,
|
|
103
|
+
task_id: null,
|
|
104
|
+
environment: input.environment,
|
|
105
|
+
cwd: input.cwd,
|
|
106
|
+
path_allowlist: [join(input.cwd, "**")],
|
|
107
|
+
path_denylist: [],
|
|
108
|
+
};
|
|
109
|
+
const unmet = [];
|
|
110
|
+
const notes = [];
|
|
111
|
+
const prose = [];
|
|
112
|
+
// Pass 1 — deterministic, safety-gated shell checks (R3).
|
|
113
|
+
for (const desc of input.criteria) {
|
|
114
|
+
const command = parseCommandCriterion(desc);
|
|
115
|
+
if (command === null) {
|
|
116
|
+
prose.push(desc);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const decision = this.safety.evaluateToolCall("Bash", { command }, ctx);
|
|
120
|
+
if (decision.verdict === "deny" ||
|
|
121
|
+
decision.verdict === "require_approval") {
|
|
122
|
+
// Blocked commands are NEVER executed (Golden Rule 2/3) — audited + unmet.
|
|
123
|
+
this.auditDenied(ctx, command, decision);
|
|
124
|
+
unmet.push(desc);
|
|
125
|
+
notes.push(`"${desc}" blocked by safety rule ` +
|
|
126
|
+
`${decision.match?.rule_id ?? "policy"} — not executed`);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const { exitCode } = this.runCommand(command, input.cwd);
|
|
130
|
+
if (exitCode !== 0) {
|
|
131
|
+
unmet.push(desc);
|
|
132
|
+
notes.push(`"${desc}" exited ${exitCode}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Pass 2 — judge the prose criteria (only if any and a session is available).
|
|
136
|
+
if (prose.length > 0) {
|
|
137
|
+
const results = await this.judgeProse(input, prose);
|
|
138
|
+
prose.forEach((desc, i) => {
|
|
139
|
+
const r = results[i];
|
|
140
|
+
if (!r || !r.met) {
|
|
141
|
+
unmet.push(desc);
|
|
142
|
+
notes.push(`"${desc}": ${r?.rationale ?? "judge did not confirm"}`);
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
satisfied: unmet.length === 0,
|
|
148
|
+
unmet,
|
|
149
|
+
rationale: notes.join("; ") || "all criteria met",
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/** Records a blocked criterion command in the audit log (§8.6, AC5). */
|
|
153
|
+
auditDenied(ctx, command, decision) {
|
|
154
|
+
const event = {
|
|
155
|
+
ts: new Date().toISOString(),
|
|
156
|
+
run_id: ctx.run_id,
|
|
157
|
+
task_id: ctx.task_id,
|
|
158
|
+
session_id: null,
|
|
159
|
+
kind: "hook_block",
|
|
160
|
+
tool_name: "Bash",
|
|
161
|
+
tool_input_hash: null,
|
|
162
|
+
tool_input: redactValue({ command }),
|
|
163
|
+
decision: "deny",
|
|
164
|
+
rule_id: decision.match?.rule_id ?? "policy",
|
|
165
|
+
detail: {
|
|
166
|
+
source: "goal_judge_criterion",
|
|
167
|
+
reason: decision.match?.reason ?? "policy",
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
this.audit.record(event);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Runs the read-only judge session over prose criteria and returns one result
|
|
174
|
+
* per criterion (in order). If no judge session is wired, or the session
|
|
175
|
+
* yields nothing usable, every prose criterion defaults to NOT met (strict).
|
|
176
|
+
*/
|
|
177
|
+
async judgeProse(input, criteria) {
|
|
178
|
+
if (!this.judgeQueryFn)
|
|
179
|
+
return criteria.map(() => notMet("no judge session"));
|
|
180
|
+
const options = {
|
|
181
|
+
cwd: input.cwd,
|
|
182
|
+
model: this.config.planner.model,
|
|
183
|
+
permissionMode: "plan",
|
|
184
|
+
allowedTools: this.config.planner.allowed_tools,
|
|
185
|
+
maxTurns: this.config.planner.max_turns,
|
|
186
|
+
outputFormat: { type: "json_schema", schema: JUDGE_SCHEMA },
|
|
187
|
+
env: buildSpawnEnv(),
|
|
188
|
+
};
|
|
189
|
+
const prompt = buildJudgePrompt(input.title, input.objective, criteria);
|
|
190
|
+
const q = this.judgeQueryFn({ prompt, options });
|
|
191
|
+
let structured;
|
|
192
|
+
let finalText = "";
|
|
193
|
+
for await (const message of q) {
|
|
194
|
+
if (message.type === "assistant") {
|
|
195
|
+
for (const block of (message.message?.content ?? [])) {
|
|
196
|
+
const b = block;
|
|
197
|
+
if (b.type === "text" && b.text)
|
|
198
|
+
finalText += b.text;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else if (message.type === "result") {
|
|
202
|
+
if (message.subtype !== "success") {
|
|
203
|
+
return criteria.map(() => notMet(`judge session failed`));
|
|
204
|
+
}
|
|
205
|
+
structured = message
|
|
206
|
+
.structured_output;
|
|
207
|
+
finalText = message.result ?? finalText;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const parsed = parseJudgeResults(structured, finalText);
|
|
211
|
+
// Missing entries default to not met (strict): align by index.
|
|
212
|
+
return criteria.map((_, i) => parsed[i] ?? notMet("judge omitted this criterion"));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function notMet(rationale) {
|
|
216
|
+
return { met: false, rationale };
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Extracts judge results from an SDK stream: prefers `structured_output.results`,
|
|
220
|
+
* falls back to a fenced/loose JSON object with a `results` array. Defensive
|
|
221
|
+
* (no validation library): anything unparseable yields an empty array so callers
|
|
222
|
+
* treat all criteria as not met.
|
|
223
|
+
*/
|
|
224
|
+
export function parseJudgeResults(structured, finalText) {
|
|
225
|
+
const fromObj = (obj) => {
|
|
226
|
+
const results = obj?.results;
|
|
227
|
+
if (!Array.isArray(results))
|
|
228
|
+
return [];
|
|
229
|
+
return results.map((r) => ({
|
|
230
|
+
met: r?.met === true,
|
|
231
|
+
rationale: typeof r?.rationale === "string"
|
|
232
|
+
? r.rationale
|
|
233
|
+
: "",
|
|
234
|
+
}));
|
|
235
|
+
};
|
|
236
|
+
if (structured && typeof structured === "object") {
|
|
237
|
+
const r = fromObj(structured);
|
|
238
|
+
if (r.length)
|
|
239
|
+
return r;
|
|
240
|
+
}
|
|
241
|
+
const fenced = finalText.match(/```(?:json)?\s*([\s\S]*?)```/i);
|
|
242
|
+
const raw = fenced ? fenced[1] : finalText;
|
|
243
|
+
const start = raw.indexOf("{");
|
|
244
|
+
const end = raw.lastIndexOf("}");
|
|
245
|
+
if (start === -1 || end === -1 || end < start)
|
|
246
|
+
return [];
|
|
247
|
+
try {
|
|
248
|
+
return fromObj(JSON.parse(raw.slice(start, end + 1)));
|
|
249
|
+
}
|
|
250
|
+
catch {
|
|
251
|
+
return [];
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=goalJudge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"goalJudge.js","sourceRoot":"","sources":["../src/goalJudge.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Public surface of the orc-brain orchestrator core. See specs/001-orchestrator-spec.md (source of truth). */
|
|
2
|
+
export * from "./orchestrator.js";
|
|
3
|
+
export * from "./planner.js";
|
|
4
|
+
export * from "./planValidation.js";
|
|
5
|
+
export * from "./goalJudge.js";
|
|
6
|
+
export * from "./autoLoop.js";
|
|
7
|
+
export * from "./escalation.js";
|
|
8
|
+
export * from "./reporting.js";
|
|
9
|
+
export * from "./backpressure.js";
|
|
10
|
+
export * from "./workerManager.js";
|
|
11
|
+
export * from "./modelRouter.js";
|
|
12
|
+
export * from "./budgetTracker.js";
|
|
13
|
+
export * from "./eventBus.js";
|
|
14
|
+
export * from "./spawnEnv.js";
|
|
15
|
+
export * from "./worktrees.js";
|
|
16
|
+
export * from "./pacing.js";
|
|
17
|
+
export * from "./config.js";
|
|
18
|
+
export * from "./system.js";
|
|
19
|
+
export * from "./preflight.js";
|
|
20
|
+
export * from "./safety/index.js";
|
|
21
|
+
export * from "./safety/envClassifier.js";
|
|
22
|
+
export * from "./safety/denyRules.js";
|
|
23
|
+
export * from "./safety/limitSignals.js";
|
|
24
|
+
export * from "./safety/redact.js";
|
|
25
|
+
export * from "./safety/paths.js";
|
|
26
|
+
export * from "./store/index.js";
|
|
27
|
+
export * from "./plugins/secrets.js";
|
|
28
|
+
export * from "./plugins/host.js";
|
|
29
|
+
export * from "./plugins/registry.js";
|
|
30
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAE/G,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Public surface of the orc-brain orchestrator core. See specs/001-orchestrator-spec.md (source of truth). */
|
|
2
|
+
export * from "./orchestrator.js";
|
|
3
|
+
export * from "./planner.js";
|
|
4
|
+
export * from "./planValidation.js";
|
|
5
|
+
export * from "./goalJudge.js";
|
|
6
|
+
export * from "./autoLoop.js";
|
|
7
|
+
export * from "./escalation.js";
|
|
8
|
+
export * from "./reporting.js";
|
|
9
|
+
export * from "./backpressure.js";
|
|
10
|
+
export * from "./workerManager.js";
|
|
11
|
+
export * from "./modelRouter.js";
|
|
12
|
+
export * from "./budgetTracker.js";
|
|
13
|
+
export * from "./eventBus.js";
|
|
14
|
+
export * from "./spawnEnv.js";
|
|
15
|
+
export * from "./worktrees.js";
|
|
16
|
+
export * from "./pacing.js";
|
|
17
|
+
export * from "./config.js";
|
|
18
|
+
export * from "./system.js";
|
|
19
|
+
export * from "./preflight.js";
|
|
20
|
+
export * from "./safety/index.js";
|
|
21
|
+
export * from "./safety/envClassifier.js";
|
|
22
|
+
export * from "./safety/denyRules.js";
|
|
23
|
+
export * from "./safety/limitSignals.js";
|
|
24
|
+
export * from "./safety/redact.js";
|
|
25
|
+
export * from "./safety/paths.js";
|
|
26
|
+
export * from "./store/index.js";
|
|
27
|
+
export * from "./plugins/secrets.js";
|
|
28
|
+
export * from "./plugins/host.js";
|
|
29
|
+
export * from "./plugins/registry.js";
|
|
30
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAE/G,cAAc,mBAAmB,CAAC;AAClC,cAAc,cAAc,CAAC;AAC7B,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AACnC,cAAc,kBAAkB,CAAC;AACjC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,mBAAmB,CAAC;AAClC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,sBAAsB,CAAC;AACrC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model router (§6): deterministic, rule-based, explainable, reproducible. No
|
|
3
|
+
* LLM in the routing decision itself — the Planner already labelled each task
|
|
4
|
+
* with `task_type`. Every decision carries the rule that fired and a reason,
|
|
5
|
+
* emitted as a `routing_decision` audit event by the caller.
|
|
6
|
+
*/
|
|
7
|
+
import type { BudgetState, ModelName, ModelTier, RoutingConfig, TaskType } from "@orc-brain/shared";
|
|
8
|
+
/** Output of a routing decision. */
|
|
9
|
+
export interface RoutingDecision {
|
|
10
|
+
model: ModelName;
|
|
11
|
+
rule_id: string;
|
|
12
|
+
reason: string;
|
|
13
|
+
/** Set when R7 routed around a rate-limited model. */
|
|
14
|
+
degraded?: boolean;
|
|
15
|
+
/** Set when R5 escalated after repeated failures. */
|
|
16
|
+
escalated_from?: ModelName;
|
|
17
|
+
}
|
|
18
|
+
/** Dynamic signals that influence routing beyond the static table. */
|
|
19
|
+
export interface RoutingContext {
|
|
20
|
+
/** Current budget backpressure state (R6). */
|
|
21
|
+
budget_state?: BudgetState;
|
|
22
|
+
/** Models currently quarantined by rate limits (R7). */
|
|
23
|
+
quarantined?: ModelName[];
|
|
24
|
+
/** Attempt count for escalation (R5). */
|
|
25
|
+
attempt?: number;
|
|
26
|
+
/** Model used on the previous attempt, for escalation bumps (R5). */
|
|
27
|
+
previous_model?: ModelName;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Computes the model for a task (§6). Rule precedence:
|
|
31
|
+
* R-F forced model → R1 scope pin → R5 escalation → R6 budget warn cap →
|
|
32
|
+
* R7 rate-limit reroute → R2–R4 static table.
|
|
33
|
+
*/
|
|
34
|
+
export declare function routeModel(input: {
|
|
35
|
+
task_type: TaskType;
|
|
36
|
+
model_tier: ModelTier;
|
|
37
|
+
routing: RoutingConfig;
|
|
38
|
+
ctx?: RoutingContext;
|
|
39
|
+
}): RoutingDecision;
|
|
40
|
+
//# sourceMappingURL=modelRouter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelRouter.d.ts","sourceRoot":"","sources":["../src/modelRouter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,SAAS,EACT,aAAa,EACb,QAAQ,EACT,MAAM,mBAAmB,CAAC;AAE3B,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,sDAAsD;IACtD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qDAAqD;IACrD,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,YAAY,CAAC,EAAE,WAAW,CAAC;IAC3B,wDAAwD;IACxD,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC;IAC1B,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,cAAc,CAAC,EAAE,SAAS,CAAC;CAC5B;AA8CD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE;IAChC,SAAS,EAAE,QAAQ,CAAC;IACpB,UAAU,EAAE,SAAS,CAAC;IACtB,OAAO,EAAE,aAAa,CAAC;IACvB,GAAG,CAAC,EAAE,cAAc,CAAC;CACtB,GAAG,eAAe,CAsElB"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model router (§6): deterministic, rule-based, explainable, reproducible. No
|
|
3
|
+
* LLM in the routing decision itself — the Planner already labelled each task
|
|
4
|
+
* with `task_type`. Every decision carries the rule that fired and a reason,
|
|
5
|
+
* emitted as a `routing_decision` audit event by the caller.
|
|
6
|
+
*/
|
|
7
|
+
const TIER_ORDER = ["haiku", "sonnet", "opus"];
|
|
8
|
+
/** Bumps a model up one tier (haiku→sonnet→opus), capped at opus. */
|
|
9
|
+
function bumpTier(model) {
|
|
10
|
+
const idx = TIER_ORDER.indexOf(model);
|
|
11
|
+
if (idx < 0)
|
|
12
|
+
return "opus";
|
|
13
|
+
return TIER_ORDER[Math.min(idx + 1, TIER_ORDER.length - 1)];
|
|
14
|
+
}
|
|
15
|
+
/** Routes around a quarantined model (R7): opus→sonnet→haiku. */
|
|
16
|
+
function routeAround(model, quarantined) {
|
|
17
|
+
let m = model;
|
|
18
|
+
const order = ["opus", "sonnet", "haiku"];
|
|
19
|
+
let guard = 0;
|
|
20
|
+
while (quarantined.includes(m) && guard++ < order.length) {
|
|
21
|
+
const idx = order.indexOf(m);
|
|
22
|
+
m = order[Math.min(idx + 1, order.length - 1)];
|
|
23
|
+
if (idx === order.length - 1)
|
|
24
|
+
break; // haiku is the floor
|
|
25
|
+
}
|
|
26
|
+
return m;
|
|
27
|
+
}
|
|
28
|
+
/** Maps a scope tier to a concrete model, or null for `auto`. */
|
|
29
|
+
function tierToModel(tier) {
|
|
30
|
+
return tier === "auto" ? null : tier;
|
|
31
|
+
}
|
|
32
|
+
/** Selects a model from the static table for a task type (§6 R2–R4). */
|
|
33
|
+
function fromTable(taskType, routing) {
|
|
34
|
+
for (const rule of routing.rules) {
|
|
35
|
+
if (rule.when.task_types?.includes(taskType)) {
|
|
36
|
+
return { model: rule.model, rule_id: rule.id, reason: rule.description };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
model: routing.default_model,
|
|
41
|
+
rule_id: "R0",
|
|
42
|
+
reason: "default model (no rule matched)",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Computes the model for a task (§6). Rule precedence:
|
|
47
|
+
* R-F forced model → R1 scope pin → R5 escalation → R6 budget warn cap →
|
|
48
|
+
* R7 rate-limit reroute → R2–R4 static table.
|
|
49
|
+
*/
|
|
50
|
+
export function routeModel(input) {
|
|
51
|
+
const { task_type, model_tier, routing } = input;
|
|
52
|
+
const ctx = input.ctx ?? {};
|
|
53
|
+
// R-F: an operator-forced model wins unconditionally — "always use it" means
|
|
54
|
+
// no escalation, budget cap, or quarantine reroute may move off it.
|
|
55
|
+
if (routing.force_model) {
|
|
56
|
+
return {
|
|
57
|
+
model: routing.force_model,
|
|
58
|
+
rule_id: "R-F",
|
|
59
|
+
reason: `force_model configured: always ${routing.force_model}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
const pinned = tierToModel(model_tier);
|
|
63
|
+
// R1: scope pin wins for the base choice, but escalation/reroute can still act.
|
|
64
|
+
let base = pinned ?? fromTable(task_type, routing).model;
|
|
65
|
+
const baseRuleId = pinned ? "R1" : fromTable(task_type, routing).rule_id;
|
|
66
|
+
const baseReason = pinned
|
|
67
|
+
? `scope pinned model_tier=${model_tier}`
|
|
68
|
+
: fromTable(task_type, routing).reason;
|
|
69
|
+
let decision = {
|
|
70
|
+
model: base,
|
|
71
|
+
rule_id: baseRuleId,
|
|
72
|
+
reason: baseReason,
|
|
73
|
+
};
|
|
74
|
+
// R5: escalation after ≥2 attempts on the current model, bump one tier.
|
|
75
|
+
if ((ctx.attempt ?? 0) >= 2 && ctx.previous_model) {
|
|
76
|
+
const bumped = bumpTier(ctx.previous_model);
|
|
77
|
+
if (bumped !== ctx.previous_model) {
|
|
78
|
+
decision = {
|
|
79
|
+
model: bumped,
|
|
80
|
+
rule_id: "R5",
|
|
81
|
+
reason: `escalated after ${ctx.attempt} attempts`,
|
|
82
|
+
escalated_from: ctx.previous_model,
|
|
83
|
+
};
|
|
84
|
+
base = bumped;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// R6: budget warn caps new dispatches at sonnet unless R1 pinned opus.
|
|
88
|
+
if (ctx.budget_state === "warn" && !(pinned === "opus")) {
|
|
89
|
+
if (base === "opus") {
|
|
90
|
+
decision = {
|
|
91
|
+
...decision,
|
|
92
|
+
model: "sonnet",
|
|
93
|
+
rule_id: "R6",
|
|
94
|
+
reason: "budget warn: capped at sonnet",
|
|
95
|
+
};
|
|
96
|
+
base = "sonnet";
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// R7: quarantined model → route around, flag degraded.
|
|
100
|
+
const quarantined = ctx.quarantined ?? [];
|
|
101
|
+
if (quarantined.includes(base)) {
|
|
102
|
+
const rerouted = routeAround(base, quarantined);
|
|
103
|
+
if (rerouted !== base) {
|
|
104
|
+
decision = {
|
|
105
|
+
model: rerouted,
|
|
106
|
+
rule_id: "R7",
|
|
107
|
+
reason: `${base} rate-limited: routed around`,
|
|
108
|
+
degraded: true,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return decision;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=modelRouter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modelRouter.js","sourceRoot":"","sources":["../src/modelRouter.ts"],"names":[],"mappings":"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"}
|