@agentskit/harness 0.6.0 → 0.7.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/CHANGELOG.md +7 -0
- package/capabilities/public-surface.json +102 -76
- package/dist/cli.js +655 -159
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +205 -73
- package/dist/index.js +661 -165
- package/dist/index.js.map +1 -1
- package/docs/LOOP.md +14 -0
- package/loop.config.example.yaml +9 -0
- package/package.json +1 -1
- package/release/manifest.json +1 -1
- package/release/notes.md +4 -0
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolve, dirname, join, relative,
|
|
1
|
+
import { resolve, dirname, join, relative, isAbsolute, delimiter, basename, extname, sep } from 'path';
|
|
2
2
|
import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
|
|
3
3
|
import { existsSync, mkdirSync, openSync, writeSync, appendFileSync, closeSync, unlinkSync, readFileSync, writeFileSync, statSync, readdirSync, mkdtempSync, renameSync, rmSync } from 'fs';
|
|
4
4
|
import { execFile, spawn, execFileSync } from 'child_process';
|
|
@@ -1266,15 +1266,174 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
|
|
|
1266
1266
|
return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString(), assurance: "contract-tested", telemetry };
|
|
1267
1267
|
}
|
|
1268
1268
|
});
|
|
1269
|
+
var executable = (path) => {
|
|
1270
|
+
try {
|
|
1271
|
+
return statSync(path).isFile();
|
|
1272
|
+
} catch {
|
|
1273
|
+
return false;
|
|
1274
|
+
}
|
|
1275
|
+
};
|
|
1276
|
+
var findExecutable = (name2, env = process.env, platform = process.platform) => {
|
|
1277
|
+
if (typeof name2 !== "string" || !name2.trim()) return null;
|
|
1278
|
+
if (isAbsolute(name2) || name2.includes("/") || name2.includes("\\")) return existsSync(name2) && executable(name2) ? name2 : null;
|
|
1279
|
+
const extensions = platform === "win32" ? (env["PATHEXT"] ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean) : [""];
|
|
1280
|
+
for (const dir of (env["PATH"] ?? "").split(delimiter).filter(Boolean)) {
|
|
1281
|
+
for (const extension of extensions) {
|
|
1282
|
+
const candidate = join(dir, `${name2}${extension}`);
|
|
1283
|
+
if (executable(candidate)) return candidate;
|
|
1284
|
+
}
|
|
1285
|
+
if (platform === "win32" && executable(join(dir, name2))) return join(dir, name2);
|
|
1286
|
+
}
|
|
1287
|
+
return null;
|
|
1288
|
+
};
|
|
1289
|
+
var parseJsonEnvelope = (stdout) => {
|
|
1290
|
+
const trimmed = stdout.trim();
|
|
1291
|
+
if (!trimmed) return null;
|
|
1292
|
+
let parsed;
|
|
1293
|
+
try {
|
|
1294
|
+
parsed = JSON.parse(trimmed);
|
|
1295
|
+
} catch {
|
|
1296
|
+
return null;
|
|
1297
|
+
}
|
|
1298
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
1299
|
+
const record3 = parsed;
|
|
1300
|
+
if (typeof record3.ok !== "boolean") return null;
|
|
1301
|
+
const error = typeof record3.error === "string" ? record3.error : typeof record3.error === "object" && record3.error !== null && typeof record3.error.message === "string" ? record3.error.message : void 0;
|
|
1302
|
+
return { ok: record3.ok, result: record3.result, ...error === void 0 ? {} : { error } };
|
|
1303
|
+
};
|
|
1269
1304
|
|
|
1270
|
-
// src/adapters/
|
|
1305
|
+
// src/adapters/providers.ts
|
|
1271
1306
|
var isRecord5 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1307
|
+
var iso = (value) => typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : typeof value === "string" && !Number.isNaN(Date.parse(value)) ? new Date(value).toISOString() : null;
|
|
1308
|
+
var parseUsageWindows = (entry) => {
|
|
1309
|
+
if (!isRecord5(entry)) return [];
|
|
1310
|
+
return Object.entries(entry).flatMap(([kind, value]) => {
|
|
1311
|
+
if (!isRecord5(value) || typeof value["usedPercent"] !== "number") return [];
|
|
1312
|
+
return [{ kind, usedPercent: value["usedPercent"], windowMinutes: typeof value["windowMinutes"] === "number" ? value["windowMinutes"] : null, resetsAt: iso(value["resetsAt"]) }];
|
|
1313
|
+
});
|
|
1314
|
+
};
|
|
1315
|
+
var parseProviderUsage = (accountList, usageKey, exhaustedPercent = 100) => {
|
|
1316
|
+
const result = isRecord5(accountList) ? accountList : {};
|
|
1317
|
+
const rateLimits = isRecord5(result["rateLimits"]) ? result["rateLimits"] : {};
|
|
1318
|
+
const entry = isRecord5(rateLimits[usageKey]) ? rateLimits[usageKey] : null;
|
|
1319
|
+
const account = isRecord5(result[usageKey]) ? result[usageKey] : null;
|
|
1320
|
+
const systemDefault = account && isRecord5(account["systemDefault"]) ? account["systemDefault"] : null;
|
|
1321
|
+
const accounts = account && Array.isArray(account["accounts"]) ? account["accounts"] : [];
|
|
1322
|
+
const hasAuth = systemDefault ? systemDefault["hasAuth"] === true : accounts.length ? true : null;
|
|
1323
|
+
if (!entry) return { status: "unknown", error: null, windows: [], exhausted: false, resetsAt: null, hasAuth };
|
|
1324
|
+
const windows = parseUsageWindows(entry);
|
|
1325
|
+
const exhaustedWindows = windows.filter((window) => window.usedPercent >= exhaustedPercent);
|
|
1326
|
+
const resetsAt = exhaustedWindows.map((window) => window.resetsAt).filter((value) => Boolean(value)).sort()[0] ?? null;
|
|
1327
|
+
return {
|
|
1328
|
+
status: entry["status"] === "ok" ? "ok" : entry["status"] === "unavailable" ? "unavailable" : "unknown",
|
|
1329
|
+
error: typeof entry["error"] === "string" ? entry["error"] : null,
|
|
1330
|
+
windows,
|
|
1331
|
+
exhausted: exhaustedWindows.length > 0,
|
|
1332
|
+
resetsAt,
|
|
1333
|
+
hasAuth
|
|
1334
|
+
};
|
|
1335
|
+
};
|
|
1336
|
+
var authStatusFor = (spec, usage2, env) => {
|
|
1337
|
+
const hasEnvKey = spec.envKeys.some((key) => Boolean(env[key]?.trim()));
|
|
1338
|
+
if (spec.auth === "api-key") return hasEnvKey ? "ok" : "missing";
|
|
1339
|
+
if (spec.auth === "subscription") return usage2.hasAuth === true || usage2.status === "ok" ? "ok" : usage2.hasAuth === false ? "missing" : hasEnvKey || usage2.status === "unknown" ? "ok" : "unknown";
|
|
1340
|
+
return hasEnvKey || usage2.status === "ok" ? "ok" : "unknown";
|
|
1341
|
+
};
|
|
1342
|
+
var runProbe = async (spec, binary, runner, timeoutMs) => {
|
|
1343
|
+
if (!spec.probe || !runner) return "skipped";
|
|
1344
|
+
const [head, ...rest] = spec.probe;
|
|
1345
|
+
const argv = [head === spec.bin ? binary : head ?? binary, ...rest];
|
|
1346
|
+
try {
|
|
1347
|
+
const outcome = await runner.run(argv, { timeoutMs });
|
|
1348
|
+
return outcome.code === 0 && !outcome.timedOut ? "passed" : "failed";
|
|
1349
|
+
} catch {
|
|
1350
|
+
return "failed";
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
var detectProviders = async (input) => {
|
|
1354
|
+
const env = input.env ?? process.env;
|
|
1355
|
+
const platform = input.platform ?? process.platform;
|
|
1356
|
+
const now4 = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
1357
|
+
const results = [];
|
|
1358
|
+
for (const spec of input.providers) {
|
|
1359
|
+
const binary = findExecutable(spec.bin, env, platform);
|
|
1360
|
+
const hookState = input.agentHooks[spec.id] ?? "unknown";
|
|
1361
|
+
const usage2 = parseProviderUsage(input.accountList, spec.orcaUsageKey, input.exhaustedPercent ?? 100);
|
|
1362
|
+
const auth = authStatusFor(spec, usage2, env);
|
|
1363
|
+
const cooldown = input.cooldowns?.[spec.id] ?? null;
|
|
1364
|
+
const coolingDownUntil = cooldown && Date.parse(cooldown) > now4().getTime() ? new Date(cooldown).toISOString() : null;
|
|
1365
|
+
const reasons = [];
|
|
1366
|
+
if (!binary) reasons.push(`binary "${spec.bin}" not found on PATH`);
|
|
1367
|
+
if (auth === "missing") reasons.push(spec.auth === "api-key" ? `none of ${spec.envKeys.join(", ") || "the configured env keys"} is set` : `Orca reports no ${spec.id} credentials`);
|
|
1368
|
+
if (usage2.exhausted) reasons.push(`usage exhausted${usage2.resetsAt ? ` until ${usage2.resetsAt}` : ""}`);
|
|
1369
|
+
if (coolingDownUntil) reasons.push(`cooling down until ${coolingDownUntil}`);
|
|
1370
|
+
const probe = binary && !reasons.length ? await runProbe(spec, binary, input.runner, input.probeTimeoutMs ?? 15e3) : "skipped";
|
|
1371
|
+
if (probe === "failed") reasons.push("probe command failed");
|
|
1372
|
+
results.push({ id: spec.id, binary, hookState, auth, usage: usage2, probe, coolingDownUntil, available: reasons.length === 0, reasons });
|
|
1373
|
+
}
|
|
1374
|
+
return results;
|
|
1375
|
+
};
|
|
1376
|
+
var cooldownUntil = (attempt, initialMin, maxMin, from, resetsAt = null) => {
|
|
1377
|
+
const minutes2 = Math.min(maxMin, initialMin * 2 ** Math.max(0, attempt));
|
|
1378
|
+
const backoff = from.getTime() + minutes2 * 6e4;
|
|
1379
|
+
const reset = resetsAt ? Date.parse(resetsAt) : Number.NaN;
|
|
1380
|
+
return new Date(Number.isFinite(reset) && reset > from.getTime() ? Math.max(reset, backoff) : backoff).toISOString();
|
|
1381
|
+
};
|
|
1382
|
+
var remainingUsagePercent = (usage2, metric = "max") => {
|
|
1383
|
+
if (usage2.status !== "ok" || !usage2.windows.length) return null;
|
|
1384
|
+
const windows = metric === "max" ? usage2.windows : usage2.windows.filter((window) => window.kind === metric);
|
|
1385
|
+
const pool = windows.length ? windows : usage2.windows;
|
|
1386
|
+
if (!pool.length) return null;
|
|
1387
|
+
const worst = Math.max(...pool.map((window) => window.usedPercent));
|
|
1388
|
+
return Math.max(0, Math.min(100, 100 - worst));
|
|
1389
|
+
};
|
|
1390
|
+
var usageRankTuple = (usage2, metric, preferKnownUsage) => {
|
|
1391
|
+
const remaining = remainingUsagePercent(usage2, metric);
|
|
1392
|
+
const known = remaining === null ? 1 : 0;
|
|
1393
|
+
const remainingKey = remaining === null ? 0 : -remaining;
|
|
1394
|
+
const resetMs = usage2.resetsAt ? Date.parse(usage2.resetsAt) : Number.POSITIVE_INFINITY;
|
|
1395
|
+
return preferKnownUsage ? [known, remainingKey, resetMs] : [remainingKey, known, resetMs];
|
|
1396
|
+
};
|
|
1397
|
+
var ORCA_META_KEYS = /* @__PURE__ */ new Set([
|
|
1398
|
+
"minimaxCookieConfigured",
|
|
1399
|
+
"minimaxApiKeyConfigured",
|
|
1400
|
+
"grokAuthConfigured",
|
|
1401
|
+
"claudeTarget",
|
|
1402
|
+
"codexTarget",
|
|
1403
|
+
"inactiveClaudeAccounts",
|
|
1404
|
+
"inactiveCodexAccounts"
|
|
1405
|
+
]);
|
|
1406
|
+
var listOrcaIntegratedProviderKeys = (accountList) => {
|
|
1407
|
+
const result = isRecord5(accountList) ? accountList : {};
|
|
1408
|
+
const rateLimits = isRecord5(result["rateLimits"]) ? result["rateLimits"] : {};
|
|
1409
|
+
const keys = /* @__PURE__ */ new Set();
|
|
1410
|
+
for (const key of Object.keys(rateLimits)) {
|
|
1411
|
+
if (!ORCA_META_KEYS.has(key) && isRecord5(rateLimits[key])) keys.add(key);
|
|
1412
|
+
}
|
|
1413
|
+
for (const key of Object.keys(result)) {
|
|
1414
|
+
if (key === "rateLimits" || ORCA_META_KEYS.has(key)) continue;
|
|
1415
|
+
if (isRecord5(result[key])) keys.add(key);
|
|
1416
|
+
}
|
|
1417
|
+
return [...keys].sort();
|
|
1418
|
+
};
|
|
1419
|
+
var undeclaredOrcaProviders = (accountList, declared) => {
|
|
1420
|
+
const usageKeyToId = /* @__PURE__ */ new Map();
|
|
1421
|
+
for (const [id2, settings] of Object.entries(declared)) {
|
|
1422
|
+
usageKeyToId.set(settings.orcaUsageKey ?? id2, id2);
|
|
1423
|
+
usageKeyToId.set(id2, id2);
|
|
1424
|
+
}
|
|
1425
|
+
usageKeyToId.set("opencodeGo", usageKeyToId.get("opencodeGo") ?? "opencode");
|
|
1426
|
+
return listOrcaIntegratedProviderKeys(accountList).filter((key) => !usageKeyToId.has(key));
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// src/adapters/rag-context.ts
|
|
1430
|
+
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1272
1431
|
var requiredString2 = (value, label) => {
|
|
1273
1432
|
if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1274
1433
|
return value;
|
|
1275
1434
|
};
|
|
1276
1435
|
var parseReference = (value, index2) => {
|
|
1277
|
-
if (!
|
|
1436
|
+
if (!isRecord6(value)) return fail(`RAG references[${index2}] must be an object.`, "INVALID_INPUT");
|
|
1278
1437
|
const relevance = value["relevance"];
|
|
1279
1438
|
if (relevance !== void 0 && (typeof relevance !== "number" || relevance < 0 || relevance > 1)) return fail(`RAG references[${index2}].relevance must be between 0 and 1.`, "INVALID_INPUT");
|
|
1280
1439
|
return {
|
|
@@ -1287,7 +1446,7 @@ var parseReference = (value, index2) => {
|
|
|
1287
1446
|
};
|
|
1288
1447
|
};
|
|
1289
1448
|
var parseRagQueryOutput = (value) => {
|
|
1290
|
-
if (!
|
|
1449
|
+
if (!isRecord6(value)) return fail("RAG query output must be a JSON object.", "INVALID_INPUT");
|
|
1291
1450
|
const rawReferences = value["references"];
|
|
1292
1451
|
if (!Array.isArray(rawReferences)) return fail("RAG query output.references must be an array.", "INVALID_INPUT");
|
|
1293
1452
|
const references = rawReferences.map((entry, index2) => parseReference(entry, index2));
|
|
@@ -2361,7 +2520,7 @@ var artifactId = (value) => {
|
|
|
2361
2520
|
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(result)) fail("Artifact artifactId is invalid.", "INVALID_INPUT");
|
|
2362
2521
|
return result;
|
|
2363
2522
|
};
|
|
2364
|
-
var
|
|
2523
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2365
2524
|
var artifactBody = (artifact) => ({
|
|
2366
2525
|
type: artifact.type,
|
|
2367
2526
|
schemaVersion: artifact.schemaVersion,
|
|
@@ -2380,7 +2539,7 @@ var artifactBody = (artifact) => ({
|
|
|
2380
2539
|
});
|
|
2381
2540
|
var expectedArtifactHash = (artifact) => hashJson(artifactBody(artifact));
|
|
2382
2541
|
var validateArtifactEnvelope = (value) => {
|
|
2383
|
-
if (!
|
|
2542
|
+
if (!isRecord7(value)) return fail("Artifact envelope must be an object.", "INVALID_INPUT");
|
|
2384
2543
|
if (value["type"] !== "agentskit-harness-artifact" || value["schemaVersion"] !== ARTIFACT_SCHEMA_VERSION) fail("Artifact envelope type or schemaVersion is invalid.", "INVALID_INPUT");
|
|
2385
2544
|
if (!ARTIFACT_TYPES.includes(value["artifactType"])) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
|
|
2386
2545
|
if (!Number.isInteger(value["artifactVersion"]) || value["artifactVersion"] < 1) fail("Artifact artifactVersion must be a positive integer.", "INVALID_INPUT");
|
|
@@ -2499,8 +2658,8 @@ var resumeStateFromArtifacts = (artifacts) => {
|
|
|
2499
2658
|
const completed = {};
|
|
2500
2659
|
const outputs = {};
|
|
2501
2660
|
for (const artifact of artifacts.filter((item) => item.artifactType === "phase").sort((left, right) => left.phase.localeCompare(right.phase) || left.artifactVersion - right.artifactVersion)) {
|
|
2502
|
-
if (!
|
|
2503
|
-
const phaseOutputs =
|
|
2661
|
+
if (!isRecord7(artifact.payload) || artifact.payload["decision"] !== "pass") continue;
|
|
2662
|
+
const phaseOutputs = isRecord7(artifact.payload["outputs"]) ? artifact.payload["outputs"] : {};
|
|
2504
2663
|
completed[artifact.phase] = { decision: "pass", outputs: phaseOutputs };
|
|
2505
2664
|
Object.assign(outputs, phaseOutputs);
|
|
2506
2665
|
}
|
|
@@ -3908,44 +4067,9 @@ var readEvidenceTrustStore = (path) => {
|
|
|
3908
4067
|
return key;
|
|
3909
4068
|
});
|
|
3910
4069
|
};
|
|
3911
|
-
var executable = (path) => {
|
|
3912
|
-
try {
|
|
3913
|
-
return statSync(path).isFile();
|
|
3914
|
-
} catch {
|
|
3915
|
-
return false;
|
|
3916
|
-
}
|
|
3917
|
-
};
|
|
3918
|
-
var findExecutable = (name2, env = process.env, platform = process.platform) => {
|
|
3919
|
-
if (typeof name2 !== "string" || !name2.trim()) return null;
|
|
3920
|
-
if (isAbsolute(name2) || name2.includes("/") || name2.includes("\\")) return existsSync(name2) && executable(name2) ? name2 : null;
|
|
3921
|
-
const extensions = platform === "win32" ? (env["PATHEXT"] ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean) : [""];
|
|
3922
|
-
for (const dir of (env["PATH"] ?? "").split(delimiter).filter(Boolean)) {
|
|
3923
|
-
for (const extension of extensions) {
|
|
3924
|
-
const candidate = join(dir, `${name2}${extension}`);
|
|
3925
|
-
if (executable(candidate)) return candidate;
|
|
3926
|
-
}
|
|
3927
|
-
if (platform === "win32" && executable(join(dir, name2))) return join(dir, name2);
|
|
3928
|
-
}
|
|
3929
|
-
return null;
|
|
3930
|
-
};
|
|
3931
|
-
var parseJsonEnvelope = (stdout) => {
|
|
3932
|
-
const trimmed = stdout.trim();
|
|
3933
|
-
if (!trimmed) return null;
|
|
3934
|
-
let parsed;
|
|
3935
|
-
try {
|
|
3936
|
-
parsed = JSON.parse(trimmed);
|
|
3937
|
-
} catch {
|
|
3938
|
-
return null;
|
|
3939
|
-
}
|
|
3940
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) return null;
|
|
3941
|
-
const record3 = parsed;
|
|
3942
|
-
if (typeof record3.ok !== "boolean") return null;
|
|
3943
|
-
const error = typeof record3.error === "string" ? record3.error : typeof record3.error === "object" && record3.error !== null && typeof record3.error.message === "string" ? record3.error.message : void 0;
|
|
3944
|
-
return { ok: record3.ok, result: record3.result, ...error === void 0 ? {} : { error } };
|
|
3945
|
-
};
|
|
3946
4070
|
|
|
3947
4071
|
// src/adapters/orca-cli.ts
|
|
3948
|
-
var
|
|
4072
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3949
4073
|
var str = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
3950
4074
|
var num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
3951
4075
|
var compareVersions = (left, right) => {
|
|
@@ -3959,9 +4083,9 @@ var compareVersions = (left, right) => {
|
|
|
3959
4083
|
};
|
|
3960
4084
|
var parseOrcaVersion = (stdout) => stdout.match(/\d+\.\d+\.\d+/)?.[0] ?? null;
|
|
3961
4085
|
var parseOrcaStatus = (result) => {
|
|
3962
|
-
const record3 =
|
|
3963
|
-
const app =
|
|
3964
|
-
const runtime =
|
|
4086
|
+
const record3 = isRecord8(result) ? result : {};
|
|
4087
|
+
const app = isRecord8(record3["app"]) ? record3["app"] : {};
|
|
4088
|
+
const runtime = isRecord8(record3["runtime"]) ? record3["runtime"] : {};
|
|
3965
4089
|
return {
|
|
3966
4090
|
appRunning: app["running"] === true,
|
|
3967
4091
|
runtimeReady: runtime["state"] === "ready" && runtime["reachable"] === true,
|
|
@@ -3972,14 +4096,14 @@ var parseOrcaStatus = (result) => {
|
|
|
3972
4096
|
};
|
|
3973
4097
|
var linkedLinear = (value) => {
|
|
3974
4098
|
if (typeof value === "string" && value.trim()) return value.trim();
|
|
3975
|
-
if (
|
|
4099
|
+
if (isRecord8(value)) {
|
|
3976
4100
|
for (const key of ["identifier", "id", "url"]) if (typeof value[key] === "string" && value[key].trim()) return value[key].trim();
|
|
3977
4101
|
}
|
|
3978
4102
|
return null;
|
|
3979
4103
|
};
|
|
3980
4104
|
var parseOrcaWorktrees = (result) => {
|
|
3981
|
-
const list2 =
|
|
3982
|
-
return list2.filter(
|
|
4105
|
+
const list2 = isRecord8(result) && Array.isArray(result["worktrees"]) ? result["worktrees"] : Array.isArray(result) ? result : [];
|
|
4106
|
+
return list2.filter(isRecord8).map((item) => ({
|
|
3983
4107
|
id: str(item["worktreeId"], str(item["id"])),
|
|
3984
4108
|
repoId: str(item["repoId"]),
|
|
3985
4109
|
repo: str(item["repo"]),
|
|
@@ -3996,8 +4120,8 @@ var parseOrcaWorktrees = (result) => {
|
|
|
3996
4120
|
})).filter((item) => item.id);
|
|
3997
4121
|
};
|
|
3998
4122
|
var parseOrcaAgentHooks = (result) => {
|
|
3999
|
-
const statuses =
|
|
4000
|
-
return Object.fromEntries(statuses.filter(
|
|
4123
|
+
const statuses = isRecord8(result) && Array.isArray(result["statuses"]) ? result["statuses"] : [];
|
|
4124
|
+
return Object.fromEntries(statuses.filter(isRecord8).flatMap((item) => {
|
|
4001
4125
|
const agent = str(item["agent"]);
|
|
4002
4126
|
if (!agent) return [];
|
|
4003
4127
|
const state = item["state"] === "installed" ? "installed" : item["state"] === "not_installed" ? "not_installed" : "unknown";
|
|
@@ -4023,9 +4147,9 @@ var orcaWorktrees = async (runner, options = {}) => parseOrcaWorktrees(await orc
|
|
|
4023
4147
|
var orcaAgentHooks = async (runner, options = {}) => parseOrcaAgentHooks(await orcaJson(runner, ["agent", "hooks", "status"], options));
|
|
4024
4148
|
var orcaAccountList = async (runner, options = {}) => orcaJson(runner, ["account", "list"], options);
|
|
4025
4149
|
var parseOrcaWorktreeCreate = (result) => {
|
|
4026
|
-
const record3 =
|
|
4027
|
-
const nested =
|
|
4028
|
-
const startup =
|
|
4150
|
+
const record3 = isRecord8(result) ? result : {};
|
|
4151
|
+
const nested = isRecord8(record3["worktree"]) ? record3["worktree"] : record3;
|
|
4152
|
+
const startup = isRecord8(record3["startupTerminal"]) ? record3["startupTerminal"] : isRecord8(nested["startupTerminal"]) ? nested["startupTerminal"] : {};
|
|
4029
4153
|
const id2 = str(nested["worktreeId"], str(nested["id"], str(record3["worktreeId"], str(record3["id"]))));
|
|
4030
4154
|
if (!id2) fail("orca worktree create returned no worktree id.", "HARNESS_ERROR");
|
|
4031
4155
|
return {
|
|
@@ -4055,8 +4179,8 @@ var orcaWorktreeSetArgv = (input, bin = "orca") => [
|
|
|
4055
4179
|
var orcaWorktreeSet = async (runner, input, options = {}) => orcaJson(runner, orcaWorktreeSetArgv(input).slice(1), options);
|
|
4056
4180
|
var orcaWorktreeRemove = async (runner, input, options = {}) => orcaJson(runner, ["worktree", "rm", "--worktree", input.worktree, ...input.force ? ["--force"] : []], { ...options, timeoutMs: options.timeoutMs ?? 6e4 });
|
|
4057
4181
|
var parseOrcaTerminals = (result) => {
|
|
4058
|
-
const list2 =
|
|
4059
|
-
return list2.filter(
|
|
4182
|
+
const list2 = isRecord8(result) ? Array.isArray(result["terminals"]) ? result["terminals"] : Array.isArray(result["items"]) ? result["items"] : [] : Array.isArray(result) ? result : [];
|
|
4183
|
+
return list2.filter(isRecord8).map((item) => ({
|
|
4060
4184
|
handle: str(item["handle"], str(item["id"])),
|
|
4061
4185
|
title: str(item["title"], str(item["name"])),
|
|
4062
4186
|
worktreeId: str(item["worktreeId"], str(item["worktree"])) || null,
|
|
@@ -4071,35 +4195,35 @@ var parseOrcaTerminals = (result) => {
|
|
|
4071
4195
|
var orcaTerminalList = async (runner, input = {}, options = {}) => parseOrcaTerminals(await orcaJson(runner, ["terminal", "list", ...input.worktree ? ["--worktree", input.worktree] : [], ...input.limit ? ["--limit", String(input.limit)] : []], options));
|
|
4072
4196
|
var orcaTerminalCreate = async (runner, input, options = {}) => {
|
|
4073
4197
|
const result = await orcaJson(runner, ["terminal", "create", "--worktree", input.worktree, "--command", input.command, ...input.title ? ["--title", input.title] : []], { ...options, timeoutMs: options.timeoutMs ?? 6e4 });
|
|
4074
|
-
const record3 =
|
|
4075
|
-
const terminal2 =
|
|
4198
|
+
const record3 = isRecord8(result) ? result : {};
|
|
4199
|
+
const terminal2 = isRecord8(record3["terminal"]) ? record3["terminal"] : record3;
|
|
4076
4200
|
const handle = str(terminal2["handle"], str(record3["handle"]));
|
|
4077
4201
|
if (!handle) fail("orca terminal create returned no terminal handle.", "HARNESS_ERROR");
|
|
4078
4202
|
return { handle, raw: result };
|
|
4079
4203
|
};
|
|
4080
4204
|
var parseOrcaSendReceipt = (result) => {
|
|
4081
|
-
const record3 =
|
|
4082
|
-
const receipt =
|
|
4083
|
-
const stages = Array.isArray(receipt["stages"]) ? receipt["stages"].map((stage) =>
|
|
4205
|
+
const record3 = isRecord8(result) ? result : {};
|
|
4206
|
+
const receipt = isRecord8(record3["receipt"]) ? record3["receipt"] : record3;
|
|
4207
|
+
const stages = Array.isArray(receipt["stages"]) ? receipt["stages"].map((stage) => isRecord8(stage) ? str(stage["stage"], str(stage["name"])) : str(stage)).filter(Boolean) : [];
|
|
4084
4208
|
const accepted = receipt["accepted"] === false ? false : receipt["accepted"] === true || stages.includes("input_accepted") || (result === null || result === void 0 || Object.keys(record3).length === 0);
|
|
4085
|
-
return { accepted, requestId: str(receipt["requestId"], str(record3["requestId"])) || null, stages, warnings: Array.isArray(record3["warnings"]) ? record3["warnings"].map((warning) =>
|
|
4209
|
+
return { accepted, requestId: str(receipt["requestId"], str(record3["requestId"])) || null, stages, warnings: Array.isArray(record3["warnings"]) ? record3["warnings"].map((warning) => isRecord8(warning) ? str(warning["message"], JSON.stringify(warning)) : str(warning)) : [] };
|
|
4086
4210
|
};
|
|
4087
4211
|
var orcaTerminalSend = async (runner, input, options = {}) => parseOrcaSendReceipt(await orcaJson(runner, ["terminal", "send", "--terminal", input.terminal, "--text", input.text, ...input.enter === false ? [] : ["--enter"], ...input.waitSubmitSeconds ? ["--wait-submit", String(input.waitSubmitSeconds)] : []], { ...options, timeoutMs: options.timeoutMs ?? (input.waitSubmitSeconds ?? 0) * 1e3 + 3e4 }));
|
|
4088
4212
|
var orcaTerminalWait = async (runner, input, options = {}) => {
|
|
4089
4213
|
const result = await orcaJson(runner, ["terminal", "wait", "--terminal", input.terminal, "--for", input.for, "--timeout-ms", String(input.timeoutMs)], { ...options, timeoutMs: input.timeoutMs + 15e3 });
|
|
4090
|
-
const record3 =
|
|
4091
|
-
const wait2 =
|
|
4214
|
+
const record3 = isRecord8(result) ? result : {};
|
|
4215
|
+
const wait2 = isRecord8(record3["wait"]) ? record3["wait"] : record3;
|
|
4092
4216
|
return { satisfied: wait2["satisfied"] === true, raw: result };
|
|
4093
4217
|
};
|
|
4094
4218
|
var orcaTerminalScreen = async (runner, input, options = {}) => {
|
|
4095
4219
|
const result = await orcaJson(runner, ["terminal", "read", "--terminal", input.terminal, "--screen"], options);
|
|
4096
|
-
const record3 =
|
|
4220
|
+
const record3 = isRecord8(result) ? isRecord8(result["terminal"]) ? result["terminal"] : result : {};
|
|
4097
4221
|
const screen = record3["tail"] ?? record3["screen"] ?? record3["lines"] ?? record3["text"] ?? record3["output"];
|
|
4098
|
-
return Array.isArray(screen) ? screen.map((line2) =>
|
|
4222
|
+
return Array.isArray(screen) ? screen.map((line2) => isRecord8(line2) ? str(line2["text"], str(line2["line"])) : String(line2)).join("\n") : typeof screen === "string" ? screen : "";
|
|
4099
4223
|
};
|
|
4100
4224
|
var parseOrcaAutomations = (result) => {
|
|
4101
|
-
const list2 =
|
|
4102
|
-
return list2.filter(
|
|
4225
|
+
const list2 = isRecord8(result) ? Array.isArray(result["automations"]) ? result["automations"] : Array.isArray(result["items"]) ? result["items"] : [] : Array.isArray(result) ? result : [];
|
|
4226
|
+
return list2.filter(isRecord8).map((item) => ({ id: str(item["id"]), name: str(item["name"]), enabled: item["enabled"] !== false && item["disabled"] !== true, trigger: str(item["rrule"], str(item["trigger"], str(item["schedule"], typeof item["schedule"] === "object" && item["schedule"] !== null ? JSON.stringify(item["schedule"]) : ""))), provider: str(item["agentId"], str(item["provider"], str(item["agent"]))) || null, raw: item })).filter((item) => item.id);
|
|
4103
4227
|
};
|
|
4104
4228
|
var orcaAutomationsList = async (runner, options = {}) => parseOrcaAutomations(await orcaJson(runner, ["automations", "list"], options));
|
|
4105
4229
|
var orcaAutomationCreateArgv = (spec, bin = "orca") => [
|
|
@@ -4147,84 +4271,6 @@ var orcaAutomationRemove = async (runner, id2, options = {}) => orcaJson(runner,
|
|
|
4147
4271
|
var orcaAutomationRun = async (runner, id2, options = {}) => orcaJson(runner, ["automations", "run", id2], options);
|
|
4148
4272
|
var orcaAutomationRuns = async (runner, id2, options = {}) => orcaJson(runner, ["automations", "runs", "--id", id2], options);
|
|
4149
4273
|
|
|
4150
|
-
// src/adapters/providers.ts
|
|
4151
|
-
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4152
|
-
var iso = (value) => typeof value === "number" && Number.isFinite(value) ? new Date(value).toISOString() : typeof value === "string" && !Number.isNaN(Date.parse(value)) ? new Date(value).toISOString() : null;
|
|
4153
|
-
var parseUsageWindows = (entry) => {
|
|
4154
|
-
if (!isRecord8(entry)) return [];
|
|
4155
|
-
return Object.entries(entry).flatMap(([kind, value]) => {
|
|
4156
|
-
if (!isRecord8(value) || typeof value["usedPercent"] !== "number") return [];
|
|
4157
|
-
return [{ kind, usedPercent: value["usedPercent"], windowMinutes: typeof value["windowMinutes"] === "number" ? value["windowMinutes"] : null, resetsAt: iso(value["resetsAt"]) }];
|
|
4158
|
-
});
|
|
4159
|
-
};
|
|
4160
|
-
var parseProviderUsage = (accountList, usageKey, exhaustedPercent = 100) => {
|
|
4161
|
-
const result = isRecord8(accountList) ? accountList : {};
|
|
4162
|
-
const rateLimits = isRecord8(result["rateLimits"]) ? result["rateLimits"] : {};
|
|
4163
|
-
const entry = isRecord8(rateLimits[usageKey]) ? rateLimits[usageKey] : null;
|
|
4164
|
-
const account = isRecord8(result[usageKey]) ? result[usageKey] : null;
|
|
4165
|
-
const systemDefault = account && isRecord8(account["systemDefault"]) ? account["systemDefault"] : null;
|
|
4166
|
-
const accounts = account && Array.isArray(account["accounts"]) ? account["accounts"] : [];
|
|
4167
|
-
const hasAuth = systemDefault ? systemDefault["hasAuth"] === true : accounts.length ? true : null;
|
|
4168
|
-
if (!entry) return { status: "unknown", error: null, windows: [], exhausted: false, resetsAt: null, hasAuth };
|
|
4169
|
-
const windows = parseUsageWindows(entry);
|
|
4170
|
-
const exhaustedWindows = windows.filter((window) => window.usedPercent >= exhaustedPercent);
|
|
4171
|
-
const resetsAt = exhaustedWindows.map((window) => window.resetsAt).filter((value) => Boolean(value)).sort()[0] ?? null;
|
|
4172
|
-
return {
|
|
4173
|
-
status: entry["status"] === "ok" ? "ok" : entry["status"] === "unavailable" ? "unavailable" : "unknown",
|
|
4174
|
-
error: typeof entry["error"] === "string" ? entry["error"] : null,
|
|
4175
|
-
windows,
|
|
4176
|
-
exhausted: exhaustedWindows.length > 0,
|
|
4177
|
-
resetsAt,
|
|
4178
|
-
hasAuth
|
|
4179
|
-
};
|
|
4180
|
-
};
|
|
4181
|
-
var authStatusFor = (spec, usage2, env) => {
|
|
4182
|
-
const hasEnvKey = spec.envKeys.some((key) => Boolean(env[key]?.trim()));
|
|
4183
|
-
if (spec.auth === "api-key") return hasEnvKey ? "ok" : "missing";
|
|
4184
|
-
if (spec.auth === "subscription") return usage2.hasAuth === true || usage2.status === "ok" ? "ok" : usage2.hasAuth === false ? "missing" : hasEnvKey || usage2.status === "unknown" ? "ok" : "unknown";
|
|
4185
|
-
return hasEnvKey || usage2.status === "ok" ? "ok" : "unknown";
|
|
4186
|
-
};
|
|
4187
|
-
var runProbe = async (spec, binary, runner, timeoutMs) => {
|
|
4188
|
-
if (!spec.probe || !runner) return "skipped";
|
|
4189
|
-
const [head, ...rest] = spec.probe;
|
|
4190
|
-
const argv = [head === spec.bin ? binary : head ?? binary, ...rest];
|
|
4191
|
-
try {
|
|
4192
|
-
const outcome = await runner.run(argv, { timeoutMs });
|
|
4193
|
-
return outcome.code === 0 && !outcome.timedOut ? "passed" : "failed";
|
|
4194
|
-
} catch {
|
|
4195
|
-
return "failed";
|
|
4196
|
-
}
|
|
4197
|
-
};
|
|
4198
|
-
var detectProviders = async (input) => {
|
|
4199
|
-
const env = input.env ?? process.env;
|
|
4200
|
-
const platform = input.platform ?? process.platform;
|
|
4201
|
-
const now4 = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
4202
|
-
const results = [];
|
|
4203
|
-
for (const spec of input.providers) {
|
|
4204
|
-
const binary = findExecutable(spec.bin, env, platform);
|
|
4205
|
-
const hookState = input.agentHooks[spec.id] ?? "unknown";
|
|
4206
|
-
const usage2 = parseProviderUsage(input.accountList, spec.orcaUsageKey, input.exhaustedPercent ?? 100);
|
|
4207
|
-
const auth = authStatusFor(spec, usage2, env);
|
|
4208
|
-
const cooldown = input.cooldowns?.[spec.id] ?? null;
|
|
4209
|
-
const coolingDownUntil = cooldown && Date.parse(cooldown) > now4().getTime() ? new Date(cooldown).toISOString() : null;
|
|
4210
|
-
const reasons = [];
|
|
4211
|
-
if (!binary) reasons.push(`binary "${spec.bin}" not found on PATH`);
|
|
4212
|
-
if (auth === "missing") reasons.push(spec.auth === "api-key" ? `none of ${spec.envKeys.join(", ") || "the configured env keys"} is set` : `Orca reports no ${spec.id} credentials`);
|
|
4213
|
-
if (usage2.exhausted) reasons.push(`usage exhausted${usage2.resetsAt ? ` until ${usage2.resetsAt}` : ""}`);
|
|
4214
|
-
if (coolingDownUntil) reasons.push(`cooling down until ${coolingDownUntil}`);
|
|
4215
|
-
const probe = binary && !reasons.length ? await runProbe(spec, binary, input.runner, input.probeTimeoutMs ?? 15e3) : "skipped";
|
|
4216
|
-
if (probe === "failed") reasons.push("probe command failed");
|
|
4217
|
-
results.push({ id: spec.id, binary, hookState, auth, usage: usage2, probe, coolingDownUntil, available: reasons.length === 0, reasons });
|
|
4218
|
-
}
|
|
4219
|
-
return results;
|
|
4220
|
-
};
|
|
4221
|
-
var cooldownUntil = (attempt, initialMin, maxMin, from, resetsAt = null) => {
|
|
4222
|
-
const minutes2 = Math.min(maxMin, initialMin * 2 ** Math.max(0, attempt));
|
|
4223
|
-
const backoff = from.getTime() + minutes2 * 6e4;
|
|
4224
|
-
const reset = resetsAt ? Date.parse(resetsAt) : Number.NaN;
|
|
4225
|
-
return new Date(Number.isFinite(reset) && reset > from.getTime() ? Math.max(reset, backoff) : backoff).toISOString();
|
|
4226
|
-
};
|
|
4227
|
-
|
|
4228
4274
|
// src/adapters/linear-orca.ts
|
|
4229
4275
|
var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4230
4276
|
var str2 = (value, fallback = "") => typeof value === "string" ? value : fallback;
|
|
@@ -4376,6 +4422,41 @@ var LoopConfigSchema = z.object({
|
|
|
4376
4422
|
reviewer: tiers,
|
|
4377
4423
|
builder: tiers,
|
|
4378
4424
|
watcher: tiers,
|
|
4425
|
+
/** How candidates are ordered. `tiers` = YAML order (0.6 behaviour). `hybrid` = keep tiers, rank by remaining usage inside each. `dynamic` = flatten + usage. `catalog` = discover models via CLI/AA/builtin + usage. */
|
|
4426
|
+
routing: z.object({
|
|
4427
|
+
mode: z.enum(["tiers", "hybrid", "dynamic", "catalog"]).default("tiers"),
|
|
4428
|
+
/** Which usage window drives remaining%. `max` = most constrained window. */
|
|
4429
|
+
usageMetric: z.enum(["max", "session", "weekly", "monthly"]).default("max"),
|
|
4430
|
+
/** Prefer providers with live usage % over those with unknown usage (e.g. grok often has no %). */
|
|
4431
|
+
preferKnownUsage: z.boolean().default(true),
|
|
4432
|
+
excludeProviders: z.array(nonEmpty5).default([]),
|
|
4433
|
+
/** If non-empty, only these providers may be selected (still must be declared under providers). */
|
|
4434
|
+
includeProviders: z.array(nonEmpty5).default([]),
|
|
4435
|
+
/** Hard pin per role (`provider/model`). If pinned provider is unavailable, fall through unless pinStrict. */
|
|
4436
|
+
pin: z.object({
|
|
4437
|
+
orchestrator: modelRef.optional(),
|
|
4438
|
+
reviewer: modelRef.optional(),
|
|
4439
|
+
builder: modelRef.optional(),
|
|
4440
|
+
watcher: modelRef.optional()
|
|
4441
|
+
}).prefault({}),
|
|
4442
|
+
pinStrict: z.boolean().default(false)
|
|
4443
|
+
}).prefault({}),
|
|
4444
|
+
/** Quality band when `routing.mode: catalog` (and as soft bias in hybrid). */
|
|
4445
|
+
roles: z.object({
|
|
4446
|
+
orchestrator: z.object({ quality: z.enum(["frontier", "balanced", "fast"]).default("frontier"), preferCreators: z.array(nonEmpty5).default([]) }).prefault({}),
|
|
4447
|
+
reviewer: z.object({ quality: z.enum(["frontier", "balanced", "fast"]).default("frontier"), preferCreators: z.array(nonEmpty5).default([]) }).prefault({}),
|
|
4448
|
+
builder: z.object({ quality: z.enum(["frontier", "balanced", "fast"]).default("balanced"), preferCreators: z.array(nonEmpty5).default([]) }).prefault({}),
|
|
4449
|
+
watcher: z.object({ quality: z.enum(["frontier", "balanced", "fast"]).default("fast"), preferCreators: z.array(nonEmpty5).default([]) }).prefault({})
|
|
4450
|
+
}).prefault({}),
|
|
4451
|
+
catalog: z.object({
|
|
4452
|
+
sources: z.array(z.enum(["cli", "artificial-analysis", "builtin"])).default(["cli", "builtin"]),
|
|
4453
|
+
artificialAnalysis: z.object({
|
|
4454
|
+
enabled: z.boolean().default(false),
|
|
4455
|
+
apiKeyEnv: nonEmpty5.default("ARTIFICIAL_ANALYSIS_API_KEY"),
|
|
4456
|
+
cacheHours: z.number().positive().default(24),
|
|
4457
|
+
endpoint: nonEmpty5.default("https://artificialanalysis.ai/api/v2/data/llms/models")
|
|
4458
|
+
}).prefault({})
|
|
4459
|
+
}).prefault({}),
|
|
4379
4460
|
cooldown: z.object({
|
|
4380
4461
|
initialMin: z.number().int().positive().default(30),
|
|
4381
4462
|
maxMin: z.number().int().positive().default(240),
|
|
@@ -4712,28 +4793,395 @@ var assessSlots = (input) => {
|
|
|
4712
4793
|
};
|
|
4713
4794
|
|
|
4714
4795
|
// src/loop/routing.ts
|
|
4715
|
-
var
|
|
4796
|
+
var allowedProvider = (config, providerId) => {
|
|
4797
|
+
const { excludeProviders, includeProviders } = config.models.routing;
|
|
4798
|
+
if (excludeProviders.includes(providerId)) return false;
|
|
4799
|
+
if (includeProviders.length && !includeProviders.includes(providerId)) return false;
|
|
4800
|
+
return true;
|
|
4801
|
+
};
|
|
4802
|
+
var materialize = (config, ref, tier, preferenceIndex, availability, reason) => {
|
|
4803
|
+
const identity = providerIdentity(config, ref.provider);
|
|
4804
|
+
return {
|
|
4805
|
+
...ref,
|
|
4806
|
+
tier,
|
|
4807
|
+
preferenceIndex,
|
|
4808
|
+
orcaAgent: identity.orcaAgent,
|
|
4809
|
+
tui: renderTuiCommand(identity.settings, ref.model),
|
|
4810
|
+
remainingPercent: availability ? remainingUsagePercent(availability.usage, config.models.routing.usageMetric) : null,
|
|
4811
|
+
reason
|
|
4812
|
+
};
|
|
4813
|
+
};
|
|
4814
|
+
var compareUsageAware = (config, left, right, byId) => {
|
|
4815
|
+
const leftAv = byId.get(left.provider);
|
|
4816
|
+
const rightAv = byId.get(right.provider);
|
|
4817
|
+
const leftTuple = leftAv ? usageRankTuple(leftAv.usage, config.models.routing.usageMetric, config.models.routing.preferKnownUsage) : [1, 0, Number.POSITIVE_INFINITY];
|
|
4818
|
+
const rightTuple = rightAv ? usageRankTuple(rightAv.usage, config.models.routing.usageMetric, config.models.routing.preferKnownUsage) : [1, 0, Number.POSITIVE_INFINITY];
|
|
4819
|
+
for (let i = 0; i < leftTuple.length; i += 1) {
|
|
4820
|
+
if (leftTuple[i] !== rightTuple[i]) return leftTuple[i] - rightTuple[i];
|
|
4821
|
+
}
|
|
4822
|
+
if (left.preferenceIndex !== right.preferenceIndex) return left.preferenceIndex - right.preferenceIndex;
|
|
4823
|
+
return left.provider.localeCompare(right.provider) || left.model.localeCompare(right.model);
|
|
4824
|
+
};
|
|
4825
|
+
var availableFromTiers = (config, role, availability) => {
|
|
4716
4826
|
const byId = new Map(availability.map((item) => [item.id, item]));
|
|
4717
4827
|
const skipped = [];
|
|
4828
|
+
const ranked = [];
|
|
4829
|
+
let preferenceIndex = 0;
|
|
4718
4830
|
for (const [tier, refs] of tiersFor(config, role).entries()) {
|
|
4719
4831
|
for (const ref of refs) {
|
|
4832
|
+
const index2 = preferenceIndex;
|
|
4833
|
+
preferenceIndex += 1;
|
|
4834
|
+
if (!allowedProvider(config, ref.provider)) {
|
|
4835
|
+
skipped.push({ tier, ref, reasons: ["provider excluded by models.routing"] });
|
|
4836
|
+
continue;
|
|
4837
|
+
}
|
|
4720
4838
|
const provider = byId.get(ref.provider);
|
|
4721
4839
|
if (provider?.available) {
|
|
4722
|
-
|
|
4723
|
-
|
|
4840
|
+
ranked.push(materialize(config, ref, tier, index2, provider, `yaml tier ${tier + 1}`));
|
|
4841
|
+
} else {
|
|
4842
|
+
skipped.push({ tier, ref, reasons: provider ? provider.reasons : ["provider was not detected"] });
|
|
4724
4843
|
}
|
|
4725
|
-
skipped.push({ tier, ref, reasons: provider ? provider.reasons : ["provider was not detected"] });
|
|
4726
4844
|
}
|
|
4727
4845
|
}
|
|
4728
|
-
return {
|
|
4846
|
+
return { ranked, skipped };
|
|
4729
4847
|
};
|
|
4730
|
-
var
|
|
4731
|
-
|
|
4848
|
+
var applyPin = (config, role, availability, skipped) => {
|
|
4849
|
+
const pin = config.models.routing.pin[role];
|
|
4850
|
+
if (!pin) return null;
|
|
4851
|
+
const ref = parseModelRef(pin);
|
|
4732
4852
|
const byId = new Map(availability.map((item) => [item.id, item]));
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
return
|
|
4736
|
-
}
|
|
4853
|
+
const provider = byId.get(ref.provider);
|
|
4854
|
+
if (provider?.available && allowedProvider(config, ref.provider)) {
|
|
4855
|
+
return materialize(config, ref, -1, -1, provider, `pinned ${pin}`);
|
|
4856
|
+
}
|
|
4857
|
+
skipped.push({ tier: -1, ref, reasons: provider ? provider.reasons : ["pinned provider was not detected"] });
|
|
4858
|
+
if (config.models.routing.pinStrict) return null;
|
|
4859
|
+
return null;
|
|
4860
|
+
};
|
|
4861
|
+
var selectModel = (config, role, availability, extraCandidates = []) => {
|
|
4862
|
+
const byId = new Map(availability.map((item) => [item.id, item]));
|
|
4863
|
+
const mode = config.models.routing.mode;
|
|
4864
|
+
const { ranked: fromYaml, skipped } = availableFromTiers(config, role, availability);
|
|
4865
|
+
if (config.models.routing.pin[role]) {
|
|
4866
|
+
const pinned = applyPin(config, role, availability, skipped);
|
|
4867
|
+
if (pinned) return { role, selected: pinned, skipped };
|
|
4868
|
+
if (config.models.routing.pinStrict) return { role, selected: null, skipped };
|
|
4869
|
+
}
|
|
4870
|
+
const extras = [];
|
|
4871
|
+
let extraIndex = 1e4;
|
|
4872
|
+
for (const ref of extraCandidates) {
|
|
4873
|
+
if (!allowedProvider(config, ref.provider)) continue;
|
|
4874
|
+
const provider = byId.get(ref.provider);
|
|
4875
|
+
if (!provider?.available) continue;
|
|
4876
|
+
extras.push(materialize(config, ref, 99, extraIndex, provider, "catalog"));
|
|
4877
|
+
extraIndex += 1;
|
|
4878
|
+
}
|
|
4879
|
+
if (mode === "tiers") {
|
|
4880
|
+
const first = fromYaml[0] ?? extras[0] ?? null;
|
|
4881
|
+
return { role, selected: first ?? null, skipped };
|
|
4882
|
+
}
|
|
4883
|
+
if (mode === "hybrid") {
|
|
4884
|
+
const byTier = /* @__PURE__ */ new Map();
|
|
4885
|
+
for (const item of fromYaml) {
|
|
4886
|
+
const list2 = byTier.get(item.tier) ?? [];
|
|
4887
|
+
list2.push(item);
|
|
4888
|
+
byTier.set(item.tier, list2);
|
|
4889
|
+
}
|
|
4890
|
+
const tiers2 = [...byTier.keys()].sort((a, b) => a - b);
|
|
4891
|
+
for (const tier of tiers2) {
|
|
4892
|
+
const pool2 = byTier.get(tier) ?? [];
|
|
4893
|
+
pool2.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4894
|
+
if (pool2[0]) {
|
|
4895
|
+
return {
|
|
4896
|
+
role,
|
|
4897
|
+
selected: {
|
|
4898
|
+
...pool2[0],
|
|
4899
|
+
reason: `hybrid tier ${tier + 1} \xB7 remaining ${pool2[0].remainingPercent ?? "unknown"}%`
|
|
4900
|
+
},
|
|
4901
|
+
skipped
|
|
4902
|
+
};
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
if (extras.length) {
|
|
4906
|
+
extras.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4907
|
+
return { role, selected: { ...extras[0], reason: `hybrid catalog \xB7 remaining ${extras[0].remainingPercent ?? "unknown"}%` }, skipped };
|
|
4908
|
+
}
|
|
4909
|
+
return { role, selected: null, skipped };
|
|
4910
|
+
}
|
|
4911
|
+
const pool = [...fromYaml, ...extras];
|
|
4912
|
+
pool.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4913
|
+
const best = pool[0] ?? null;
|
|
4914
|
+
return {
|
|
4915
|
+
role,
|
|
4916
|
+
selected: best ? { ...best, reason: `${mode} \xB7 remaining ${best.remainingPercent ?? "unknown"}% \xB7 ${best.reason}` } : null,
|
|
4917
|
+
skipped
|
|
4918
|
+
};
|
|
4919
|
+
};
|
|
4920
|
+
var routeAllRoles = (config, availability, extrasByRole = {}) => Object.fromEntries(MODEL_ROLES.map((role) => [role, selectModel(config, role, availability, extrasByRole[role] ?? [])]));
|
|
4921
|
+
var rankModels = (config, role, availability, extraCandidates = []) => {
|
|
4922
|
+
const byId = new Map(availability.map((item) => [item.id, item]));
|
|
4923
|
+
const { ranked } = availableFromTiers(config, role, availability);
|
|
4924
|
+
const extras = [];
|
|
4925
|
+
let extraIndex = 1e4;
|
|
4926
|
+
for (const ref of extraCandidates) {
|
|
4927
|
+
if (!allowedProvider(config, ref.provider)) continue;
|
|
4928
|
+
const provider = byId.get(ref.provider);
|
|
4929
|
+
if (!provider?.available) continue;
|
|
4930
|
+
extras.push(materialize(config, ref, 99, extraIndex, provider, "catalog"));
|
|
4931
|
+
extraIndex += 1;
|
|
4932
|
+
}
|
|
4933
|
+
const mode = config.models.routing.mode;
|
|
4934
|
+
if (mode === "tiers") return [...ranked, ...extras];
|
|
4935
|
+
if (mode === "hybrid") {
|
|
4936
|
+
const byTier = /* @__PURE__ */ new Map();
|
|
4937
|
+
for (const item of ranked) {
|
|
4938
|
+
const list2 = byTier.get(item.tier) ?? [];
|
|
4939
|
+
list2.push(item);
|
|
4940
|
+
byTier.set(item.tier, list2);
|
|
4941
|
+
}
|
|
4942
|
+
const ordered = [];
|
|
4943
|
+
for (const tier of [...byTier.keys()].sort((a, b) => a - b)) {
|
|
4944
|
+
const pool2 = byTier.get(tier) ?? [];
|
|
4945
|
+
pool2.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4946
|
+
ordered.push(...pool2);
|
|
4947
|
+
}
|
|
4948
|
+
extras.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4949
|
+
return [...ordered, ...extras];
|
|
4950
|
+
}
|
|
4951
|
+
const pool = [...ranked, ...extras];
|
|
4952
|
+
pool.sort((left, right) => compareUsageAware(config, left, right, byId));
|
|
4953
|
+
return pool;
|
|
4954
|
+
};
|
|
4955
|
+
|
|
4956
|
+
// src/loop/model-catalog/builtin.json
|
|
4957
|
+
var builtin_default = {
|
|
4958
|
+
providers: {
|
|
4959
|
+
claude: {
|
|
4960
|
+
creator: "anthropic",
|
|
4961
|
+
models: [
|
|
4962
|
+
{ id: "opus", quality: "frontier", codingScore: 90 },
|
|
4963
|
+
{ id: "sonnet", quality: "balanced", codingScore: 80 },
|
|
4964
|
+
{ id: "haiku", quality: "fast", codingScore: 55 }
|
|
4965
|
+
]
|
|
4966
|
+
},
|
|
4967
|
+
codex: {
|
|
4968
|
+
creator: "openai",
|
|
4969
|
+
models: [
|
|
4970
|
+
{ id: "gpt-5.6-sol", quality: "frontier", codingScore: 92 },
|
|
4971
|
+
{ id: "gpt-5.6-luna", quality: "balanced", codingScore: 78 },
|
|
4972
|
+
{ id: "gpt-5.4", quality: "balanced", codingScore: 70 }
|
|
4973
|
+
]
|
|
4974
|
+
},
|
|
4975
|
+
opencode: {
|
|
4976
|
+
creator: "opencode",
|
|
4977
|
+
models: [
|
|
4978
|
+
{ id: "opencode-go/glm-5.3", quality: "balanced", codingScore: 72 },
|
|
4979
|
+
{ id: "opencode-go/glm-5.3-flash", quality: "fast", codingScore: 58 }
|
|
4980
|
+
]
|
|
4981
|
+
},
|
|
4982
|
+
grok: {
|
|
4983
|
+
creator: "xai",
|
|
4984
|
+
models: [
|
|
4985
|
+
{ id: "grok-4.6", quality: "frontier", codingScore: 85 },
|
|
4986
|
+
{ id: "grok-4.5", quality: "balanced", codingScore: 75 },
|
|
4987
|
+
{ id: "grok-4-fast", quality: "fast", codingScore: 60 }
|
|
4988
|
+
]
|
|
4989
|
+
}
|
|
4990
|
+
}
|
|
4991
|
+
};
|
|
4992
|
+
|
|
4993
|
+
// src/loop/model-catalog/aliases.json
|
|
4994
|
+
var aliases_default = {
|
|
4995
|
+
aliases: {
|
|
4996
|
+
claude: {
|
|
4997
|
+
"claude-opus-4": "opus",
|
|
4998
|
+
"claude-sonnet-4": "sonnet",
|
|
4999
|
+
"claude-haiku-4": "haiku",
|
|
5000
|
+
"opus-4": "opus",
|
|
5001
|
+
"sonnet-4": "sonnet",
|
|
5002
|
+
"haiku-4": "haiku"
|
|
5003
|
+
},
|
|
5004
|
+
codex: {
|
|
5005
|
+
"gpt-5.6": "gpt-5.6-sol",
|
|
5006
|
+
o3: "gpt-5.6-sol"
|
|
5007
|
+
},
|
|
5008
|
+
grok: {
|
|
5009
|
+
"grok-4": "grok-4.5",
|
|
5010
|
+
"grok-4-latest": "grok-4.6"
|
|
5011
|
+
},
|
|
5012
|
+
opencode: {}
|
|
5013
|
+
}
|
|
5014
|
+
};
|
|
5015
|
+
|
|
5016
|
+
// src/loop/model-catalog/index.ts
|
|
5017
|
+
var readJson2 = (path) => JSON.parse(readFileSync(path, "utf8"));
|
|
5018
|
+
var loadBuiltinCatalog = () => {
|
|
5019
|
+
const raw = builtin_default;
|
|
5020
|
+
return Object.fromEntries(Object.entries(raw.providers).map(([id2, value]) => [id2, {
|
|
5021
|
+
creator: value.creator,
|
|
5022
|
+
models: value.models.map((model) => ({ ...model, source: "builtin", creator: value.creator }))
|
|
5023
|
+
}]));
|
|
5024
|
+
};
|
|
5025
|
+
var loadAliases = () => aliases_default.aliases;
|
|
5026
|
+
var resolveAlias = (provider, modelId, aliases = loadAliases()) => aliases[provider]?.[modelId] ?? aliases[provider]?.[modelId.toLowerCase()] ?? modelId;
|
|
5027
|
+
var parseGrokModelsOutput = (stdout) => {
|
|
5028
|
+
const models = [];
|
|
5029
|
+
for (const line2 of stdout.split(/\r?\n/)) {
|
|
5030
|
+
const match = line2.match(/^\s*[-*]?\s*(grok-[a-z0-9][a-z0-9._-]*)\b/i) ?? line2.match(/^\s*\*\s*(grok-[a-z0-9][a-z0-9._-]*)\b/i);
|
|
5031
|
+
if (match?.[1]) models.push(match[1]);
|
|
5032
|
+
}
|
|
5033
|
+
return [...new Set(models)];
|
|
5034
|
+
};
|
|
5035
|
+
var listCliModels = async (provider, bin, runner, timeoutMs = 2e4) => {
|
|
5036
|
+
if (provider === "grok") {
|
|
5037
|
+
const outcome = await runner.run([bin, "models"], { timeoutMs });
|
|
5038
|
+
if (outcome.code !== 0 && !outcome.stdout.trim()) return [];
|
|
5039
|
+
return parseGrokModelsOutput(`${outcome.stdout}
|
|
5040
|
+
${outcome.stderr}`);
|
|
5041
|
+
}
|
|
5042
|
+
return [];
|
|
5043
|
+
};
|
|
5044
|
+
var parseArtificialAnalysisPayload = (payload) => {
|
|
5045
|
+
const root = payload && typeof payload === "object" && !Array.isArray(payload) ? payload : {};
|
|
5046
|
+
const data = Array.isArray(root["data"]) ? root["data"] : Array.isArray(payload) ? payload : [];
|
|
5047
|
+
return data.flatMap((item) => {
|
|
5048
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return [];
|
|
5049
|
+
const row = item;
|
|
5050
|
+
const creator = row["model_creator"] && typeof row["model_creator"] === "object" && !Array.isArray(row["model_creator"]) ? row["model_creator"] : {};
|
|
5051
|
+
const evaluations = row["evaluations"] && typeof row["evaluations"] === "object" && !Array.isArray(row["evaluations"]) ? row["evaluations"] : {};
|
|
5052
|
+
const slug = typeof row["slug"] === "string" ? row["slug"] : typeof row["id"] === "string" ? row["id"] : null;
|
|
5053
|
+
if (!slug) return [];
|
|
5054
|
+
return [{
|
|
5055
|
+
slug,
|
|
5056
|
+
name: typeof row["name"] === "string" ? row["name"] : slug,
|
|
5057
|
+
creatorSlug: typeof creator["slug"] === "string" ? creator["slug"] : "unknown",
|
|
5058
|
+
codingIndex: typeof evaluations["artificial_analysis_coding_index"] === "number" ? evaluations["artificial_analysis_coding_index"] : null,
|
|
5059
|
+
intelligenceIndex: typeof evaluations["artificial_analysis_intelligence_index"] === "number" ? evaluations["artificial_analysis_intelligence_index"] : null
|
|
5060
|
+
}];
|
|
5061
|
+
});
|
|
5062
|
+
};
|
|
5063
|
+
var readAaCache = (stateDir) => {
|
|
5064
|
+
const path = join(stateDir, "catalog", "artificial-analysis.json");
|
|
5065
|
+
if (!existsSync(path)) return null;
|
|
5066
|
+
try {
|
|
5067
|
+
const raw = readJson2(path);
|
|
5068
|
+
return { fetchedAt: raw.fetchedAt, models: raw.models };
|
|
5069
|
+
} catch {
|
|
5070
|
+
return null;
|
|
5071
|
+
}
|
|
5072
|
+
};
|
|
5073
|
+
var writeAaCache = (stateDir, models) => {
|
|
5074
|
+
const path = join(stateDir, "catalog", "artificial-analysis.json");
|
|
5075
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
5076
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
5077
|
+
writeFileSync(tmp, `${JSON.stringify({ fetchedAt: (/* @__PURE__ */ new Date()).toISOString(), models }, null, 2)}
|
|
5078
|
+
`, "utf8");
|
|
5079
|
+
renameSync(tmp, path);
|
|
5080
|
+
};
|
|
5081
|
+
var fetchArtificialAnalysisModels = async (input) => {
|
|
5082
|
+
const controller = new AbortController();
|
|
5083
|
+
const timer = setTimeout(() => controller.abort(), input.timeoutMs ?? 2e4);
|
|
5084
|
+
try {
|
|
5085
|
+
const response = await fetch(input.endpoint, {
|
|
5086
|
+
headers: { "x-api-key": input.apiKey, accept: "application/json" },
|
|
5087
|
+
signal: controller.signal
|
|
5088
|
+
});
|
|
5089
|
+
if (!response.ok) throw new Error(`Artificial Analysis HTTP ${response.status}`);
|
|
5090
|
+
return parseArtificialAnalysisPayload(await response.json());
|
|
5091
|
+
} finally {
|
|
5092
|
+
clearTimeout(timer);
|
|
5093
|
+
}
|
|
5094
|
+
};
|
|
5095
|
+
var creatorForProvider = {
|
|
5096
|
+
claude: "anthropic",
|
|
5097
|
+
codex: "openai",
|
|
5098
|
+
grok: "xai",
|
|
5099
|
+
opencode: "opencode"
|
|
5100
|
+
};
|
|
5101
|
+
var qualityRank = { frontier: 3, balanced: 2, fast: 1 };
|
|
5102
|
+
var matchesQuality = (model, wanted) => {
|
|
5103
|
+
if (wanted === "frontier") return model.quality === "frontier" || model.codingScore >= 80;
|
|
5104
|
+
if (wanted === "balanced") return model.quality !== "fast" || model.codingScore >= 65;
|
|
5105
|
+
return true;
|
|
5106
|
+
};
|
|
5107
|
+
var resolveCatalogCandidates = async (input) => {
|
|
5108
|
+
const { config, role } = input;
|
|
5109
|
+
const policy = config.models.roles[role];
|
|
5110
|
+
const sources = config.models.catalog.sources;
|
|
5111
|
+
const builtin = loadBuiltinCatalog();
|
|
5112
|
+
const aliases = loadAliases();
|
|
5113
|
+
const byProvider = /* @__PURE__ */ new Map();
|
|
5114
|
+
const push = (provider, model) => {
|
|
5115
|
+
const list2 = byProvider.get(provider) ?? [];
|
|
5116
|
+
if (list2.some((item) => item.id === model.id)) return;
|
|
5117
|
+
list2.push(model);
|
|
5118
|
+
byProvider.set(provider, list2);
|
|
5119
|
+
};
|
|
5120
|
+
for (const provider of input.availableProviderIds) {
|
|
5121
|
+
if (sources.includes("builtin") && builtin[provider]) {
|
|
5122
|
+
for (const model of builtin[provider].models) push(provider, model);
|
|
5123
|
+
}
|
|
5124
|
+
if (sources.includes("cli") && input.runner) {
|
|
5125
|
+
const settings = config.models.providers[provider];
|
|
5126
|
+
if (settings) {
|
|
5127
|
+
try {
|
|
5128
|
+
const ids = await listCliModels(provider, settings.bin, input.runner);
|
|
5129
|
+
for (const id2 of ids) {
|
|
5130
|
+
const resolved = resolveAlias(provider, id2, aliases);
|
|
5131
|
+
const existing = builtin[provider]?.models.find((model) => model.id === resolved);
|
|
5132
|
+
push(provider, existing ?? { id: resolved, quality: "balanced", codingScore: 70, source: "cli", creator: creatorForProvider[provider] });
|
|
5133
|
+
}
|
|
5134
|
+
} catch {
|
|
5135
|
+
}
|
|
5136
|
+
}
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
if (sources.includes("artificial-analysis") && config.models.catalog.artificialAnalysis.enabled && input.stateDir) {
|
|
5140
|
+
const aa = config.models.catalog.artificialAnalysis;
|
|
5141
|
+
const env = input.env ?? process.env;
|
|
5142
|
+
const key = env[aa.apiKeyEnv]?.trim();
|
|
5143
|
+
let models = readAaCache(input.stateDir);
|
|
5144
|
+
const now4 = input.now ?? (() => /* @__PURE__ */ new Date());
|
|
5145
|
+
const stale = !models || now4().getTime() - Date.parse(models.fetchedAt) > aa.cacheHours * 36e5;
|
|
5146
|
+
if (key && stale) {
|
|
5147
|
+
try {
|
|
5148
|
+
const fresh = await fetchArtificialAnalysisModels({ endpoint: aa.endpoint, apiKey: key });
|
|
5149
|
+
writeAaCache(input.stateDir, fresh);
|
|
5150
|
+
models = { fetchedAt: now4().toISOString(), models: fresh };
|
|
5151
|
+
} catch {
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
if (models) {
|
|
5155
|
+
for (const provider of input.availableProviderIds) {
|
|
5156
|
+
const creator = creatorForProvider[provider] ?? provider;
|
|
5157
|
+
const matches2 = models.models.filter((model) => model.creatorSlug === creator || model.creatorSlug.includes(creator));
|
|
5158
|
+
for (const model of matches2) {
|
|
5159
|
+
const id2 = resolveAlias(provider, model.slug, aliases);
|
|
5160
|
+
const score3 = model.codingIndex ?? model.intelligenceIndex ?? 50;
|
|
5161
|
+
const quality = score3 >= 80 ? "frontier" : score3 >= 60 ? "balanced" : "fast";
|
|
5162
|
+
push(provider, { id: id2, quality, codingScore: score3, source: "artificial-analysis", creator });
|
|
5163
|
+
}
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
}
|
|
5167
|
+
const refs = [];
|
|
5168
|
+
for (const provider of input.availableProviderIds) {
|
|
5169
|
+
let models = byProvider.get(provider) ?? [];
|
|
5170
|
+
if (policy.preferCreators.length) {
|
|
5171
|
+
const preferred = models.filter((model) => model.creator && policy.preferCreators.includes(model.creator));
|
|
5172
|
+
if (preferred.length) models = preferred;
|
|
5173
|
+
}
|
|
5174
|
+
models = models.filter((model) => matchesQuality(model, policy.quality));
|
|
5175
|
+
models = [...models].sort((left, right) => {
|
|
5176
|
+
const qualityDelta = qualityRank[right.quality] - qualityRank[left.quality];
|
|
5177
|
+
if (qualityDelta) return qualityDelta;
|
|
5178
|
+
return right.codingScore - left.codingScore;
|
|
5179
|
+
});
|
|
5180
|
+
for (const model of models.slice(0, 3)) {
|
|
5181
|
+
refs.push(parseModelRef(`${provider}/${model.id}`));
|
|
5182
|
+
}
|
|
5183
|
+
}
|
|
5184
|
+
return refs;
|
|
4737
5185
|
};
|
|
4738
5186
|
var cooldownPath = (stateDir) => join(stateDir, "provider-cooldowns.json");
|
|
4739
5187
|
var readCooldowns = (stateDir) => {
|
|
@@ -4810,11 +5258,31 @@ var runLoopDoctor = async (input) => {
|
|
|
4810
5258
|
]);
|
|
4811
5259
|
const cooldowns = activeCooldowns(readCooldowns(loaded.stateDir), now4());
|
|
4812
5260
|
const providers = await detectProviders({ providers: providerSpecs(config), accountList: accountList ?? {}, agentHooks, env: input.env, platform: input.platform, exhaustedPercent: config.models.cooldown.exhaustedPercent, cooldowns, now: now4, ...input.probe === false ? {} : { runner: input.runner } });
|
|
4813
|
-
for (const provider of providers)
|
|
4814
|
-
|
|
5261
|
+
for (const provider of providers) {
|
|
5262
|
+
const remaining = remainingUsagePercent(provider.usage, config.models.routing.usageMetric);
|
|
5263
|
+
const usageDetail = provider.usage.windows.length ? ` (${provider.usage.windows.map((window) => `${window.kind} ${window.usedPercent}%`).join(", ")}; remaining~${remaining ?? "?"}%)` : "";
|
|
5264
|
+
push(`provider.${provider.id}`, provider.available ? "passed" : "warning", provider.available ? `available${usageDetail}` : provider.reasons.join("; "));
|
|
5265
|
+
}
|
|
5266
|
+
const undeclared = undeclaredOrcaProviders(accountList ?? {}, Object.fromEntries(Object.entries(config.models.providers).map(([id2, settings]) => [id2, { orcaUsageKey: settings.orcaUsageKey ?? id2 }])));
|
|
5267
|
+
if (undeclared.length) push("orca.undeclared-providers", "warning", `Orca shows integrations without models.providers entries: ${undeclared.join(", ")} \u2014 add a provider block (bin/tui) or ignore`);
|
|
5268
|
+
const extrasByRole = config.models.routing.mode === "catalog" ? Object.fromEntries(await Promise.all(MODEL_ROLES.map(async (role) => [role, await resolveCatalogCandidates({
|
|
5269
|
+
config,
|
|
5270
|
+
role,
|
|
5271
|
+
availableProviderIds: providers.filter((provider) => provider.available).map((provider) => provider.id),
|
|
5272
|
+
runner: input.runner,
|
|
5273
|
+
stateDir: loaded.stateDir,
|
|
5274
|
+
env: input.env,
|
|
5275
|
+
now: now4
|
|
5276
|
+
})]))) : {};
|
|
5277
|
+
const routing = routeAllRoles(config, providers, extrasByRole);
|
|
4815
5278
|
for (const role of MODEL_ROLES) {
|
|
4816
5279
|
const decision = routing[role];
|
|
4817
|
-
|
|
5280
|
+
const selected = decision.selected;
|
|
5281
|
+
push(
|
|
5282
|
+
`routing.${role}`,
|
|
5283
|
+
selected ? "passed" : "failed",
|
|
5284
|
+
selected ? `${selected.provider}/${selected.model} \xB7 mode ${config.models.routing.mode} \xB7 ${selected.reason}${selected.remainingPercent !== null ? ` \xB7 remaining ${selected.remainingPercent}%` : ""}` : `no available provider (${decision.skipped.length} skipped; mode ${config.models.routing.mode})`
|
|
5285
|
+
);
|
|
4818
5286
|
}
|
|
4819
5287
|
let worktrees = [];
|
|
4820
5288
|
let workersError = null;
|
|
@@ -5473,7 +5941,17 @@ var gatherLoopState = async (input) => {
|
|
|
5473
5941
|
fetchLinearQueue(input.runner, { bin: config.orca.bin, workspaceId: config.linear.workspaceId, teamKey: config.linear.teamKey, assignee: config.linear.person, filter: config.linear, orca })
|
|
5474
5942
|
]);
|
|
5475
5943
|
const providers = await detectProviders({ providers: providerSpecs(config), accountList, agentHooks, env: input.env, platform: input.platform, exhaustedPercent: config.models.cooldown.exhaustedPercent, cooldowns: activeCooldowns(readCooldowns(input.loaded.stateDir), input.now()), now: input.now });
|
|
5476
|
-
const
|
|
5944
|
+
const availableIds = providers.filter((provider) => provider.available).map((provider) => provider.id);
|
|
5945
|
+
const extrasByRole = config.models.routing.mode === "catalog" ? Object.fromEntries(await Promise.all(MODEL_ROLES.map(async (role) => [role, await resolveCatalogCandidates({
|
|
5946
|
+
config,
|
|
5947
|
+
role,
|
|
5948
|
+
availableProviderIds: availableIds,
|
|
5949
|
+
runner: input.runner,
|
|
5950
|
+
stateDir: input.loaded.stateDir,
|
|
5951
|
+
env: input.env,
|
|
5952
|
+
now: input.now
|
|
5953
|
+
})]))) : {};
|
|
5954
|
+
const routing = routeAllRoles(config, providers, extrasByRole);
|
|
5477
5955
|
const running = countRunningWorkers(worktrees);
|
|
5478
5956
|
const slots = assessSlots({ machine: config.machine, running, platform: input.platform, ...input.machine });
|
|
5479
5957
|
const leases = input.ledger.active();
|
|
@@ -5515,7 +5993,16 @@ var runTick = async (input) => {
|
|
|
5515
5993
|
const results = [];
|
|
5516
5994
|
const state = await gatherLoopState({ loaded, runner: input.runner, ledger, env: input.env, platform: input.platform, now: now4, onlyIssue: input.onlyIssue, machine: input.machine });
|
|
5517
5995
|
const orchestrator = state.routing["orchestrator"] ?? { role: "orchestrator", selected: null, skipped: [] };
|
|
5518
|
-
const
|
|
5996
|
+
const orchestratorExtras = config.models.routing.mode === "catalog" ? await resolveCatalogCandidates({
|
|
5997
|
+
config,
|
|
5998
|
+
role: "orchestrator",
|
|
5999
|
+
availableProviderIds: state.providers.filter((provider) => provider.available).map((provider) => provider.id),
|
|
6000
|
+
runner: input.runner,
|
|
6001
|
+
stateDir: loaded.stateDir,
|
|
6002
|
+
env: input.env,
|
|
6003
|
+
now: now4
|
|
6004
|
+
}) : [];
|
|
6005
|
+
const orchestratorCandidates = rankModels(config, "orchestrator", state.providers, orchestratorExtras);
|
|
5519
6006
|
const onProviderFailure = (failure) => {
|
|
5520
6007
|
if (dryRun) return;
|
|
5521
6008
|
const entry = markProviderExhausted(loaded.stateDir, failure.provider, { initialMin: config.models.cooldown.initialMin, maxMin: config.models.cooldown.maxMin, reason: `${failure.kind}: ${(failure.detail.split("\n")[0] ?? "").slice(0, 200)}`, now: now4() });
|
|
@@ -6016,7 +6503,16 @@ var runDeliver = async (input) => {
|
|
|
6016
6503
|
const orca = orcaOptions(config);
|
|
6017
6504
|
const [accountList, agentHooks] = await Promise.all([orcaAccountList(input.runner, orca).catch(() => ({})), orcaAgentHooks(input.runner, orca).catch(() => ({}))]);
|
|
6018
6505
|
const providers = await detectProviders({ providers: providerSpecs(config), accountList, agentHooks, env: input.env, platform: input.platform, exhaustedPercent: config.models.cooldown.exhaustedPercent, cooldowns: activeCooldowns(readCooldowns(loaded.stateDir), now4()), now: now4 });
|
|
6019
|
-
const
|
|
6506
|
+
const reviewerExtras = config.models.routing.mode === "catalog" ? await resolveCatalogCandidates({
|
|
6507
|
+
config,
|
|
6508
|
+
role: "reviewer",
|
|
6509
|
+
availableProviderIds: providers.filter((provider) => provider.available).map((provider) => provider.id),
|
|
6510
|
+
runner: input.runner,
|
|
6511
|
+
stateDir: loaded.stateDir,
|
|
6512
|
+
env: input.env,
|
|
6513
|
+
now: now4
|
|
6514
|
+
}) : [];
|
|
6515
|
+
const reviewer = rankModels(config, "reviewer", providers, reviewerExtras)[0] ?? null;
|
|
6020
6516
|
let env = input.env ?? process.env;
|
|
6021
6517
|
if (!env["GITHUB_TOKEN"] && !env["GH_TOKEN"]) {
|
|
6022
6518
|
try {
|
|
@@ -7114,6 +7610,6 @@ var watchDeliveries = async (input) => {
|
|
|
7114
7610
|
};
|
|
7115
7611
|
var formatWatchEvent = (event2) => `${event2.kind}: ${event2.issue} \xB7 ${event2.message}`;
|
|
7116
7612
|
|
|
7117
|
-
export { AGENT_REGISTRY_SCHEMA_VERSION, ARTIFACT_SCHEMA_VERSION, ARTIFACT_TYPES, ASSURANCE_LEVELS, AgentRegistryEntrySchema, AgentRegistrySchema, BENCHMARK_SCHEMA_VERSION, BLOCK_STATUSES, CAPABILITY_KINDS, CAPABILITY_MANIFEST_SCHEMA_VERSION, COMPATIBILITY_COMPONENTS, COMPATIBILITY_SCHEMA_VERSION, CONTEXT_PROVIDER_SLOT, CONTRACT_CLOSE, CONTRACT_OPEN, CONTRACT_SCHEMA_VERSION, ContractOutcomeSchema, EVAL_COMPONENTS, EVAL_LAYERS, EVAL_MANIFEST_SCHEMA_VERSION, EVENT_LOG_GENESIS, EVIDENCE_BUNDLE_SCHEMA_VERSION, FileArtifactStore, FileEventStore, HARNESS_ERROR_CATALOG, HARNESS_ERROR_CODES, HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION, HARNESS_EVENT_SCHEMA_VERSION, HARNESS_EVENT_TYPES, HARNESS_PLUGIN_API_VERSION, HARNESS_REPO_URL, HarnessError, IMPROVEMENT_CYCLE_STEPS, LEARNING_STATUSES, LEGAL_TRANSITIONS, LOOP_CONFIG_FILE, LOOP_CONFIG_SCHEMA_VERSION, LOOP_LOCAL_CONFIG_FILE, LOOP_STAGES, LoopConfigSchema, MEMORY_SCOPES, MODEL_ROLES, PHASE_DECISIONS, PHASE_EFFECTS, PHASE_EFFECT_ACTIONS, PHASE_MODES, PR_FIELDS, QUALITY_DIMENSIONS, REVIEW_SEVERITIES, STATES, TaskContractSchema, WIP_STATES, activeCooldowns, adaptiveConcurrency, appendLoopEvent, approveRun, approvedDecision, artifactDigest, artifactFilePath, artifactIsFresh, artifactMarkdownPath, assertHuman, assessAcceptance, assessAgentEval, assessBlock, assessChecks, assessCompatibility, assessContract, assessDiscovery, assessImprovementCycle, assessIntegration, assessPilot, assessPreflight, assessProduction, assessQaTransition, assessSlots, assessWip, assessWorktreeCleanup, atLeast, authStatusFor, authorizeRun, automationName, automationPrompt, automationSpecs, availableMemoryBytes, benchmarkRuns, branchFor, buildDebriefReport, buildListIssuesArgv, buildRetroReport, buildReviewArgv, buildSuggestions, busyIssues, cancelRun, classifyFailure, classifyHarnessError, classifyProviderFailure, classifyWatchEvent, classifyWatchPhase, cleanTaskArtifacts, clearProviderCooldown, compareOptimization, compareVersions, composePullRequest, contractIsFresh, contractPath, cooldownPath, cooldownUntil, countRunningWorkers, createArgvRagContextProvider, createArtifactEnvelope, createCapabilityManifest, createCodingAgentAdapter, createCompatibilityManifest, createConfiguredToolRuntime, createDispatchLedger, createDocBridgeContextProvider, createDockerToolRuntime, createEvalManifest, createFileMemoryAdapter, createFileMemoryKvStore, createHarnessEventEnvelope, createInMemoryMemoryAdapter, createKvMemoryAdapter, createLinearTrackingAdapter, createLlmCache, createLlmCacheKey, createMachineMonitor, createMcpToolBridge, createModelPolicy, createOrcaDispatchPlan, createOrcaLifecycleProjection, createPhaseArtifact, createPhaseProfile, createPluginRegistry, createPluginSlot, createPolicyGate, createProcessRunner, createProcessToolRuntime, createPullRequestApproval, createQualityMatrix, createRagContextProvider, createRichIO, createSessionRecorder, createStatusSnapshot, createToolRuntime, createTrackingAdapter, createTrackingTransition, deliveryStatePath, detectProviders, dispatchRecordPath, evaluateWatchdog, executePhaseProfile, exportEvidenceBundle, fetchLinearIssue, fetchLinearQueue, fetchTeamMembers, filterAndOrderQueue, findExecutable, formatWatchEvent, gatherLoopState, generateContract, githubComment, githubCommentArgv, githubCommentExists, githubMerge, githubMergeArgv, githubOpenPullRequests, githubPullRequest, githubPullRequestsForBranch, hasLocalConfig, hashContextSnapshot, hashContextSnapshots, hashMcpArgs, inspectDocBridgeIndex, inspectEventLogLock, installLoopAutomations, installPreflight, isDiscoveryCurrent, isWsl, launchWorkerTerminal, learningToMemoryRecord, learningsPath, linearAttach, linearAttachArgv, linearCommentAdd, linearCommentAddArgv, linearLabelAdd, linearLabelArgv, linearLabelRemove, linearStatusSet, linearStatusSetArgv, listDispatched, loadAgentRegistry, loadBenchmarkManifest, loadConfig, loadLatestRun, loadLoopConfig, localConfigPath, loopStatus, markProviderExhausted, memoryDigestOf, mergeLoopConfig, modelFor, normalizeReason, openLoopMemory, orcaAccountList, orcaAgentHooks, orcaAutomationCreateArgv, orcaAutomationEditArgv, orcaAutomationRemove, orcaAutomationRun, orcaAutomationRuns, orcaAutomationsList, orcaJson, orcaStatus, orcaTerminalCreate, orcaTerminalList, orcaTerminalScreen, orcaTerminalSend, orcaTerminalWait, orcaVersion, orcaWorktreeCreate, orcaWorktreeRemove, orcaWorktreeSet, orcaWorktreeSetArgv, orcaWorktrees, parseAgentRegistryText, parseAutomationRuns, parseContractOutput, parseJsonEnvelope, parseLinearIssueDetail, parseLinearIssues, parseLoopConfigText, parseMemInfo, parseModelRef, parseOrcaAgentHooks, parseOrcaAutomations, parseOrcaSendReceipt, parseOrcaStatus, parseOrcaTerminals, parseOrcaVersion, parseOrcaWorktreeCreate, parseOrcaWorktrees, parseProviderUsage, parsePullRequest, parseRagQueryOutput, parseRetro, parseReviewResult, parseSince, parseTeamMembers, parseUsageWindows, parseVmStat, planFilePreflight, planMemoryContext, planPhaseProfile, planRun, precheckCommand, precheckDeliver, precheckTick, preferMemoryOverDocBridge, promoteLearnings, promoteLearningsToMemory, promptLocalConfig, providerIdentity, providerSpecs, rankModels, readArtifactFile, readContextSnapshots, readCooldowns, readDeliveryState, readDispatchRecord, readEvidenceTrustStore, readLearningsLedger, readLoopEvents, readStoredContract, reconcileRun, recordBenchmarkObservation, recoverEventLogLock, recoveryDelayMs, renderArtifactMarkdown, renderContractPrompt, renderDebriefMarkdown, renderFindingsForWorker, renderHeadlessArgv, renderLocalConfig, renderRetroMarkdown, renderTuiCommand, renderWorkerBrief, resolveAgentForRole, resolveDocContext, resumeStateFromArtifacts, retroLearnings, retryRun, routeAllRoles, runAdversarialReview, runAgentEval, runCodeReview, runDeliver, runEvalBattery, runGuidedInstall, runLoopDoctor, runRetroStage, runTick, runWithRecovery, runWorkflow, sampleMachine, selectMemoryForPrompt, selectModel, selectRuntime, severityRank, shellQuote, snapshotWatchTargets, startRun, summarizeMachine, tiersFor, touchesProtectedPaths, transition, uninstallLoopAutomations, unknownTelemetry, untrusted, upsertProposedLearnings, validateAdapterMetadata, validateArtifactEnvelope, validateBenchmarkManifest, validateBlockManifest, validateCacheableOperation, validateCapabilityManifest, validateCompatibilityManifest, validateConfig, validateContextSnapshot, validateContextSnapshots, validateEvalManifest, validateHarnessErrorClassification, validateHarnessEventEnvelope, validateLoopConfig, validateMemoryRecord, validateOptimizationObservation, validatePhaseTelemetry, validateSafeCommand, validateStatusSnapshot, verifyEvidenceBundle, verifyPullRequestApproval, verifyRun, watchDeliveries, worktreeNameFor, writeIdFor, writeLearningsLedger, writeLocalConfig, writeStoredContract };
|
|
7613
|
+
export { AGENT_REGISTRY_SCHEMA_VERSION, ARTIFACT_SCHEMA_VERSION, ARTIFACT_TYPES, ASSURANCE_LEVELS, AgentRegistryEntrySchema, AgentRegistrySchema, BENCHMARK_SCHEMA_VERSION, BLOCK_STATUSES, CAPABILITY_KINDS, CAPABILITY_MANIFEST_SCHEMA_VERSION, COMPATIBILITY_COMPONENTS, COMPATIBILITY_SCHEMA_VERSION, CONTEXT_PROVIDER_SLOT, CONTRACT_CLOSE, CONTRACT_OPEN, CONTRACT_SCHEMA_VERSION, ContractOutcomeSchema, EVAL_COMPONENTS, EVAL_LAYERS, EVAL_MANIFEST_SCHEMA_VERSION, EVENT_LOG_GENESIS, EVIDENCE_BUNDLE_SCHEMA_VERSION, FileArtifactStore, FileEventStore, HARNESS_ERROR_CATALOG, HARNESS_ERROR_CODES, HARNESS_EVENT_ENVELOPE_SCHEMA_VERSION, HARNESS_EVENT_SCHEMA_VERSION, HARNESS_EVENT_TYPES, HARNESS_PLUGIN_API_VERSION, HARNESS_REPO_URL, HarnessError, IMPROVEMENT_CYCLE_STEPS, LEARNING_STATUSES, LEGAL_TRANSITIONS, LOOP_CONFIG_FILE, LOOP_CONFIG_SCHEMA_VERSION, LOOP_LOCAL_CONFIG_FILE, LOOP_STAGES, LoopConfigSchema, MEMORY_SCOPES, MODEL_ROLES, PHASE_DECISIONS, PHASE_EFFECTS, PHASE_EFFECT_ACTIONS, PHASE_MODES, PR_FIELDS, QUALITY_DIMENSIONS, REVIEW_SEVERITIES, STATES, TaskContractSchema, WIP_STATES, activeCooldowns, adaptiveConcurrency, appendLoopEvent, approveRun, approvedDecision, artifactDigest, artifactFilePath, artifactIsFresh, artifactMarkdownPath, assertHuman, assessAcceptance, assessAgentEval, assessBlock, assessChecks, assessCompatibility, assessContract, assessDiscovery, assessImprovementCycle, assessIntegration, assessPilot, assessPreflight, assessProduction, assessQaTransition, assessSlots, assessWip, assessWorktreeCleanup, atLeast, authStatusFor, authorizeRun, automationName, automationPrompt, automationSpecs, availableMemoryBytes, benchmarkRuns, branchFor, buildDebriefReport, buildListIssuesArgv, buildRetroReport, buildReviewArgv, buildSuggestions, busyIssues, cancelRun, classifyFailure, classifyHarnessError, classifyProviderFailure, classifyWatchEvent, classifyWatchPhase, cleanTaskArtifacts, clearProviderCooldown, compareOptimization, compareVersions, composePullRequest, contractIsFresh, contractPath, cooldownPath, cooldownUntil, countRunningWorkers, createArgvRagContextProvider, createArtifactEnvelope, createCapabilityManifest, createCodingAgentAdapter, createCompatibilityManifest, createConfiguredToolRuntime, createDispatchLedger, createDocBridgeContextProvider, createDockerToolRuntime, createEvalManifest, createFileMemoryAdapter, createFileMemoryKvStore, createHarnessEventEnvelope, createInMemoryMemoryAdapter, createKvMemoryAdapter, createLinearTrackingAdapter, createLlmCache, createLlmCacheKey, createMachineMonitor, createMcpToolBridge, createModelPolicy, createOrcaDispatchPlan, createOrcaLifecycleProjection, createPhaseArtifact, createPhaseProfile, createPluginRegistry, createPluginSlot, createPolicyGate, createProcessRunner, createProcessToolRuntime, createPullRequestApproval, createQualityMatrix, createRagContextProvider, createRichIO, createSessionRecorder, createStatusSnapshot, createToolRuntime, createTrackingAdapter, createTrackingTransition, deliveryStatePath, detectProviders, dispatchRecordPath, evaluateWatchdog, executePhaseProfile, exportEvidenceBundle, fetchArtificialAnalysisModels, fetchLinearIssue, fetchLinearQueue, fetchTeamMembers, filterAndOrderQueue, findExecutable, formatWatchEvent, gatherLoopState, generateContract, githubComment, githubCommentArgv, githubCommentExists, githubMerge, githubMergeArgv, githubOpenPullRequests, githubPullRequest, githubPullRequestsForBranch, hasLocalConfig, hashContextSnapshot, hashContextSnapshots, hashMcpArgs, inspectDocBridgeIndex, inspectEventLogLock, installLoopAutomations, installPreflight, isDiscoveryCurrent, isWsl, launchWorkerTerminal, learningToMemoryRecord, learningsPath, linearAttach, linearAttachArgv, linearCommentAdd, linearCommentAddArgv, linearLabelAdd, linearLabelArgv, linearLabelRemove, linearStatusSet, linearStatusSetArgv, listCliModels, listDispatched, loadAgentRegistry, loadAliases, loadBenchmarkManifest, loadBuiltinCatalog, loadConfig, loadLatestRun, loadLoopConfig, localConfigPath, loopStatus, markProviderExhausted, memoryDigestOf, mergeLoopConfig, modelFor, normalizeReason, openLoopMemory, orcaAccountList, orcaAgentHooks, orcaAutomationCreateArgv, orcaAutomationEditArgv, orcaAutomationRemove, orcaAutomationRun, orcaAutomationRuns, orcaAutomationsList, orcaJson, orcaStatus, orcaTerminalCreate, orcaTerminalList, orcaTerminalScreen, orcaTerminalSend, orcaTerminalWait, orcaVersion, orcaWorktreeCreate, orcaWorktreeRemove, orcaWorktreeSet, orcaWorktreeSetArgv, orcaWorktrees, parseAgentRegistryText, parseArtificialAnalysisPayload, parseAutomationRuns, parseContractOutput, parseGrokModelsOutput, parseJsonEnvelope, parseLinearIssueDetail, parseLinearIssues, parseLoopConfigText, parseMemInfo, parseModelRef, parseOrcaAgentHooks, parseOrcaAutomations, parseOrcaSendReceipt, parseOrcaStatus, parseOrcaTerminals, parseOrcaVersion, parseOrcaWorktreeCreate, parseOrcaWorktrees, parseProviderUsage, parsePullRequest, parseRagQueryOutput, parseRetro, parseReviewResult, parseSince, parseTeamMembers, parseUsageWindows, parseVmStat, planFilePreflight, planMemoryContext, planPhaseProfile, planRun, precheckCommand, precheckDeliver, precheckTick, preferMemoryOverDocBridge, promoteLearnings, promoteLearningsToMemory, promptLocalConfig, providerIdentity, providerSpecs, rankModels, readAaCache, readArtifactFile, readContextSnapshots, readCooldowns, readDeliveryState, readDispatchRecord, readEvidenceTrustStore, readLearningsLedger, readLoopEvents, readStoredContract, reconcileRun, recordBenchmarkObservation, recoverEventLogLock, recoveryDelayMs, remainingUsagePercent, renderArtifactMarkdown, renderContractPrompt, renderDebriefMarkdown, renderFindingsForWorker, renderHeadlessArgv, renderLocalConfig, renderRetroMarkdown, renderTuiCommand, renderWorkerBrief, resolveAgentForRole, resolveAlias, resolveCatalogCandidates, resolveDocContext, resumeStateFromArtifacts, retroLearnings, retryRun, routeAllRoles, runAdversarialReview, runAgentEval, runCodeReview, runDeliver, runEvalBattery, runGuidedInstall, runLoopDoctor, runRetroStage, runTick, runWithRecovery, runWorkflow, sampleMachine, selectMemoryForPrompt, selectModel, selectRuntime, severityRank, shellQuote, snapshotWatchTargets, startRun, summarizeMachine, tiersFor, touchesProtectedPaths, transition, undeclaredOrcaProviders, uninstallLoopAutomations, unknownTelemetry, untrusted, upsertProposedLearnings, usageRankTuple, validateAdapterMetadata, validateArtifactEnvelope, validateBenchmarkManifest, validateBlockManifest, validateCacheableOperation, validateCapabilityManifest, validateCompatibilityManifest, validateConfig, validateContextSnapshot, validateContextSnapshots, validateEvalManifest, validateHarnessErrorClassification, validateHarnessEventEnvelope, validateLoopConfig, validateMemoryRecord, validateOptimizationObservation, validatePhaseTelemetry, validateSafeCommand, validateStatusSnapshot, verifyEvidenceBundle, verifyPullRequestApproval, verifyRun, watchDeliveries, worktreeNameFor, writeAaCache, writeIdFor, writeLearningsLedger, writeLocalConfig, writeStoredContract };
|
|
7118
7614
|
//# sourceMappingURL=index.js.map
|
|
7119
7615
|
//# sourceMappingURL=index.js.map
|