@kynver-app/runtime 0.1.21 → 0.1.22
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/dist/cli.js +144 -20
- package/dist/cli.js.map +4 -4
- package/dist/index.js +144 -20
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -182,7 +182,7 @@ function resolveConfiguredBaseUrl(argsBaseUrl) {
|
|
|
182
182
|
return baseUrl ? trimTrailingSlash(String(baseUrl)) : void 0;
|
|
183
183
|
}
|
|
184
184
|
function resolveConfiguredCallbackSecret(argsSecret, agentOsId) {
|
|
185
|
-
const scoped = argsSecret || loadRunnerToken(agentOsId) || loadRunnerToken(loadUserConfig().agentOsId);
|
|
185
|
+
const scoped = argsSecret || loadRunnerToken(agentOsId) || (agentOsId ? void 0 : loadRunnerToken(loadUserConfig().agentOsId));
|
|
186
186
|
if (scoped) return String(scoped);
|
|
187
187
|
const globalSecret = process.env.KYNVER_RUNTIME_SECRET || process.env.OPENCLAW_CRON_SECRET;
|
|
188
188
|
if (globalSecret) {
|
|
@@ -223,6 +223,23 @@ async function refreshRunnerToken(agentOsId, opts) {
|
|
|
223
223
|
return null;
|
|
224
224
|
}
|
|
225
225
|
}
|
|
226
|
+
async function refreshRunnerTokenForAuthFailure(rejectedSecret, agentOsId, opts) {
|
|
227
|
+
const apiKey = loadApiKey();
|
|
228
|
+
const baseUrl = resolveConfiguredBaseUrl(opts?.baseUrl);
|
|
229
|
+
if (!apiKey) return { ok: false, reason: "KYNVER_API_KEY is required to refresh a rejected runner token" };
|
|
230
|
+
if (!agentOsId) return { ok: false, reason: "agentOsId is required to refresh a rejected runner token" };
|
|
231
|
+
if (!baseUrl) return { ok: false, reason: "KYNVER_API_URL or --base-url is required to refresh a rejected runner token" };
|
|
232
|
+
try {
|
|
233
|
+
const token = await fetchRunnerCredential(agentOsId, { baseUrl, apiKey });
|
|
234
|
+
if (token && token !== rejectedSecret) {
|
|
235
|
+
saveRunnerToken(agentOsId, token);
|
|
236
|
+
return { ok: true, token };
|
|
237
|
+
}
|
|
238
|
+
return { ok: false, reason: "runner credential refresh returned the rejected token" };
|
|
239
|
+
} catch (error) {
|
|
240
|
+
return { ok: false, reason: error.message };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
226
243
|
async function fetchRunnerCredential(agentOsId, opts) {
|
|
227
244
|
const apiKey = opts?.apiKey || loadApiKey();
|
|
228
245
|
if (!apiKey) throw new Error("API key required \u2014 run `kynver login` first");
|
|
@@ -375,6 +392,14 @@ async function postJson(url, secret, body) {
|
|
|
375
392
|
}
|
|
376
393
|
return { ok: res.ok, status: res.status, response };
|
|
377
394
|
}
|
|
395
|
+
async function postJsonWithCredentialRefresh(url, secret, body, opts) {
|
|
396
|
+
const first = await postJson(url, secret, body);
|
|
397
|
+
if (first.ok || first.status !== 401) return first;
|
|
398
|
+
const refreshed = await refreshRunnerTokenForAuthFailure(secret, opts.agentOsId, { baseUrl: opts.baseUrl });
|
|
399
|
+
if (!refreshed.ok) return { ...first, authRefreshFailure: refreshed.reason };
|
|
400
|
+
const retry = await postJson(url, refreshed.token, body);
|
|
401
|
+
return { ...retry, refreshedAuth: true };
|
|
402
|
+
}
|
|
378
403
|
async function getJson(url, secret) {
|
|
379
404
|
const res = await fetch(url, {
|
|
380
405
|
method: "GET",
|
|
@@ -1055,6 +1080,59 @@ function observeRunnerResourceGate(input) {
|
|
|
1055
1080
|
};
|
|
1056
1081
|
}
|
|
1057
1082
|
|
|
1083
|
+
// src/model-routing-task-enrich.ts
|
|
1084
|
+
function taskString(task, key) {
|
|
1085
|
+
const v = task[key];
|
|
1086
|
+
return typeof v === "string" ? v.trim() : "";
|
|
1087
|
+
}
|
|
1088
|
+
function normalize(value) {
|
|
1089
|
+
return value.toLowerCase();
|
|
1090
|
+
}
|
|
1091
|
+
var PERSONA_DEFAULT_LANE = {
|
|
1092
|
+
dalton: "implementer",
|
|
1093
|
+
lorentz: "report_reviewer"
|
|
1094
|
+
};
|
|
1095
|
+
function inferRoleLaneFromTask(task) {
|
|
1096
|
+
const existing = taskString(task, "roleLane");
|
|
1097
|
+
if (existing) return existing;
|
|
1098
|
+
const ref = normalize(taskString(task, "executorRef"));
|
|
1099
|
+
const title = normalize(taskString(task, "title"));
|
|
1100
|
+
const persona = normalize(taskString(task, "personaSlug"));
|
|
1101
|
+
const combined = `${ref} ${title}`;
|
|
1102
|
+
if (combined.includes("deep review") || combined.includes("security review") || ref.includes("deep-reviewer")) {
|
|
1103
|
+
return "deep_reviewer";
|
|
1104
|
+
}
|
|
1105
|
+
if (combined.includes("plan author") || combined.includes("plan-author") || title.includes("strategy plan")) {
|
|
1106
|
+
return "plan_author";
|
|
1107
|
+
}
|
|
1108
|
+
if (combined.includes("plan review") || ref.includes("plan-reviewer")) {
|
|
1109
|
+
return "plan_reviewer";
|
|
1110
|
+
}
|
|
1111
|
+
if (combined.includes("report review") || combined.includes("completion report")) {
|
|
1112
|
+
return "report_reviewer";
|
|
1113
|
+
}
|
|
1114
|
+
if (combined.includes("repair") || title.startsWith("fix ") || ref.includes("repair")) {
|
|
1115
|
+
return "repair_implementer";
|
|
1116
|
+
}
|
|
1117
|
+
if (ref.includes("cursor") || ref.includes("codex") || ref.includes("composer") || title.includes("implement") || title.includes("land:")) {
|
|
1118
|
+
return "implementer";
|
|
1119
|
+
}
|
|
1120
|
+
if (persona && PERSONA_DEFAULT_LANE[persona]) {
|
|
1121
|
+
const base = PERSONA_DEFAULT_LANE[persona];
|
|
1122
|
+
if (persona === "lorentz" && (combined.includes("deep") || combined.includes("security"))) {
|
|
1123
|
+
return "deep_reviewer";
|
|
1124
|
+
}
|
|
1125
|
+
return base;
|
|
1126
|
+
}
|
|
1127
|
+
if (combined.includes("review")) return "report_reviewer";
|
|
1128
|
+
return void 0;
|
|
1129
|
+
}
|
|
1130
|
+
function enrichTaskForModelRouting(task) {
|
|
1131
|
+
const roleLane = inferRoleLaneFromTask(task);
|
|
1132
|
+
if (!roleLane) return task;
|
|
1133
|
+
return { ...task, roleLane };
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1058
1136
|
// src/providers/claude.ts
|
|
1059
1137
|
import { closeSync, openSync } from "node:fs";
|
|
1060
1138
|
import { spawn } from "node:child_process";
|
|
@@ -1161,7 +1239,7 @@ var claudeProvider = {
|
|
|
1161
1239
|
|
|
1162
1240
|
// src/model-routing.ts
|
|
1163
1241
|
var GLOBAL_DEFAULT_MODEL = "claude-sonnet-4-6";
|
|
1164
|
-
function
|
|
1242
|
+
function taskString2(task, key) {
|
|
1165
1243
|
const v = task[key];
|
|
1166
1244
|
return typeof v === "string" ? v.trim() : "";
|
|
1167
1245
|
}
|
|
@@ -1194,10 +1272,10 @@ function isOpusLane(ref, title) {
|
|
|
1194
1272
|
return false;
|
|
1195
1273
|
}
|
|
1196
1274
|
function inferModelRoutingFromTask(task) {
|
|
1197
|
-
const ref = normalizeRef(
|
|
1198
|
-
const title =
|
|
1199
|
-
const priority =
|
|
1200
|
-
const roleLane = normalizeRef(
|
|
1275
|
+
const ref = normalizeRef(taskString2(task, "executorRef"));
|
|
1276
|
+
const title = taskString2(task, "title").toLowerCase();
|
|
1277
|
+
const priority = taskString2(task, "priority") || "normal";
|
|
1278
|
+
const roleLane = normalizeRef(taskString2(task, "roleLane"));
|
|
1201
1279
|
if (ref.includes("cursor") || ref.includes("codex") || ref.includes("composer") || ref.includes("copilot") || roleLane === "implementer" || roleLane === "repair_implementer") {
|
|
1202
1280
|
return { provider: "cursor", rule: "lane:implementation" };
|
|
1203
1281
|
}
|
|
@@ -1220,6 +1298,9 @@ function inferModelRoutingFromTask(task) {
|
|
|
1220
1298
|
if (priority === "critical") {
|
|
1221
1299
|
return { model: "claude-opus-4-7", provider: "claude", rule: "priority:critical" };
|
|
1222
1300
|
}
|
|
1301
|
+
if (priority === "high") {
|
|
1302
|
+
return { model: "claude-sonnet-4-6", provider: "claude", rule: "priority:high" };
|
|
1303
|
+
}
|
|
1223
1304
|
if (priority === "low") {
|
|
1224
1305
|
return {
|
|
1225
1306
|
model: "claude-haiku-4-5-20251001",
|
|
@@ -1296,6 +1377,14 @@ function buildPrompt(input) {
|
|
|
1296
1377
|
"- After implementation: wait for report_reviewer then deep_reviewer confirmation (via MCP/session agents) before follow-up rows close.",
|
|
1297
1378
|
input.planId ? `Active planId: ${input.planId}${input.taskId ? ` \xB7 taskId: ${input.taskId}` : ""}` : "No planId on this worker \u2014 still emit progress when you touch plan-scoped work."
|
|
1298
1379
|
];
|
|
1380
|
+
const planArtifactLines = compact ? [
|
|
1381
|
+
"Plan artifacts: when authoring/revising docs/superpowers/plans/, open a GitHub PR early and iterate from that PR branch; do not leave the canonical plan only in the harness worktree."
|
|
1382
|
+
] : [
|
|
1383
|
+
"PR-first plan artifacts (when authoring or revising docs/superpowers/plans/):",
|
|
1384
|
+
"- Before substantial plan drafting: create a feature branch, open a GitHub PR (draft OK), commit and push the plan file \u2014 do not leave the canonical plan only in this harness worktree.",
|
|
1385
|
+
"- Iterate review on that PR branch; link prUrl on the AgentOS task and plan progress evidence (`--evidence pr:<url>`).",
|
|
1386
|
+
"- See docs/superpowers/plans/2026-05-25-pr-first-plan-artifact-preservation.md for the full checklist."
|
|
1387
|
+
];
|
|
1299
1388
|
return [
|
|
1300
1389
|
"You are running under the Kynver AgentOS runtime.",
|
|
1301
1390
|
"Immediately state your plan before editing.",
|
|
@@ -1307,6 +1396,8 @@ function buildPrompt(input) {
|
|
|
1307
1396
|
"",
|
|
1308
1397
|
...progressLines,
|
|
1309
1398
|
"",
|
|
1399
|
+
...planArtifactLines,
|
|
1400
|
+
"",
|
|
1310
1401
|
"Task:",
|
|
1311
1402
|
input.task
|
|
1312
1403
|
].join("\n");
|
|
@@ -1618,8 +1709,8 @@ function workerStatus(args) {
|
|
|
1618
1709
|
writeJson(path8.join(worker.workerDir, "last-status.json"), status);
|
|
1619
1710
|
console.log(JSON.stringify(status, null, 2));
|
|
1620
1711
|
}
|
|
1621
|
-
function
|
|
1622
|
-
const run = loadRun(
|
|
1712
|
+
function buildRunBoard(runId) {
|
|
1713
|
+
const run = loadRun(runId);
|
|
1623
1714
|
const names = Object.keys(run.workers || {});
|
|
1624
1715
|
const workers = names.map((name) => {
|
|
1625
1716
|
const worker = readJson(
|
|
@@ -1674,6 +1765,34 @@ function runStatus(args) {
|
|
|
1674
1765
|
workers
|
|
1675
1766
|
};
|
|
1676
1767
|
writeJson(path8.join(runDirectory(run.id), "last-board.json"), board);
|
|
1768
|
+
return board;
|
|
1769
|
+
}
|
|
1770
|
+
async function publishHarnessBoardSnapshot(args, source) {
|
|
1771
|
+
const runId = String(args.run || "");
|
|
1772
|
+
const agentOsId = String(args.agentOsId || "");
|
|
1773
|
+
if (!runId || !agentOsId) return null;
|
|
1774
|
+
const board = buildRunBoard(runId);
|
|
1775
|
+
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
1776
|
+
const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, {
|
|
1777
|
+
baseUrl: base
|
|
1778
|
+
});
|
|
1779
|
+
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/harness/snapshot`;
|
|
1780
|
+
const res = await postJsonWithCredentialRefresh(
|
|
1781
|
+
url,
|
|
1782
|
+
secret,
|
|
1783
|
+
{ agentOsId, runId, source, snapshot: board },
|
|
1784
|
+
{ agentOsId, baseUrl: base }
|
|
1785
|
+
);
|
|
1786
|
+
return {
|
|
1787
|
+
ok: res.ok,
|
|
1788
|
+
httpStatus: res.status,
|
|
1789
|
+
response: res.response,
|
|
1790
|
+
authRefreshed: res.refreshedAuth,
|
|
1791
|
+
authRefreshFailure: res.authRefreshFailure
|
|
1792
|
+
};
|
|
1793
|
+
}
|
|
1794
|
+
function runStatus(args) {
|
|
1795
|
+
const board = buildRunBoard(String(args.run));
|
|
1677
1796
|
console.log(JSON.stringify(board, null, 2));
|
|
1678
1797
|
}
|
|
1679
1798
|
function tailWorker(args) {
|
|
@@ -2066,9 +2185,10 @@ async function dispatchRun(args) {
|
|
|
2066
2185
|
runnerDiskGate,
|
|
2067
2186
|
runnerResourceGate,
|
|
2068
2187
|
...args.lane ? { lane: String(args.lane) } : {},
|
|
2188
|
+
executor: args.executor ? String(args.executor) : "harness",
|
|
2069
2189
|
...args.diskPath ? { diskPath: String(args.diskPath) } : {}
|
|
2070
2190
|
};
|
|
2071
|
-
const dispatch = await
|
|
2191
|
+
const dispatch = await postJsonWithCredentialRefresh(dispatchUrl, secret, body, { agentOsId, baseUrl: base });
|
|
2072
2192
|
const responseBody = dispatch.response;
|
|
2073
2193
|
if (!dispatch.ok || !responseBody?.result) {
|
|
2074
2194
|
const failure = {
|
|
@@ -2076,7 +2196,9 @@ async function dispatchRun(args) {
|
|
|
2076
2196
|
agentOsId,
|
|
2077
2197
|
action: "dispatch",
|
|
2078
2198
|
httpStatus: dispatch.status,
|
|
2079
|
-
response: dispatch.response
|
|
2199
|
+
response: dispatch.response,
|
|
2200
|
+
authRefreshed: dispatch.refreshedAuth === true,
|
|
2201
|
+
authRefreshFailure: dispatch.authRefreshFailure
|
|
2080
2202
|
};
|
|
2081
2203
|
if (pipeline) return { ok: false, ...failure };
|
|
2082
2204
|
console.log(JSON.stringify(failure, null, 2));
|
|
@@ -2133,7 +2255,7 @@ async function dispatchRun(args) {
|
|
|
2133
2255
|
const name = safeSlug(`t-${task.id}-a${task.attempt}`);
|
|
2134
2256
|
const routing = resolveWorkerLaunch({
|
|
2135
2257
|
explicitModel: args.model ? String(args.model) : void 0,
|
|
2136
|
-
task
|
|
2258
|
+
task: enrichTaskForModelRouting(task)
|
|
2137
2259
|
});
|
|
2138
2260
|
try {
|
|
2139
2261
|
const planId = task.planId ? String(task.planId) : void 0;
|
|
@@ -2165,7 +2287,7 @@ async function dispatchRun(args) {
|
|
|
2165
2287
|
const releaseUrl = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/tasks/${encodeURIComponent(String(task.id))}/release`;
|
|
2166
2288
|
let release;
|
|
2167
2289
|
try {
|
|
2168
|
-
release = await
|
|
2290
|
+
release = await postJsonWithCredentialRefresh(releaseUrl, secret, { agentOsId, leaseOwner }, { agentOsId, baseUrl: base });
|
|
2169
2291
|
} catch (relErr) {
|
|
2170
2292
|
release = { ok: false, error: relErr.message };
|
|
2171
2293
|
}
|
|
@@ -2214,6 +2336,7 @@ async function sweepRun(args) {
|
|
|
2214
2336
|
const base = resolveBaseUrl(args.baseUrl ? String(args.baseUrl) : void 0);
|
|
2215
2337
|
const secret = await resolveCallbackSecretWithMint(args.secret ? String(args.secret) : void 0, agentOsId, { baseUrl: base });
|
|
2216
2338
|
const leaseOwner = `openclaw-harness:${run.id}`;
|
|
2339
|
+
const snapshotPublished = await publishHarnessBoardSnapshot({ run: run.id, agentOsId, ...args }, "run_sweep");
|
|
2217
2340
|
const releasedLocalOrphans = [];
|
|
2218
2341
|
for (const name of Object.keys(run.workers || {})) {
|
|
2219
2342
|
const worker = readJson(
|
|
@@ -2223,11 +2346,11 @@ async function sweepRun(args) {
|
|
|
2223
2346
|
if (!worker || !worker.dispatched || !worker.taskId) continue;
|
|
2224
2347
|
const status = computeWorkerStatus(worker);
|
|
2225
2348
|
if (status.alive) continue;
|
|
2226
|
-
if (status.finalResult) continue;
|
|
2349
|
+
if (status.finalResult || worker.completionReportedAt) continue;
|
|
2227
2350
|
const releaseUrl = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/tasks/${encodeURIComponent(String(worker.taskId))}/release`;
|
|
2228
2351
|
let release;
|
|
2229
2352
|
try {
|
|
2230
|
-
release = await
|
|
2353
|
+
release = await postJsonWithCredentialRefresh(releaseUrl, secret, { agentOsId, leaseOwner }, { agentOsId, baseUrl: base });
|
|
2231
2354
|
} catch (relErr) {
|
|
2232
2355
|
release = { ok: false, error: relErr.message };
|
|
2233
2356
|
}
|
|
@@ -2242,14 +2365,14 @@ async function sweepRun(args) {
|
|
|
2242
2365
|
const reapUrl = `${base}/api/agent-os/by-id/${encodeURIComponent(agentOsId)}/tasks/reap`;
|
|
2243
2366
|
let reap;
|
|
2244
2367
|
try {
|
|
2245
|
-
reap = await
|
|
2368
|
+
reap = await postJsonWithCredentialRefresh(reapUrl, secret, {
|
|
2246
2369
|
agentOsId,
|
|
2247
2370
|
...Number(args.graceMs) >= 0 && args.graceMs !== void 0 && args.graceMs !== true ? { graceMs: Math.floor(Number(args.graceMs)) } : {}
|
|
2248
|
-
});
|
|
2371
|
+
}, { agentOsId, baseUrl: base });
|
|
2249
2372
|
} catch (reapErr) {
|
|
2250
2373
|
reap = { ok: false, error: reapErr.message };
|
|
2251
2374
|
}
|
|
2252
|
-
const summary = { runId: run.id, agentOsId, leaseOwner, releasedLocalOrphans, reap: reap.response ?? reap };
|
|
2375
|
+
const summary = { runId: run.id, agentOsId, leaseOwner, snapshotPublished, releasedLocalOrphans, reap: reap.response ?? reap };
|
|
2253
2376
|
if (pipeline) return { ok: true, ...summary };
|
|
2254
2377
|
console.log(JSON.stringify(summary, null, 2));
|
|
2255
2378
|
} catch (error) {
|
|
@@ -2455,12 +2578,12 @@ async function syncPlanProgress(args) {
|
|
|
2455
2578
|
const base = resolveBaseUrl(args.baseUrl);
|
|
2456
2579
|
const secret = await resolveCallbackSecretWithMint(args.secret, args.agentOsId, { baseUrl: base });
|
|
2457
2580
|
const url = `${base}/api/agent-os/by-id/${encodeURIComponent(args.agentOsId)}/tasks/${encodeURIComponent(args.taskId)}/plan-progress-sync`;
|
|
2458
|
-
const res = await
|
|
2581
|
+
const res = await postJsonWithCredentialRefresh(url, secret, {
|
|
2459
2582
|
phase: args.phase,
|
|
2460
2583
|
taskId: args.taskId,
|
|
2461
2584
|
blocker: args.blocker,
|
|
2462
2585
|
artifact: args.artifact
|
|
2463
|
-
});
|
|
2586
|
+
}, { agentOsId: args.agentOsId, baseUrl: base });
|
|
2464
2587
|
return { ok: res.ok, status: res.status, response: res.response };
|
|
2465
2588
|
}
|
|
2466
2589
|
|
|
@@ -2551,6 +2674,7 @@ async function postOperatorTick(agentOsId, runId, resourceGate, args) {
|
|
|
2551
2674
|
agentOsId,
|
|
2552
2675
|
runId,
|
|
2553
2676
|
ingestHarness: true,
|
|
2677
|
+
harnessBoardSnapshot: buildRunBoard(runId),
|
|
2554
2678
|
resourceGate
|
|
2555
2679
|
});
|
|
2556
2680
|
return { ok: res.ok, httpStatus: res.status, response: res.response };
|
|
@@ -2762,7 +2886,7 @@ function usage(code = 0) {
|
|
|
2762
2886
|
" kynver run create --repo /path/repo [--name name] [--base origin/main]",
|
|
2763
2887
|
" kynver run list",
|
|
2764
2888
|
" kynver run status --run RUN_ID",
|
|
2765
|
-
" kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-7] [--disk-path /]",
|
|
2889
|
+
" kynver run dispatch --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--execute] [--lane any|implementation|review|landing] [--executor harness] [--max-starts 1] [--lease-ms MS] [--owned path[,path]] [--model claude-opus-4-7] [--disk-path /]",
|
|
2766
2890
|
" kynver run sweep --run RUN_ID --agent-os-id AOS_ID [--base-url URL] [--secret SECRET] [--grace-ms MS]",
|
|
2767
2891
|
' kynver worker start --run RUN_ID --name worker --task "..." [--owned path[,path]] [--model MODEL] [--provider claude|cursor] [--agent-os-id AOS_ID] [--task-id TASK_ID]',
|
|
2768
2892
|
" kynver worker status --run RUN_ID --name worker",
|