@algosuite/vo-mcp 0.2.0-beta.71 → 0.2.0-beta.72
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/agent-auth-probe-cli.mjs +178 -11
- package/dist/runner-cli.js +431 -162
- package/dist/runner-cli.js.map +3 -3
- package/package.json +1 -1
package/dist/runner-cli.js
CHANGED
|
@@ -314,21 +314,21 @@ function backupConfigOnce(configPath) {
|
|
|
314
314
|
copyFileSync(configPath, backupPath);
|
|
315
315
|
return backupPath;
|
|
316
316
|
}
|
|
317
|
-
function writeFileAtomic(
|
|
318
|
-
sweepStaleTempFiles(
|
|
319
|
-
const temp = `${
|
|
317
|
+
function writeFileAtomic(path24, content) {
|
|
318
|
+
sweepStaleTempFiles(path24);
|
|
319
|
+
const temp = `${path24}.vo-mcp-tmp-${process.pid}-${Date.now()}`;
|
|
320
320
|
try {
|
|
321
321
|
writeFileSync2(temp, content, { encoding: "utf8", mode: 384 });
|
|
322
|
-
if (existsSync3(
|
|
322
|
+
if (existsSync3(path24)) {
|
|
323
323
|
try {
|
|
324
|
-
chmodSync2(temp, statSync(
|
|
324
|
+
chmodSync2(temp, statSync(path24).mode & 511);
|
|
325
325
|
} catch {
|
|
326
326
|
}
|
|
327
327
|
}
|
|
328
328
|
let lastErr = null;
|
|
329
329
|
for (let attempt = 0; attempt < RENAME_RETRIES; attempt += 1) {
|
|
330
330
|
try {
|
|
331
|
-
renameSync(temp,
|
|
331
|
+
renameSync(temp, path24);
|
|
332
332
|
return;
|
|
333
333
|
} catch (err) {
|
|
334
334
|
lastErr = err;
|
|
@@ -337,7 +337,7 @@ function writeFileAtomic(path23, content) {
|
|
|
337
337
|
sleepSync(RENAME_RETRY_MS);
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
|
-
writeFileSync2(
|
|
340
|
+
writeFileSync2(path24, content, "utf8");
|
|
341
341
|
try {
|
|
342
342
|
unlinkSync2(temp);
|
|
343
343
|
} catch {
|
|
@@ -503,8 +503,8 @@ function tablePath(line) {
|
|
|
503
503
|
function tableSections(lines) {
|
|
504
504
|
const starts = [];
|
|
505
505
|
for (let index = 0; index < lines.length; index += 1) {
|
|
506
|
-
const
|
|
507
|
-
if (
|
|
506
|
+
const path24 = tablePath(lines[index] ?? "");
|
|
507
|
+
if (path24) starts.push({ path: path24, start: index });
|
|
508
508
|
}
|
|
509
509
|
return starts.map((section, index) => ({
|
|
510
510
|
...section,
|
|
@@ -696,11 +696,11 @@ function resolveLinuxConfigHome(home, env2) {
|
|
|
696
696
|
const configured = env2["XDG_CONFIG_HOME"]?.trim();
|
|
697
697
|
return configured && isAbsolute2(configured) ? configured : join5(home, ".config");
|
|
698
698
|
}
|
|
699
|
-
function launcherIsCurrent(
|
|
700
|
-
if (!existsSync6(
|
|
701
|
-
if (readFileSync5(
|
|
702
|
-
const backupPath = `${
|
|
703
|
-
copyFileSync2(
|
|
699
|
+
function launcherIsCurrent(path24, desiredContent, label, log2) {
|
|
700
|
+
if (!existsSync6(path24)) return false;
|
|
701
|
+
if (readFileSync5(path24, "utf8") === desiredContent) return true;
|
|
702
|
+
const backupPath = `${path24}.backup-${Date.now()}`;
|
|
703
|
+
copyFileSync2(path24, backupPath);
|
|
704
704
|
log2(` Backed up existing ${label} to: ${backupPath}`);
|
|
705
705
|
return false;
|
|
706
706
|
}
|
|
@@ -916,17 +916,17 @@ function resolveDesktopConfigPath(home, plat, appData) {
|
|
|
916
916
|
}
|
|
917
917
|
return join6(home, ".config", "Claude", "claude_desktop_config.json");
|
|
918
918
|
}
|
|
919
|
-
function readClaudeConfig(
|
|
920
|
-
if (!existsSync7(
|
|
919
|
+
function readClaudeConfig(path24) {
|
|
920
|
+
if (!existsSync7(path24)) return { kind: "absent", config: {}, mtimeMs: null };
|
|
921
921
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
922
|
-
const before = statSync3(
|
|
922
|
+
const before = statSync3(path24).mtimeMs;
|
|
923
923
|
let raw;
|
|
924
924
|
try {
|
|
925
|
-
raw = readFileSync6(
|
|
925
|
+
raw = readFileSync6(path24, "utf8");
|
|
926
926
|
} catch {
|
|
927
927
|
return { kind: "invalid", config: {}, mtimeMs: before };
|
|
928
928
|
}
|
|
929
|
-
if (!existsSync7(
|
|
929
|
+
if (!existsSync7(path24) || statSync3(path24).mtimeMs !== before) continue;
|
|
930
930
|
const text = raw.replace(/^\uFEFF/u, "");
|
|
931
931
|
if (!text.trim()) return { kind: "empty", config: {}, mtimeMs: before };
|
|
932
932
|
try {
|
|
@@ -938,9 +938,9 @@ function readClaudeConfig(path23) {
|
|
|
938
938
|
}
|
|
939
939
|
return { kind: "invalid", config: {}, mtimeMs: null };
|
|
940
940
|
}
|
|
941
|
-
function writeClaudeConfig(
|
|
942
|
-
mkdirSync6(dirname4(
|
|
943
|
-
writeFileAtomic(
|
|
941
|
+
function writeClaudeConfig(path24, config) {
|
|
942
|
+
mkdirSync6(dirname4(path24), { recursive: true });
|
|
943
|
+
writeFileAtomic(path24, `${JSON.stringify(config, null, 2)}
|
|
944
944
|
`);
|
|
945
945
|
}
|
|
946
946
|
function carriedEntryKeys(entry) {
|
|
@@ -3879,13 +3879,13 @@ async function getTaskKnowledgeContextRequest(req, taskId, { query, knowledgeReq
|
|
|
3879
3879
|
const canonicalQuery = canonicalizeKnowledgeContextQuery(query);
|
|
3880
3880
|
if (canonicalQuery.trim()) body.query = canonicalQuery;
|
|
3881
3881
|
if (typeof knowledgeRequestId === "string" && knowledgeRequestId) body.knowledge_request_id = knowledgeRequestId;
|
|
3882
|
-
const
|
|
3882
|
+
const path24 = `/api/v1/code-task/${encodeURIComponent(taskId)}/knowledge-context`;
|
|
3883
3883
|
const timeoutMs = Math.max(Number(taskRequestTimeoutMs) || 0, MIN_KNOWLEDGE_CONTEXT_TIMEOUT_MS);
|
|
3884
3884
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) {
|
|
3885
3885
|
let res;
|
|
3886
3886
|
let cause;
|
|
3887
3887
|
try {
|
|
3888
|
-
res = await req("POST",
|
|
3888
|
+
res = await req("POST", path24, body, { timeoutMs });
|
|
3889
3889
|
} catch (err) {
|
|
3890
3890
|
cause = err;
|
|
3891
3891
|
}
|
|
@@ -3953,10 +3953,10 @@ async function getPreparedJobRequest(req, taskId, options = {}, invalidateToken
|
|
|
3953
3953
|
if (typeof taskId !== "string" || taskId.length === 0) {
|
|
3954
3954
|
return { ok: false, reason: "missing_task_id", status: 0, ...envMeta };
|
|
3955
3955
|
}
|
|
3956
|
-
const
|
|
3956
|
+
const path24 = `/api/v1/code-task/${encodeURIComponent(taskId)}/prepared-job?${query}`;
|
|
3957
3957
|
let res;
|
|
3958
3958
|
try {
|
|
3959
|
-
res = await req("GET",
|
|
3959
|
+
res = await req("GET", path24, void 0, { timeoutMs });
|
|
3960
3960
|
} catch (err) {
|
|
3961
3961
|
return { ok: false, reason: `transport: ${err?.message || String(err)}`, status: 0, ...envMeta };
|
|
3962
3962
|
}
|
|
@@ -4056,11 +4056,11 @@ function createControlPlaneClient({
|
|
|
4056
4056
|
if (!resolvedBaseUrl) throw new Error("VO_CONTROL_PLANE_URL is required for the code-runner daemon");
|
|
4057
4057
|
const root = resolvedBaseUrl.replace(/\/+$/, "");
|
|
4058
4058
|
const claimOccurrences = /* @__PURE__ */ new Map();
|
|
4059
|
-
async function req(method,
|
|
4059
|
+
async function req(method, path24, body, { timeoutMs } = {}) {
|
|
4060
4060
|
const bearer = await resolveBearer(env2);
|
|
4061
4061
|
const controller = timeoutMs ? new AbortController() : null;
|
|
4062
4062
|
let timeoutId;
|
|
4063
|
-
const request = Promise.resolve(fetchImpl(`${root}${
|
|
4063
|
+
const request = Promise.resolve(fetchImpl(`${root}${path24}`, {
|
|
4064
4064
|
method,
|
|
4065
4065
|
headers: {
|
|
4066
4066
|
"content-type": "application/json",
|
|
@@ -4073,7 +4073,7 @@ function createControlPlaneClient({
|
|
|
4073
4073
|
const timeout = new Promise((_, reject) => {
|
|
4074
4074
|
timeoutId = setTimeout(() => {
|
|
4075
4075
|
controller.abort();
|
|
4076
|
-
reject(new Error(`control-plane ${
|
|
4076
|
+
reject(new Error(`control-plane ${path24} timed out after ${timeoutMs}ms`));
|
|
4077
4077
|
}, timeoutMs);
|
|
4078
4078
|
});
|
|
4079
4079
|
try {
|
|
@@ -4082,7 +4082,7 @@ function createControlPlaneClient({
|
|
|
4082
4082
|
clearTimeout(timeoutId);
|
|
4083
4083
|
}
|
|
4084
4084
|
}
|
|
4085
|
-
const taskReq = (method,
|
|
4085
|
+
const taskReq = (method, path24, body, options = {}) => req(method, path24, body, { timeoutMs: taskRequestTimeoutMs, ...options });
|
|
4086
4086
|
const claimGate = makeClaimGateNotice({ log: (m) => console.warn(`[code-runner ${(/* @__PURE__ */ new Date()).toISOString()}] ${m}`) });
|
|
4087
4087
|
return {
|
|
4088
4088
|
getClaimGate: () => claimGate.current(),
|
|
@@ -4253,8 +4253,8 @@ function createControlPlaneClient({
|
|
|
4253
4253
|
return listAllPrOpenedTasks(taskReq);
|
|
4254
4254
|
},
|
|
4255
4255
|
async downloadTaskAttachment(taskId, attachmentId) {
|
|
4256
|
-
const
|
|
4257
|
-
const res = await taskReq("GET",
|
|
4256
|
+
const path24 = `/api/v1/code-task/${encodeURIComponent(taskId)}/attachment/${encodeURIComponent(attachmentId)}`;
|
|
4257
|
+
const res = await taskReq("GET", path24);
|
|
4258
4258
|
if (res.status === 401) cachedFirebaseToken = null;
|
|
4259
4259
|
if (!res.ok) throw new Error(`attachment download failed: HTTP ${res.status}`);
|
|
4260
4260
|
return Buffer.from(await res.arrayBuffer());
|
|
@@ -4907,7 +4907,7 @@ function normalizeClaudePermissionMode(value) {
|
|
|
4907
4907
|
}
|
|
4908
4908
|
return normalized;
|
|
4909
4909
|
}
|
|
4910
|
-
function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, model, effort, maxBudgetUsd, researchHarness = false, toolPolicy = "default", env: env2 = process.env } = {}) {
|
|
4910
|
+
function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, model, effort, maxBudgetUsd, researchHarness = false, toolPolicy = "default", structuredOutputSchema, env: env2 = process.env } = {}) {
|
|
4911
4911
|
const effectivePermissionMode = normalizeClaudePermissionMode(permissionMode);
|
|
4912
4912
|
if (!["default", "skill_readonly", "frozen_inputs_only"].includes(toolPolicy)) {
|
|
4913
4913
|
throw new Error(`unsupported Claude tool policy "${toolPolicy}"`);
|
|
@@ -4945,6 +4945,12 @@ function buildClaudeArgs({ permissionMode = DEFAULT_PERMISSION_MODE, maxTurns, m
|
|
|
4945
4945
|
"none"
|
|
4946
4946
|
);
|
|
4947
4947
|
}
|
|
4948
|
+
if (structuredOutputSchema !== void 0) {
|
|
4949
|
+
if (!restrictedSkill || !structuredOutputSchema || typeof structuredOutputSchema !== "object" || Array.isArray(structuredOutputSchema)) {
|
|
4950
|
+
throw new Error("structured output schema is allowed only for a restricted skill");
|
|
4951
|
+
}
|
|
4952
|
+
args.push("--json-schema", JSON.stringify(structuredOutputSchema));
|
|
4953
|
+
}
|
|
4948
4954
|
if (frozenInputsOnly) {
|
|
4949
4955
|
args.push("--strict-mcp-config", "--safe-mode");
|
|
4950
4956
|
}
|
|
@@ -5370,13 +5376,15 @@ function cappedRunLastMessage(evt, lastProgress) {
|
|
|
5370
5376
|
return salvage;
|
|
5371
5377
|
}
|
|
5372
5378
|
function buildResultEvent(evt) {
|
|
5373
|
-
const isError = Boolean(evt.is_error) || evt.subtype === "error_max_turns" || evt.subtype === "error_during_execution";
|
|
5379
|
+
const isError = Boolean(evt.is_error) || evt.subtype === "error_max_budget_usd" || evt.subtype === "error_max_turns" || evt.subtype === "error_max_structured_output_retries" || evt.subtype === "error_during_execution";
|
|
5374
5380
|
return {
|
|
5375
5381
|
kind: "result",
|
|
5376
5382
|
isError,
|
|
5377
5383
|
costUsd: typeof evt.total_cost_usd === "number" ? evt.total_cost_usd : null,
|
|
5378
5384
|
summary: typeof evt.result === "string" && evt.result.length > 0 ? evt.result : evt.subtype || (isError ? "error" : "completed"),
|
|
5385
|
+
terminalSubtype: typeof evt.subtype === "string" ? evt.subtype : null,
|
|
5379
5386
|
numTurns: typeof evt.num_turns === "number" ? evt.num_turns : null,
|
|
5387
|
+
structuredOutput: Object.hasOwn(evt, "structured_output") ? evt.structured_output : null,
|
|
5380
5388
|
tokenUsage: extractTokenUsage(evt),
|
|
5381
5389
|
modelUsage: extractModelUsage(evt)
|
|
5382
5390
|
};
|
|
@@ -5520,6 +5528,148 @@ var init_cli_version_floor = __esm({
|
|
|
5520
5528
|
}
|
|
5521
5529
|
});
|
|
5522
5530
|
|
|
5531
|
+
// ../../scripts/virtual-office/code-runner/claude-skill-capability.mjs
|
|
5532
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
5533
|
+
import { accessSync, constants, realpathSync as realpathSync2, statSync as statSync4 } from "node:fs";
|
|
5534
|
+
import path14 from "node:path";
|
|
5535
|
+
function hasOption(help, option) {
|
|
5536
|
+
const literal = option.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
|
5537
|
+
return new RegExp(`(^|\\s)${literal}(?=\\s|,|=|<|$)`, "mu").test(help);
|
|
5538
|
+
}
|
|
5539
|
+
function assessClaudeSkillCapability({ versionOutput, helpOutput }) {
|
|
5540
|
+
const version = parseCliVersion(versionOutput);
|
|
5541
|
+
if (!version || !VALIDATED_CLAUDE_SKILL_VERSIONS.includes(version)) {
|
|
5542
|
+
return { compatible: false, version, reason: "claude version is not in the validated restricted-skill manifest" };
|
|
5543
|
+
}
|
|
5544
|
+
const help = String(helpOutput ?? "");
|
|
5545
|
+
const missing = REQUIRED_CLAUDE_SKILL_HELP.filter((option) => !hasOption(help, option));
|
|
5546
|
+
if (missing.length > 0) {
|
|
5547
|
+
return { compatible: false, version, reason: `claude help is missing required options: ${missing.join(", ")}` };
|
|
5548
|
+
}
|
|
5549
|
+
if (!/--permission-prompts[\s\S]{0,300}(?:"none"|\bnone\b)/mu.test(help) || !/--output-format[\s\S]{0,300}\bstream-json\b/mu.test(help)) {
|
|
5550
|
+
return { compatible: false, version, reason: "claude help does not prove required none/stream-json values" };
|
|
5551
|
+
}
|
|
5552
|
+
return { compatible: true, version, reason: "validated restricted-skill CLI contract" };
|
|
5553
|
+
}
|
|
5554
|
+
function runProbe(bin, args, env2, timeoutMs = PROBE_TIMEOUT_MS) {
|
|
5555
|
+
if (process.platform === "win32") {
|
|
5556
|
+
try {
|
|
5557
|
+
const launch = buildWindowsClaudeLaunch({ bin, args, env: env2 });
|
|
5558
|
+
return spawnSync6(launch.bin, launch.args, {
|
|
5559
|
+
...launch.spawnOptions,
|
|
5560
|
+
env: env2,
|
|
5561
|
+
encoding: "utf8",
|
|
5562
|
+
timeout: timeoutMs
|
|
5563
|
+
});
|
|
5564
|
+
} catch (error) {
|
|
5565
|
+
return { status: null, stdout: "", stderr: "", error };
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
return spawnSync6(bin, args, { env: env2, encoding: "utf8", timeout: timeoutMs, windowsHide: true });
|
|
5569
|
+
}
|
|
5570
|
+
function probeText(probe) {
|
|
5571
|
+
return `${String(probe?.stdout ?? "")}
|
|
5572
|
+
${String(probe?.stderr ?? "")}`.trim();
|
|
5573
|
+
}
|
|
5574
|
+
function resolveClaudeBinaryIdentity(bin = "claude", env2 = process.env) {
|
|
5575
|
+
let resolvedBin = String(bin);
|
|
5576
|
+
try {
|
|
5577
|
+
if (process.platform === "win32") {
|
|
5578
|
+
resolvedBin = buildWindowsClaudeLaunch({ bin: resolvedBin, args: [], env: env2 }).bin;
|
|
5579
|
+
} else if (!path14.isAbsolute(resolvedBin)) {
|
|
5580
|
+
const found = String(env2?.PATH ?? "").split(path14.delimiter).find((dir) => {
|
|
5581
|
+
try {
|
|
5582
|
+
accessSync(path14.join(dir, resolvedBin), constants.X_OK);
|
|
5583
|
+
return true;
|
|
5584
|
+
} catch {
|
|
5585
|
+
return false;
|
|
5586
|
+
}
|
|
5587
|
+
});
|
|
5588
|
+
if (found) resolvedBin = path14.join(found, resolvedBin);
|
|
5589
|
+
}
|
|
5590
|
+
const canonical = realpathSync2(resolvedBin);
|
|
5591
|
+
const stat3 = statSync4(canonical);
|
|
5592
|
+
return { resolvedBin: canonical, fingerprint: `${canonical}\0${stat3.size}\0${stat3.mtimeMs}` };
|
|
5593
|
+
} catch {
|
|
5594
|
+
const pathValue2 = String(env2?.PATH ?? env2?.Path ?? "");
|
|
5595
|
+
return { resolvedBin, fingerprint: `${resolvedBin}\0${pathValue2}` };
|
|
5596
|
+
}
|
|
5597
|
+
}
|
|
5598
|
+
function probeClaudeSkillCapability({
|
|
5599
|
+
bin = "claude",
|
|
5600
|
+
env: env2 = process.env,
|
|
5601
|
+
versionOutput,
|
|
5602
|
+
spawnProbe = runProbe,
|
|
5603
|
+
now = () => Date.now(),
|
|
5604
|
+
cacheTtlMs = CACHE_TTL_MS,
|
|
5605
|
+
timeoutMs = PROBE_TIMEOUT_MS,
|
|
5606
|
+
freshIdentity = false,
|
|
5607
|
+
resolveIdentity = resolveClaudeBinaryIdentity
|
|
5608
|
+
} = {}) {
|
|
5609
|
+
const identity = resolveIdentity(bin, env2);
|
|
5610
|
+
const key = identity.fingerprint;
|
|
5611
|
+
const existing = cache.get(key);
|
|
5612
|
+
if (!freshIdentity && versionOutput === void 0 && existing && now() - existing.at < cacheTtlMs) {
|
|
5613
|
+
return existing.value;
|
|
5614
|
+
}
|
|
5615
|
+
const versionProbe = freshIdentity || versionOutput === void 0 ? spawnProbe(identity.resolvedBin, ["--version"], env2, timeoutMs) : null;
|
|
5616
|
+
if (versionProbe?.error || versionProbe && versionProbe.status !== 0) {
|
|
5617
|
+
return {
|
|
5618
|
+
compatible: false,
|
|
5619
|
+
version: null,
|
|
5620
|
+
resolvedBin: identity.resolvedBin,
|
|
5621
|
+
reason: "claude version capability probe failed"
|
|
5622
|
+
};
|
|
5623
|
+
}
|
|
5624
|
+
const effectiveVersionOutput = versionProbe ? probeText(versionProbe) : versionOutput;
|
|
5625
|
+
const suppliedVersion = parseCliVersion(effectiveVersionOutput);
|
|
5626
|
+
if (existing && now() - existing.at < cacheTtlMs && suppliedVersion === existing.value.version) {
|
|
5627
|
+
return existing.value;
|
|
5628
|
+
}
|
|
5629
|
+
const helpProbe = spawnProbe(identity.resolvedBin, ["--help"], env2, timeoutMs);
|
|
5630
|
+
if (helpProbe?.error || helpProbe?.status !== 0) {
|
|
5631
|
+
return {
|
|
5632
|
+
compatible: false,
|
|
5633
|
+
version: suppliedVersion,
|
|
5634
|
+
resolvedBin: identity.resolvedBin,
|
|
5635
|
+
reason: "claude help capability probe failed"
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
const assessed = assessClaudeSkillCapability({
|
|
5639
|
+
versionOutput: effectiveVersionOutput,
|
|
5640
|
+
helpOutput: probeText(helpProbe)
|
|
5641
|
+
});
|
|
5642
|
+
const value = { ...assessed, resolvedBin: identity.resolvedBin };
|
|
5643
|
+
cache.set(key, { at: now(), value });
|
|
5644
|
+
return value;
|
|
5645
|
+
}
|
|
5646
|
+
var VALIDATED_CLAUDE_SKILL_VERSIONS, REQUIRED_CLAUDE_SKILL_HELP, PROBE_TIMEOUT_MS, CACHE_TTL_MS, cache;
|
|
5647
|
+
var init_claude_skill_capability = __esm({
|
|
5648
|
+
"../../scripts/virtual-office/code-runner/claude-skill-capability.mjs"() {
|
|
5649
|
+
"use strict";
|
|
5650
|
+
init_cli_version_floor();
|
|
5651
|
+
init_windows_claude_launch();
|
|
5652
|
+
VALIDATED_CLAUDE_SKILL_VERSIONS = Object.freeze(["2.1.263"]);
|
|
5653
|
+
REQUIRED_CLAUDE_SKILL_HELP = Object.freeze([
|
|
5654
|
+
"--allowedTools",
|
|
5655
|
+
"--disable-slash-commands",
|
|
5656
|
+
"--json-schema",
|
|
5657
|
+
"--max-budget-usd",
|
|
5658
|
+
"--no-chrome",
|
|
5659
|
+
"--no-session-persistence",
|
|
5660
|
+
"--output-format",
|
|
5661
|
+
"--permission-mode",
|
|
5662
|
+
"--permission-prompts",
|
|
5663
|
+
"--safe-mode",
|
|
5664
|
+
"--strict-mcp-config",
|
|
5665
|
+
"--tools"
|
|
5666
|
+
]);
|
|
5667
|
+
PROBE_TIMEOUT_MS = 2e3;
|
|
5668
|
+
CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
5669
|
+
cache = /* @__PURE__ */ new Map();
|
|
5670
|
+
}
|
|
5671
|
+
});
|
|
5672
|
+
|
|
5523
5673
|
// ../../scripts/virtual-office/code-runner/claude-auth-check.mjs
|
|
5524
5674
|
function errorCode(error) {
|
|
5525
5675
|
return String(error?.code || "").toUpperCase();
|
|
@@ -5546,9 +5696,12 @@ async function checkClaudeAuth({
|
|
|
5546
5696
|
spawnVersion = spawnClaudeSync,
|
|
5547
5697
|
probeLogin = probeClaudeLoginState,
|
|
5548
5698
|
getStoredKey = getAnthropicKey,
|
|
5549
|
-
|
|
5699
|
+
probeSkillCapability = probeClaudeSkillCapability,
|
|
5700
|
+
env: env2 = process.env,
|
|
5701
|
+
now = () => Date.now()
|
|
5550
5702
|
} = {}) {
|
|
5551
5703
|
try {
|
|
5704
|
+
const startedAt = now();
|
|
5552
5705
|
let probe = spawnVersion(["--version"], {
|
|
5553
5706
|
timeout: FIRST_VERSION_TIMEOUT_MS,
|
|
5554
5707
|
encoding: "utf8",
|
|
@@ -5582,23 +5735,40 @@ async function checkClaudeAuth({
|
|
|
5582
5735
|
}
|
|
5583
5736
|
const floorGate = applyCliVersionFloor({ versionOutput: probe.stdout, env: env2 });
|
|
5584
5737
|
if (floorGate.refused) {
|
|
5585
|
-
return {
|
|
5738
|
+
return {
|
|
5739
|
+
installed: true,
|
|
5740
|
+
authenticated: false,
|
|
5741
|
+
version: floorGate.check.version ?? void 0,
|
|
5742
|
+
skillCapable: false,
|
|
5743
|
+
message: floorGate.message
|
|
5744
|
+
};
|
|
5586
5745
|
}
|
|
5587
5746
|
const loggedIn = retriedAfterTimeout ? null : probeLogin();
|
|
5588
5747
|
if (loggedIn === false) {
|
|
5589
5748
|
return {
|
|
5590
5749
|
installed: true,
|
|
5591
5750
|
authenticated: false,
|
|
5751
|
+
version: floorGate.check.version ?? void 0,
|
|
5752
|
+
skillCapable: false,
|
|
5592
5753
|
message: "claude CLI is installed but NOT logged in \u2014 its login is SEPARATE from the Claude Desktop app and the Claude Code IDE extension. Run: claude auth login (Claude subscription), then restart the runner."
|
|
5593
5754
|
};
|
|
5594
5755
|
}
|
|
5756
|
+
const authTier = resolveClaudeAuthTier({ env: env2, loggedIn, getStoredKey });
|
|
5757
|
+
const remainingMs = AUTH_PROBE_BUDGET_MS - (now() - startedAt);
|
|
5758
|
+
const skillCapability = loggedIn === true && remainingMs >= MIN_SKILL_PROBE_MS ? probeSkillCapability({
|
|
5759
|
+
versionOutput: probe.stdout,
|
|
5760
|
+
env: env2,
|
|
5761
|
+
timeoutMs: Math.min(2e3, remainingMs)
|
|
5762
|
+
}) : { compatible: false };
|
|
5595
5763
|
return {
|
|
5596
5764
|
installed: true,
|
|
5597
5765
|
authenticated: true,
|
|
5766
|
+
version: floorGate.check.version ?? void 0,
|
|
5767
|
+
skillCapable: skillCapability.compatible === true,
|
|
5598
5768
|
// Dispatch-time billing signal, carried on the same probe that already
|
|
5599
5769
|
// paid for the login read. Never sent for a non-authenticated result:
|
|
5600
5770
|
// there is no tier without a working credential.
|
|
5601
|
-
authTier
|
|
5771
|
+
authTier,
|
|
5602
5772
|
message: loggedIn === true ? "claude CLI installed and logged in (claude auth status)" : "claude binary found (login state unknown \u2014 auth check is best-effort)"
|
|
5603
5773
|
};
|
|
5604
5774
|
} catch (error) {
|
|
@@ -5609,7 +5779,7 @@ async function checkClaudeAuth({
|
|
|
5609
5779
|
};
|
|
5610
5780
|
}
|
|
5611
5781
|
}
|
|
5612
|
-
var FIRST_VERSION_TIMEOUT_MS, RETRY_VERSION_TIMEOUT_MS;
|
|
5782
|
+
var FIRST_VERSION_TIMEOUT_MS, RETRY_VERSION_TIMEOUT_MS, AUTH_PROBE_BUDGET_MS, MIN_SKILL_PROBE_MS;
|
|
5613
5783
|
var init_claude_auth_check = __esm({
|
|
5614
5784
|
"../../scripts/virtual-office/code-runner/claude-auth-check.mjs"() {
|
|
5615
5785
|
"use strict";
|
|
@@ -5617,8 +5787,11 @@ var init_claude_auth_check = __esm({
|
|
|
5617
5787
|
init_agent_auth_tier();
|
|
5618
5788
|
init_cli_version_floor();
|
|
5619
5789
|
init_windows_claude_launch();
|
|
5790
|
+
init_claude_skill_capability();
|
|
5620
5791
|
FIRST_VERSION_TIMEOUT_MS = 4500;
|
|
5621
5792
|
RETRY_VERSION_TIMEOUT_MS = 2e3;
|
|
5793
|
+
AUTH_PROBE_BUDGET_MS = 9500;
|
|
5794
|
+
MIN_SKILL_PROBE_MS = 250;
|
|
5622
5795
|
}
|
|
5623
5796
|
});
|
|
5624
5797
|
|
|
@@ -5639,6 +5812,7 @@ function runAgentTask({
|
|
|
5639
5812
|
maxBudgetUsd = null,
|
|
5640
5813
|
researchHarness = false,
|
|
5641
5814
|
toolPolicy = "default",
|
|
5815
|
+
structuredOutputSchema,
|
|
5642
5816
|
env: env2 = process.env,
|
|
5643
5817
|
onProgress = () => {
|
|
5644
5818
|
},
|
|
@@ -5656,7 +5830,7 @@ function runAgentTask({
|
|
|
5656
5830
|
sandbox = null
|
|
5657
5831
|
}) {
|
|
5658
5832
|
return new Promise((resolve3) => {
|
|
5659
|
-
const args = runner.buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, prompt });
|
|
5833
|
+
const args = runner.buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, structuredOutputSchema, prompt });
|
|
5660
5834
|
const spawnEnv = typeof runner.applyAuthEnv === "function" ? runner.applyAuthEnv(env2) : env2;
|
|
5661
5835
|
const costBasis = typeof runner.costBasis === "function" ? runner.costBasis(spawnEnv) : "unknown";
|
|
5662
5836
|
if (costBasis === "vendor_billed" && runner.enforcesBudgetCap !== true && env2.VO_CODE_RUNNER_ALLOW_UNCAPPED_VENDOR_BILLED !== "1") {
|
|
@@ -5701,7 +5875,7 @@ function runAgentTask({
|
|
|
5701
5875
|
} catch {
|
|
5702
5876
|
}
|
|
5703
5877
|
let buffer = "";
|
|
5704
|
-
let result = { ok: false, costUsd: null, costBasis, summary: "", lastAgentMessage: null, numTurns: null, tokenUsage: null, modelUsage: null, executionStarted: false, killed: false };
|
|
5878
|
+
let result = { ok: false, costUsd: null, costBasis, summary: "", lastAgentMessage: null, structuredOutput: null, terminalSubtype: null, numTurns: null, tokenUsage: null, modelUsage: null, executionStarted: false, killed: false };
|
|
5705
5879
|
child.once("spawn", () => {
|
|
5706
5880
|
result = { ...result, executionStarted: true };
|
|
5707
5881
|
Promise.resolve(onSpawn()).catch(() => {
|
|
@@ -5739,6 +5913,8 @@ function runAgentTask({
|
|
|
5739
5913
|
// A budget/turn-capped run's honest last message, kept OUT of summary (see
|
|
5740
5914
|
// claude-result-event.cappedRunLastMessage) and surfaced in the PR body.
|
|
5741
5915
|
lastAgentMessage: cappedRunLastMessage(evt, lastProgress),
|
|
5916
|
+
structuredOutput: Object.hasOwn(evt, "structuredOutput") ? evt.structuredOutput : result.structuredOutput,
|
|
5917
|
+
terminalSubtype: Object.hasOwn(evt, "terminalSubtype") ? evt.terminalSubtype : result.terminalSubtype,
|
|
5742
5918
|
numTurns: evt.numTurns,
|
|
5743
5919
|
// MUST be listed explicitly. This assignment spreads the PREVIOUS
|
|
5744
5920
|
// result and then names each field it carries forward, so anything
|
|
@@ -5820,7 +5996,9 @@ function runAgentTask({
|
|
|
5820
5996
|
hardKill();
|
|
5821
5997
|
return;
|
|
5822
5998
|
}
|
|
5823
|
-
if (decision.delayMs
|
|
5999
|
+
if (decision.delayMs !== null && decision.delayMs !== void 0) {
|
|
6000
|
+
wallTimer = setTimeout(armDeadline, decision.delayMs);
|
|
6001
|
+
}
|
|
5824
6002
|
};
|
|
5825
6003
|
armDeadline();
|
|
5826
6004
|
child.stdout.on("data", (chunk) => {
|
|
@@ -5881,6 +6059,7 @@ var init_claude_runner = __esm({
|
|
|
5881
6059
|
init_claude_stream_event();
|
|
5882
6060
|
init_claude_result_event();
|
|
5883
6061
|
init_claude_auth_check();
|
|
6062
|
+
init_claude_skill_capability();
|
|
5884
6063
|
ClaudeRunner = class {
|
|
5885
6064
|
get enforcesBudgetCap() {
|
|
5886
6065
|
return true;
|
|
@@ -5888,8 +6067,8 @@ var init_claude_runner = __esm({
|
|
|
5888
6067
|
get binary() {
|
|
5889
6068
|
return "claude";
|
|
5890
6069
|
}
|
|
5891
|
-
buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy } = {}) {
|
|
5892
|
-
return buildClaudeArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy });
|
|
6070
|
+
buildArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, structuredOutputSchema } = {}) {
|
|
6071
|
+
return buildClaudeArgs({ permissionMode, maxTurns, model, effort, maxBudgetUsd, researchHarness, toolPolicy, structuredOutputSchema });
|
|
5893
6072
|
}
|
|
5894
6073
|
parseEvent(line) {
|
|
5895
6074
|
return parseStreamEvent(line);
|
|
@@ -5913,6 +6092,9 @@ var init_claude_runner = __esm({
|
|
|
5913
6092
|
async checkAuth() {
|
|
5914
6093
|
return checkClaudeAuth();
|
|
5915
6094
|
}
|
|
6095
|
+
checkSkillCapability({ bin = this.binary, env: env2 = process.env } = {}) {
|
|
6096
|
+
return probeClaudeSkillCapability({ bin, env: env2, freshIdentity: true });
|
|
6097
|
+
}
|
|
5916
6098
|
};
|
|
5917
6099
|
claudeRunner = new ClaudeRunner();
|
|
5918
6100
|
}
|
|
@@ -6096,7 +6278,7 @@ var init_error_message = __esm({
|
|
|
6096
6278
|
});
|
|
6097
6279
|
|
|
6098
6280
|
// ../../scripts/virtual-office/code-runner/codex-runner.mjs
|
|
6099
|
-
import { spawnSync as
|
|
6281
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
6100
6282
|
import { existsSync as existsSync10 } from "node:fs";
|
|
6101
6283
|
import { win32 as win322 } from "node:path";
|
|
6102
6284
|
function isTruthyFlag2(value) {
|
|
@@ -6211,7 +6393,7 @@ var init_codex_runner = __esm({
|
|
|
6211
6393
|
CODEX_PREFER_LOGIN_ENV = "VO_RUNNER_CODEX_PREFER_LOGIN";
|
|
6212
6394
|
LEGACY_PREFER_LOGIN_ENV = "VO_RUNNER_PREFER_LOGIN";
|
|
6213
6395
|
CodexRunner = class {
|
|
6214
|
-
constructor({ spawn: spawn5 =
|
|
6396
|
+
constructor({ spawn: spawn5 = spawnSync7, resolveBinary = resolveCodexBinary, env: env2 = process.env } = {}) {
|
|
6215
6397
|
this.spawn = spawn5;
|
|
6216
6398
|
this.resolveBinary = resolveBinary;
|
|
6217
6399
|
this.env = env2;
|
|
@@ -6324,7 +6506,7 @@ ${login.stderr || ""}`.trim();
|
|
|
6324
6506
|
});
|
|
6325
6507
|
|
|
6326
6508
|
// ../../scripts/virtual-office/code-runner/cursor-runner.mjs
|
|
6327
|
-
import { spawnSync as
|
|
6509
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
6328
6510
|
function buildCursorArgs({ model, prompt } = {}) {
|
|
6329
6511
|
const args = ["-p", "--output-format", "stream-json", "--force"];
|
|
6330
6512
|
if (model) {
|
|
@@ -6436,7 +6618,7 @@ var init_cursor_runner = __esm({
|
|
|
6436
6618
|
/** Best-effort: is `cursor-agent` on PATH? Never throws. */
|
|
6437
6619
|
async checkAuth() {
|
|
6438
6620
|
try {
|
|
6439
|
-
const { status, error } =
|
|
6621
|
+
const { status, error } = spawnSync8("cursor-agent", ["--version"], {
|
|
6440
6622
|
shell: false,
|
|
6441
6623
|
windowsHide: true,
|
|
6442
6624
|
timeout: 3e3,
|
|
@@ -7155,9 +7337,9 @@ var init_rate_limit_detector_core = __esm({
|
|
|
7155
7337
|
|
|
7156
7338
|
// ../../scripts/virtual-office/code-runner/rate-limit-resume-state.mjs
|
|
7157
7339
|
import fsp10 from "node:fs/promises";
|
|
7158
|
-
import
|
|
7340
|
+
import path15 from "node:path";
|
|
7159
7341
|
async function atomicWrite(file, content) {
|
|
7160
|
-
await fsp10.mkdir(
|
|
7342
|
+
await fsp10.mkdir(path15.dirname(file), { recursive: true });
|
|
7161
7343
|
const temp = `${file}.tmp-${process.pid}-${Date.now()}`;
|
|
7162
7344
|
const handle = await fsp10.open(temp, "wx");
|
|
7163
7345
|
try {
|
|
@@ -7218,7 +7400,7 @@ function writeResumeAttempts(file, store) {
|
|
|
7218
7400
|
}
|
|
7219
7401
|
async function acquireLock(lockFile, { now = Date.now, sleep: sleep3 = delay } = {}) {
|
|
7220
7402
|
const deadline = now() + LOCK_WAIT_MS;
|
|
7221
|
-
await fsp10.mkdir(
|
|
7403
|
+
await fsp10.mkdir(path15.dirname(lockFile), { recursive: true });
|
|
7222
7404
|
for (; ; ) {
|
|
7223
7405
|
let handle;
|
|
7224
7406
|
try {
|
|
@@ -7522,7 +7704,7 @@ var init_auto_merge = __esm({
|
|
|
7522
7704
|
});
|
|
7523
7705
|
|
|
7524
7706
|
// ../../scripts/virtual-office/code-runner/pr-overlap-gate.mjs
|
|
7525
|
-
import { spawnSync as
|
|
7707
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
7526
7708
|
import { existsSync as existsSync11 } from "node:fs";
|
|
7527
7709
|
import { dirname as dirname6, join as join9 } from "node:path";
|
|
7528
7710
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
@@ -7688,14 +7870,14 @@ function parsePorcelainZ(out) {
|
|
|
7688
7870
|
for (let i = 0; i < tokens.length; i += 1) {
|
|
7689
7871
|
const token2 = tokens[i];
|
|
7690
7872
|
if (!token2) continue;
|
|
7691
|
-
const
|
|
7692
|
-
if (
|
|
7873
|
+
const path24 = token2.slice(3);
|
|
7874
|
+
if (path24) files.push(path24);
|
|
7693
7875
|
if (token2[0] === "R" || token2[0] === "C") i += 1;
|
|
7694
7876
|
}
|
|
7695
7877
|
return files;
|
|
7696
7878
|
}
|
|
7697
|
-
function isAgentScratch(
|
|
7698
|
-
const normalized = String(
|
|
7879
|
+
function isAgentScratch(path24) {
|
|
7880
|
+
const normalized = String(path24 || "");
|
|
7699
7881
|
return SCRATCH_PATTERNS.some((pattern) => pattern.test(normalized));
|
|
7700
7882
|
}
|
|
7701
7883
|
var SCRATCH_PATTERNS;
|
|
@@ -7713,7 +7895,7 @@ var init_publish_file_state = __esm({
|
|
|
7713
7895
|
});
|
|
7714
7896
|
|
|
7715
7897
|
// ../../scripts/virtual-office/code-runner/publish.mjs
|
|
7716
|
-
import { spawnSync as
|
|
7898
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
7717
7899
|
function isMaxTurnsResult(summary) {
|
|
7718
7900
|
return /(^|[^a-z])error[-_ ]?max[-_ ]?turns([^a-z]|$)|max[-_ ]?turns/i.test(String(summary || ""));
|
|
7719
7901
|
}
|
|
@@ -7952,7 +8134,7 @@ var init_executor = __esm({
|
|
|
7952
8134
|
|
|
7953
8135
|
// ../../scripts/virtual-office/code-runner/test-gen-gate.mjs
|
|
7954
8136
|
import fs7 from "node:fs";
|
|
7955
|
-
import
|
|
8137
|
+
import path16 from "node:path";
|
|
7956
8138
|
async function postFailed(client, id, message, result) {
|
|
7957
8139
|
try {
|
|
7958
8140
|
await client.postProgress(id, {
|
|
@@ -7988,7 +8170,7 @@ async function gateTestGenTaskOrFail({ client, id, task, files, worktreeDir, env
|
|
|
7988
8170
|
}
|
|
7989
8171
|
let testSource = "";
|
|
7990
8172
|
try {
|
|
7991
|
-
testSource = fs7.readFileSync(
|
|
8173
|
+
testSource = fs7.readFileSync(path16.join(worktreeDir, testFile), "utf8");
|
|
7992
8174
|
} catch (err) {
|
|
7993
8175
|
await postFailed(client, id, `could not read generated test ${testFile}: ${err.message}`, "gate_test_unreadable");
|
|
7994
8176
|
return true;
|
|
@@ -8038,7 +8220,7 @@ var init_test_gen_gate = __esm({
|
|
|
8038
8220
|
// ../../scripts/virtual-office/code-runner/completion-gate.mjs
|
|
8039
8221
|
import { execFile } from "node:child_process";
|
|
8040
8222
|
import fs8 from "node:fs";
|
|
8041
|
-
import
|
|
8223
|
+
import path17 from "node:path";
|
|
8042
8224
|
function resolveCompletionGate(task) {
|
|
8043
8225
|
const raw = task?.completion_gate;
|
|
8044
8226
|
if (raw === void 0 || raw === null) return null;
|
|
@@ -8076,14 +8258,14 @@ function workspaceFingerprint(worktreeDir, execFileImpl = execFile) {
|
|
|
8076
8258
|
}
|
|
8077
8259
|
function readState(worktreeDir) {
|
|
8078
8260
|
try {
|
|
8079
|
-
return JSON.parse(fs8.readFileSync(
|
|
8261
|
+
return JSON.parse(fs8.readFileSync(path17.join(worktreeDir, COMPLETION_GATE_STATE_FILE), "utf8"));
|
|
8080
8262
|
} catch {
|
|
8081
8263
|
return null;
|
|
8082
8264
|
}
|
|
8083
8265
|
}
|
|
8084
8266
|
function writeState(worktreeDir, state) {
|
|
8085
8267
|
try {
|
|
8086
|
-
fs8.writeFileSync(
|
|
8268
|
+
fs8.writeFileSync(path17.join(worktreeDir, COMPLETION_GATE_STATE_FILE), `${JSON.stringify(state)}
|
|
8087
8269
|
`, "utf8");
|
|
8088
8270
|
} catch {
|
|
8089
8271
|
}
|
|
@@ -9221,7 +9403,7 @@ var init_headless_execution_contract = __esm({
|
|
|
9221
9403
|
});
|
|
9222
9404
|
|
|
9223
9405
|
// ../../scripts/virtual-office/code-runner/skill-catalog.mjs
|
|
9224
|
-
import { readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as
|
|
9406
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync5 } from "node:fs";
|
|
9225
9407
|
import { dirname as dirname7, join as join10 } from "node:path";
|
|
9226
9408
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
9227
9409
|
function parseFrontmatterNameDescription(raw) {
|
|
@@ -9243,12 +9425,12 @@ function parseFrontmatterNameDescription(raw) {
|
|
|
9243
9425
|
}
|
|
9244
9426
|
function isRepoCheckout(dir) {
|
|
9245
9427
|
try {
|
|
9246
|
-
if (!
|
|
9428
|
+
if (!statSync5(join10(dir, ".claude", "skills")).isDirectory()) return false;
|
|
9247
9429
|
} catch {
|
|
9248
9430
|
return false;
|
|
9249
9431
|
}
|
|
9250
9432
|
try {
|
|
9251
|
-
|
|
9433
|
+
statSync5(join10(dir, ".git"));
|
|
9252
9434
|
return true;
|
|
9253
9435
|
} catch {
|
|
9254
9436
|
return false;
|
|
@@ -9274,7 +9456,7 @@ function loadSkillCatalog({ repoRoot: repoRoot2 = resolveDefaultRepoRoot() } = {
|
|
|
9274
9456
|
for (const entry of readdirSync3(skillsDir)) {
|
|
9275
9457
|
const dir = join10(skillsDir, entry);
|
|
9276
9458
|
try {
|
|
9277
|
-
if (!
|
|
9459
|
+
if (!statSync5(dir).isDirectory()) continue;
|
|
9278
9460
|
const parsed = parseFrontmatterNameDescription(
|
|
9279
9461
|
readFileSync8(join10(dir, "SKILL.md"), "utf8")
|
|
9280
9462
|
);
|
|
@@ -9804,7 +9986,7 @@ var init_task_prompt = __esm({
|
|
|
9804
9986
|
import { createHash as createHash5, randomUUID as randomUUID4 } from "node:crypto";
|
|
9805
9987
|
import { chmod, mkdir, mkdtemp, readFile, readdir, realpath, rm, stat, writeFile } from "node:fs/promises";
|
|
9806
9988
|
import os2 from "node:os";
|
|
9807
|
-
import
|
|
9989
|
+
import path18 from "node:path";
|
|
9808
9990
|
function safeTaskToken(taskId) {
|
|
9809
9991
|
return String(taskId || "task").replace(/[^0-9A-Za-z_-]/gu, "_").slice(0, 48) || "task";
|
|
9810
9992
|
}
|
|
@@ -9817,9 +9999,9 @@ function hasGeneratedPrefix(name) {
|
|
|
9817
9999
|
return name.startsWith(DIRECTORY_PREFIX) || name.startsWith(LEGACY_DIRECTORY_PREFIX);
|
|
9818
10000
|
}
|
|
9819
10001
|
function assertGeneratedDirectory(directory, containmentRoot) {
|
|
9820
|
-
const resolvedDirectory =
|
|
9821
|
-
const resolvedRoot =
|
|
9822
|
-
if (
|
|
10002
|
+
const resolvedDirectory = path18.resolve(directory);
|
|
10003
|
+
const resolvedRoot = path18.resolve(containmentRoot);
|
|
10004
|
+
if (path18.dirname(resolvedDirectory) !== resolvedRoot || !hasGeneratedPrefix(path18.basename(resolvedDirectory))) {
|
|
9823
10005
|
throw new Error("refusing to clean an unverified task-attachment directory");
|
|
9824
10006
|
}
|
|
9825
10007
|
return resolvedDirectory;
|
|
@@ -9828,7 +10010,7 @@ async function resolveContainmentRoot(worktreeDir) {
|
|
|
9828
10010
|
if (typeof worktreeDir !== "string" || !worktreeDir.trim()) {
|
|
9829
10011
|
throw new Error("refusing to materialize task attachments outside an agent-readable worktree: no worktreeDir given");
|
|
9830
10012
|
}
|
|
9831
|
-
const root =
|
|
10013
|
+
const root = path18.resolve(worktreeDir);
|
|
9832
10014
|
const stats = await stat(root).catch(() => null);
|
|
9833
10015
|
if (!stats?.isDirectory()) {
|
|
9834
10016
|
throw new Error(`refusing to materialize task attachments: agent worktree root is not a directory (${root})`);
|
|
@@ -9837,15 +10019,15 @@ async function resolveContainmentRoot(worktreeDir) {
|
|
|
9837
10019
|
}
|
|
9838
10020
|
async function createAttachmentDirectory(taskId, containmentRoot) {
|
|
9839
10021
|
const root = await resolveContainmentRoot(containmentRoot);
|
|
9840
|
-
const directory = await mkdtemp(
|
|
10022
|
+
const directory = await mkdtemp(path18.join(root, `${DIRECTORY_PREFIX}${safeTaskToken(taskId)}-`));
|
|
9841
10023
|
const [realRoot, realDirectory] = await Promise.all([realpath(root), realpath(directory)]);
|
|
9842
|
-
if (
|
|
10024
|
+
if (path18.dirname(realDirectory) !== realRoot) {
|
|
9843
10025
|
await rm(directory, { recursive: true, force: true }).catch(() => void 0);
|
|
9844
10026
|
throw new Error("task-attachment directory escaped the agent worktree root");
|
|
9845
10027
|
}
|
|
9846
|
-
await writeFile(
|
|
9847
|
-
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID4(), directory:
|
|
9848
|
-
await writeFile(
|
|
10028
|
+
await writeFile(path18.join(directory, GITIGNORE_FILE), GITIGNORE_BODY, { encoding: "utf8", mode: 384 });
|
|
10029
|
+
const marker = JSON.stringify({ owner: MARKER_OWNER, token: randomUUID4(), directory: path18.basename(directory), created_at: (/* @__PURE__ */ new Date()).toISOString() });
|
|
10030
|
+
await writeFile(path18.join(directory, MARKER_FILE), marker, { encoding: "utf8", mode: 384 });
|
|
9849
10031
|
return { directory, marker, root, cleaned: false };
|
|
9850
10032
|
}
|
|
9851
10033
|
async function cleanupGeneratedDirectory(state) {
|
|
@@ -9859,7 +10041,7 @@ async function cleanupGeneratedDirectory(state) {
|
|
|
9859
10041
|
state.cleaned = true;
|
|
9860
10042
|
return;
|
|
9861
10043
|
}
|
|
9862
|
-
const marker = await readFile(
|
|
10044
|
+
const marker = await readFile(path18.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
9863
10045
|
if (marker !== state.marker) throw new Error("refusing to clean a task-attachment directory without its exact marker");
|
|
9864
10046
|
await rm(directory, { recursive: true, force: true });
|
|
9865
10047
|
state.cleaned = true;
|
|
@@ -9878,7 +10060,7 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
9878
10060
|
now = Date.now(),
|
|
9879
10061
|
maxAgeMs = DEFAULT_STALE_AGE_MS
|
|
9880
10062
|
} = {}) {
|
|
9881
|
-
const root =
|
|
10063
|
+
const root = path18.resolve(tempRoot);
|
|
9882
10064
|
if (!Number.isFinite(maxAgeMs) || maxAgeMs <= 0) throw new Error("stale attachment age must be positive");
|
|
9883
10065
|
const entries = await readdir(root, { withFileTypes: true }).catch((error) => {
|
|
9884
10066
|
if (error?.code === "ENOENT") return [];
|
|
@@ -9887,8 +10069,8 @@ async function sweepStaleTaskAttachmentDirectories({
|
|
|
9887
10069
|
let removed = 0;
|
|
9888
10070
|
for (const entry of entries) {
|
|
9889
10071
|
if (!entry.isDirectory() || !hasGeneratedPrefix(entry.name)) continue;
|
|
9890
|
-
const directory = assertGeneratedDirectory(
|
|
9891
|
-
const markerRaw = await readFile(
|
|
10072
|
+
const directory = assertGeneratedDirectory(path18.join(root, entry.name), root);
|
|
10073
|
+
const markerRaw = await readFile(path18.join(directory, MARKER_FILE), "utf8").catch(() => "");
|
|
9892
10074
|
const marker = parseOwnedMarker(markerRaw, entry.name);
|
|
9893
10075
|
if (!marker) continue;
|
|
9894
10076
|
const directoryStat = await stat(directory);
|
|
@@ -9945,8 +10127,8 @@ async function materializeTaskAttachments(client, task, { worktreeDir } = {}) {
|
|
|
9945
10127
|
const sha2562 = createHash5("sha256").update(content).digest("hex");
|
|
9946
10128
|
if (sha2562 !== ref.sha256) throw new Error(`attachment ${ref.attachment_id} sha256 mismatch`);
|
|
9947
10129
|
const name = sanitizeTaskAttachmentName(ref.name, index);
|
|
9948
|
-
const filePath =
|
|
9949
|
-
if (
|
|
10130
|
+
const filePath = path18.resolve(state.directory, name);
|
|
10131
|
+
if (path18.dirname(filePath) !== state.directory) throw new Error(`attachment ${ref.attachment_id} resolved outside its task directory`);
|
|
9950
10132
|
await writeFile(filePath, content, { flag: "wx", mode: 384 });
|
|
9951
10133
|
await chmod(filePath, 384);
|
|
9952
10134
|
files.push({ attachmentId: ref.attachment_id, name, mime: ref.mime, sizeBytes: ref.size_bytes, sha256: sha2562, path: filePath });
|
|
@@ -10031,9 +10213,9 @@ async function readSpool(spoolDir = SPOOL_DIR) {
|
|
|
10031
10213
|
}
|
|
10032
10214
|
return out;
|
|
10033
10215
|
}
|
|
10034
|
-
async function readCloudMap(
|
|
10216
|
+
async function readCloudMap(path24) {
|
|
10035
10217
|
try {
|
|
10036
|
-
return JSON.parse(await readFile2(
|
|
10218
|
+
return JSON.parse(await readFile2(path24, "utf8"));
|
|
10037
10219
|
} catch {
|
|
10038
10220
|
return {};
|
|
10039
10221
|
}
|
|
@@ -10334,9 +10516,9 @@ function backoffMs(streak, baseMs) {
|
|
|
10334
10516
|
if (streak <= 0) return 0;
|
|
10335
10517
|
return Math.min(baseMs * 2 ** Math.min(streak - 1, 20), MAX_BACKOFF_MS);
|
|
10336
10518
|
}
|
|
10337
|
-
async function loadState(
|
|
10519
|
+
async function loadState(path24) {
|
|
10338
10520
|
try {
|
|
10339
|
-
const parsed = JSON.parse(await readFile3(
|
|
10521
|
+
const parsed = JSON.parse(await readFile3(path24, "utf8"));
|
|
10340
10522
|
if (parsed && typeof parsed === "object" && Number.isInteger(parsed.byte_offset) && parsed.byte_offset >= 0) {
|
|
10341
10523
|
return { ...parsed, byte_offset: parsed.byte_offset };
|
|
10342
10524
|
}
|
|
@@ -10344,15 +10526,15 @@ async function loadState(path23) {
|
|
|
10344
10526
|
}
|
|
10345
10527
|
return { byte_offset: 0, last_event_id: null, forwarded_total: 0, rejected_total: 0, rejected_event_ids: [] };
|
|
10346
10528
|
}
|
|
10347
|
-
async function saveState(
|
|
10348
|
-
await mkdir2(dirname9(
|
|
10349
|
-
await writeFile3(
|
|
10529
|
+
async function saveState(path24, state) {
|
|
10530
|
+
await mkdir2(dirname9(path24), { recursive: true });
|
|
10531
|
+
await writeFile3(path24, JSON.stringify(state, null, 2), "utf8");
|
|
10350
10532
|
}
|
|
10351
|
-
async function readNewBytes(
|
|
10352
|
-
const st = await stat2(
|
|
10533
|
+
async function readNewBytes(path24, offset, max) {
|
|
10534
|
+
const st = await stat2(path24);
|
|
10353
10535
|
if (st.size <= offset) return { buf: Buffer.alloc(0), size: st.size };
|
|
10354
10536
|
const length = Math.min(st.size - offset, max);
|
|
10355
|
-
const fh = await open(
|
|
10537
|
+
const fh = await open(path24, "r");
|
|
10356
10538
|
try {
|
|
10357
10539
|
const buf = Buffer.alloc(length);
|
|
10358
10540
|
const { bytesRead } = await fh.read(buf, 0, length, offset);
|
|
@@ -10603,6 +10785,10 @@ var init_telemetry_forwarder = __esm({
|
|
|
10603
10785
|
});
|
|
10604
10786
|
|
|
10605
10787
|
// ../../scripts/virtual-office/code-runner/loop-ticks.mjs
|
|
10788
|
+
function supportedTaskKindsFor(availableAgents) {
|
|
10789
|
+
const claude = Array.isArray(availableAgents) ? availableAgents.find((row) => row?.agent === "claude") : null;
|
|
10790
|
+
return claude?.installed === true && claude.authenticated === true && claude.skill_capable === true ? RUNNER_SUPPORTED_TASK_KINDS : RUNNER_SUPPORTED_TASK_KINDS.filter((kind) => kind !== "skill");
|
|
10791
|
+
}
|
|
10606
10792
|
function makeLoopTicks({
|
|
10607
10793
|
client,
|
|
10608
10794
|
cfg,
|
|
@@ -10739,7 +10925,7 @@ function makeLoopTicks({
|
|
|
10739
10925
|
uptimeSec: Math.floor(process.uptime()),
|
|
10740
10926
|
activeTasks: getActive(),
|
|
10741
10927
|
maxConcurrency: cfg.maxConcurrency,
|
|
10742
|
-
supportedTaskKinds:
|
|
10928
|
+
supportedTaskKinds: supportedTaskKindsFor(availableAgents),
|
|
10743
10929
|
...capacityFields,
|
|
10744
10930
|
...localModelFields,
|
|
10745
10931
|
...preparedJobFields
|
|
@@ -10899,7 +11085,7 @@ function resolveAgentClaimContext(provider, defaultAgent) {
|
|
|
10899
11085
|
async function collectAgentAvailability({
|
|
10900
11086
|
agents = listAgents(),
|
|
10901
11087
|
runnerFor,
|
|
10902
|
-
probeTimeoutMs =
|
|
11088
|
+
probeTimeoutMs = PROBE_TIMEOUT_MS2
|
|
10903
11089
|
} = {}) {
|
|
10904
11090
|
const probes = agents.map(async (agent) => {
|
|
10905
11091
|
const degraded = { agent, installed: false, authenticated: false };
|
|
@@ -10917,6 +11103,7 @@ async function collectAgentAvailability({
|
|
|
10917
11103
|
installed,
|
|
10918
11104
|
authenticated,
|
|
10919
11105
|
...typeof r?.version === "string" && r.version ? { version: r.version } : {},
|
|
11106
|
+
...typeof r?.skillCapable === "boolean" ? { skill_capable: r.skillCapable } : {},
|
|
10920
11107
|
// Omitted when unknown, which is what an older daemon's silence already
|
|
10921
11108
|
// means — the control-plane schema resolves BOTH to 'unknown'. Never
|
|
10922
11109
|
// invent a tier to fill the gap.
|
|
@@ -10977,7 +11164,7 @@ function makeAgentAvailabilityProvider({
|
|
|
10977
11164
|
}
|
|
10978
11165
|
};
|
|
10979
11166
|
}
|
|
10980
|
-
var DEFAULT_TTL_MS,
|
|
11167
|
+
var DEFAULT_TTL_MS, PROBE_TIMEOUT_MS2;
|
|
10981
11168
|
var init_agent_availability = __esm({
|
|
10982
11169
|
"../../scripts/virtual-office/code-runner/agent-availability.mjs"() {
|
|
10983
11170
|
"use strict";
|
|
@@ -10985,7 +11172,7 @@ var init_agent_availability = __esm({
|
|
|
10985
11172
|
init_agent_auth_probe_process();
|
|
10986
11173
|
init_agent_auth_tier();
|
|
10987
11174
|
DEFAULT_TTL_MS = 5 * 60 * 1e3;
|
|
10988
|
-
|
|
11175
|
+
PROBE_TIMEOUT_MS2 = 1e4;
|
|
10989
11176
|
}
|
|
10990
11177
|
});
|
|
10991
11178
|
|
|
@@ -11447,10 +11634,10 @@ function formatShadowLogLine(record) {
|
|
|
11447
11634
|
const loud = record.unexplained_fields?.length ? "!! " : "";
|
|
11448
11635
|
return `${loud}[prepared-job-shadow] ${parts.join(" ")}`;
|
|
11449
11636
|
}
|
|
11450
|
-
function appendShadowRecord(record, { path:
|
|
11637
|
+
function appendShadowRecord(record, { path: path24 = PREPARED_JOB_SHADOW_SINK, append = appendFileSync, mkdir: mkdir5 = mkdirSync8 } = {}) {
|
|
11451
11638
|
try {
|
|
11452
|
-
mkdir5(dirname10(
|
|
11453
|
-
append(
|
|
11639
|
+
mkdir5(dirname10(path24), { recursive: true });
|
|
11640
|
+
append(path24, `${JSON.stringify(record)}
|
|
11454
11641
|
`, "utf8");
|
|
11455
11642
|
return true;
|
|
11456
11643
|
} catch {
|
|
@@ -11770,7 +11957,7 @@ var init_shared = __esm({
|
|
|
11770
11957
|
// ../../scripts/virtual-office/code-runner/account-usage/claude.mjs
|
|
11771
11958
|
import fs10 from "node:fs";
|
|
11772
11959
|
import os3 from "node:os";
|
|
11773
|
-
import
|
|
11960
|
+
import path19 from "node:path";
|
|
11774
11961
|
function fileCaptureTime(filePath, explicit, statFn) {
|
|
11775
11962
|
if (typeof explicit === "string" && explicit) return explicit;
|
|
11776
11963
|
try {
|
|
@@ -11784,7 +11971,7 @@ function usageBaseUrl(env2 = process.env) {
|
|
|
11784
11971
|
return String(raw).replace(/\/+$/, "");
|
|
11785
11972
|
}
|
|
11786
11973
|
function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.now() } = {}) {
|
|
11787
|
-
const creds = read(
|
|
11974
|
+
const creds = read(path19.join(homeDir, ".claude", ".credentials.json"));
|
|
11788
11975
|
const oauth = creds && typeof creds === "object" ? creds.claudeAiOauth : null;
|
|
11789
11976
|
if (!oauth || typeof oauth !== "object") return null;
|
|
11790
11977
|
const token2 = typeof oauth.accessToken === "string" ? oauth.accessToken.trim() : "";
|
|
@@ -11794,7 +11981,7 @@ function readOAuthToken({ homeDir = os3.homedir(), read = readJson2, now = Date.
|
|
|
11794
11981
|
return token2;
|
|
11795
11982
|
}
|
|
11796
11983
|
function readAccountId({ homeDir = os3.homedir(), read = readJson2 } = {}) {
|
|
11797
|
-
const cfg = read(
|
|
11984
|
+
const cfg = read(path19.join(homeDir, ".claude.json"));
|
|
11798
11985
|
const account = cfg && typeof cfg === "object" ? cfg.oauthAccount : null;
|
|
11799
11986
|
return account && typeof account.accountUuid === "string" ? account.accountUuid : null;
|
|
11800
11987
|
}
|
|
@@ -11904,7 +12091,7 @@ function readClaudeFileUsage({
|
|
|
11904
12091
|
if (age === null || age > MAX_FILE_AGE_MS) return null;
|
|
11905
12092
|
return row;
|
|
11906
12093
|
};
|
|
11907
|
-
const statusPath =
|
|
12094
|
+
const statusPath = path19.join(homeDir, ".claude", "claude-usage.json");
|
|
11908
12095
|
const status = read(statusPath);
|
|
11909
12096
|
if (status && (status.seven_day || status.five_hour)) {
|
|
11910
12097
|
const row = fresh(makeUsageRow({
|
|
@@ -11919,7 +12106,7 @@ function readClaudeFileUsage({
|
|
|
11919
12106
|
}));
|
|
11920
12107
|
if (row) return row;
|
|
11921
12108
|
}
|
|
11922
|
-
const weeklyPath =
|
|
12109
|
+
const weeklyPath = path19.join(homeDir, ".claude", "claude-weekly-usage.json");
|
|
11923
12110
|
const weekly = read(weeklyPath);
|
|
11924
12111
|
if (weekly) {
|
|
11925
12112
|
const row = fresh(makeUsageRow({
|
|
@@ -12733,11 +12920,11 @@ var init_watcher_adoption = __esm({
|
|
|
12733
12920
|
// ../../scripts/virtual-office/code-runner/watcher-github-token.mjs
|
|
12734
12921
|
function makeWatcherTokenProvider(client, { required = true, allowAmbient = false, now = () => Date.now(), log: log2 = () => {
|
|
12735
12922
|
} } = {}) {
|
|
12736
|
-
const
|
|
12923
|
+
const cache2 = /* @__PURE__ */ new Map();
|
|
12737
12924
|
let ciUnreadableLoggedAt = null;
|
|
12738
12925
|
return async (repo) => {
|
|
12739
12926
|
const key = String(repo).toLowerCase();
|
|
12740
|
-
const prior =
|
|
12927
|
+
const prior = cache2.get(key);
|
|
12741
12928
|
if (prior && now() - prior.at < 45 * 60 * 1e3) return prior.token;
|
|
12742
12929
|
const result = await client.getInstallationToken({ required, readOnly: true, repo });
|
|
12743
12930
|
if (!result?.token) {
|
|
@@ -12748,7 +12935,7 @@ function makeWatcherTokenProvider(client, { required = true, allowAmbient = fals
|
|
|
12748
12935
|
ciUnreadableLoggedAt = now();
|
|
12749
12936
|
log2(`watch: the plane minted a read token for ${repo} WITHOUT CI read (ci_readable=false \u2014 the GitHub App installation has not accepted checks:read/statuses:read); PR CI stays unreadable until the operator accepts the App permission update`);
|
|
12750
12937
|
}
|
|
12751
|
-
|
|
12938
|
+
cache2.set(key, { token: result.token, at: now() });
|
|
12752
12939
|
return result.token;
|
|
12753
12940
|
};
|
|
12754
12941
|
}
|
|
@@ -12914,7 +13101,7 @@ function noteCiViaRest(log2) {
|
|
|
12914
13101
|
log2("watch: CI status read via REST check-runs/status (gh's GraphQL rollup needs actions:read for checkSuite.workflowRun, which the read scope does not carry)");
|
|
12915
13102
|
}
|
|
12916
13103
|
async function readCommitCiViaRest(repo, sha, { run, env: env2 }) {
|
|
12917
|
-
const api = async (
|
|
13104
|
+
const api = async (path24) => JSON.parse(await run("gh", ["api", path24], { timeout: 3e4, env: env2 }) || "{}");
|
|
12918
13105
|
const rollup = [];
|
|
12919
13106
|
let total = null;
|
|
12920
13107
|
for (let page = 1; page <= REST_MAX_PAGES && (total === null || rollup.length < total); page += 1) {
|
|
@@ -13578,9 +13765,9 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
13578
13765
|
res.end();
|
|
13579
13766
|
return;
|
|
13580
13767
|
}
|
|
13581
|
-
const
|
|
13768
|
+
const path24 = String(req.url || "").split("?")[0];
|
|
13582
13769
|
res.setHeader("content-type", "application/json");
|
|
13583
|
-
if (req.method === "GET" &&
|
|
13770
|
+
if (req.method === "GET" && path24 === "/status") {
|
|
13584
13771
|
let status;
|
|
13585
13772
|
try {
|
|
13586
13773
|
status = getStatus();
|
|
@@ -13591,7 +13778,7 @@ function buildControlHandler({ getStatus, requestStop, allowedOrigin }) {
|
|
|
13591
13778
|
res.end(JSON.stringify({ ok: true, ...status }));
|
|
13592
13779
|
return;
|
|
13593
13780
|
}
|
|
13594
|
-
if (req.method === "POST" &&
|
|
13781
|
+
if (req.method === "POST" && path24 === "/stop") {
|
|
13595
13782
|
if (!isControlOriginAllowed(req.headers.origin, allowedOrigin) || !req.headers["x-vo-control"]) {
|
|
13596
13783
|
res.statusCode = 403;
|
|
13597
13784
|
res.end(JSON.stringify({ ok: false, error: "forbidden" }));
|
|
@@ -13785,22 +13972,22 @@ var init_effort_mode_config = __esm({
|
|
|
13785
13972
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
13786
13973
|
import fs11 from "node:fs";
|
|
13787
13974
|
import os4 from "node:os";
|
|
13788
|
-
import
|
|
13975
|
+
import path20 from "node:path";
|
|
13789
13976
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
13790
13977
|
function userCacheRoot() {
|
|
13791
13978
|
try {
|
|
13792
13979
|
const home = os4.homedir();
|
|
13793
|
-
if (home) return
|
|
13980
|
+
if (home) return path20.join(home, ".claude");
|
|
13794
13981
|
} catch {
|
|
13795
13982
|
}
|
|
13796
|
-
return
|
|
13983
|
+
return path20.join(os4.tmpdir(), `vo-model-registry-${randomUUID7()}`);
|
|
13797
13984
|
}
|
|
13798
13985
|
function resolveCacheBaseDir(env2 = process.env, moduleDir = __dirname) {
|
|
13799
13986
|
if (env2.VO_MODEL_REGISTRY_CACHE_DIR) return env2.VO_MODEL_REGISTRY_CACHE_DIR;
|
|
13800
13987
|
if (env2.VO_RUNNER_RUNTIME_ROOT) return env2.VO_RUNNER_RUNTIME_ROOT;
|
|
13801
|
-
const segments = moduleDir.split(
|
|
13988
|
+
const segments = moduleDir.split(path20.sep);
|
|
13802
13989
|
const isRepoCheckout2 = segments.at(-1) === "virtual-office" && segments.at(-2) === "scripts";
|
|
13803
|
-
return isRepoCheckout2 ?
|
|
13990
|
+
return isRepoCheckout2 ? path20.resolve(moduleDir, "..", "..") : userCacheRoot();
|
|
13804
13991
|
}
|
|
13805
13992
|
function uniqueModels(models = []) {
|
|
13806
13993
|
return [...new Set(models.map((model) => String(model || "").trim()).filter(Boolean))];
|
|
@@ -13923,7 +14110,7 @@ function readCache(cacheFile = DEFAULT_CACHE_FILE, nowMs = Date.now(), ttlMs = D
|
|
|
13923
14110
|
}
|
|
13924
14111
|
}
|
|
13925
14112
|
function writeCache(cacheFile = DEFAULT_CACHE_FILE, payload) {
|
|
13926
|
-
fs11.mkdirSync(
|
|
14113
|
+
fs11.mkdirSync(path20.dirname(cacheFile), { recursive: true });
|
|
13927
14114
|
fs11.writeFileSync(cacheFile, JSON.stringify(payload, null, 2));
|
|
13928
14115
|
}
|
|
13929
14116
|
async function fetchRegistryCatalog({
|
|
@@ -13981,13 +14168,13 @@ var __dirname, DEFAULT_CACHE_DIR, DEFAULT_CACHE_FILE, DEFAULT_TTL_MS3, ANTHROPIC
|
|
|
13981
14168
|
var init_model_registry = __esm({
|
|
13982
14169
|
"../../scripts/virtual-office/model-registry.mjs"() {
|
|
13983
14170
|
"use strict";
|
|
13984
|
-
__dirname =
|
|
13985
|
-
DEFAULT_CACHE_DIR =
|
|
14171
|
+
__dirname = path20.dirname(fileURLToPath6(import.meta.url));
|
|
14172
|
+
DEFAULT_CACHE_DIR = path20.join(
|
|
13986
14173
|
resolveCacheBaseDir(),
|
|
13987
14174
|
".virtual-office-cache",
|
|
13988
14175
|
"model-registry"
|
|
13989
14176
|
);
|
|
13990
|
-
DEFAULT_CACHE_FILE =
|
|
14177
|
+
DEFAULT_CACHE_FILE = path20.join(DEFAULT_CACHE_DIR, "catalog.json");
|
|
13991
14178
|
DEFAULT_TTL_MS3 = 60 * 60 * 1e3;
|
|
13992
14179
|
ANTHROPIC_API_VERSION = "2023-06-01";
|
|
13993
14180
|
FAMILY_DEFINITIONS = {
|
|
@@ -14626,18 +14813,18 @@ function operatorTierToRung(tier, difficulty, thresholds) {
|
|
|
14626
14813
|
if (tier === "best" && difficulty >= thresholds.rungBounds.R5) return "R5";
|
|
14627
14814
|
return base;
|
|
14628
14815
|
}
|
|
14629
|
-
function readCodexModelsCache({ path:
|
|
14816
|
+
function readCodexModelsCache({ path: path24 = DEFAULT_CODEX_MODELS_CACHE, read = readFileSync9 } = {}) {
|
|
14630
14817
|
try {
|
|
14631
|
-
const parsed = JSON.parse(read(
|
|
14818
|
+
const parsed = JSON.parse(read(path24, "utf8"));
|
|
14632
14819
|
return Array.isArray(parsed?.models) ? parsed : null;
|
|
14633
14820
|
} catch {
|
|
14634
14821
|
return null;
|
|
14635
14822
|
}
|
|
14636
14823
|
}
|
|
14637
|
-
function clampCodexEffort(effort,
|
|
14824
|
+
function clampCodexEffort(effort, cache2) {
|
|
14638
14825
|
if (!effort) return { effort: null, degraded: false };
|
|
14639
14826
|
const supported = /* @__PURE__ */ new Set();
|
|
14640
|
-
for (const model of
|
|
14827
|
+
for (const model of cache2?.models || []) {
|
|
14641
14828
|
if (model?.visibility === "hide") continue;
|
|
14642
14829
|
for (const lvl of model?.supported_reasoning_levels || []) {
|
|
14643
14830
|
if (lvl?.effort) supported.add(lvl.effort);
|
|
@@ -14885,15 +15072,15 @@ function formatDecisionReason(decision, maxLen = 480) {
|
|
|
14885
15072
|
const s = `[${decision.routerVersion}] ${decision.taskClass} d=${decision.difficulty} c=${decision.confidence} \u2192 ${decision.rung}/${decision.tier}${decision.effort ? ` effort=${decision.effort}` : ""} turns=${decision.maxTurns} $${decision.maxBudgetUsd}${decision.flags.length ? ` [${decision.flags.join(",")}]` : ""} :: ${decision.reasons.join("; ")}`;
|
|
14886
15073
|
return s.length > maxLen ? `${s.slice(0, maxLen - 1)}\u2026` : s;
|
|
14887
15074
|
}
|
|
14888
|
-
function appendDecisionFallback(decision, { path:
|
|
15075
|
+
function appendDecisionFallback(decision, { path: path24 = DECISION_FALLBACK_PATH, append = appendFileSync2, mkdir: mkdir5 = mkdirSync9, task, thresholds, roleCostInputs } = {}) {
|
|
14889
15076
|
try {
|
|
14890
|
-
mkdir5(dirname12(
|
|
14891
|
-
append(
|
|
15077
|
+
mkdir5(dirname12(path24), { recursive: true });
|
|
15078
|
+
append(path24, `${JSON.stringify(decision)}
|
|
14892
15079
|
`, "utf8");
|
|
14893
15080
|
if (isRouterDecision(decision)) {
|
|
14894
15081
|
try {
|
|
14895
15082
|
const records = buildShadowRecords({ decision, task, thresholds: thresholds || loadThresholds(), roleCostInputs });
|
|
14896
|
-
for (const record of records) append(
|
|
15083
|
+
for (const record of records) append(path24, `${JSON.stringify(record)}
|
|
14897
15084
|
`, "utf8");
|
|
14898
15085
|
} catch {
|
|
14899
15086
|
}
|
|
@@ -16215,6 +16402,53 @@ var init_terminal_delivery = __esm({
|
|
|
16215
16402
|
}
|
|
16216
16403
|
});
|
|
16217
16404
|
|
|
16405
|
+
// ../../scripts/virtual-office/code-runner/skill-result-json-schema.mjs
|
|
16406
|
+
function buildSkillResultJsonSchema({ expectedSkill, producedByAgent }) {
|
|
16407
|
+
return {
|
|
16408
|
+
type: "object",
|
|
16409
|
+
additionalProperties: false,
|
|
16410
|
+
properties: {
|
|
16411
|
+
schema_version: { const: 1 },
|
|
16412
|
+
skill: { const: expectedSkill },
|
|
16413
|
+
outcome: { enum: ["findings", "no_findings", "refused"] },
|
|
16414
|
+
findings: {
|
|
16415
|
+
type: "array",
|
|
16416
|
+
maxItems: MAX_FINDINGS,
|
|
16417
|
+
items: {
|
|
16418
|
+
type: "object",
|
|
16419
|
+
additionalProperties: false,
|
|
16420
|
+
properties: {
|
|
16421
|
+
claim: { type: "string", minLength: 1, maxLength: 500 },
|
|
16422
|
+
evidence: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
16423
|
+
source: { type: "string", maxLength: 500 },
|
|
16424
|
+
confidence: { enum: ["high", "medium", "low"] }
|
|
16425
|
+
},
|
|
16426
|
+
required: ["claim", "evidence", "confidence"]
|
|
16427
|
+
}
|
|
16428
|
+
},
|
|
16429
|
+
findings_truncated: { type: "boolean" },
|
|
16430
|
+
summary: { type: "string", minLength: 1, maxLength: 2e3 },
|
|
16431
|
+
produced_by_agent: { const: producedByAgent }
|
|
16432
|
+
},
|
|
16433
|
+
required: [
|
|
16434
|
+
"schema_version",
|
|
16435
|
+
"skill",
|
|
16436
|
+
"outcome",
|
|
16437
|
+
"findings",
|
|
16438
|
+
"findings_truncated",
|
|
16439
|
+
"summary",
|
|
16440
|
+
"produced_by_agent"
|
|
16441
|
+
]
|
|
16442
|
+
};
|
|
16443
|
+
}
|
|
16444
|
+
var MAX_FINDINGS;
|
|
16445
|
+
var init_skill_result_json_schema = __esm({
|
|
16446
|
+
"../../scripts/virtual-office/code-runner/skill-result-json-schema.mjs"() {
|
|
16447
|
+
"use strict";
|
|
16448
|
+
MAX_FINDINGS = 50;
|
|
16449
|
+
}
|
|
16450
|
+
});
|
|
16451
|
+
|
|
16218
16452
|
// ../../scripts/virtual-office/code-runner/skill-task-runner.mjs
|
|
16219
16453
|
import { createHash as createHash9 } from "node:crypto";
|
|
16220
16454
|
import { mkdtemp as mkdtemp2, rm as rm2 } from "node:fs/promises";
|
|
@@ -16275,13 +16509,7 @@ function skillResultPayloadSha256(result) {
|
|
|
16275
16509
|
};
|
|
16276
16510
|
return createHash9("sha256").update(JSON.stringify(payload), "utf8").digest("hex");
|
|
16277
16511
|
}
|
|
16278
|
-
function
|
|
16279
|
-
let value;
|
|
16280
|
-
try {
|
|
16281
|
-
value = JSON.parse(String(text ?? "").trim());
|
|
16282
|
-
} catch {
|
|
16283
|
-
throw new Error("skill agent did not return one strict JSON result");
|
|
16284
|
-
}
|
|
16512
|
+
function parseSkillResultValue(value, { expectedSkill, producedByAgent }) {
|
|
16285
16513
|
if (!exactKeys(value, ["schema_version", "skill", "outcome", "findings", "findings_truncated", "summary", "produced_by_agent"])) {
|
|
16286
16514
|
throw new Error("skill result shape is invalid");
|
|
16287
16515
|
}
|
|
@@ -16290,7 +16518,7 @@ function parseSkillResult(text, { expectedSkill, producedByAgent }) {
|
|
|
16290
16518
|
if (value.produced_by_agent !== producedByAgent) throw new Error("skill result agent attribution is invalid");
|
|
16291
16519
|
if (typeof value.findings_truncated !== "boolean") throw new Error("skill result truncation flag is invalid");
|
|
16292
16520
|
boundedString(value.summary, 1, 2e3, "skill result summary");
|
|
16293
|
-
if (!Array.isArray(value.findings) || value.findings.length >
|
|
16521
|
+
if (!Array.isArray(value.findings) || value.findings.length > MAX_FINDINGS2) throw new Error("skill result findings are invalid");
|
|
16294
16522
|
if (value.outcome === "findings" ? value.findings.length === 0 : value.findings.length > 0) {
|
|
16295
16523
|
throw new Error("skill result findings do not match its outcome");
|
|
16296
16524
|
}
|
|
@@ -16307,6 +16535,27 @@ function parseSkillResult(text, { expectedSkill, producedByAgent }) {
|
|
|
16307
16535
|
}
|
|
16308
16536
|
return value;
|
|
16309
16537
|
}
|
|
16538
|
+
function assertSkillRunWithinDispatch(run, { maxTurns, maxBudgetUsd } = {}) {
|
|
16539
|
+
if (run?.terminalSubtype !== "success") {
|
|
16540
|
+
throw new Error(`skill agent terminal subtype was not success (${run?.terminalSubtype || "unknown"})`);
|
|
16541
|
+
}
|
|
16542
|
+
if (Number.isInteger(maxTurns) && maxTurns > 0) {
|
|
16543
|
+
if (!Number.isInteger(run?.numTurns) || run.numTurns < 0) {
|
|
16544
|
+
throw new Error("skill agent did not report valid turn usage for the active ceiling");
|
|
16545
|
+
}
|
|
16546
|
+
if (run.numTurns > maxTurns) {
|
|
16547
|
+
throw new Error(`skill agent exceeded the turn ceiling (${run.numTurns} > ${maxTurns})`);
|
|
16548
|
+
}
|
|
16549
|
+
}
|
|
16550
|
+
if (typeof maxBudgetUsd === "number" && Number.isFinite(maxBudgetUsd) && maxBudgetUsd > 0) {
|
|
16551
|
+
if (typeof run?.costUsd !== "number" || !Number.isFinite(run.costUsd) || run.costUsd < 0) {
|
|
16552
|
+
throw new Error("skill agent did not report valid cost usage for the active ceiling");
|
|
16553
|
+
}
|
|
16554
|
+
if (run.costUsd > maxBudgetUsd) {
|
|
16555
|
+
throw new Error(`skill agent exceeded the cost ceiling (${run.costUsd} > ${maxBudgetUsd})`);
|
|
16556
|
+
}
|
|
16557
|
+
}
|
|
16558
|
+
}
|
|
16310
16559
|
function composeSkillTaskPrompt({ skillBody, invocation, taskPrompt, producedByAgent }) {
|
|
16311
16560
|
const body = boundedString(skillBody, 1, MAX_SKILL_BODY_CHARS, "skill body");
|
|
16312
16561
|
const request = boundedString(taskPrompt, 1, 810201, "skill task request");
|
|
@@ -16341,8 +16590,9 @@ async function processSkillTask(client, task, cfg, {
|
|
|
16341
16590
|
swarmAdmission = null,
|
|
16342
16591
|
runTask = runAgentTask,
|
|
16343
16592
|
resolveDispatch = resolveEffortDispatch,
|
|
16593
|
+
checkSkillCapability = ({ runner, bin, env: capabilityEnv }) => typeof runner.checkSkillCapability === "function" ? runner.checkSkillCapability({ bin, env: capabilityEnv }) : { compatible: false, reason: "resolved runner has no restricted-skill capability probe" },
|
|
16344
16594
|
createScratch = () => mkdtemp2(join19(tmpdir(), "algohq-skill-task-")),
|
|
16345
|
-
removeScratch = (
|
|
16595
|
+
removeScratch = (path24) => rm2(path24, { recursive: true, force: true })
|
|
16346
16596
|
} = {}) {
|
|
16347
16597
|
const id = task.code_task_id;
|
|
16348
16598
|
let run = null;
|
|
@@ -16389,12 +16639,25 @@ async function processSkillTask(client, task, cfg, {
|
|
|
16389
16639
|
if (selected.agent !== "claude") {
|
|
16390
16640
|
throw new Error("skill execution requires the policy-restricted Claude runner");
|
|
16391
16641
|
}
|
|
16642
|
+
const capability = await checkSkillCapability({
|
|
16643
|
+
runner: selected.runner,
|
|
16644
|
+
bin: selected.runnerBin,
|
|
16645
|
+
env: env2
|
|
16646
|
+
});
|
|
16647
|
+
if (!capability?.compatible) {
|
|
16648
|
+
throw new Error(`resolved Claude CLI is incompatible with restricted skill execution: ${capability?.reason || "unknown capability"}`);
|
|
16649
|
+
}
|
|
16650
|
+
const executionBin = capability.resolvedBin || selected.runnerBin;
|
|
16392
16651
|
const basePrompt = composeSkillTaskPrompt({
|
|
16393
16652
|
skillBody: skill.body,
|
|
16394
16653
|
invocation,
|
|
16395
16654
|
taskPrompt: task.prompt,
|
|
16396
16655
|
producedByAgent: selected.agent
|
|
16397
16656
|
});
|
|
16657
|
+
const structuredOutputSchema = buildSkillResultJsonSchema({
|
|
16658
|
+
expectedSkill: invocation.skill,
|
|
16659
|
+
producedByAgent: selected.agent
|
|
16660
|
+
});
|
|
16398
16661
|
const dispatch = await resolveDispatch({ client, task, agent: selected.agent, env: env2, basePrompt });
|
|
16399
16662
|
assertRunnerGovernors({ agent: selected.agent, task });
|
|
16400
16663
|
scratch = await createScratch();
|
|
@@ -16412,10 +16675,11 @@ async function processSkillTask(client, task, cfg, {
|
|
|
16412
16675
|
});
|
|
16413
16676
|
run = await runTask({
|
|
16414
16677
|
runner: selected.runner,
|
|
16415
|
-
bin:
|
|
16678
|
+
bin: executionBin,
|
|
16416
16679
|
prompt: dispatch.prompt,
|
|
16417
16680
|
cwd: scratch,
|
|
16418
16681
|
toolPolicy: frozenInputsOnly ? "frozen_inputs_only" : "skill_readonly",
|
|
16682
|
+
structuredOutputSchema,
|
|
16419
16683
|
permissionMode: dispatch.permissionMode,
|
|
16420
16684
|
maxTurns: selected.agent === "claude" ? dispatch.maxTurns : void 0,
|
|
16421
16685
|
model: dispatch.model,
|
|
@@ -16466,7 +16730,11 @@ async function processSkillTask(client, task, cfg, {
|
|
|
16466
16730
|
} });
|
|
16467
16731
|
return;
|
|
16468
16732
|
}
|
|
16469
|
-
|
|
16733
|
+
assertSkillRunWithinDispatch(run, dispatch);
|
|
16734
|
+
if (!run.structuredOutput || typeof run.structuredOutput !== "object" || Array.isArray(run.structuredOutput)) {
|
|
16735
|
+
throw new Error("skill agent did not return provider-validated structured output");
|
|
16736
|
+
}
|
|
16737
|
+
const parsedResult = parseSkillResultValue(run.structuredOutput, {
|
|
16470
16738
|
expectedSkill: invocation.skill,
|
|
16471
16739
|
producedByAgent: selected.agent
|
|
16472
16740
|
});
|
|
@@ -16509,7 +16777,7 @@ async function processSkillTask(client, task, cfg, {
|
|
|
16509
16777
|
} });
|
|
16510
16778
|
}
|
|
16511
16779
|
}
|
|
16512
|
-
var SKILL_SUCCESS_STATUS, SKILL_NAME_RE, INPUT_KEY_RE, MAX_SKILL_BODY_CHARS, MAX_INPUTS,
|
|
16780
|
+
var SKILL_SUCCESS_STATUS, SKILL_NAME_RE, INPUT_KEY_RE, MAX_SKILL_BODY_CHARS, MAX_INPUTS, MAX_FINDINGS2, SHA256_RE;
|
|
16513
16781
|
var init_skill_task_runner = __esm({
|
|
16514
16782
|
"../../scripts/virtual-office/code-runner/skill-task-runner.mjs"() {
|
|
16515
16783
|
"use strict";
|
|
@@ -16523,12 +16791,13 @@ var init_skill_task_runner = __esm({
|
|
|
16523
16791
|
init_killed_run_outcome();
|
|
16524
16792
|
init_terminal_delivery();
|
|
16525
16793
|
init_cancelled_run_report();
|
|
16794
|
+
init_skill_result_json_schema();
|
|
16526
16795
|
SKILL_SUCCESS_STATUS = "no_changes_needed";
|
|
16527
16796
|
SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]{0,62}(?::[a-z0-9][a-z0-9-]{0,62})?$/u;
|
|
16528
16797
|
INPUT_KEY_RE = /^[a-z][a-z0-9_]{0,39}$/u;
|
|
16529
16798
|
MAX_SKILL_BODY_CHARS = 256e3;
|
|
16530
16799
|
MAX_INPUTS = 12;
|
|
16531
|
-
|
|
16800
|
+
MAX_FINDINGS2 = 50;
|
|
16532
16801
|
SHA256_RE = /^[a-f0-9]{64}$/u;
|
|
16533
16802
|
}
|
|
16534
16803
|
});
|
|
@@ -16536,7 +16805,7 @@ var init_skill_task_runner = __esm({
|
|
|
16536
16805
|
// ../../scripts/virtual-office/code-runner/isolation-audit.mjs
|
|
16537
16806
|
import fs12 from "node:fs";
|
|
16538
16807
|
import fsp11 from "node:fs/promises";
|
|
16539
|
-
import
|
|
16808
|
+
import path21 from "node:path";
|
|
16540
16809
|
async function defaultRun3(command, args, cwd, options = {}) {
|
|
16541
16810
|
return runProcess2(command, args, { cwd, timeout: 6e4, ...options });
|
|
16542
16811
|
}
|
|
@@ -16549,7 +16818,7 @@ async function canonicalRootForWorktree(worktreeDir, run) {
|
|
|
16549
16818
|
"--path-format=absolute",
|
|
16550
16819
|
"--git-common-dir"
|
|
16551
16820
|
])).trim();
|
|
16552
|
-
const root =
|
|
16821
|
+
const root = path21.dirname(commonDir);
|
|
16553
16822
|
return samePath3(root, worktreeDir) ? null : root;
|
|
16554
16823
|
}
|
|
16555
16824
|
async function snapshot(root, run) {
|
|
@@ -16591,21 +16860,21 @@ async function changedPaths(root, run) {
|
|
|
16591
16860
|
}
|
|
16592
16861
|
async function quarantineCanonicalWrites({ baseline, worktreeDir, taskId, run, now }) {
|
|
16593
16862
|
const paths = await changedPaths(baseline.root, run);
|
|
16594
|
-
const quarantineDir =
|
|
16595
|
-
|
|
16863
|
+
const quarantineDir = path21.join(
|
|
16864
|
+
path21.dirname(worktreeDir),
|
|
16596
16865
|
".canonical-recovery",
|
|
16597
16866
|
`${String(taskId || "unknown").replace(/[^a-z0-9-]/gi, "-")}-${now().toISOString().replace(/[:.]/g, "-")}`
|
|
16598
16867
|
);
|
|
16599
16868
|
await fsp11.mkdir(quarantineDir, { recursive: true });
|
|
16600
16869
|
const patch = await git2(run, baseline.root, ["diff", "--binary", "HEAD"], { raw: true });
|
|
16601
|
-
await fsp11.writeFile(
|
|
16870
|
+
await fsp11.writeFile(path21.join(quarantineDir, "tracked.patch"), patch, "utf8");
|
|
16602
16871
|
for (const relative of paths.untracked) {
|
|
16603
|
-
const source =
|
|
16604
|
-
const target =
|
|
16605
|
-
await fsp11.mkdir(
|
|
16872
|
+
const source = path21.join(baseline.root, relative);
|
|
16873
|
+
const target = path21.join(quarantineDir, "untracked", relative);
|
|
16874
|
+
await fsp11.mkdir(path21.dirname(target), { recursive: true });
|
|
16606
16875
|
await fsp11.copyFile(source, target);
|
|
16607
16876
|
}
|
|
16608
|
-
await fsp11.writeFile(
|
|
16877
|
+
await fsp11.writeFile(path21.join(quarantineDir, "manifest.json"), `${JSON.stringify({
|
|
16609
16878
|
taskId,
|
|
16610
16879
|
canonicalRoot: baseline.root,
|
|
16611
16880
|
canonicalHead: baseline.head,
|
|
@@ -16627,8 +16896,8 @@ async function restoreExactCanonicalPaths(baseline, evidence, run) {
|
|
|
16627
16896
|
]);
|
|
16628
16897
|
}
|
|
16629
16898
|
for (const relative of evidence.untracked) {
|
|
16630
|
-
const target =
|
|
16631
|
-
const prefix = `${
|
|
16899
|
+
const target = path21.resolve(baseline.root, relative);
|
|
16900
|
+
const prefix = `${path21.resolve(baseline.root)}${path21.sep}`;
|
|
16632
16901
|
if (!target.startsWith(prefix) || !fs12.existsSync(target)) continue;
|
|
16633
16902
|
await fsp11.rm(target, { force: true });
|
|
16634
16903
|
}
|
|
@@ -16665,7 +16934,7 @@ var init_isolation_audit = __esm({
|
|
|
16665
16934
|
init_process_runner2();
|
|
16666
16935
|
splitZ2 = (value) => String(value || "").split("\0").map((item) => item.trim()).filter(Boolean);
|
|
16667
16936
|
samePath3 = (left, right) => {
|
|
16668
|
-
const [a, b] = [left, right].map((value) =>
|
|
16937
|
+
const [a, b] = [left, right].map((value) => path21.resolve(value));
|
|
16669
16938
|
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
16670
16939
|
};
|
|
16671
16940
|
}
|
|
@@ -17082,7 +17351,7 @@ var init_publication_outcome = __esm({
|
|
|
17082
17351
|
|
|
17083
17352
|
// ../../scripts/virtual-office/code-runner/committed-scratch-cleanup.mjs
|
|
17084
17353
|
import fsp12 from "node:fs/promises";
|
|
17085
|
-
import
|
|
17354
|
+
import path22 from "node:path";
|
|
17086
17355
|
function defaultRun4(command, args, cwd, options = {}) {
|
|
17087
17356
|
return runProcess2(command, args, { cwd, ...options });
|
|
17088
17357
|
}
|
|
@@ -17090,13 +17359,13 @@ async function resolveSafeScratchTarget(worktreeDir, file) {
|
|
|
17090
17359
|
if (!isAgentScratch(file)) {
|
|
17091
17360
|
throw new Error(`refusing to remove non-scratch publication path: ${file}`);
|
|
17092
17361
|
}
|
|
17093
|
-
const root =
|
|
17094
|
-
const target =
|
|
17095
|
-
const relative =
|
|
17096
|
-
if (!relative || relative.startsWith(`..${
|
|
17362
|
+
const root = path22.resolve(worktreeDir);
|
|
17363
|
+
const target = path22.resolve(root, file);
|
|
17364
|
+
const relative = path22.relative(root, target);
|
|
17365
|
+
if (!relative || relative.startsWith(`..${path22.sep}`) || path22.isAbsolute(relative)) {
|
|
17097
17366
|
throw new Error(`refusing to remove publication scratch outside worktree: ${file}`);
|
|
17098
17367
|
}
|
|
17099
|
-
for (let cursor = target; cursor !== root; cursor =
|
|
17368
|
+
for (let cursor = target; cursor !== root; cursor = path22.dirname(cursor)) {
|
|
17100
17369
|
try {
|
|
17101
17370
|
if ((await fsp12.lstat(cursor)).isSymbolicLink()) {
|
|
17102
17371
|
throw new Error(`refusing to follow symlink while removing publication scratch: ${file}`);
|
|
@@ -17218,7 +17487,7 @@ var init_publication_scope = __esm({
|
|
|
17218
17487
|
// ../../scripts/virtual-office/code-runner/recovery-ledger.mjs
|
|
17219
17488
|
import fs13 from "node:fs";
|
|
17220
17489
|
import fsp13 from "node:fs/promises";
|
|
17221
|
-
import
|
|
17490
|
+
import path23 from "node:path";
|
|
17222
17491
|
function recoveryTaskId(prompt) {
|
|
17223
17492
|
const match = String(prompt || "").match(/VO_RECOVERY_FROM_CODE_TASK:\s*([0-9a-f-]{36})/i);
|
|
17224
17493
|
return match ? match[1].toLowerCase() : null;
|
|
@@ -17232,10 +17501,10 @@ function cloneLeaf(repo) {
|
|
|
17232
17501
|
function recoveryLedgerCandidates(repo, clonesRoot2) {
|
|
17233
17502
|
const leaf = cloneLeaf(repo);
|
|
17234
17503
|
if (!leaf || !clonesRoot2) return [];
|
|
17235
|
-
const canonical =
|
|
17504
|
+
const canonical = path23.join(clonesRoot2, leaf);
|
|
17236
17505
|
return [
|
|
17237
|
-
|
|
17238
|
-
|
|
17506
|
+
path23.join(clonesRoot2, ".agent-worktrees", leaf, "recovery-ledger.jsonl"),
|
|
17507
|
+
path23.join(canonical, ".agent-worktrees", "recovery-ledger.jsonl")
|
|
17239
17508
|
];
|
|
17240
17509
|
}
|
|
17241
17510
|
async function readLedger(file, readFile6) {
|