@kynver-app/runtime 0.1.17 → 0.1.18
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 +149 -8
- package/dist/cli.js.map +4 -4
- package/dist/index.js +149 -8
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -604,6 +604,52 @@ function summarizeEvent(event) {
|
|
|
604
604
|
return void 0;
|
|
605
605
|
}
|
|
606
606
|
|
|
607
|
+
// src/exit-classify.ts
|
|
608
|
+
var FAILURE_PATTERNS = [
|
|
609
|
+
{
|
|
610
|
+
test: /\b(?:invalid|unknown|unsupported|unrecognized)\b[^.\n]*\bmodel\b/i,
|
|
611
|
+
label: "provider rejected the requested model"
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
test: /\bmodel\b[^.\n]*\b(?:not\s+(?:found|supported|available|recognized|valid)|is\s+not\s+valid|does\s+not\s+exist)/i,
|
|
615
|
+
label: "provider rejected the requested model"
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
test: /\b(?:did you mean|available models|choose (?:a|one of)|supported models)\b/i,
|
|
619
|
+
label: "provider rejected the requested model"
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
test: /model preflight failed/i,
|
|
623
|
+
label: "model/provider preflight failed"
|
|
624
|
+
},
|
|
625
|
+
{
|
|
626
|
+
test: /\b(?:command not found|ENOENT|is the .*CLI on PATH|executable not found|no such file or directory)\b/i,
|
|
627
|
+
label: "provider CLI is missing or not on PATH"
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
test: /\bfailed to spawn\b/i,
|
|
631
|
+
label: "provider failed to spawn the worker process"
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
test: /\b(?:not logged in|unauthorized|authentication (?:failed|required)|invalid api key|missing api key|401)\b/i,
|
|
635
|
+
label: "provider authentication failed"
|
|
636
|
+
}
|
|
637
|
+
];
|
|
638
|
+
function tidy(errorText, max = 240) {
|
|
639
|
+
const oneLine2 = errorText.replace(/\s+/g, " ").trim();
|
|
640
|
+
return oneLine2.length > max ? `${oneLine2.slice(0, max - 1)}\u2026` : oneLine2;
|
|
641
|
+
}
|
|
642
|
+
function classifyExitFailure(errorText) {
|
|
643
|
+
const text = (errorText ?? "").trim();
|
|
644
|
+
if (!text) return null;
|
|
645
|
+
for (const pattern of FAILURE_PATTERNS) {
|
|
646
|
+
if (pattern.test.test(text)) {
|
|
647
|
+
return { blocked: true, reason: `${pattern.label}: ${tidy(text)}` };
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return null;
|
|
651
|
+
}
|
|
652
|
+
|
|
607
653
|
// src/git.ts
|
|
608
654
|
import { spawnSync } from "node:child_process";
|
|
609
655
|
function git(cwd, args, options = {}) {
|
|
@@ -719,7 +765,15 @@ var STALE_MS = 6e5;
|
|
|
719
765
|
function computeAttention(input) {
|
|
720
766
|
const now = Date.now();
|
|
721
767
|
if (input.finalResult) return { state: "done", reason: "final result recorded" };
|
|
722
|
-
if (!input.alive)
|
|
768
|
+
if (!input.alive) {
|
|
769
|
+
const classified = classifyExitFailure(input.error);
|
|
770
|
+
if (classified) return { state: "blocked", reason: classified.reason };
|
|
771
|
+
const tail = input.error?.trim();
|
|
772
|
+
return {
|
|
773
|
+
state: "needs_attention",
|
|
774
|
+
reason: tail ? `process exited without a final result: ${tail}` : "process exited without a final result"
|
|
775
|
+
};
|
|
776
|
+
}
|
|
723
777
|
if (input.heartbeatBlocker) {
|
|
724
778
|
return { state: "blocked", reason: `worker heartbeat reported blocker: ${input.heartbeatBlocker}` };
|
|
725
779
|
}
|
|
@@ -749,6 +803,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
749
803
|
fileMtime(worker.stderrPath),
|
|
750
804
|
fileMtime(worker.heartbeatPath)
|
|
751
805
|
]);
|
|
806
|
+
const error = parsed.error || (!alive && !parsed.finalResult ? tailFile(worker.stderrPath, 10).trim() || void 0 : void 0);
|
|
752
807
|
const attention = computeAttention({
|
|
753
808
|
alive,
|
|
754
809
|
finalResult: parsed.finalResult,
|
|
@@ -757,7 +812,8 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
757
812
|
heartbeatBytes,
|
|
758
813
|
lastActivityAt,
|
|
759
814
|
heartbeatBlocker: heartbeat.heartbeatBlocker,
|
|
760
|
-
startedAt: worker.startedAt
|
|
815
|
+
startedAt: worker.startedAt,
|
|
816
|
+
error
|
|
761
817
|
});
|
|
762
818
|
return {
|
|
763
819
|
runId: worker.runId,
|
|
@@ -782,7 +838,7 @@ function computeWorkerStatus(worker, options = {}) {
|
|
|
782
838
|
lastHeartbeatSummary: heartbeat.lastHeartbeatSummary,
|
|
783
839
|
heartbeatBlocker: heartbeat.heartbeatBlocker,
|
|
784
840
|
finalResult: parsed.finalResult,
|
|
785
|
-
error
|
|
841
|
+
error,
|
|
786
842
|
changedFiles,
|
|
787
843
|
gitAncestry
|
|
788
844
|
};
|
|
@@ -942,10 +998,74 @@ function buildPrompt(input) {
|
|
|
942
998
|
// src/providers/claude.ts
|
|
943
999
|
import { closeSync, openSync } from "node:fs";
|
|
944
1000
|
import { spawn } from "node:child_process";
|
|
1001
|
+
|
|
1002
|
+
// src/providers/model-preflight.ts
|
|
1003
|
+
var REASONING_SUFFIX_RE = /-(?:thinking(?:-(?:high|medium|low|minimal|max|none))?|high|medium|low|minimal)$/i;
|
|
1004
|
+
function stripReasoningSuffix(model) {
|
|
1005
|
+
return model.replace(REASONING_SUFFIX_RE, "");
|
|
1006
|
+
}
|
|
1007
|
+
var FOREIGN_MODEL_RE = /^(?:gpt-|gpt5|o1|o3|o4|gemini-|grok-|composer|deepseek|llama|mistral|qwen|command-)/i;
|
|
1008
|
+
function looksLikeClaudeModel(model) {
|
|
1009
|
+
return /^claude[-_]/i.test(model) || /^(?:opus|sonnet|haiku)\b/i.test(model);
|
|
1010
|
+
}
|
|
1011
|
+
function preflightClaudeModel(model, defaultModel) {
|
|
1012
|
+
const requested = (model ?? "").trim();
|
|
1013
|
+
if (!requested) {
|
|
1014
|
+
return { ok: true, model: defaultModel, normalized: false };
|
|
1015
|
+
}
|
|
1016
|
+
const stripped = stripReasoningSuffix(requested).trim();
|
|
1017
|
+
const launch = stripped || defaultModel;
|
|
1018
|
+
if (FOREIGN_MODEL_RE.test(launch) || !looksLikeClaudeModel(launch) && launch !== defaultModel) {
|
|
1019
|
+
return {
|
|
1020
|
+
ok: false,
|
|
1021
|
+
model: requested,
|
|
1022
|
+
normalized: false,
|
|
1023
|
+
requested,
|
|
1024
|
+
note: `model "${requested}" is not a Claude model \u2014 the "claude" provider drives the Claude CLI, which only accepts claude-* model ids (got "${launch}"). Pick a Claude model or switch the worker provider.`
|
|
1025
|
+
};
|
|
1026
|
+
}
|
|
1027
|
+
if (launch !== requested) {
|
|
1028
|
+
return {
|
|
1029
|
+
ok: true,
|
|
1030
|
+
model: launch,
|
|
1031
|
+
normalized: true,
|
|
1032
|
+
requested,
|
|
1033
|
+
note: `normalized model "${requested}" \u2192 "${launch}" (the Claude CLI rejects reasoning-effort suffixes)`
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
return { ok: true, model: launch, normalized: false };
|
|
1037
|
+
}
|
|
1038
|
+
function preflightCursorModel(model, defaultModel) {
|
|
1039
|
+
const requested = (model ?? "").trim();
|
|
1040
|
+
if (!requested) {
|
|
1041
|
+
return { ok: true, model: defaultModel, normalized: false };
|
|
1042
|
+
}
|
|
1043
|
+
if (looksLikeClaudeModel(requested)) {
|
|
1044
|
+
return {
|
|
1045
|
+
ok: false,
|
|
1046
|
+
model: requested,
|
|
1047
|
+
normalized: false,
|
|
1048
|
+
requested,
|
|
1049
|
+
note: `model "${requested}" is a Claude model but the worker provider is "cursor". Switch the provider to "claude" or pick a Cursor model.`
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
return { ok: true, model: requested, normalized: false };
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// src/providers/claude.ts
|
|
1056
|
+
var CLAUDE_DEFAULT_MODEL = "claude-opus-4-7";
|
|
945
1057
|
var claudeProvider = {
|
|
946
1058
|
name: "claude",
|
|
1059
|
+
defaultModel: CLAUDE_DEFAULT_MODEL,
|
|
1060
|
+
preflightModel(model) {
|
|
1061
|
+
return preflightClaudeModel(model, CLAUDE_DEFAULT_MODEL);
|
|
1062
|
+
},
|
|
947
1063
|
start(opts) {
|
|
948
|
-
const
|
|
1064
|
+
const preflight = preflightClaudeModel(opts.model, CLAUDE_DEFAULT_MODEL);
|
|
1065
|
+
if (!preflight.ok) {
|
|
1066
|
+
throw new Error(`claude provider model preflight failed: ${preflight.note}`);
|
|
1067
|
+
}
|
|
1068
|
+
const model = preflight.model;
|
|
949
1069
|
const stdoutFd = openSync(opts.stdoutPath, "a");
|
|
950
1070
|
const stderrFd = openSync(opts.stderrPath, "a");
|
|
951
1071
|
const child = spawn(
|
|
@@ -1021,8 +1141,16 @@ function resolveAgentBin() {
|
|
|
1021
1141
|
}
|
|
1022
1142
|
var cursorProvider = {
|
|
1023
1143
|
name: "cursor",
|
|
1144
|
+
defaultModel: DEFAULT_CURSOR_MODEL,
|
|
1145
|
+
preflightModel(model) {
|
|
1146
|
+
return preflightCursorModel(model, DEFAULT_CURSOR_MODEL);
|
|
1147
|
+
},
|
|
1024
1148
|
start(opts) {
|
|
1025
|
-
const
|
|
1149
|
+
const preflight = preflightCursorModel(opts.model, DEFAULT_CURSOR_MODEL);
|
|
1150
|
+
if (!preflight.ok) {
|
|
1151
|
+
throw new Error(`cursor provider model preflight failed: ${preflight.note}`);
|
|
1152
|
+
}
|
|
1153
|
+
const model = preflight.model;
|
|
1026
1154
|
const stdoutFd = openSync2(opts.stdoutPath, "a");
|
|
1027
1155
|
const stderrFd = openSync2(opts.stderrPath, "a");
|
|
1028
1156
|
const agentBin = resolveAgentBin();
|
|
@@ -1454,6 +1582,20 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1454
1582
|
const name = safeSlug(rawName);
|
|
1455
1583
|
if (run.workers?.[name]) throw new Error(`worker already exists in run ${run.id}: ${name}`);
|
|
1456
1584
|
if (!opts.task) throw new Error(`missing task text for worker ${name}`);
|
|
1585
|
+
const provider = resolveWorkerProvider(opts.provider);
|
|
1586
|
+
let launchModel = opts.model;
|
|
1587
|
+
if (provider.preflightModel) {
|
|
1588
|
+
const preflight = provider.preflightModel(opts.model);
|
|
1589
|
+
if (!preflight.ok) {
|
|
1590
|
+
throw new Error(
|
|
1591
|
+
`model preflight failed for provider "${provider.name}": ${preflight.note ?? "invalid model/provider combination"}`
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
if (preflight.normalized) {
|
|
1595
|
+
console.error(`[supervisor] ${name}: ${preflight.note}`);
|
|
1596
|
+
}
|
|
1597
|
+
launchModel = preflight.model;
|
|
1598
|
+
}
|
|
1457
1599
|
const { worktreesDir } = getPaths();
|
|
1458
1600
|
const workerDir = path9.join(runDirectory(run.id), "workers", name);
|
|
1459
1601
|
mkdirSync3(workerDir, { recursive: true });
|
|
@@ -1471,14 +1613,13 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1471
1613
|
worktreePath,
|
|
1472
1614
|
heartbeatPath
|
|
1473
1615
|
});
|
|
1474
|
-
const provider = resolveWorkerProvider(opts.provider);
|
|
1475
1616
|
let started;
|
|
1476
1617
|
try {
|
|
1477
1618
|
started = provider.start({
|
|
1478
1619
|
name,
|
|
1479
1620
|
task: opts.task,
|
|
1480
1621
|
ownedPaths: opts.ownedPaths,
|
|
1481
|
-
model:
|
|
1622
|
+
model: launchModel,
|
|
1482
1623
|
branch,
|
|
1483
1624
|
worktreePath,
|
|
1484
1625
|
workerDir,
|
|
@@ -1492,7 +1633,7 @@ function spawnWorkerProcess(run, opts) {
|
|
|
1492
1633
|
git(run.repo, ["branch", "-D", branch], { allowFailure: true });
|
|
1493
1634
|
throw error;
|
|
1494
1635
|
}
|
|
1495
|
-
const model = started.model ||
|
|
1636
|
+
const model = started.model || launchModel || provider.defaultModel || "claude-opus-4-7";
|
|
1496
1637
|
const worker = {
|
|
1497
1638
|
name,
|
|
1498
1639
|
runId: run.id,
|