usebeeline 0.0.37 → 0.0.40
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/usebeeline.mjs +1425 -449
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -307,14 +307,14 @@ __export(self_update_exports, {
|
|
|
307
307
|
writeUpdateAttemptFixture: () => writeUpdateAttemptFixture,
|
|
308
308
|
writeUpdateState: () => writeUpdateState
|
|
309
309
|
});
|
|
310
|
-
import { createHash as
|
|
310
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
311
311
|
import { constants as fsConstants } from "node:fs";
|
|
312
|
-
import { access, chmod as chmod3, lstat as lstat2, mkdir as
|
|
312
|
+
import { access, chmod as chmod3, lstat as lstat2, mkdir as mkdir10, open, readFile as readFile8, rename as rename3, rm as rm4, symlink as symlink2, writeFile as writeFile8 } from "node:fs/promises";
|
|
313
313
|
import { spawn as spawn4 } from "node:child_process";
|
|
314
314
|
import { homedir as homedir9 } from "node:os";
|
|
315
|
-
import { dirname as
|
|
315
|
+
import { dirname as dirname9, join as join6, resolve as resolve18 } from "node:path";
|
|
316
316
|
function anchorLayout(rawLibDir) {
|
|
317
|
-
const libDir =
|
|
317
|
+
const libDir = resolve18(rawLibDir);
|
|
318
318
|
const segments = libDir.split(/[/\\]/);
|
|
319
319
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
320
320
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -330,9 +330,9 @@ function anchorLayout(rawLibDir) {
|
|
|
330
330
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
331
331
|
// defect that made activateRelease write its stable forwarders where
|
|
332
332
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
333
|
-
binDir:
|
|
333
|
+
binDir: resolve18(libDir, "../../bin"),
|
|
334
334
|
libDir,
|
|
335
|
-
releasesRoot:
|
|
335
|
+
releasesRoot: resolve18(libDir, `../${RELEASES_SEGMENT}`)
|
|
336
336
|
};
|
|
337
337
|
}
|
|
338
338
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -343,7 +343,7 @@ function beelineInstallLayout(env = process.env) {
|
|
|
343
343
|
}
|
|
344
344
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
345
345
|
const home = env.HOME?.trim() || homedir9();
|
|
346
|
-
return anchorLayout(
|
|
346
|
+
return anchorLayout(resolve18(home, ".local", "lib", "beeline"));
|
|
347
347
|
}
|
|
348
348
|
function hostPlatformKey() {
|
|
349
349
|
const os = process.platform === "linux" ? "linux" : process.platform === "darwin" ? "darwin" : "";
|
|
@@ -359,7 +359,7 @@ async function readBundleJson(bundleDir) {
|
|
|
359
359
|
let raw;
|
|
360
360
|
for (const candidate of bundleJsonCandidates(bundleDir)) {
|
|
361
361
|
try {
|
|
362
|
-
raw = await
|
|
362
|
+
raw = await readFile8(candidate, "utf8");
|
|
363
363
|
break;
|
|
364
364
|
} catch {
|
|
365
365
|
}
|
|
@@ -381,14 +381,14 @@ function updateStatePath(layout) {
|
|
|
381
381
|
}
|
|
382
382
|
async function readUpdateState(layout) {
|
|
383
383
|
try {
|
|
384
|
-
return JSON.parse(await
|
|
384
|
+
return JSON.parse(await readFile8(updateStatePath(layout), "utf8"));
|
|
385
385
|
} catch {
|
|
386
386
|
return {};
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
async function writeUpdateState(layout, state) {
|
|
390
|
-
await
|
|
391
|
-
await
|
|
390
|
+
await mkdir10(join6(layout.releasesRoot, ".state"), { recursive: true });
|
|
391
|
+
await writeFile8(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
|
|
392
392
|
`, "utf8");
|
|
393
393
|
}
|
|
394
394
|
async function readInstalledBundleIdentity(layout, _state = {}) {
|
|
@@ -495,13 +495,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
495
495
|
const okMarker = join6(releaseDir, ".stage-ok");
|
|
496
496
|
let previouslyVerified = false;
|
|
497
497
|
try {
|
|
498
|
-
const recorded = await
|
|
498
|
+
const recorded = await readFile8(okMarker, "utf8");
|
|
499
499
|
if (recorded.trim() === published.sha256)
|
|
500
500
|
return releaseId;
|
|
501
501
|
previouslyVerified = true;
|
|
502
502
|
} catch {
|
|
503
503
|
}
|
|
504
|
-
await
|
|
504
|
+
await mkdir10(releaseDir, { recursive: true });
|
|
505
505
|
const tempArchive = join6(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
|
|
506
506
|
try {
|
|
507
507
|
log(`downloading ${published.file}`);
|
|
@@ -511,7 +511,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
511
511
|
if (!response.ok || !response.body) {
|
|
512
512
|
throw new Error(`downloading ${published.file} failed: HTTP ${response.status}`);
|
|
513
513
|
}
|
|
514
|
-
const hash =
|
|
514
|
+
const hash = createHash3("sha256");
|
|
515
515
|
const chunks = [];
|
|
516
516
|
for await (const chunk of response.body) {
|
|
517
517
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
@@ -522,7 +522,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
522
522
|
if (actual !== published.sha256.toLowerCase()) {
|
|
523
523
|
throw new Error(`checksum mismatch for ${published.file}: expected ${published.sha256}, got ${actual} \u2014 aborting without touching the installed bundle`);
|
|
524
524
|
}
|
|
525
|
-
await
|
|
525
|
+
await writeFile8(tempArchive, Buffer.concat(chunks), { mode: 384 });
|
|
526
526
|
const entries = (await new Promise((resolveList, rejectList) => {
|
|
527
527
|
const child = spawn4("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
|
|
528
528
|
let out = "";
|
|
@@ -553,7 +553,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
553
553
|
throw new Error(`staged bundle failed its startup smoke test (--version exited ${probe.status})${probe.stderr ? `: ${probe.stderr.trim()}` : ""}`);
|
|
554
554
|
}
|
|
555
555
|
}
|
|
556
|
-
await
|
|
556
|
+
await writeFile8(okMarker, `${published.sha256}
|
|
557
557
|
`, "utf8");
|
|
558
558
|
log(`staged release ${releaseId} (sha256 verified)`);
|
|
559
559
|
return releaseId;
|
|
@@ -581,15 +581,15 @@ function forwarderScript(tool) {
|
|
|
581
581
|
}
|
|
582
582
|
async function replaceFile(path, contents, mode) {
|
|
583
583
|
const temp = `${path}.new-${process.pid}`;
|
|
584
|
-
await
|
|
584
|
+
await writeFile8(temp, contents, { mode });
|
|
585
585
|
await chmod3(temp, mode);
|
|
586
586
|
await rename3(temp, path);
|
|
587
587
|
}
|
|
588
588
|
async function activateRelease(layout, releaseId) {
|
|
589
589
|
const releaseDir = join6(layout.releasesRoot, releaseId);
|
|
590
590
|
await access(join6(releaseDir, BUNDLE_ENTRYPOINT), fsConstants.F_OK);
|
|
591
|
-
await
|
|
592
|
-
await
|
|
591
|
+
await mkdir10(layout.releasesRoot, { recursive: true });
|
|
592
|
+
await mkdir10(layout.binDir, { recursive: true });
|
|
593
593
|
let previousReleaseId = await activeReleaseId(layout);
|
|
594
594
|
const kind = await pathKind(layout.libDir);
|
|
595
595
|
if (kind === "directory") {
|
|
@@ -611,7 +611,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
611
611
|
await rm4(tempLink, { force: true });
|
|
612
612
|
await symlink2(join6("beeline-releases", releaseId), tempLink);
|
|
613
613
|
await rename3(tempLink, layout.libDir);
|
|
614
|
-
await fsyncDir(
|
|
614
|
+
await fsyncDir(dirname9(layout.libDir));
|
|
615
615
|
await writeBinForwarders(layout, releaseDir);
|
|
616
616
|
return { previousReleaseId };
|
|
617
617
|
}
|
|
@@ -628,7 +628,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
628
628
|
}
|
|
629
629
|
if (!anyFlat)
|
|
630
630
|
return;
|
|
631
|
-
await
|
|
631
|
+
await mkdir10(innerLib, { recursive: true });
|
|
632
632
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
633
633
|
try {
|
|
634
634
|
await access(join6(innerLib, name), fsConstants.F_OK);
|
|
@@ -655,13 +655,13 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
655
655
|
const forwarderPath = join6(layout.binDir, "beeline");
|
|
656
656
|
let current;
|
|
657
657
|
try {
|
|
658
|
-
current = await
|
|
658
|
+
current = await readFile8(forwarderPath, "utf8");
|
|
659
659
|
} catch {
|
|
660
660
|
current = void 0;
|
|
661
661
|
}
|
|
662
662
|
if (current === forwarderScript("beeline"))
|
|
663
663
|
return false;
|
|
664
|
-
await
|
|
664
|
+
await mkdir10(layout.binDir, { recursive: true });
|
|
665
665
|
await writeBinForwarders(layout, layout.libDir);
|
|
666
666
|
opts.logger?.(`[body] self-update: repaired <prefix>/bin forwarders to follow the active-bundle anchor (${layout.libDir})`);
|
|
667
667
|
return true;
|
|
@@ -676,14 +676,14 @@ async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
|
676
676
|
await rm4(tempLink, { force: true });
|
|
677
677
|
await symlink2(join6("beeline-releases", previousReleaseId), tempLink);
|
|
678
678
|
await rename3(tempLink, layout.libDir);
|
|
679
|
-
await fsyncDir(
|
|
679
|
+
await fsyncDir(dirname9(layout.libDir));
|
|
680
680
|
}
|
|
681
681
|
function updateAttemptPath(layout) {
|
|
682
682
|
return join6(layout.releasesRoot, ".state", "update-attempt.json");
|
|
683
683
|
}
|
|
684
684
|
async function readUpdateAttempt(layout) {
|
|
685
685
|
try {
|
|
686
|
-
const raw = JSON.parse(await
|
|
686
|
+
const raw = JSON.parse(await readFile8(updateAttemptPath(layout), "utf8"));
|
|
687
687
|
if (raw.version !== 1 || typeof raw.appliedAt !== "number" || typeof raw.confirmBy !== "number" || typeof raw.releaseId !== "string" || !["pending", "confirmed", "reverted"].includes(raw.status)) {
|
|
688
688
|
return void 0;
|
|
689
689
|
}
|
|
@@ -693,10 +693,10 @@ async function readUpdateAttempt(layout) {
|
|
|
693
693
|
}
|
|
694
694
|
}
|
|
695
695
|
async function writeUpdateAttempt(layout, record2) {
|
|
696
|
-
await
|
|
696
|
+
await mkdir10(join6(layout.releasesRoot, ".state"), { recursive: true });
|
|
697
697
|
const path = updateAttemptPath(layout);
|
|
698
698
|
const staged = `${path}.${process.pid}.tmp`;
|
|
699
|
-
await
|
|
699
|
+
await writeFile8(staged, `${JSON.stringify(record2, null, 2)}
|
|
700
700
|
`, { mode: 384 });
|
|
701
701
|
await rename3(staged, path);
|
|
702
702
|
}
|
|
@@ -955,8 +955,8 @@ var init_self_update = __esm({
|
|
|
955
955
|
});
|
|
956
956
|
|
|
957
957
|
// apps/body/dist/cli.js
|
|
958
|
-
import { dirname as
|
|
959
|
-
import { readFile as
|
|
958
|
+
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
959
|
+
import { readFile as readFile13, unlink as unlink3, writeFile as writeFile14 } from "node:fs/promises";
|
|
960
960
|
import { stdin as stdin4, stdout as stdout5 } from "node:process";
|
|
961
961
|
|
|
962
962
|
// node_modules/@clack/core/dist/index.mjs
|
|
@@ -3099,6 +3099,9 @@ function agentMessageChunkText(update) {
|
|
|
3099
3099
|
}
|
|
3100
3100
|
var CHUNK_CONTINUES_PREVIOUS_WORD = /^[\s'\u2018\u2019\u02bc.,!?;:%)\]}-]/;
|
|
3101
3101
|
var PI_ACP_HARNESS = /(^|[/\\])pi-acp(?:\.[a-z]+)?$/i;
|
|
3102
|
+
function isPiAcpHarness(agentLabel) {
|
|
3103
|
+
return Boolean(agentLabel && PI_ACP_HARNESS.test(agentLabel));
|
|
3104
|
+
}
|
|
3102
3105
|
function withoutOneTrailingLineEnding(text2) {
|
|
3103
3106
|
if (/\r?\n\r?\n$/.test(text2))
|
|
3104
3107
|
return text2;
|
|
@@ -3166,6 +3169,19 @@ function finalAgentMessageText(updates, agentLabel) {
|
|
|
3166
3169
|
return "";
|
|
3167
3170
|
return last;
|
|
3168
3171
|
}
|
|
3172
|
+
function describeEmptyTurn(result, agentLabel) {
|
|
3173
|
+
const counts = /* @__PURE__ */ new Map();
|
|
3174
|
+
for (const { update } of result.updates) {
|
|
3175
|
+
const kind = typeof update.sessionUpdate === "string" ? update.sessionUpdate : "unknown";
|
|
3176
|
+
if (kind === "session_info_update" || kind === "available_commands_update")
|
|
3177
|
+
continue;
|
|
3178
|
+
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
3179
|
+
}
|
|
3180
|
+
const stream = counts.size ? `the stream carried only ${[...counts].map(([kind, count]) => `${kind}\xD7${count}`).join(", ")}` : "the stream carried no content updates";
|
|
3181
|
+
const lastRun = agentMessageRuns(result.updates, agentLabel).at(-1);
|
|
3182
|
+
const narration = lastRun && isPureRetryNarration(lastRun) ? `; the last message was retry narration "${lastRun.trim().slice(0, 80)}"` : "";
|
|
3183
|
+
return `harness ended the turn (${result.stopReason}) with no answer text; ${stream}${narration}`;
|
|
3184
|
+
}
|
|
3169
3185
|
function updateText(update) {
|
|
3170
3186
|
const content = update.content;
|
|
3171
3187
|
if (typeof content === "string")
|
|
@@ -3186,7 +3202,7 @@ var THOUGHT_UPDATE_TYPES = /* @__PURE__ */ new Set([
|
|
|
3186
3202
|
]);
|
|
3187
3203
|
function agentStreamSnapshot(updates, agentLabel) {
|
|
3188
3204
|
const completedMessages = [];
|
|
3189
|
-
let
|
|
3205
|
+
let messageText2 = "";
|
|
3190
3206
|
let lastWasMessage = false;
|
|
3191
3207
|
let thoughtText = "";
|
|
3192
3208
|
let thoughtRunOpen = false;
|
|
@@ -3196,20 +3212,20 @@ function agentStreamSnapshot(updates, agentLabel) {
|
|
|
3196
3212
|
const delta = normalizeStreamDelta(agentMessageChunkText(update), agentLabel);
|
|
3197
3213
|
if (!delta)
|
|
3198
3214
|
continue;
|
|
3199
|
-
if (!lastWasMessage &&
|
|
3200
|
-
if (!completedMessages.includes(
|
|
3201
|
-
completedMessages.push(
|
|
3202
|
-
|
|
3215
|
+
if (!lastWasMessage && messageText2 && !/\s$/.test(messageText2) && !CHUNK_CONTINUES_PREVIOUS_WORD.test(delta)) {
|
|
3216
|
+
if (!completedMessages.includes(messageText2))
|
|
3217
|
+
completedMessages.push(messageText2);
|
|
3218
|
+
messageText2 = "";
|
|
3203
3219
|
}
|
|
3204
|
-
|
|
3220
|
+
messageText2 += delta;
|
|
3205
3221
|
lastWasMessage = true;
|
|
3206
3222
|
thoughtRunOpen = false;
|
|
3207
3223
|
continue;
|
|
3208
3224
|
}
|
|
3209
|
-
if (lastWasMessage &&
|
|
3210
|
-
if (!completedMessages.includes(
|
|
3211
|
-
completedMessages.push(
|
|
3212
|
-
|
|
3225
|
+
if (lastWasMessage && messageText2) {
|
|
3226
|
+
if (!completedMessages.includes(messageText2))
|
|
3227
|
+
completedMessages.push(messageText2);
|
|
3228
|
+
messageText2 = "";
|
|
3213
3229
|
}
|
|
3214
3230
|
lastWasMessage = false;
|
|
3215
3231
|
if (THOUGHT_UPDATE_TYPES.has(kind)) {
|
|
@@ -3225,7 +3241,7 @@ function agentStreamSnapshot(updates, agentLabel) {
|
|
|
3225
3241
|
}
|
|
3226
3242
|
}
|
|
3227
3243
|
return {
|
|
3228
|
-
messageText,
|
|
3244
|
+
messageText: messageText2,
|
|
3229
3245
|
...thoughtText || completedMessages.length ? { thoughtText: thoughtText || completedMessages.at(-1) } : {}
|
|
3230
3246
|
};
|
|
3231
3247
|
}
|
|
@@ -3720,7 +3736,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
3720
3736
|
const current = this.activeRunIds.get(sessionId);
|
|
3721
3737
|
if (current)
|
|
3722
3738
|
return Promise.resolve(current);
|
|
3723
|
-
return new Promise((
|
|
3739
|
+
return new Promise((resolve26, reject) => {
|
|
3724
3740
|
const onUpdate = (update) => {
|
|
3725
3741
|
if (update.sessionId !== sessionId)
|
|
3726
3742
|
return;
|
|
@@ -3728,7 +3744,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
3728
3744
|
if (!runId)
|
|
3729
3745
|
return;
|
|
3730
3746
|
cleanup();
|
|
3731
|
-
|
|
3747
|
+
resolve26(runId);
|
|
3732
3748
|
};
|
|
3733
3749
|
const timer = setTimeout(() => {
|
|
3734
3750
|
cleanup();
|
|
@@ -3748,23 +3764,23 @@ var AcpClient = class extends EventEmitter {
|
|
|
3748
3764
|
const tracked = metadataKey ? this.toolCallMetadata.get(metadataKey) : void 0;
|
|
3749
3765
|
if (tracked)
|
|
3750
3766
|
p.toolCall = { ...tracked, ...p.toolCall };
|
|
3751
|
-
let
|
|
3767
|
+
let decision2 = this.autoApprove ? "allow" : "reject";
|
|
3752
3768
|
if (!this.autoApprove && this.permissionAllowlist) {
|
|
3753
3769
|
try {
|
|
3754
|
-
|
|
3770
|
+
decision2 = this.permissionAllowlist(p) ? "allow" : "reject";
|
|
3755
3771
|
} catch (error) {
|
|
3756
3772
|
this.emit("permission/error", error);
|
|
3757
|
-
|
|
3773
|
+
decision2 = "reject";
|
|
3758
3774
|
}
|
|
3759
3775
|
} else if (this.permissionHandler) {
|
|
3760
3776
|
try {
|
|
3761
|
-
|
|
3777
|
+
decision2 = await this.permissionHandler(p);
|
|
3762
3778
|
} catch (error) {
|
|
3763
3779
|
this.emit("permission/error", error);
|
|
3764
|
-
|
|
3780
|
+
decision2 = "reject";
|
|
3765
3781
|
}
|
|
3766
3782
|
}
|
|
3767
|
-
if (
|
|
3783
|
+
if (decision2 === "allow") {
|
|
3768
3784
|
const allow = p?.options?.find((o) => o.kind === "allow_once" || o.kind === "allow_always") ?? p?.options?.[0];
|
|
3769
3785
|
if (allow?.optionId) {
|
|
3770
3786
|
if (metadataKey)
|
|
@@ -3812,13 +3828,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
3812
3828
|
}
|
|
3813
3829
|
const id = this.nextId++;
|
|
3814
3830
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
3815
|
-
return new Promise((
|
|
3831
|
+
return new Promise((resolve26, reject) => {
|
|
3816
3832
|
const timer = setTimeout(() => {
|
|
3817
3833
|
this.pending.delete(id);
|
|
3818
3834
|
reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart)));
|
|
3819
3835
|
}, timeoutMs);
|
|
3820
3836
|
this.pending.set(id, {
|
|
3821
|
-
resolve:
|
|
3837
|
+
resolve: resolve26,
|
|
3822
3838
|
reject,
|
|
3823
3839
|
timer,
|
|
3824
3840
|
method,
|
|
@@ -4236,31 +4252,433 @@ async function applyRuntimeModelPreflight(config, agent, selection, validate = v
|
|
|
4236
4252
|
config.modelUnavailable = await revalidateRuntimeModelSelection(agent, config.agentEnv, selection, validate);
|
|
4237
4253
|
}
|
|
4238
4254
|
|
|
4239
|
-
// apps/body/dist/
|
|
4240
|
-
import { execFile as execFile3 } from "node:child_process";
|
|
4255
|
+
// apps/body/dist/model-catalog-sync.js
|
|
4241
4256
|
import { createHash } from "node:crypto";
|
|
4257
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
4258
|
+
import { resolve as resolve5 } from "node:path";
|
|
4259
|
+
var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
|
|
4260
|
+
var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
|
|
4261
|
+
function modelCatalogHash(options, selection) {
|
|
4262
|
+
return createHash("sha256").update(JSON.stringify({ options, selection: selection ?? null })).digest("hex");
|
|
4263
|
+
}
|
|
4264
|
+
async function withTimeout(work, timeoutMs, what) {
|
|
4265
|
+
let timer;
|
|
4266
|
+
const expiry = new Promise((_, reject) => {
|
|
4267
|
+
timer = setTimeout(() => reject(new Error(`${what} timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
4268
|
+
});
|
|
4269
|
+
try {
|
|
4270
|
+
return await Promise.race([work, expiry]);
|
|
4271
|
+
} finally {
|
|
4272
|
+
clearTimeout(timer);
|
|
4273
|
+
work.catch(() => void 0);
|
|
4274
|
+
}
|
|
4275
|
+
}
|
|
4276
|
+
async function syncAgentModelCatalog(input) {
|
|
4277
|
+
const log = input.log ?? ((line) => console.log(line));
|
|
4278
|
+
const fetchCatalog = input.fetchCatalog ?? fetchAgentModelCatalog;
|
|
4279
|
+
const hashPath = resolve5(input.runtimeDir, MODEL_CATALOG_HASH_FILE);
|
|
4280
|
+
try {
|
|
4281
|
+
const configuration = await input.api.execute("getAgentConfiguration", {
|
|
4282
|
+
agentId: input.agentId
|
|
4283
|
+
});
|
|
4284
|
+
const selection = configuration.model || configuration.effort ? {
|
|
4285
|
+
...configuration.model ? { model: configuration.model } : {},
|
|
4286
|
+
...configuration.effort ? { effort: configuration.effort } : {}
|
|
4287
|
+
} : input.runtimeSelection;
|
|
4288
|
+
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
4289
|
+
const hash = modelCatalogHash(catalog, selection);
|
|
4290
|
+
const previous = await readFile(hashPath, "utf8").catch(() => "");
|
|
4291
|
+
if (previous.trim() === hash)
|
|
4292
|
+
return "unchanged";
|
|
4293
|
+
await input.api.execute("postAgentModelCatalog", {
|
|
4294
|
+
agentId: input.agentId,
|
|
4295
|
+
workspaceId: input.workspaceId,
|
|
4296
|
+
// `fetchAgentModelCatalog` already applied the category allow-list.
|
|
4297
|
+
options: catalog,
|
|
4298
|
+
...selection ? { selection } : {}
|
|
4299
|
+
});
|
|
4300
|
+
await writeFile(hashPath, `${hash}
|
|
4301
|
+
`, { mode: 384 });
|
|
4302
|
+
log(`[body] model catalog posted: ${catalog.length} axis(es)` + (selection?.model ? `, model ${selection.model}` : "") + (selection?.effort ? `, effort ${selection.effort}` : ""));
|
|
4303
|
+
return "posted";
|
|
4304
|
+
} catch (error) {
|
|
4305
|
+
log(`[body] model catalog not posted this activation: ${error instanceof Error ? error.message : String(error)}`);
|
|
4306
|
+
return "failed";
|
|
4307
|
+
}
|
|
4308
|
+
}
|
|
4309
|
+
|
|
4310
|
+
// apps/body/dist/room-runtime.js
|
|
4311
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
4312
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
4242
4313
|
import { existsSync as existsSync4, mkdirSync } from "node:fs";
|
|
4243
|
-
import { mkdir as
|
|
4244
|
-
import { dirname as
|
|
4314
|
+
import { mkdir as mkdir8, rm as rm3 } from "node:fs/promises";
|
|
4315
|
+
import { dirname as dirname6, resolve as resolve15 } from "node:path";
|
|
4245
4316
|
import { promisify as promisify3 } from "node:util";
|
|
4246
4317
|
|
|
4318
|
+
// apps/body/dist/grant-runner.js
|
|
4319
|
+
import { execFile } from "node:child_process";
|
|
4320
|
+
import { randomBytes } from "node:crypto";
|
|
4321
|
+
import { readFile as readFile3 } from "node:fs/promises";
|
|
4322
|
+
import { createServer } from "node:http";
|
|
4323
|
+
import { dirname as dirname3, resolve as resolve7 } from "node:path";
|
|
4324
|
+
|
|
4325
|
+
// packages/api-contract/dist/agent-grants.js
|
|
4326
|
+
var AGENT_GRANT_KINDS = ["path", "host", "secret", "device", "budget", "command"];
|
|
4327
|
+
var SHELL_METACHARACTERS = /[;&|<>$`\\'"(){}\n\r\t*?[\]~#!]/;
|
|
4328
|
+
var SECRET_NAME = /^[A-Z][A-Z0-9_]{0,63}$/;
|
|
4329
|
+
function parseCommandGrantTarget(target) {
|
|
4330
|
+
if (typeof target !== "string" || !target.trim()) {
|
|
4331
|
+
throw new Error("command target is required");
|
|
4332
|
+
}
|
|
4333
|
+
if (target !== target.trim() || /\s{2,}/.test(target)) {
|
|
4334
|
+
throw new Error("command target must be one line with single spaces");
|
|
4335
|
+
}
|
|
4336
|
+
if (SHELL_METACHARACTERS.test(target)) {
|
|
4337
|
+
throw new Error("command target must not contain shell metacharacters");
|
|
4338
|
+
}
|
|
4339
|
+
const words = target.split(" ");
|
|
4340
|
+
const argv = [];
|
|
4341
|
+
const secrets = [];
|
|
4342
|
+
for (let index = 0; index < words.length; index += 1) {
|
|
4343
|
+
const word = words[index];
|
|
4344
|
+
if (word === "--with") {
|
|
4345
|
+
const name = words[index + 1];
|
|
4346
|
+
if (!name || !SECRET_NAME.test(name)) {
|
|
4347
|
+
throw new Error("--with must name one UPPER_CASE secret");
|
|
4348
|
+
}
|
|
4349
|
+
if (!secrets.includes(name))
|
|
4350
|
+
secrets.push(name);
|
|
4351
|
+
index += 1;
|
|
4352
|
+
continue;
|
|
4353
|
+
}
|
|
4354
|
+
if (secrets.length)
|
|
4355
|
+
throw new Error("--with suffixes must come after the command words");
|
|
4356
|
+
argv.push(word);
|
|
4357
|
+
}
|
|
4358
|
+
if (!argv.length)
|
|
4359
|
+
throw new Error("command target must name a command");
|
|
4360
|
+
return { argv, secrets };
|
|
4361
|
+
}
|
|
4362
|
+
function commandGrantMatches(rule, requested) {
|
|
4363
|
+
if (requested.length < rule.argv.length)
|
|
4364
|
+
return false;
|
|
4365
|
+
return rule.argv.every((word, index) => requested[index] === word);
|
|
4366
|
+
}
|
|
4367
|
+
var DECISION_LINE = new RegExp(`^(.+?) (approved once|approved|declined) (${AGENT_GRANT_KINDS.join("|")}) (.+)$`, "s");
|
|
4368
|
+
function parseGrantDecisionLine(body) {
|
|
4369
|
+
const match = DECISION_LINE.exec(body);
|
|
4370
|
+
if (!match)
|
|
4371
|
+
return void 0;
|
|
4372
|
+
const decision2 = match[2] === "approved once" ? "once" : match[2] === "approved" ? "always" : "deny";
|
|
4373
|
+
return {
|
|
4374
|
+
deciderName: match[1],
|
|
4375
|
+
decision: decision2,
|
|
4376
|
+
kind: match[3],
|
|
4377
|
+
target: match[4]
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
|
|
4381
|
+
// apps/body/dist/provider-key-store.js
|
|
4382
|
+
import { chmod, mkdir, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
4383
|
+
import { homedir as homedir2 } from "node:os";
|
|
4384
|
+
import { dirname as dirname2, resolve as resolve6 } from "node:path";
|
|
4385
|
+
var PROVIDER_KEY_ENV_VARS = {
|
|
4386
|
+
openrouter: "OPENROUTER_API_KEY",
|
|
4387
|
+
openai: "OPENAI_API_KEY",
|
|
4388
|
+
anthropic: "ANTHROPIC_API_KEY",
|
|
4389
|
+
google: "GOOGLE_API_KEY",
|
|
4390
|
+
xai: "XAI_API_KEY"
|
|
4391
|
+
};
|
|
4392
|
+
var GOOGLE_ENV_ALIAS = "GEMINI_API_KEY";
|
|
4393
|
+
function providerKeyStorePath(env = process.env) {
|
|
4394
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve6(homedir2(), ".config");
|
|
4395
|
+
return resolve6(configRoot, "beeline", "providers.json");
|
|
4396
|
+
}
|
|
4397
|
+
async function readProviderKeyStore(env = process.env) {
|
|
4398
|
+
const path = providerKeyStorePath(env);
|
|
4399
|
+
const raw = await readFile2(path, "utf8").catch(() => void 0);
|
|
4400
|
+
if (!raw)
|
|
4401
|
+
return {};
|
|
4402
|
+
try {
|
|
4403
|
+
const parsed = JSON.parse(raw);
|
|
4404
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
4405
|
+
return {};
|
|
4406
|
+
const entries = Object.entries(parsed).filter((entry) => entry[0] in PROVIDER_KEY_ENV_VARS && typeof entry[1] === "string" && entry[1].length > 0);
|
|
4407
|
+
return Object.fromEntries(entries);
|
|
4408
|
+
} catch {
|
|
4409
|
+
return {};
|
|
4410
|
+
}
|
|
4411
|
+
}
|
|
4412
|
+
async function readSavedProviderKey(provider, env = process.env) {
|
|
4413
|
+
return (await readProviderKeyStore(env))[provider];
|
|
4414
|
+
}
|
|
4415
|
+
async function saveProviderKey(provider, key, env = process.env) {
|
|
4416
|
+
const path = providerKeyStorePath(env);
|
|
4417
|
+
const store = { ...await readProviderKeyStore(env), [provider]: key };
|
|
4418
|
+
await mkdir(dirname2(path), { recursive: true, mode: 448 });
|
|
4419
|
+
await writeFile2(path, `${JSON.stringify(store, null, 2)}
|
|
4420
|
+
`, { mode: 384 });
|
|
4421
|
+
await chmod(path, 384);
|
|
4422
|
+
}
|
|
4423
|
+
function providerKeyFromEnvironment(provider, env = process.env) {
|
|
4424
|
+
const primary = env[PROVIDER_KEY_ENV_VARS[provider]]?.trim();
|
|
4425
|
+
if (primary)
|
|
4426
|
+
return primary;
|
|
4427
|
+
if (provider === "google")
|
|
4428
|
+
return env[GOOGLE_ENV_ALIAS]?.trim() || void 0;
|
|
4429
|
+
return void 0;
|
|
4430
|
+
}
|
|
4431
|
+
function maskProviderKey(key) {
|
|
4432
|
+
const trimmed = key.trim();
|
|
4433
|
+
if (trimmed.length <= 9)
|
|
4434
|
+
return "\u2026";
|
|
4435
|
+
return `${trimmed.slice(0, 6)}\u2026${trimmed.slice(-3)}`;
|
|
4436
|
+
}
|
|
4437
|
+
|
|
4438
|
+
// apps/body/dist/grant-runner.js
|
|
4439
|
+
var GRANT_COMMAND_TIMEOUT_MS = 10 * 6e4;
|
|
4440
|
+
var GRANT_COMMAND_OUTPUT_CAP_BYTES = 64 * 1024;
|
|
4441
|
+
var ARGV_MAX_WORDS = 256;
|
|
4442
|
+
var ARGV_WORD_MAX_LENGTH = 4096;
|
|
4443
|
+
function operatorSecretResolver(env = process.env) {
|
|
4444
|
+
const providerByEnvVar = new Map(Object.entries(PROVIDER_KEY_ENV_VARS).map(([provider, envVar]) => [envVar, provider]));
|
|
4445
|
+
return async (name) => {
|
|
4446
|
+
const provider = providerByEnvVar.get(name);
|
|
4447
|
+
if (provider) {
|
|
4448
|
+
const saved = (await readProviderKeyStore(env))[provider];
|
|
4449
|
+
if (saved)
|
|
4450
|
+
return saved;
|
|
4451
|
+
}
|
|
4452
|
+
const raw = await readFile3(resolve7(dirname3(providerKeyStorePath(env)), "secrets.json"), "utf8").catch(() => void 0);
|
|
4453
|
+
if (!raw)
|
|
4454
|
+
return void 0;
|
|
4455
|
+
try {
|
|
4456
|
+
const parsed = JSON.parse(raw);
|
|
4457
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
4458
|
+
return void 0;
|
|
4459
|
+
const value = parsed[name];
|
|
4460
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
4461
|
+
} catch {
|
|
4462
|
+
return void 0;
|
|
4463
|
+
}
|
|
4464
|
+
};
|
|
4465
|
+
}
|
|
4466
|
+
function validateGrantArgv(value) {
|
|
4467
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
4468
|
+
throw new Error("argv must be a non-empty array");
|
|
4469
|
+
if (value.length > ARGV_MAX_WORDS)
|
|
4470
|
+
throw new Error(`argv exceeds ${ARGV_MAX_WORDS} words`);
|
|
4471
|
+
return value.map((word) => {
|
|
4472
|
+
if (typeof word !== "string" || !word)
|
|
4473
|
+
throw new Error("argv words must be non-empty strings");
|
|
4474
|
+
if (word.length > ARGV_WORD_MAX_LENGTH || /[\0\r\n]/.test(word)) {
|
|
4475
|
+
throw new Error("argv words must be single-line and bounded");
|
|
4476
|
+
}
|
|
4477
|
+
return word;
|
|
4478
|
+
});
|
|
4479
|
+
}
|
|
4480
|
+
function matchCommandGrant(grants, workspaceId, argv) {
|
|
4481
|
+
for (const grant of grants) {
|
|
4482
|
+
if (grant.kind !== "command" || grant.workspaceId !== workspaceId)
|
|
4483
|
+
continue;
|
|
4484
|
+
let rule;
|
|
4485
|
+
try {
|
|
4486
|
+
rule = parseCommandGrantTarget(grant.target);
|
|
4487
|
+
} catch {
|
|
4488
|
+
continue;
|
|
4489
|
+
}
|
|
4490
|
+
if (commandGrantMatches(rule, argv))
|
|
4491
|
+
return { grant, rule };
|
|
4492
|
+
}
|
|
4493
|
+
return void 0;
|
|
4494
|
+
}
|
|
4495
|
+
function capOutput(value, cap) {
|
|
4496
|
+
if (Buffer.byteLength(value) <= cap)
|
|
4497
|
+
return value;
|
|
4498
|
+
const half = Math.floor(cap / 2);
|
|
4499
|
+
const bytes = Buffer.from(value);
|
|
4500
|
+
return `${bytes.subarray(0, half).toString("utf8")}
|
|
4501
|
+
\u2026[${bytes.length - cap} bytes omitted]\u2026
|
|
4502
|
+
${bytes.subarray(bytes.length - half).toString("utf8")}`;
|
|
4503
|
+
}
|
|
4504
|
+
function scrubSecrets(value, secrets) {
|
|
4505
|
+
let scrubbed = value;
|
|
4506
|
+
for (const [name, secret] of secrets) {
|
|
4507
|
+
if (secret.length >= 4)
|
|
4508
|
+
scrubbed = scrubbed.split(secret).join(`[${name}]`);
|
|
4509
|
+
}
|
|
4510
|
+
return scrubbed;
|
|
4511
|
+
}
|
|
4512
|
+
var GrantCommandRunner = class {
|
|
4513
|
+
options;
|
|
4514
|
+
rooms = /* @__PURE__ */ new Map();
|
|
4515
|
+
resolveSecret;
|
|
4516
|
+
constructor(options) {
|
|
4517
|
+
this.options = options;
|
|
4518
|
+
this.resolveSecret = options.resolveSecret ?? operatorSecretResolver(options.env ?? process.env);
|
|
4519
|
+
}
|
|
4520
|
+
register(roomId, room) {
|
|
4521
|
+
this.rooms.set(roomId, room);
|
|
4522
|
+
}
|
|
4523
|
+
unregister(roomId) {
|
|
4524
|
+
this.rooms.delete(roomId);
|
|
4525
|
+
}
|
|
4526
|
+
async run(input) {
|
|
4527
|
+
if (typeof input.roomId !== "string" || !input.roomId)
|
|
4528
|
+
throw new Error("roomId is required");
|
|
4529
|
+
const room = this.rooms.get(input.roomId);
|
|
4530
|
+
if (!room)
|
|
4531
|
+
throw new Error("this daemon is not serving that Room");
|
|
4532
|
+
const argv = validateGrantArgv(input.argv);
|
|
4533
|
+
const live = await this.options.api.execute("listAgentGrants", { agentId: this.options.agentId });
|
|
4534
|
+
const match = matchCommandGrant(live.grants, room.workspaceId, argv);
|
|
4535
|
+
if (!match) {
|
|
4536
|
+
throw new Error(`no approved command grant matches: ${argv.join(" ")}. Ask with request_grant kind=command first.`);
|
|
4537
|
+
}
|
|
4538
|
+
const { grant, rule } = match;
|
|
4539
|
+
const secrets = /* @__PURE__ */ new Map();
|
|
4540
|
+
for (const name of rule.secrets) {
|
|
4541
|
+
const value = await this.resolveSecret(name);
|
|
4542
|
+
if (!value)
|
|
4543
|
+
throw new Error(`secret ${name} is not in the operator key store`);
|
|
4544
|
+
secrets.set(name, value);
|
|
4545
|
+
}
|
|
4546
|
+
if (grant.status === "once") {
|
|
4547
|
+
await this.options.api.execute("consumeAgentGrant", { grantId: grant.grantId });
|
|
4548
|
+
}
|
|
4549
|
+
const source = this.options.env ?? process.env;
|
|
4550
|
+
const env = {
|
|
4551
|
+
...source.PATH ? { PATH: source.PATH } : {},
|
|
4552
|
+
...source.HOME ? { HOME: source.HOME } : {},
|
|
4553
|
+
...Object.fromEntries(secrets)
|
|
4554
|
+
};
|
|
4555
|
+
const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
|
|
4556
|
+
const outcome = await new Promise((resolveRun) => {
|
|
4557
|
+
const child = execFile(argv[0], argv.slice(1), {
|
|
4558
|
+
cwd: room.cwd,
|
|
4559
|
+
env,
|
|
4560
|
+
timeout: this.options.timeoutMs ?? GRANT_COMMAND_TIMEOUT_MS,
|
|
4561
|
+
killSignal: "SIGKILL",
|
|
4562
|
+
maxBuffer: cap * 4,
|
|
4563
|
+
encoding: "utf8"
|
|
4564
|
+
}, (error, stdout6, stderr) => {
|
|
4565
|
+
const combined = [stdout6, stderr].filter(Boolean).join(stderr && stdout6 ? "\n" : "");
|
|
4566
|
+
const failure = error;
|
|
4567
|
+
const spawnFailure = Boolean(failure && typeof failure.code === "string");
|
|
4568
|
+
resolveRun({
|
|
4569
|
+
exitCode: spawnFailure ? null : child.exitCode ?? (typeof failure?.code === "number" ? failure.code : 0),
|
|
4570
|
+
...failure?.signal ? { signal: failure.signal } : {},
|
|
4571
|
+
timedOut: Boolean(failure?.killed && failure?.signal === "SIGKILL"),
|
|
4572
|
+
output: spawnFailure ? `${combined}${combined ? "\n" : ""}${failure.message}` : combined
|
|
4573
|
+
});
|
|
4574
|
+
});
|
|
4575
|
+
});
|
|
4576
|
+
const output = capOutput(scrubSecrets(outcome.output, secrets), cap);
|
|
4577
|
+
const turn = room.turn();
|
|
4578
|
+
const requester = turn?.requester ?? {
|
|
4579
|
+
pubkey: grant.requestedBy,
|
|
4580
|
+
...grant.requestedByName ? { name: grant.requestedByName } : {}
|
|
4581
|
+
};
|
|
4582
|
+
const status = outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
|
|
4583
|
+
await this.options.api.execute("postAgentActivity", {
|
|
4584
|
+
agentId: this.options.agentId,
|
|
4585
|
+
roomId: input.roomId,
|
|
4586
|
+
requestId: turn?.requestId ?? `grant:${grant.grantId}`,
|
|
4587
|
+
activity: [
|
|
4588
|
+
{
|
|
4589
|
+
kind: "tool",
|
|
4590
|
+
title: `ran ${argv.join(" ")} under grant ${grant.grantId} \xB7 asked by ${requester.name ?? requester.pubkey.slice(0, 12)}`,
|
|
4591
|
+
operation: "execute",
|
|
4592
|
+
status,
|
|
4593
|
+
command: argv.join(" "),
|
|
4594
|
+
...output ? { output } : {},
|
|
4595
|
+
requestedBy: requester
|
|
4596
|
+
}
|
|
4597
|
+
]
|
|
4598
|
+
});
|
|
4599
|
+
return {
|
|
4600
|
+
grantId: grant.grantId,
|
|
4601
|
+
exitCode: outcome.exitCode,
|
|
4602
|
+
...outcome.signal ? { signal: outcome.signal } : {},
|
|
4603
|
+
timedOut: outcome.timedOut,
|
|
4604
|
+
output
|
|
4605
|
+
};
|
|
4606
|
+
}
|
|
4607
|
+
};
|
|
4608
|
+
var GrantRunnerServer = class {
|
|
4609
|
+
runner;
|
|
4610
|
+
server;
|
|
4611
|
+
endpoint;
|
|
4612
|
+
constructor(runner) {
|
|
4613
|
+
this.runner = runner;
|
|
4614
|
+
}
|
|
4615
|
+
async start() {
|
|
4616
|
+
if (this.endpoint)
|
|
4617
|
+
return this.endpoint;
|
|
4618
|
+
const token = randomBytes(32).toString("base64url");
|
|
4619
|
+
const server = createServer((request, response) => {
|
|
4620
|
+
void this.handle(request, response, token);
|
|
4621
|
+
});
|
|
4622
|
+
await new Promise((resolveListen, reject) => {
|
|
4623
|
+
server.once("error", reject);
|
|
4624
|
+
server.listen(0, "127.0.0.1", () => resolveListen());
|
|
4625
|
+
});
|
|
4626
|
+
server.unref?.();
|
|
4627
|
+
const address = server.address();
|
|
4628
|
+
this.server = server;
|
|
4629
|
+
this.endpoint = { url: `http://127.0.0.1:${address.port}`, token };
|
|
4630
|
+
return this.endpoint;
|
|
4631
|
+
}
|
|
4632
|
+
async close() {
|
|
4633
|
+
const server = this.server;
|
|
4634
|
+
this.server = void 0;
|
|
4635
|
+
this.endpoint = void 0;
|
|
4636
|
+
if (!server)
|
|
4637
|
+
return;
|
|
4638
|
+
await new Promise((resolveClose) => server.close(() => resolveClose()));
|
|
4639
|
+
}
|
|
4640
|
+
async handle(request, response, token) {
|
|
4641
|
+
const send = (status, body) => {
|
|
4642
|
+
response.writeHead(status, { "content-type": "application/json" });
|
|
4643
|
+
response.end(JSON.stringify(body));
|
|
4644
|
+
};
|
|
4645
|
+
if (request.headers.authorization !== `Bearer ${token}`) {
|
|
4646
|
+
send(401, { error: "grant runner token required" });
|
|
4647
|
+
return;
|
|
4648
|
+
}
|
|
4649
|
+
if (request.method !== "POST" || request.url !== "/run") {
|
|
4650
|
+
send(404, { error: "not found" });
|
|
4651
|
+
return;
|
|
4652
|
+
}
|
|
4653
|
+
const chunks = [];
|
|
4654
|
+
for await (const chunk of request)
|
|
4655
|
+
chunks.push(chunk);
|
|
4656
|
+
try {
|
|
4657
|
+
const body = JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
4658
|
+
send(200, await this.runner.run({ roomId: body.roomId, argv: body.argv }));
|
|
4659
|
+
} catch (error) {
|
|
4660
|
+
send(400, { error: error instanceof Error ? error.message : String(error) });
|
|
4661
|
+
}
|
|
4662
|
+
}
|
|
4663
|
+
};
|
|
4664
|
+
|
|
4247
4665
|
// apps/body/dist/monolith-corner-turn.js
|
|
4248
|
-
import { execFile as
|
|
4249
|
-
import { mkdir as
|
|
4250
|
-
import { homedir as
|
|
4666
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
4667
|
+
import { mkdir as mkdir6 } from "node:fs/promises";
|
|
4668
|
+
import { homedir as homedir6 } from "node:os";
|
|
4251
4669
|
import { join as join4 } from "node:path";
|
|
4252
4670
|
import { promisify as promisify2 } from "node:util";
|
|
4253
4671
|
|
|
4254
4672
|
// apps/body/dist/agent-home.js
|
|
4255
4673
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
4256
4674
|
import { randomUUID } from "node:crypto";
|
|
4257
|
-
import { chmod, copyFile, lstat, mkdir, readdir, realpath, rename, rm as rm2, symlink, unlink, writeFile } from "node:fs/promises";
|
|
4258
|
-
import { homedir as
|
|
4259
|
-
import { basename as basename3, dirname as
|
|
4675
|
+
import { chmod as chmod2, copyFile, lstat, mkdir as mkdir3, readdir, realpath, rename, rm as rm2, symlink, unlink, writeFile as writeFile4 } from "node:fs/promises";
|
|
4676
|
+
import { homedir as homedir3 } from "node:os";
|
|
4677
|
+
import { basename as basename3, dirname as dirname4, relative, resolve as resolve10, sep } from "node:path";
|
|
4260
4678
|
|
|
4261
4679
|
// apps/body/dist/beeline-skill.js
|
|
4262
4680
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
4263
|
-
import { resolve as
|
|
4681
|
+
import { resolve as resolve8 } from "node:path";
|
|
4264
4682
|
var USING_BEELINE_SKILL_NAME = "using-beeline";
|
|
4265
4683
|
var BEELINE_ROOM_CAPABILITIES = [
|
|
4266
4684
|
"The repository filesystem is read-only in this Room session.",
|
|
@@ -4304,7 +4722,7 @@ function runningBeelineReleaseId(env = process.env, read = (path) => readFileSyn
|
|
|
4304
4722
|
const lib = env.BEELINE_LIB_DIR;
|
|
4305
4723
|
if (!lib)
|
|
4306
4724
|
return "source";
|
|
4307
|
-
const manifest = JSON.parse(read(
|
|
4725
|
+
const manifest = JSON.parse(read(resolve8(lib, "bundle.json")));
|
|
4308
4726
|
return [manifest.version, manifest.commit].filter(Boolean).join("-") || "source";
|
|
4309
4727
|
} catch {
|
|
4310
4728
|
return "source";
|
|
@@ -4340,6 +4758,226 @@ var SQUIRE_GOVERNED_TOOLS = [
|
|
|
4340
4758
|
];
|
|
4341
4759
|
var SQUIRE_GOVERNED_TOOL_SET = new Set(SQUIRE_GOVERNED_TOOLS);
|
|
4342
4760
|
|
|
4761
|
+
// apps/body/dist/openrouter-routing.js
|
|
4762
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile3 } from "node:fs/promises";
|
|
4763
|
+
import { resolve as resolve9 } from "node:path";
|
|
4764
|
+
var OPENROUTER_ENDPOINTS_BASE_URL = "https://openrouter.ai/api/v1/models";
|
|
4765
|
+
var OPENROUTER_ROUTING_CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
4766
|
+
var OPENROUTER_ROUTING_FETCH_TIMEOUT_MS = 1e4;
|
|
4767
|
+
var OPENROUTER_UPTIME_BAR = 98;
|
|
4768
|
+
var OPENROUTER_UPTIME_BAR_RELAXED = 95;
|
|
4769
|
+
var OPENROUTER_MIN_PROVIDERS = 2;
|
|
4770
|
+
var OPENROUTER_FALLBACK_PROVIDERS = ["deepinfra", "novita"];
|
|
4771
|
+
function openRouterModelId(model, env = {}) {
|
|
4772
|
+
const trimmed = model?.trim();
|
|
4773
|
+
if (!trimmed)
|
|
4774
|
+
return void 0;
|
|
4775
|
+
if (trimmed.startsWith("openrouter/")) {
|
|
4776
|
+
const id = trimmed.slice("openrouter/".length);
|
|
4777
|
+
return id.includes("/") ? id : void 0;
|
|
4778
|
+
}
|
|
4779
|
+
if (env.OPENROUTER_API_KEY?.trim() && /^[^/\s]+\/[^/\s][^\s]*$/.test(trimmed))
|
|
4780
|
+
return trimmed;
|
|
4781
|
+
return void 0;
|
|
4782
|
+
}
|
|
4783
|
+
function parseOpenRouterEndpoints(payload) {
|
|
4784
|
+
const data = payload?.data;
|
|
4785
|
+
const record2 = data && typeof data === "object" ? data : {};
|
|
4786
|
+
const raw = Array.isArray(record2.endpoints) ? record2.endpoints : [];
|
|
4787
|
+
const endpoints = [];
|
|
4788
|
+
for (const entry of raw) {
|
|
4789
|
+
if (!entry || typeof entry !== "object")
|
|
4790
|
+
continue;
|
|
4791
|
+
const endpoint2 = entry;
|
|
4792
|
+
const tag = typeof endpoint2.tag === "string" ? endpoint2.tag : "";
|
|
4793
|
+
const provider = tag.split("/")[0]?.trim() ?? "";
|
|
4794
|
+
if (!provider)
|
|
4795
|
+
continue;
|
|
4796
|
+
const uptime = typeof endpoint2.uptime_last_30m === "number" ? endpoint2.uptime_last_30m : 0;
|
|
4797
|
+
const contextLength = typeof endpoint2.context_length === "number" ? endpoint2.context_length : 0;
|
|
4798
|
+
const supported = Array.isArray(endpoint2.supported_parameters) ? endpoint2.supported_parameters : [];
|
|
4799
|
+
endpoints.push({ provider, uptime, contextLength, tools: supported.includes("tools") });
|
|
4800
|
+
}
|
|
4801
|
+
return {
|
|
4802
|
+
endpoints,
|
|
4803
|
+
contextLength: typeof record2.context_length === "number" ? record2.context_length : advertisedContextLength(endpoints)
|
|
4804
|
+
};
|
|
4805
|
+
}
|
|
4806
|
+
function advertisedContextLength(endpoints) {
|
|
4807
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4808
|
+
for (const endpoint2 of endpoints) {
|
|
4809
|
+
if (!endpoint2.tools || endpoint2.contextLength <= 0)
|
|
4810
|
+
continue;
|
|
4811
|
+
counts.set(endpoint2.contextLength, (counts.get(endpoint2.contextLength) ?? 0) + 1);
|
|
4812
|
+
}
|
|
4813
|
+
let best;
|
|
4814
|
+
for (const [length, count] of counts) {
|
|
4815
|
+
if (best === void 0) {
|
|
4816
|
+
best = length;
|
|
4817
|
+
continue;
|
|
4818
|
+
}
|
|
4819
|
+
const bestCount = counts.get(best) ?? 0;
|
|
4820
|
+
if (count > bestCount || count === bestCount && length > best)
|
|
4821
|
+
best = length;
|
|
4822
|
+
}
|
|
4823
|
+
return best;
|
|
4824
|
+
}
|
|
4825
|
+
function selectReliableOpenRouterProviders(endpoints, contextLength) {
|
|
4826
|
+
const eligible = endpoints.filter((endpoint2) => endpoint2.tools && (contextLength === void 0 || endpoint2.contextLength >= contextLength));
|
|
4827
|
+
const atBar = (bar) => {
|
|
4828
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4829
|
+
return eligible.filter((endpoint2) => endpoint2.uptime >= bar).sort((left, right) => right.uptime - left.uptime).map((endpoint2) => endpoint2.provider).filter((provider) => seen.has(provider) ? false : (seen.add(provider), true));
|
|
4830
|
+
};
|
|
4831
|
+
const strict = atBar(OPENROUTER_UPTIME_BAR);
|
|
4832
|
+
if (strict.length >= OPENROUTER_MIN_PROVIDERS) {
|
|
4833
|
+
return { providers: strict, bar: OPENROUTER_UPTIME_BAR, contextLength };
|
|
4834
|
+
}
|
|
4835
|
+
const relaxed = atBar(OPENROUTER_UPTIME_BAR_RELAXED);
|
|
4836
|
+
if (relaxed.length > 0) {
|
|
4837
|
+
return { providers: relaxed, bar: OPENROUTER_UPTIME_BAR_RELAXED, contextLength };
|
|
4838
|
+
}
|
|
4839
|
+
return { providers: [], bar: null, contextLength };
|
|
4840
|
+
}
|
|
4841
|
+
function openRouterRoutingFor(providers) {
|
|
4842
|
+
return {
|
|
4843
|
+
only: [...providers],
|
|
4844
|
+
order: [...providers],
|
|
4845
|
+
allow_fallbacks: true,
|
|
4846
|
+
require_parameters: true
|
|
4847
|
+
};
|
|
4848
|
+
}
|
|
4849
|
+
function openRouterRoutingCacheDir(runtimeDir) {
|
|
4850
|
+
return resolve9(runtimeDir, "openrouter-routing");
|
|
4851
|
+
}
|
|
4852
|
+
function cachePath(cacheDir, model) {
|
|
4853
|
+
return resolve9(cacheDir, `${model.replace(/[^A-Za-z0-9._-]+/g, "_")}.json`);
|
|
4854
|
+
}
|
|
4855
|
+
async function readCache(cacheDir, model) {
|
|
4856
|
+
try {
|
|
4857
|
+
const parsed = JSON.parse(await readFile4(cachePath(cacheDir, model), "utf8"));
|
|
4858
|
+
if (!parsed || typeof parsed !== "object")
|
|
4859
|
+
return void 0;
|
|
4860
|
+
const cached = parsed;
|
|
4861
|
+
if (cached.model !== model || typeof cached.fetchedAt !== "number" || !Array.isArray(cached.providers) || !cached.providers.every((provider) => typeof provider === "string" && provider.length > 0)) {
|
|
4862
|
+
return void 0;
|
|
4863
|
+
}
|
|
4864
|
+
return {
|
|
4865
|
+
model,
|
|
4866
|
+
fetchedAt: cached.fetchedAt,
|
|
4867
|
+
providers: cached.providers,
|
|
4868
|
+
bar: typeof cached.bar === "number" ? cached.bar : null
|
|
4869
|
+
};
|
|
4870
|
+
} catch {
|
|
4871
|
+
return void 0;
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
async function writeCache(cacheDir, value) {
|
|
4875
|
+
await mkdir2(cacheDir, { recursive: true, mode: 448 });
|
|
4876
|
+
await writeFile3(cachePath(cacheDir, value.model), `${JSON.stringify(value, null, 2)}
|
|
4877
|
+
`, {
|
|
4878
|
+
mode: 384
|
|
4879
|
+
});
|
|
4880
|
+
}
|
|
4881
|
+
function decision(model, providers, bar, source, note) {
|
|
4882
|
+
const criteria = bar === null ? "fallback pair" : `uptime \u2265${bar}%, tools`;
|
|
4883
|
+
const suffix = [
|
|
4884
|
+
bar === OPENROUTER_UPTIME_BAR_RELAXED ? "bar lowered: fewer than 2 providers at 98%" : "",
|
|
4885
|
+
source === "cache" ? "cached" : "",
|
|
4886
|
+
source === "stale-cache" ? "stale cache" : "",
|
|
4887
|
+
note ?? ""
|
|
4888
|
+
].filter(Boolean).join("; ");
|
|
4889
|
+
return {
|
|
4890
|
+
model,
|
|
4891
|
+
routing: openRouterRoutingFor(providers),
|
|
4892
|
+
providers: [...providers],
|
|
4893
|
+
bar,
|
|
4894
|
+
source,
|
|
4895
|
+
line: `[body] openrouter routing for ${model}: ${providers.join(", ")} (${criteria}` + (suffix ? `; ${suffix})` : ")")
|
|
4896
|
+
};
|
|
4897
|
+
}
|
|
4898
|
+
async function resolveOpenRouterRouting(input) {
|
|
4899
|
+
const now2 = input.now ?? Date.now;
|
|
4900
|
+
const cached = await readCache(input.cacheDir, input.model);
|
|
4901
|
+
if (cached && now2() - cached.fetchedAt < OPENROUTER_ROUTING_CACHE_TTL_MS) {
|
|
4902
|
+
return decision(input.model, cached.providers, cached.bar, "cache");
|
|
4903
|
+
}
|
|
4904
|
+
let failure;
|
|
4905
|
+
try {
|
|
4906
|
+
const doFetch = input.fetchImpl ?? fetch;
|
|
4907
|
+
const response = await doFetch(`${OPENROUTER_ENDPOINTS_BASE_URL}/${input.model}/endpoints`, {
|
|
4908
|
+
signal: AbortSignal.timeout(input.timeoutMs ?? OPENROUTER_ROUTING_FETCH_TIMEOUT_MS)
|
|
4909
|
+
});
|
|
4910
|
+
if (!response.ok)
|
|
4911
|
+
throw new Error(`HTTP ${response.status}`);
|
|
4912
|
+
const { endpoints, contextLength } = parseOpenRouterEndpoints(await response.json());
|
|
4913
|
+
if (endpoints.length === 0)
|
|
4914
|
+
throw new Error("no endpoints listed");
|
|
4915
|
+
const selected = selectReliableOpenRouterProviders(endpoints, contextLength);
|
|
4916
|
+
if (selected.providers.length === 0) {
|
|
4917
|
+
return decision(input.model, OPENROUTER_FALLBACK_PROVIDERS, null, "fallback", "no provider met the bar");
|
|
4918
|
+
}
|
|
4919
|
+
await writeCache(input.cacheDir, {
|
|
4920
|
+
model: input.model,
|
|
4921
|
+
fetchedAt: now2(),
|
|
4922
|
+
providers: selected.providers,
|
|
4923
|
+
bar: selected.bar
|
|
4924
|
+
}).catch(() => void 0);
|
|
4925
|
+
return decision(input.model, selected.providers, selected.bar, "live");
|
|
4926
|
+
} catch (error) {
|
|
4927
|
+
failure = error instanceof Error ? error.message : String(error);
|
|
4928
|
+
}
|
|
4929
|
+
if (cached) {
|
|
4930
|
+
return decision(input.model, cached.providers, cached.bar, "stale-cache", `api unreachable: ${failure}`);
|
|
4931
|
+
}
|
|
4932
|
+
return decision(input.model, OPENROUTER_FALLBACK_PROVIDERS, null, "fallback", `api unreachable: ${failure}`);
|
|
4933
|
+
}
|
|
4934
|
+
function withOpenRouterModelRouting(value, pin) {
|
|
4935
|
+
const root = value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
|
|
4936
|
+
const applyOverride = (provider) => {
|
|
4937
|
+
if (!pin)
|
|
4938
|
+
return provider;
|
|
4939
|
+
const overrides = provider.modelOverrides && typeof provider.modelOverrides === "object" && !Array.isArray(provider.modelOverrides) ? { ...provider.modelOverrides } : {};
|
|
4940
|
+
const existing = overrides[pin.model];
|
|
4941
|
+
const override = existing && typeof existing === "object" && !Array.isArray(existing) ? { ...existing } : {};
|
|
4942
|
+
const compat = override.compat && typeof override.compat === "object" && !Array.isArray(override.compat) ? { ...override.compat } : {};
|
|
4943
|
+
override.compat = { ...compat, openRouterRouting: pin.routing };
|
|
4944
|
+
overrides[pin.model] = override;
|
|
4945
|
+
return { ...provider, modelOverrides: overrides };
|
|
4946
|
+
};
|
|
4947
|
+
if (Array.isArray(root.providers)) {
|
|
4948
|
+
if (!pin)
|
|
4949
|
+
return root;
|
|
4950
|
+
const providers2 = root.providers.map((provider) => provider && typeof provider === "object" ? { ...provider } : provider);
|
|
4951
|
+
const index = providers2.findIndex((provider) => provider && typeof provider === "object" && provider.name === "openrouter");
|
|
4952
|
+
const current = applyOverride(index >= 0 ? providers2[index] : { name: "openrouter" });
|
|
4953
|
+
if (index >= 0)
|
|
4954
|
+
providers2[index] = current;
|
|
4955
|
+
else
|
|
4956
|
+
providers2.push(current);
|
|
4957
|
+
root.providers = providers2;
|
|
4958
|
+
return root;
|
|
4959
|
+
}
|
|
4960
|
+
const providers = root.providers && typeof root.providers === "object" ? { ...root.providers } : {};
|
|
4961
|
+
if (pin) {
|
|
4962
|
+
const current = providers.openrouter && typeof providers.openrouter === "object" && !Array.isArray(providers.openrouter) ? { ...providers.openrouter } : {};
|
|
4963
|
+
providers.openrouter = applyOverride(current);
|
|
4964
|
+
}
|
|
4965
|
+
root.providers = providers;
|
|
4966
|
+
return root;
|
|
4967
|
+
}
|
|
4968
|
+
function openRouterRoutingInput(config, selection, fetchImpl) {
|
|
4969
|
+
const model = openRouterModelId(selection?.model, config.agentEnv);
|
|
4970
|
+
if (!model || !config.openRouterRoutingCacheDir)
|
|
4971
|
+
return {};
|
|
4972
|
+
return {
|
|
4973
|
+
openRouterRouting: {
|
|
4974
|
+
model,
|
|
4975
|
+
cacheDir: config.openRouterRoutingCacheDir,
|
|
4976
|
+
...fetchImpl ? { fetchImpl } : {}
|
|
4977
|
+
}
|
|
4978
|
+
};
|
|
4979
|
+
}
|
|
4980
|
+
|
|
4343
4981
|
// apps/body/dist/toml-section.js
|
|
4344
4982
|
function scanLine(line, openString) {
|
|
4345
4983
|
if (openString.kind) {
|
|
@@ -4523,54 +5161,21 @@ var PI_CUSTOM_MODEL_CONFIG = {
|
|
|
4523
5161
|
source: ".pi/agent/models.json",
|
|
4524
5162
|
target: "models.json"
|
|
4525
5163
|
};
|
|
4526
|
-
var OPENROUTER_RELIABLE_PROVIDERS = ["deepinfra", "novita"];
|
|
4527
|
-
function withReliableOpenRouterRouting(value) {
|
|
4528
|
-
const root = value && typeof value === "object" && !Array.isArray(value) ? { ...value } : {};
|
|
4529
|
-
const routing = {
|
|
4530
|
-
only: [...OPENROUTER_RELIABLE_PROVIDERS],
|
|
4531
|
-
order: [...OPENROUTER_RELIABLE_PROVIDERS],
|
|
4532
|
-
allow_fallbacks: false,
|
|
4533
|
-
require_parameters: true
|
|
4534
|
-
};
|
|
4535
|
-
if (Array.isArray(root.providers)) {
|
|
4536
|
-
const providers2 = root.providers.map((provider) => provider && typeof provider === "object" ? { ...provider } : provider);
|
|
4537
|
-
const index = providers2.findIndex((provider) => provider && typeof provider === "object" && provider.name === "openrouter");
|
|
4538
|
-
const current2 = index >= 0 ? providers2[index] : { name: "openrouter" };
|
|
4539
|
-
const compat2 = current2.compat && typeof current2.compat === "object" && !Array.isArray(current2.compat) ? { ...current2.compat } : {};
|
|
4540
|
-
current2.compat = { ...compat2, openRouterRouting: routing };
|
|
4541
|
-
if (index >= 0)
|
|
4542
|
-
providers2[index] = current2;
|
|
4543
|
-
else
|
|
4544
|
-
providers2.push(current2);
|
|
4545
|
-
root.providers = providers2;
|
|
4546
|
-
return root;
|
|
4547
|
-
}
|
|
4548
|
-
const providers = root.providers && typeof root.providers === "object" ? { ...root.providers } : {};
|
|
4549
|
-
const current = providers.openrouter && typeof providers.openrouter === "object" && !Array.isArray(providers.openrouter) ? { ...providers.openrouter } : {};
|
|
4550
|
-
const compat = current.compat && typeof current.compat === "object" && !Array.isArray(current.compat) ? { ...current.compat } : {};
|
|
4551
|
-
current.compat = {
|
|
4552
|
-
...compat,
|
|
4553
|
-
openRouterRouting: routing
|
|
4554
|
-
};
|
|
4555
|
-
providers.openrouter = current;
|
|
4556
|
-
root.providers = providers;
|
|
4557
|
-
return root;
|
|
4558
|
-
}
|
|
4559
5164
|
var CODEX_ROOM_AGENT_LOCKDOWN_TOML = "[agents]\nenabled = false\n";
|
|
4560
5165
|
var CODEX_ROOM_WEB_SEARCH_TOML = "[features]\nstandalone_web_search = true\n";
|
|
4561
5166
|
var HOME_SUBDIRS = ["user", "claude", "codex", "grok", "pi", "state", "cache", "tmp"];
|
|
4562
5167
|
async function prepareRoomAgentHome(input) {
|
|
4563
|
-
const root =
|
|
4564
|
-
const operatorHome = input.operatorHome ??
|
|
5168
|
+
const root = resolve10(input.root);
|
|
5169
|
+
const operatorHome = input.operatorHome ?? homedir3();
|
|
4565
5170
|
try {
|
|
4566
|
-
await
|
|
5171
|
+
await mkdir3(root, { recursive: true, mode: 448 });
|
|
4567
5172
|
const rootStats = await lstat(root);
|
|
4568
5173
|
if (!rootStats.isDirectory() || rootStats.isSymbolicLink()) {
|
|
4569
5174
|
throw new AgentHomeSecurityError(`agent home root is not an ordinary directory: ${root}`);
|
|
4570
5175
|
}
|
|
4571
5176
|
for (const subdir of HOME_SUBDIRS) {
|
|
4572
|
-
const path =
|
|
4573
|
-
await
|
|
5177
|
+
const path = resolve10(root, subdir);
|
|
5178
|
+
await mkdir3(path, { recursive: true, mode: 448 });
|
|
4574
5179
|
await assertRealContainedDirectory(path, root);
|
|
4575
5180
|
}
|
|
4576
5181
|
} catch (error) {
|
|
@@ -4580,14 +5185,14 @@ async function prepareRoomAgentHome(input) {
|
|
|
4580
5185
|
return {};
|
|
4581
5186
|
}
|
|
4582
5187
|
for (const credential of SHARED_CREDENTIALS) {
|
|
4583
|
-
const source =
|
|
4584
|
-
const target =
|
|
5188
|
+
const source = resolve10(operatorHome, credential.source);
|
|
5189
|
+
const target = resolve10(root, credential.dir, credential.target);
|
|
4585
5190
|
if (!existsSync3(source) || existsSync3(target))
|
|
4586
5191
|
continue;
|
|
4587
5192
|
await symlink(source, target).catch(() => void 0);
|
|
4588
5193
|
}
|
|
4589
5194
|
const prior = agentHomeProvisionQueues.get(root) ?? Promise.resolve();
|
|
4590
|
-
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? []));
|
|
5195
|
+
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? [], input.openRouterRouting));
|
|
4591
5196
|
agentHomeProvisionQueues.set(root, provision);
|
|
4592
5197
|
try {
|
|
4593
5198
|
await provision;
|
|
@@ -4597,19 +5202,19 @@ async function prepareRoomAgentHome(input) {
|
|
|
4597
5202
|
}
|
|
4598
5203
|
return roomAgentHomeEnv(root);
|
|
4599
5204
|
}
|
|
4600
|
-
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills) {
|
|
5205
|
+
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, openRouterRouting) {
|
|
4601
5206
|
const managedSkills = [
|
|
4602
5207
|
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) }
|
|
4603
5208
|
];
|
|
4604
5209
|
const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
|
|
4605
5210
|
for (const dir of AGENT_SKILL_DIRS) {
|
|
4606
|
-
const target =
|
|
5211
|
+
const target = resolve10(root, dir, "skills");
|
|
4607
5212
|
await provisionManagedSkillsDir(target, managedSkills, shared, sharedSkills.length === 0);
|
|
4608
5213
|
}
|
|
4609
5214
|
for (const config of HARNESS_MCP_CONFIGS) {
|
|
4610
5215
|
try {
|
|
4611
|
-
const source =
|
|
4612
|
-
const target =
|
|
5216
|
+
const source = resolve10(operatorHome, config.toml);
|
|
5217
|
+
const target = resolve10(root, config.dir, "config.toml");
|
|
4613
5218
|
const mcpSection = existsSync3(source) ? filteredHarnessMcpToml(readFileSync4(source, "utf8")) : void 0;
|
|
4614
5219
|
const section = config.dir === "codex" ? [CODEX_ROOM_AGENT_LOCKDOWN_TOML, CODEX_ROOM_WEB_SEARCH_TOML, mcpSection].filter(Boolean).join("\n") : mcpSection;
|
|
4615
5220
|
if (!section) {
|
|
@@ -4624,8 +5229,8 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
4624
5229
|
}
|
|
4625
5230
|
}
|
|
4626
5231
|
try {
|
|
4627
|
-
const claudeJson =
|
|
4628
|
-
const claudeTarget =
|
|
5232
|
+
const claudeJson = resolve10(operatorHome, ".claude.json");
|
|
5233
|
+
const claudeTarget = resolve10(root, "claude", ".claude.json");
|
|
4629
5234
|
const mcpServers = existsSync3(claudeJson) ? readClaudeUserScopeMcpServers(claudeJson) : void 0;
|
|
4630
5235
|
if (mcpServers && Object.keys(mcpServers).length > 0) {
|
|
4631
5236
|
await writeIsolatedHarnessFile(claudeTarget, `${JSON.stringify({ mcpServers }, null, 2)}
|
|
@@ -4640,22 +5245,28 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
4640
5245
|
}
|
|
4641
5246
|
try {
|
|
4642
5247
|
const settings2 = { permissions: { allow: ["WebSearch"] } };
|
|
4643
|
-
await writeIsolatedHarnessFile(
|
|
5248
|
+
await writeIsolatedHarnessFile(resolve10(root, "claude", "settings.json"), `${JSON.stringify(settings2, null, 2)}
|
|
4644
5249
|
`);
|
|
4645
5250
|
} catch (error) {
|
|
4646
5251
|
if (failClosed)
|
|
4647
5252
|
throw error;
|
|
4648
5253
|
console.warn("[body] claude web-search settings provisioning failed:", error);
|
|
4649
5254
|
}
|
|
4650
|
-
await provisionPiCustomModelConfig(root, operatorHome, failClosed);
|
|
5255
|
+
await provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting);
|
|
4651
5256
|
}
|
|
4652
|
-
async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
|
|
4653
|
-
const source =
|
|
4654
|
-
const target =
|
|
5257
|
+
async function provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting) {
|
|
5258
|
+
const source = resolve10(operatorHome, PI_CUSTOM_MODEL_CONFIG.source);
|
|
5259
|
+
const target = resolve10(root, "pi", PI_CUSTOM_MODEL_CONFIG.target);
|
|
5260
|
+
let decision2;
|
|
5261
|
+
if (openRouterRouting) {
|
|
5262
|
+
decision2 = await resolveOpenRouterRouting(openRouterRouting);
|
|
5263
|
+
console.log(decision2.line);
|
|
5264
|
+
}
|
|
5265
|
+
const pin = decision2 ? { model: decision2.model, routing: decision2.routing } : void 0;
|
|
4655
5266
|
try {
|
|
4656
5267
|
const sourceStats = await lstat(source).catch(() => void 0);
|
|
4657
5268
|
if (!sourceStats) {
|
|
4658
|
-
await writeIsolatedHarnessFile(target, `${JSON.stringify(
|
|
5269
|
+
await writeIsolatedHarnessFile(target, `${JSON.stringify(withOpenRouterModelRouting({}, pin), null, 2)}
|
|
4659
5270
|
`);
|
|
4660
5271
|
return;
|
|
4661
5272
|
}
|
|
@@ -4667,7 +5278,7 @@ async function provisionPiCustomModelConfig(root, operatorHome, failClosed) {
|
|
|
4667
5278
|
throw new AgentHomeSecurityError(`Pi custom model config resolves through a link: ${source}`);
|
|
4668
5279
|
}
|
|
4669
5280
|
const sourceValue = JSON.parse(readFileSync4(resolvedSource, "utf8"));
|
|
4670
|
-
await writeIsolatedHarnessFile(target, `${JSON.stringify(
|
|
5281
|
+
await writeIsolatedHarnessFile(target, `${JSON.stringify(withOpenRouterModelRouting(sourceValue, pin), null, 2)}
|
|
4671
5282
|
`);
|
|
4672
5283
|
} catch (error) {
|
|
4673
5284
|
await unlink(target).catch(() => void 0);
|
|
@@ -4702,16 +5313,16 @@ function filteredHarnessMcpToml(source) {
|
|
|
4702
5313
|
return extractTomlSections(source, ["mcp_servers"], excluded);
|
|
4703
5314
|
}
|
|
4704
5315
|
async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, optionalShares) {
|
|
4705
|
-
const parent =
|
|
4706
|
-
await assertRealContainedDirectory(parent,
|
|
4707
|
-
const staged =
|
|
4708
|
-
await
|
|
5316
|
+
const parent = dirname4(target);
|
|
5317
|
+
await assertRealContainedDirectory(parent, dirname4(parent));
|
|
5318
|
+
const staged = resolve10(parent, `.skills.${process.pid}.${randomUUID()}.tmp`);
|
|
5319
|
+
await mkdir3(staged, { mode: 448 });
|
|
4709
5320
|
const names = new Set(managedSkills.map((skill) => skill.name));
|
|
4710
5321
|
try {
|
|
4711
5322
|
for (const skill of managedSkills) {
|
|
4712
|
-
const skillDir =
|
|
4713
|
-
await
|
|
4714
|
-
await writeIsolatedHarnessFile(
|
|
5323
|
+
const skillDir = resolve10(staged, skill.name);
|
|
5324
|
+
await mkdir3(skillDir, { recursive: true });
|
|
5325
|
+
await writeIsolatedHarnessFile(resolve10(skillDir, "SKILL.md"), skill.content);
|
|
4715
5326
|
}
|
|
4716
5327
|
for (const shared of sharedSkills) {
|
|
4717
5328
|
if (names.has(shared.name)) {
|
|
@@ -4719,7 +5330,7 @@ async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, op
|
|
|
4719
5330
|
}
|
|
4720
5331
|
names.add(shared.name);
|
|
4721
5332
|
try {
|
|
4722
|
-
await copySafeSkillTree(shared.source,
|
|
5333
|
+
await copySafeSkillTree(shared.source, resolve10(staged, shared.name), shared.source);
|
|
4723
5334
|
} catch (error) {
|
|
4724
5335
|
if (!optionalShares)
|
|
4725
5336
|
throw error;
|
|
@@ -4740,20 +5351,20 @@ async function resolveSharedSkillSources(operatorHome, names) {
|
|
|
4740
5351
|
const seen = /* @__PURE__ */ new Set();
|
|
4741
5352
|
const resolved = [];
|
|
4742
5353
|
for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
|
|
4743
|
-
const sourceRoot =
|
|
5354
|
+
const sourceRoot = resolve10(operatorHome, relativeRoot);
|
|
4744
5355
|
const rootStats = await lstat(sourceRoot).catch(() => void 0);
|
|
4745
5356
|
if (!rootStats?.isDirectory() || rootStats.isSymbolicLink())
|
|
4746
5357
|
continue;
|
|
4747
5358
|
for (const entry of await readdir(sourceRoot)) {
|
|
4748
5359
|
if (!isSharedSkillName(entry) || seen.has(entry))
|
|
4749
5360
|
continue;
|
|
4750
|
-
const candidate =
|
|
5361
|
+
const candidate = resolve10(sourceRoot, entry);
|
|
4751
5362
|
try {
|
|
4752
5363
|
const candidateStats = await lstat(candidate);
|
|
4753
5364
|
if (!candidateStats.isDirectory() || candidateStats.isSymbolicLink())
|
|
4754
5365
|
continue;
|
|
4755
5366
|
assertContained(sourceRoot, candidate);
|
|
4756
|
-
const skillMd =
|
|
5367
|
+
const skillMd = resolve10(candidate, "SKILL.md");
|
|
4757
5368
|
const skillStats = await lstat(skillMd);
|
|
4758
5369
|
if (!skillStats.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
|
|
4759
5370
|
throw new Error(`shared skill requires an ordinary SKILL.md: ${entry}`);
|
|
@@ -4777,8 +5388,8 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
4777
5388
|
for (const name of unique) {
|
|
4778
5389
|
const matches = [];
|
|
4779
5390
|
for (const relativeRoot of OPERATOR_SKILL_SOURCE_DIRS) {
|
|
4780
|
-
const sourceRoot =
|
|
4781
|
-
const candidate =
|
|
5391
|
+
const sourceRoot = resolve10(operatorHome, relativeRoot);
|
|
5392
|
+
const candidate = resolve10(sourceRoot, name);
|
|
4782
5393
|
const rootStats = await lstat(sourceRoot).catch(() => void 0);
|
|
4783
5394
|
const candidateStats = await lstat(candidate).catch(() => void 0);
|
|
4784
5395
|
if (!candidateStats)
|
|
@@ -4795,7 +5406,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
4795
5406
|
if (matches.length !== 1) {
|
|
4796
5407
|
throw new Error(matches.length === 0 ? `shared skill is unavailable: ${name}` : `shared skill source is ambiguous: ${name}`);
|
|
4797
5408
|
}
|
|
4798
|
-
const skillMd =
|
|
5409
|
+
const skillMd = resolve10(matches[0], "SKILL.md");
|
|
4799
5410
|
const skillStats = await lstat(skillMd).catch(() => void 0);
|
|
4800
5411
|
if (!skillStats?.isFile() || skillStats.isSymbolicLink() || skillStats.nlink !== 1) {
|
|
4801
5412
|
throw new Error(`shared skill requires an ordinary SKILL.md: ${name}`);
|
|
@@ -4806,7 +5417,7 @@ async function resolveExplicitSkillSources(operatorHome, names) {
|
|
|
4806
5417
|
}
|
|
4807
5418
|
function assertContained(root, candidate) {
|
|
4808
5419
|
const rel = relative(root, candidate);
|
|
4809
|
-
if (rel === ".." || rel.startsWith(`..${sep}`) ||
|
|
5420
|
+
if (rel === ".." || rel.startsWith(`..${sep}`) || resolve10(root, rel) !== resolve10(candidate)) {
|
|
4810
5421
|
throw new Error(`path escapes the agent skill boundary: ${candidate}`);
|
|
4811
5422
|
}
|
|
4812
5423
|
}
|
|
@@ -4821,7 +5432,7 @@ var BLOCKED_SHARED_FILENAMES = /^(?:\.env(?:\..*)?|auth\.json|\.credentials\.jso
|
|
|
4821
5432
|
async function copySafeSkillTree(source, target, sourceRoot) {
|
|
4822
5433
|
assertContained(sourceRoot, source);
|
|
4823
5434
|
const resolvedSource = await realpath(source);
|
|
4824
|
-
if (resolvedSource !==
|
|
5435
|
+
if (resolvedSource !== resolve10(source)) {
|
|
4825
5436
|
throw new Error(`shared skill path resolves through a link: ${source}`);
|
|
4826
5437
|
}
|
|
4827
5438
|
assertContained(sourceRoot, resolvedSource);
|
|
@@ -4832,11 +5443,11 @@ async function copySafeSkillTree(source, target, sourceRoot) {
|
|
|
4832
5443
|
throw new Error(`shared skill contains credential or configuration material: ${source}`);
|
|
4833
5444
|
}
|
|
4834
5445
|
if (stats.isDirectory()) {
|
|
4835
|
-
await
|
|
5446
|
+
await mkdir3(target, { mode: 448 });
|
|
4836
5447
|
for (const entry of await readdir(resolvedSource)) {
|
|
4837
5448
|
if (entry === "." || entry === "..")
|
|
4838
5449
|
throw new Error("invalid shared skill entry");
|
|
4839
|
-
await copySafeSkillTree(
|
|
5450
|
+
await copySafeSkillTree(resolve10(source, entry), resolve10(target, entry), sourceRoot);
|
|
4840
5451
|
}
|
|
4841
5452
|
return;
|
|
4842
5453
|
}
|
|
@@ -4844,34 +5455,34 @@ async function copySafeSkillTree(source, target, sourceRoot) {
|
|
|
4844
5455
|
throw new Error(`shared skill contains a nonordinary file: ${source}`);
|
|
4845
5456
|
}
|
|
4846
5457
|
await copyFile(resolvedSource, target);
|
|
4847
|
-
await
|
|
5458
|
+
await chmod2(target, 384);
|
|
4848
5459
|
}
|
|
4849
5460
|
async function writeIsolatedHarnessFile(path, content) {
|
|
4850
|
-
const parent =
|
|
5461
|
+
const parent = dirname4(path);
|
|
4851
5462
|
const parentStats = await lstat(parent);
|
|
4852
5463
|
if (!parentStats.isDirectory() || parentStats.isSymbolicLink()) {
|
|
4853
5464
|
throw new Error(`isolated harness parent is not a real directory: ${parent}`);
|
|
4854
5465
|
}
|
|
4855
|
-
const temporary =
|
|
5466
|
+
const temporary = resolve10(parent, `.${basename3(path)}.${process.pid}.${randomUUID()}.tmp`);
|
|
4856
5467
|
try {
|
|
4857
|
-
await
|
|
4858
|
-
await
|
|
5468
|
+
await writeFile4(temporary, content, { mode: 384, flag: "wx" });
|
|
5469
|
+
await chmod2(temporary, 384);
|
|
4859
5470
|
await rename(temporary, path);
|
|
4860
5471
|
} finally {
|
|
4861
5472
|
await unlink(temporary).catch(() => void 0);
|
|
4862
5473
|
}
|
|
4863
5474
|
}
|
|
4864
5475
|
function roomAgentHomeEnv(root) {
|
|
4865
|
-
const resolved =
|
|
5476
|
+
const resolved = resolve10(root);
|
|
4866
5477
|
return {
|
|
4867
|
-
HOME:
|
|
4868
|
-
CLAUDE_CONFIG_DIR:
|
|
4869
|
-
CODEX_HOME:
|
|
4870
|
-
GROK_HOME:
|
|
4871
|
-
PI_CODING_AGENT_DIR:
|
|
4872
|
-
XDG_STATE_HOME:
|
|
4873
|
-
XDG_CACHE_HOME:
|
|
4874
|
-
TMPDIR:
|
|
5478
|
+
HOME: resolve10(resolved, "user"),
|
|
5479
|
+
CLAUDE_CONFIG_DIR: resolve10(resolved, "claude"),
|
|
5480
|
+
CODEX_HOME: resolve10(resolved, "codex"),
|
|
5481
|
+
GROK_HOME: resolve10(resolved, "grok"),
|
|
5482
|
+
PI_CODING_AGENT_DIR: resolve10(resolved, "pi"),
|
|
5483
|
+
XDG_STATE_HOME: resolve10(resolved, "state"),
|
|
5484
|
+
XDG_CACHE_HOME: resolve10(resolved, "cache"),
|
|
5485
|
+
TMPDIR: resolve10(resolved, "tmp")
|
|
4875
5486
|
};
|
|
4876
5487
|
}
|
|
4877
5488
|
var HARNESS_STATE_ENV_VARS = [
|
|
@@ -4888,14 +5499,14 @@ function harnessStateDirsFromEnv(env) {
|
|
|
4888
5499
|
for (const name of HARNESS_STATE_ENV_VARS) {
|
|
4889
5500
|
const value = env[name];
|
|
4890
5501
|
if (value)
|
|
4891
|
-
stateDirs.push(
|
|
5502
|
+
stateDirs.push(resolve10(value));
|
|
4892
5503
|
}
|
|
4893
5504
|
const tmp = env.TMPDIR;
|
|
4894
|
-
return { stateDirs, ...tmp ? { tmpDir:
|
|
5505
|
+
return { stateDirs, ...tmp ? { tmpDir: resolve10(tmp) } : {} };
|
|
4895
5506
|
}
|
|
4896
5507
|
|
|
4897
5508
|
// apps/body/dist/attachment-delivery.js
|
|
4898
|
-
import { mkdir as
|
|
5509
|
+
import { mkdir as mkdir4, writeFile as writeFile5 } from "node:fs/promises";
|
|
4899
5510
|
import { basename as basename4, extname, join as join2 } from "node:path";
|
|
4900
5511
|
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
4901
5512
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
@@ -4913,7 +5524,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
|
|
|
4913
5524
|
if (!attachments.length)
|
|
4914
5525
|
return [];
|
|
4915
5526
|
const taken = /* @__PURE__ */ new Set();
|
|
4916
|
-
await
|
|
5527
|
+
await mkdir4(dir, { recursive: true });
|
|
4917
5528
|
return Promise.all(attachments.map(async (attachment, index) => {
|
|
4918
5529
|
const tooLarge = (bytes) => ({
|
|
4919
5530
|
attachment,
|
|
@@ -4934,7 +5545,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
|
|
|
4934
5545
|
if (bytes.length > MAX_ATTACHMENT_BYTES)
|
|
4935
5546
|
return tooLarge(bytes.length);
|
|
4936
5547
|
const path = join2(dir, safeFileName(attachment, index, taken));
|
|
4937
|
-
await
|
|
5548
|
+
await writeFile5(path, bytes);
|
|
4938
5549
|
const mimeType = attachment.mimeType ?? response.headers.get("content-type") ?? "";
|
|
4939
5550
|
return {
|
|
4940
5551
|
attachment,
|
|
@@ -5047,8 +5658,23 @@ function sanitizeAgentReply(message) {
|
|
|
5047
5658
|
return lines.slice(1).join("\n").trim();
|
|
5048
5659
|
}
|
|
5049
5660
|
|
|
5661
|
+
// apps/body/dist/turn-failure-reason.js
|
|
5662
|
+
var TURN_FAILURE_REASON_MAX = 200;
|
|
5663
|
+
function redactToolDetail(value) {
|
|
5664
|
+
return value.replace(/\b(["']?)(api[_-]?key|token|secret|password|passwd|authorization|credential|cookie|private[_-]?key)\1\s*[:=]\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,}\]]+)/gi, '"$2": "[REDACTED]"').replace(/\b(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, (assignment) => `${assignment.slice(0, assignment.indexOf("="))}=[REDACTED]`).replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/gi, "[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(Bearer\s+)[^\s,]+/gi, "$1[REDACTED]").replace(/\bsk-[A-Za-z0-9_-]{12,}\b/g, "[REDACTED]").replace(/(--?(?:api[_-]?key|token|secret|password|authorization|credential|cookie)\s+)(?:"[^"]*"|'[^']*'|\S+)/gi, "$1[REDACTED]");
|
|
5665
|
+
}
|
|
5666
|
+
function distillTurnFailureReason(error) {
|
|
5667
|
+
const raw = error instanceof Error ? error.message : typeof error === "string" ? error : error && typeof error === "object" && "message" in error ? String(error.message) : error == null ? "" : String(error);
|
|
5668
|
+
const firstLine = raw.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^at\s/.test(line)) ?? "";
|
|
5669
|
+
const stripped = firstLine.replace(/^(?:[A-Za-z]*Error|Error):\s*/, "").replace(/\s+/g, " ");
|
|
5670
|
+
const clean4 = redactToolDetail(stripped).trim();
|
|
5671
|
+
if (!clean4)
|
|
5672
|
+
return "turn failed";
|
|
5673
|
+
return clean4.length > TURN_FAILURE_REASON_MAX ? `${clean4.slice(0, TURN_FAILURE_REASON_MAX - 1)}\u2026` : clean4;
|
|
5674
|
+
}
|
|
5675
|
+
|
|
5050
5676
|
// apps/body/dist/room-session.js
|
|
5051
|
-
import { resolve as
|
|
5677
|
+
import { resolve as resolve11 } from "node:path";
|
|
5052
5678
|
|
|
5053
5679
|
// apps/body/dist/read-only-policy.js
|
|
5054
5680
|
var READ_ONLY_MCP_SERVER_NAME = "beeline-readonly-mcp";
|
|
@@ -5180,7 +5806,11 @@ function beelineAgentMcpServer(config, api, context) {
|
|
|
5180
5806
|
{ name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
|
|
5181
5807
|
{ name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
|
|
5182
5808
|
...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
|
|
5183
|
-
...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : []
|
|
5809
|
+
...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : [],
|
|
5810
|
+
...context.grantRunner ? [
|
|
5811
|
+
{ name: "BEELINE_GRANT_RUNNER_URL", value: context.grantRunner.url },
|
|
5812
|
+
{ name: "BEELINE_GRANT_RUNNER_TOKEN", value: context.grantRunner.token }
|
|
5813
|
+
] : []
|
|
5184
5814
|
]
|
|
5185
5815
|
};
|
|
5186
5816
|
}
|
|
@@ -5194,14 +5824,14 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
|
|
|
5194
5824
|
command: config.readonlyMcpCommand,
|
|
5195
5825
|
args: [...config.readonlyMcpArgs ?? []],
|
|
5196
5826
|
env: [
|
|
5197
|
-
{ name: "BEELINE_READONLY_ROOT", value:
|
|
5827
|
+
{ name: "BEELINE_READONLY_ROOT", value: resolve11(cwd) },
|
|
5198
5828
|
...config.agentHomeRoot ? [
|
|
5199
5829
|
{
|
|
5200
5830
|
name: "BEELINE_READONLY_AGENT_SKILLS_ROOT",
|
|
5201
|
-
value:
|
|
5831
|
+
value: resolve11(config.agentHomeRoot, skillDir, "skills")
|
|
5202
5832
|
}
|
|
5203
5833
|
] : [],
|
|
5204
|
-
...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value:
|
|
5834
|
+
...agentMemoryDir ? [{ name: "BEELINE_READONLY_AGENT_MEMORY_ROOT", value: resolve11(agentMemoryDir) }] : []
|
|
5205
5835
|
]
|
|
5206
5836
|
};
|
|
5207
5837
|
}
|
|
@@ -5209,8 +5839,8 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
|
|
|
5209
5839
|
// apps/body/dist/bwrap-sandbox.js
|
|
5210
5840
|
import { spawnSync } from "node:child_process";
|
|
5211
5841
|
import { lstatSync as lstatSync2 } from "node:fs";
|
|
5212
|
-
import { homedir as
|
|
5213
|
-
import { isAbsolute as isAbsolute2, relative as relative2, resolve as
|
|
5842
|
+
import { homedir as homedir4 } from "node:os";
|
|
5843
|
+
import { isAbsolute as isAbsolute2, relative as relative2, resolve as resolve12 } from "node:path";
|
|
5214
5844
|
var DEFAULT_SANDBOX_POLICY = "bwrap";
|
|
5215
5845
|
function isSandboxPolicy(value) {
|
|
5216
5846
|
return value === "bwrap" || value === "off";
|
|
@@ -5242,12 +5872,12 @@ var HARNESS_HOME_STATE_DIRS = [
|
|
|
5242
5872
|
dirs: [".grok"]
|
|
5243
5873
|
}
|
|
5244
5874
|
];
|
|
5245
|
-
function harnessHomeStateDirs(agentCommand, home =
|
|
5875
|
+
function harnessHomeStateDirs(agentCommand, home = homedir4()) {
|
|
5246
5876
|
if (!agentCommand)
|
|
5247
5877
|
return [];
|
|
5248
5878
|
for (const { match, dirs } of HARNESS_HOME_STATE_DIRS) {
|
|
5249
5879
|
if (match.test(agentCommand))
|
|
5250
|
-
return dirs.map((dir) =>
|
|
5880
|
+
return dirs.map((dir) => resolve12(home, dir));
|
|
5251
5881
|
}
|
|
5252
5882
|
return [];
|
|
5253
5883
|
}
|
|
@@ -5259,7 +5889,7 @@ var KNOWN_CREDENTIAL_MASK_PATHS = [
|
|
|
5259
5889
|
".git-credentials",
|
|
5260
5890
|
".secrets.env"
|
|
5261
5891
|
];
|
|
5262
|
-
function credentialMaskPaths(extraPaths, home =
|
|
5892
|
+
function credentialMaskPaths(extraPaths, home = homedir4(), stat3 = (path) => {
|
|
5263
5893
|
try {
|
|
5264
5894
|
const info = lstatSync2(path);
|
|
5265
5895
|
return { isDirectory: info.isDirectory() };
|
|
@@ -5267,10 +5897,10 @@ function credentialMaskPaths(extraPaths, home = homedir3(), stat3 = (path) => {
|
|
|
5267
5897
|
return void 0;
|
|
5268
5898
|
}
|
|
5269
5899
|
}, requiredPaths = []) {
|
|
5270
|
-
const required = new Set(requiredPaths.map((path) =>
|
|
5900
|
+
const required = new Set(requiredPaths.map((path) => resolve12(path)));
|
|
5271
5901
|
const candidates = [
|
|
5272
|
-
...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) =>
|
|
5273
|
-
...(extraPaths ?? []).map((entry) =>
|
|
5902
|
+
...KNOWN_CREDENTIAL_MASK_PATHS.map((entry) => resolve12(home, entry)),
|
|
5903
|
+
...(extraPaths ?? []).map((entry) => resolve12(entry))
|
|
5274
5904
|
];
|
|
5275
5905
|
const seen = /* @__PURE__ */ new Set();
|
|
5276
5906
|
const masks = [];
|
|
@@ -5297,7 +5927,7 @@ function normalize(paths) {
|
|
|
5297
5927
|
for (const path of paths) {
|
|
5298
5928
|
if (!path)
|
|
5299
5929
|
continue;
|
|
5300
|
-
seen.add(
|
|
5930
|
+
seen.add(resolve12(path));
|
|
5301
5931
|
}
|
|
5302
5932
|
return Array.from(seen).sort();
|
|
5303
5933
|
}
|
|
@@ -5335,7 +5965,7 @@ function sandboxMountPlan(spec) {
|
|
|
5335
5965
|
writable,
|
|
5336
5966
|
quotaTmpfs: spec.workbench ? [
|
|
5337
5967
|
{
|
|
5338
|
-
target:
|
|
5968
|
+
target: resolve12(spec.workbench.dir),
|
|
5339
5969
|
maxBytes: spec.workbench.maxBytes,
|
|
5340
5970
|
maxInodes: spec.workbench.maxInodes,
|
|
5341
5971
|
blockGit: true
|
|
@@ -5390,7 +6020,7 @@ function buildBwrapArgv(input) {
|
|
|
5390
6020
|
for (const binding of quotaTmpfs) {
|
|
5391
6021
|
args.push("--dir", binding.target, "--size", String(binding.maxBytes), "--tmpfs", binding.target);
|
|
5392
6022
|
if (binding.blockGit)
|
|
5393
|
-
args.push("--ro-bind", "/dev/null",
|
|
6023
|
+
args.push("--ro-bind", "/dev/null", resolve12(binding.target, ".git"));
|
|
5394
6024
|
}
|
|
5395
6025
|
args.push("--chdir", input.cwd);
|
|
5396
6026
|
args.push("--die-with-parent");
|
|
@@ -5454,13 +6084,156 @@ function detectBwrapSandbox(options = {}) {
|
|
|
5454
6084
|
};
|
|
5455
6085
|
}
|
|
5456
6086
|
|
|
6087
|
+
// apps/body/dist/pi-turn-record.js
|
|
6088
|
+
import { readdir as readdir2, readFile as readFile5 } from "node:fs/promises";
|
|
6089
|
+
import { resolve as resolve13 } from "node:path";
|
|
6090
|
+
function summarizeProviderError(errorMessage2) {
|
|
6091
|
+
const trimmed = errorMessage2.trim();
|
|
6092
|
+
const statusMatch = /^(\d{3}):\s*([\s\S]*)$/.exec(trimmed);
|
|
6093
|
+
const status = statusMatch ? Number(statusMatch[1]) : void 0;
|
|
6094
|
+
let body = statusMatch ? statusMatch[2].trim() : trimmed;
|
|
6095
|
+
if (body.startsWith("{")) {
|
|
6096
|
+
try {
|
|
6097
|
+
const parsed = JSON.parse(body);
|
|
6098
|
+
const message = typeof parsed.message === "string" ? parsed.message : typeof parsed.error === "string" ? parsed.error : typeof parsed.error?.message === "string" ? parsed.error.message : void 0;
|
|
6099
|
+
if (message)
|
|
6100
|
+
body = message;
|
|
6101
|
+
} catch {
|
|
6102
|
+
}
|
|
6103
|
+
}
|
|
6104
|
+
const firstLine = body.split(/\r?\n/).find((line) => line.trim()) ?? "";
|
|
6105
|
+
const reason = `provider error${status === void 0 ? "" : ` ${status}`}${firstLine ? `: ${firstLine.trim()}` : ""}`;
|
|
6106
|
+
return status === void 0 ? { reason } : { reason, status };
|
|
6107
|
+
}
|
|
6108
|
+
async function sessionFileFromMap(home, sessionId) {
|
|
6109
|
+
try {
|
|
6110
|
+
const raw = await readFile5(resolve13(home, ".pi", "pi-acp", "session-map.json"), "utf8");
|
|
6111
|
+
const map = JSON.parse(raw);
|
|
6112
|
+
const file = map.sessions?.[sessionId]?.sessionFile;
|
|
6113
|
+
return typeof file === "string" && file ? file : void 0;
|
|
6114
|
+
} catch {
|
|
6115
|
+
return void 0;
|
|
6116
|
+
}
|
|
6117
|
+
}
|
|
6118
|
+
async function sessionFileFromLayout(piDir, sessionId) {
|
|
6119
|
+
const sessionsRoot = resolve13(piDir, "sessions");
|
|
6120
|
+
const suffix = `_${sessionId}.jsonl`;
|
|
6121
|
+
let projects;
|
|
6122
|
+
try {
|
|
6123
|
+
projects = await readdir2(sessionsRoot);
|
|
6124
|
+
} catch {
|
|
6125
|
+
return void 0;
|
|
6126
|
+
}
|
|
6127
|
+
for (const project of projects) {
|
|
6128
|
+
const dir = resolve13(sessionsRoot, project);
|
|
6129
|
+
let files;
|
|
6130
|
+
try {
|
|
6131
|
+
files = await readdir2(dir);
|
|
6132
|
+
} catch {
|
|
6133
|
+
continue;
|
|
6134
|
+
}
|
|
6135
|
+
const match = files.find((file) => file.endsWith(suffix));
|
|
6136
|
+
if (match)
|
|
6137
|
+
return resolve13(dir, match);
|
|
6138
|
+
}
|
|
6139
|
+
return void 0;
|
|
6140
|
+
}
|
|
6141
|
+
function messageText(content) {
|
|
6142
|
+
if (!Array.isArray(content))
|
|
6143
|
+
return "";
|
|
6144
|
+
return content.map((block2) => block2 && typeof block2 === "object" && block2.type === "text" ? String(block2.text ?? "") : "").join("").trim();
|
|
6145
|
+
}
|
|
6146
|
+
async function readPiTurnRecord(input) {
|
|
6147
|
+
const piDir = input.agentEnv.PI_CODING_AGENT_DIR;
|
|
6148
|
+
if (!piDir || !input.sessionId)
|
|
6149
|
+
return void 0;
|
|
6150
|
+
const file = (input.agentEnv.HOME ? await sessionFileFromMap(input.agentEnv.HOME, input.sessionId) : void 0) ?? await sessionFileFromLayout(piDir, input.sessionId);
|
|
6151
|
+
if (!file)
|
|
6152
|
+
return void 0;
|
|
6153
|
+
let raw;
|
|
6154
|
+
try {
|
|
6155
|
+
raw = await readFile5(file, "utf8");
|
|
6156
|
+
} catch {
|
|
6157
|
+
return void 0;
|
|
6158
|
+
}
|
|
6159
|
+
let sawUser = false;
|
|
6160
|
+
let last;
|
|
6161
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
6162
|
+
if (!line.trim())
|
|
6163
|
+
continue;
|
|
6164
|
+
let entry;
|
|
6165
|
+
try {
|
|
6166
|
+
entry = JSON.parse(line);
|
|
6167
|
+
} catch {
|
|
6168
|
+
continue;
|
|
6169
|
+
}
|
|
6170
|
+
if (entry.type !== "message" || !entry.message)
|
|
6171
|
+
continue;
|
|
6172
|
+
if (entry.message.role === "user") {
|
|
6173
|
+
sawUser = true;
|
|
6174
|
+
last = void 0;
|
|
6175
|
+
} else if (entry.message.role === "assistant") {
|
|
6176
|
+
last = entry.message;
|
|
6177
|
+
}
|
|
6178
|
+
}
|
|
6179
|
+
if (!sawUser && !last)
|
|
6180
|
+
return void 0;
|
|
6181
|
+
if (!last)
|
|
6182
|
+
return { kind: "missing" };
|
|
6183
|
+
if (last.stopReason === "error") {
|
|
6184
|
+
return {
|
|
6185
|
+
kind: "error",
|
|
6186
|
+
...summarizeProviderError(typeof last.errorMessage === "string" && last.errorMessage.trim() ? last.errorMessage : "unknown error")
|
|
6187
|
+
};
|
|
6188
|
+
}
|
|
6189
|
+
const text2 = messageText(last.content);
|
|
6190
|
+
if (text2)
|
|
6191
|
+
return { kind: "answer", text: text2 };
|
|
6192
|
+
return {
|
|
6193
|
+
kind: "empty",
|
|
6194
|
+
stopReason: typeof last.stopReason === "string" ? last.stopReason : "unknown"
|
|
6195
|
+
};
|
|
6196
|
+
}
|
|
6197
|
+
|
|
6198
|
+
// apps/body/dist/empty-turn.js
|
|
6199
|
+
async function explainEmptyAgentTurn(input) {
|
|
6200
|
+
const streamFact = describeEmptyTurn(input.result, input.agentLabel);
|
|
6201
|
+
if (!isPiAcpHarness(input.agentLabel))
|
|
6202
|
+
return { reason: streamFact };
|
|
6203
|
+
const record2 = await readPiTurnRecord({ agentEnv: input.agentEnv, sessionId: input.sessionId });
|
|
6204
|
+
if (!record2)
|
|
6205
|
+
return { reason: `pi left no readable turn record; ${streamFact}` };
|
|
6206
|
+
switch (record2.kind) {
|
|
6207
|
+
case "error":
|
|
6208
|
+
return { reason: record2.reason, record: record2 };
|
|
6209
|
+
case "empty":
|
|
6210
|
+
return {
|
|
6211
|
+
reason: `the model ended its turn with no text (stop reason ${record2.stopReason})`,
|
|
6212
|
+
record: record2
|
|
6213
|
+
};
|
|
6214
|
+
case "answer":
|
|
6215
|
+
return {
|
|
6216
|
+
recoveredText: record2.text,
|
|
6217
|
+
reason: "pi recorded the answer but the ACP stream delivered no text",
|
|
6218
|
+
record: record2
|
|
6219
|
+
};
|
|
6220
|
+
case "missing":
|
|
6221
|
+
return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record: record2 };
|
|
6222
|
+
}
|
|
6223
|
+
}
|
|
6224
|
+
function isAccountOrProviderRefusal(record2) {
|
|
6225
|
+
if (!record2 || record2.kind !== "error" || record2.status === void 0)
|
|
6226
|
+
return false;
|
|
6227
|
+
return [401, 402, 403, 407, 408, 429].includes(record2.status) || record2.status >= 500;
|
|
6228
|
+
}
|
|
6229
|
+
|
|
5457
6230
|
// apps/body/dist/runtime.js
|
|
5458
|
-
import { randomBytes as
|
|
5459
|
-
import { execFile } from "node:child_process";
|
|
6231
|
+
import { randomBytes as randomBytes6 } from "node:crypto";
|
|
6232
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
5460
6233
|
import { closeSync, openSync } from "node:fs";
|
|
5461
|
-
import { mkdir as
|
|
5462
|
-
import { homedir as
|
|
5463
|
-
import { dirname as
|
|
6234
|
+
import { mkdir as mkdir5, readFile as readFile6, readdir as readdir3, rename as rename2, stat, writeFile as writeFile6 } from "node:fs/promises";
|
|
6235
|
+
import { homedir as homedir5 } from "node:os";
|
|
6236
|
+
import { dirname as dirname5, resolve as resolve14 } from "node:path";
|
|
5464
6237
|
import { spawn as spawn2 } from "node:child_process";
|
|
5465
6238
|
import { promisify } from "node:util";
|
|
5466
6239
|
|
|
@@ -5608,7 +6381,7 @@ function createHasher(hashCons, info = {}) {
|
|
|
5608
6381
|
Object.assign(hashC, info);
|
|
5609
6382
|
return Object.freeze(hashC);
|
|
5610
6383
|
}
|
|
5611
|
-
function
|
|
6384
|
+
function randomBytes2(bytesLength = 32) {
|
|
5612
6385
|
anumber(bytesLength, "bytesLength");
|
|
5613
6386
|
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
5614
6387
|
if (typeof cr?.getRandomValues !== "function")
|
|
@@ -5926,7 +6699,7 @@ var bytesToHex2 = bytesToHex;
|
|
|
5926
6699
|
var concatBytes2 = (...arrays) => concatBytes(...arrays);
|
|
5927
6700
|
var hexToBytes2 = (hex) => hexToBytes(hex);
|
|
5928
6701
|
var isBytes2 = isBytes;
|
|
5929
|
-
var
|
|
6702
|
+
var randomBytes3 = (bytesLength) => randomBytes2(bytesLength);
|
|
5930
6703
|
var _0n = /* @__PURE__ */ BigInt(0);
|
|
5931
6704
|
var _1n = /* @__PURE__ */ BigInt(1);
|
|
5932
6705
|
var atitle2 = (title) => title ? `"${title}" ` : "";
|
|
@@ -6518,18 +7291,18 @@ function validateTableBytes(numPoints, fpBytes) {
|
|
|
6518
7291
|
if (bytes > TABLE_BYTES_MAX)
|
|
6519
7292
|
throw new Error("invalid window size: table would need ~" + Math.ceil(bytes / 2 ** 20) + " MiB, max " + TABLE_BYTES_MAX / 2 ** 20 + " MiB");
|
|
6520
7293
|
}
|
|
6521
|
-
function probeRandomBytes(
|
|
6522
|
-
if (
|
|
7294
|
+
function probeRandomBytes(randomBytes7, length) {
|
|
7295
|
+
if (randomBytes7 === void 0)
|
|
6523
7296
|
return void 0;
|
|
6524
|
-
afunction(
|
|
7297
|
+
afunction(randomBytes7, "randomBytes");
|
|
6525
7298
|
try {
|
|
6526
|
-
const probe =
|
|
7299
|
+
const probe = randomBytes7(length);
|
|
6527
7300
|
if (!isBytes2(probe) || probe.length !== length)
|
|
6528
7301
|
return void 0;
|
|
6529
7302
|
} catch {
|
|
6530
7303
|
return void 0;
|
|
6531
7304
|
}
|
|
6532
|
-
return
|
|
7305
|
+
return randomBytes7;
|
|
6533
7306
|
}
|
|
6534
7307
|
function validateMSMPoints(points, c2) {
|
|
6535
7308
|
aarray(points, "points");
|
|
@@ -6622,9 +7395,9 @@ var ScalarMultiplier = class {
|
|
|
6622
7395
|
baseCanBeBlinded;
|
|
6623
7396
|
bits;
|
|
6624
7397
|
// Parametrized with a given Point class (not individual point)
|
|
6625
|
-
constructor(Point,
|
|
7398
|
+
constructor(Point, randomBytes7) {
|
|
6626
7399
|
validatePointCons(Point);
|
|
6627
|
-
this.randomBytes = probeRandomBytes(
|
|
7400
|
+
this.randomBytes = probeRandomBytes(randomBytes7, BLIND_BYTES);
|
|
6628
7401
|
this.Point = Point;
|
|
6629
7402
|
this.BASE = Point.BASE;
|
|
6630
7403
|
this.ZERO = Point.ZERO;
|
|
@@ -6912,7 +7685,7 @@ function weierstrass(params, extraOpts = {}) {
|
|
|
6912
7685
|
randomBytes: "function"
|
|
6913
7686
|
});
|
|
6914
7687
|
const { endo, allowInfinityPoint } = extraOpts;
|
|
6915
|
-
const
|
|
7688
|
+
const randomBytes7 = extraOpts.randomBytes === void 0 ? randomBytes3 : extraOpts.randomBytes;
|
|
6916
7689
|
if (endo) {
|
|
6917
7690
|
if (!Fp.is0(CURVE.a) || typeof endo.beta !== "bigint" || !Array.isArray(endo.basises)) {
|
|
6918
7691
|
throw new Error('invalid endo: expected "beta": bigint and "basises": array');
|
|
@@ -7324,7 +8097,7 @@ function weierstrass(params, extraOpts = {}) {
|
|
|
7324
8097
|
}
|
|
7325
8098
|
}
|
|
7326
8099
|
const normalize2 = (points) => normalizeZ(Point, points);
|
|
7327
|
-
const wnaf = new ScalarMultiplier(Point,
|
|
8100
|
+
const wnaf = new ScalarMultiplier(Point, randomBytes7);
|
|
7328
8101
|
if (wnaf.bits >= 6)
|
|
7329
8102
|
Point.BASE.precompute(6);
|
|
7330
8103
|
Object.freeze(Point.prototype);
|
|
@@ -7433,7 +8206,7 @@ function challenge(...args) {
|
|
|
7433
8206
|
function schnorrGetPublicKey(secretKey) {
|
|
7434
8207
|
return schnorrGetExtPubKey(secretKey).bytes;
|
|
7435
8208
|
}
|
|
7436
|
-
function schnorrSign(message, secretKey, auxRand =
|
|
8209
|
+
function schnorrSign(message, secretKey, auxRand = randomBytes2(32)) {
|
|
7437
8210
|
const { Fn, BASE } = Pointk1;
|
|
7438
8211
|
const m2 = abytes2(message, void 0, "message");
|
|
7439
8212
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey(secretKey);
|
|
@@ -7483,7 +8256,7 @@ var schnorr = /* @__PURE__ */ (() => {
|
|
|
7483
8256
|
const size = 32;
|
|
7484
8257
|
const seedLength = 48;
|
|
7485
8258
|
const randomSecretKey = (seed) => {
|
|
7486
|
-
seed = seed === void 0 ?
|
|
8259
|
+
seed = seed === void 0 ? randomBytes2(seedLength) : seed;
|
|
7487
8260
|
return mapHashToField(abytes2(seed, seedLength, "seed"), secp256k1_CURVE.n);
|
|
7488
8261
|
};
|
|
7489
8262
|
return Object.freeze({
|
|
@@ -7630,7 +8403,7 @@ function createHasher2(hashCons, info = {}) {
|
|
|
7630
8403
|
Object.assign(hashC, info);
|
|
7631
8404
|
return Object.freeze(hashC);
|
|
7632
8405
|
}
|
|
7633
|
-
function
|
|
8406
|
+
function randomBytes4(bytesLength = 32) {
|
|
7634
8407
|
const cr = typeof globalThis === "object" ? globalThis.crypto : null;
|
|
7635
8408
|
if (typeof cr?.getRandomValues !== "function")
|
|
7636
8409
|
throw new Error("crypto.getRandomValues must be defined");
|
|
@@ -9326,7 +10099,7 @@ function getWLengths2(Fp, Fn) {
|
|
|
9326
10099
|
}
|
|
9327
10100
|
function ecdh(Point, ecdhOpts = {}) {
|
|
9328
10101
|
const { Fn } = Point;
|
|
9329
|
-
const randomBytes_ = ecdhOpts.randomBytes ||
|
|
10102
|
+
const randomBytes_ = ecdhOpts.randomBytes || randomBytes4;
|
|
9330
10103
|
const lengths = Object.assign(getWLengths2(Point.Fp, Fn), { seed: getMinHashLength2(Fn.ORDER) });
|
|
9331
10104
|
function isValidSecretKey(secretKey) {
|
|
9332
10105
|
try {
|
|
@@ -9391,7 +10164,7 @@ function ecdsa2(Point, hash, ecdsaOpts = {}) {
|
|
|
9391
10164
|
bits2int_modN: "function"
|
|
9392
10165
|
});
|
|
9393
10166
|
ecdsaOpts = Object.assign({}, ecdsaOpts);
|
|
9394
|
-
const
|
|
10167
|
+
const randomBytes7 = ecdsaOpts.randomBytes || randomBytes4;
|
|
9395
10168
|
const hmac2 = ecdsaOpts.hmac || ((key, msg) => hmac(hash, key, msg));
|
|
9396
10169
|
const { Fp, Fn } = Point;
|
|
9397
10170
|
const { ORDER: CURVE_ORDER, BITS: fnBits } = Fn;
|
|
@@ -9533,7 +10306,7 @@ function ecdsa2(Point, hash, ecdsaOpts = {}) {
|
|
|
9533
10306
|
throw new Error("invalid private key");
|
|
9534
10307
|
const seedArgs = [int2octets(d), int2octets(h1int)];
|
|
9535
10308
|
if (extraEntropy != null && extraEntropy !== false) {
|
|
9536
|
-
const e = extraEntropy === true ?
|
|
10309
|
+
const e = extraEntropy === true ? randomBytes7(lengths.secretKey) : extraEntropy;
|
|
9537
10310
|
seedArgs.push(abytes3(e, void 0, "extraEntropy"));
|
|
9538
10311
|
}
|
|
9539
10312
|
const seed = concatBytes3(...seedArgs);
|
|
@@ -9700,7 +10473,7 @@ function challenge2(...args) {
|
|
|
9700
10473
|
function schnorrGetPublicKey2(secretKey) {
|
|
9701
10474
|
return schnorrGetExtPubKey2(secretKey).bytes;
|
|
9702
10475
|
}
|
|
9703
|
-
function schnorrSign2(message, secretKey, auxRand =
|
|
10476
|
+
function schnorrSign2(message, secretKey, auxRand = randomBytes4(32)) {
|
|
9704
10477
|
const { Fn } = Pointk12;
|
|
9705
10478
|
const m2 = abytes3(message, void 0, "message");
|
|
9706
10479
|
const { bytes: px, scalar: d } = schnorrGetExtPubKey2(secretKey);
|
|
@@ -9742,7 +10515,7 @@ function schnorrVerify2(signature, message, publicKey) {
|
|
|
9742
10515
|
var schnorr2 = /* @__PURE__ */ (() => {
|
|
9743
10516
|
const size = 32;
|
|
9744
10517
|
const seedLength = 48;
|
|
9745
|
-
const randomSecretKey = (seed =
|
|
10518
|
+
const randomSecretKey = (seed = randomBytes4(seedLength)) => {
|
|
9746
10519
|
return mapHashToField2(seed, secp256k1_CURVE2.n);
|
|
9747
10520
|
};
|
|
9748
10521
|
return {
|
|
@@ -12106,7 +12879,7 @@ function encrypt2(secretKey, pubkey, text2) {
|
|
|
12106
12879
|
const privkey = secretKey instanceof Uint8Array ? secretKey : hexToBytes3(secretKey);
|
|
12107
12880
|
const key = secp256k1.getSharedSecret(privkey, hexToBytes3("02" + pubkey));
|
|
12108
12881
|
const normalizedKey = getNormalizedX(key);
|
|
12109
|
-
let iv = Uint8Array.from(
|
|
12882
|
+
let iv = Uint8Array.from(randomBytes4(16));
|
|
12110
12883
|
let plaintext = utf8Encoder.encode(text2);
|
|
12111
12884
|
let ciphertext = cbc(normalizedKey, iv).encrypt(plaintext);
|
|
12112
12885
|
let ctb64 = base64.encode(new Uint8Array(ciphertext));
|
|
@@ -12477,7 +13250,7 @@ function decodePayload(payload) {
|
|
|
12477
13250
|
mac: data.subarray(-32)
|
|
12478
13251
|
};
|
|
12479
13252
|
}
|
|
12480
|
-
function encrypt22(plaintext, conversationKey, nonce =
|
|
13253
|
+
function encrypt22(plaintext, conversationKey, nonce = randomBytes4(32)) {
|
|
12481
13254
|
const { chacha_key, chacha_nonce, hmac_key } = getMessageKeys(conversationKey, nonce);
|
|
12482
13255
|
const padded = pad(plaintext);
|
|
12483
13256
|
const ciphertext = chacha20(chacha_key, chacha_nonce, padded);
|
|
@@ -13992,23 +14765,23 @@ function decodeNsec(nsec) {
|
|
|
13992
14765
|
}
|
|
13993
14766
|
|
|
13994
14767
|
// apps/body/dist/runtime.js
|
|
13995
|
-
var execFileAsync = promisify(
|
|
14768
|
+
var execFileAsync = promisify(execFile2);
|
|
13996
14769
|
var DEFAULT_AGENT_IDENTITY_NAME = "beeline-agent";
|
|
13997
14770
|
var DEFAULT_BODY_IDENTITY_NAME = "beeline-body";
|
|
13998
14771
|
var DEFAULT_DAEMON_MONOLITH_BASE_URL = "https://server.usebeeline.app";
|
|
13999
14772
|
function defaultSupervisorRoot(env = process.env) {
|
|
14000
|
-
return
|
|
14773
|
+
return resolve14(env.XDG_STATE_HOME ?? resolve14(homedir5(), ".local", "state"));
|
|
14001
14774
|
}
|
|
14002
14775
|
function runtimeDirectory(supervisorRoot, publicKey) {
|
|
14003
14776
|
if (!/^[0-9a-f]{64}$/i.test(publicKey))
|
|
14004
14777
|
throw new Error("invalid agent public key");
|
|
14005
|
-
return
|
|
14778
|
+
return resolve14(supervisorRoot, "beeline", "agents", publicKey.toLowerCase());
|
|
14006
14779
|
}
|
|
14007
14780
|
function runtimeConfigPath(supervisorRoot, publicKey) {
|
|
14008
|
-
return
|
|
14781
|
+
return resolve14(runtimeDirectory(supervisorRoot, publicKey), "runtime.json");
|
|
14009
14782
|
}
|
|
14010
14783
|
function identityFromKey(value, name) {
|
|
14011
|
-
const secretKey = value ? value.startsWith("nsec1") ? decodeNsec(value) : Uint8Array.from(Buffer.from(value, "hex")) :
|
|
14784
|
+
const secretKey = value ? value.startsWith("nsec1") ? decodeNsec(value) : Uint8Array.from(Buffer.from(value, "hex")) : randomBytes6(32);
|
|
14012
14785
|
if (secretKey.length !== 32)
|
|
14013
14786
|
throw new Error("identity secret key must be 32 bytes");
|
|
14014
14787
|
return { name, secretKey, publicKey: getPublicKey2(secretKey) };
|
|
@@ -14035,15 +14808,15 @@ function runtimeAgentCommand(runtime) {
|
|
|
14035
14808
|
}
|
|
14036
14809
|
async function writeRuntimeRecord(runtime) {
|
|
14037
14810
|
const path = runtimeConfigPath(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
14038
|
-
await
|
|
14811
|
+
await mkdir5(dirname5(path), { recursive: true, mode: 448 });
|
|
14039
14812
|
const staged = `${path}.${process.pid}.tmp`;
|
|
14040
|
-
await
|
|
14813
|
+
await writeFile6(staged, `${JSON.stringify(runtime, null, 2)}
|
|
14041
14814
|
`, { mode: 384 });
|
|
14042
14815
|
await rename2(staged, path);
|
|
14043
14816
|
return path;
|
|
14044
14817
|
}
|
|
14045
14818
|
async function readRuntimeRecord(path) {
|
|
14046
|
-
const parsed = JSON.parse(await
|
|
14819
|
+
const parsed = JSON.parse(await readFile6(path, "utf8"));
|
|
14047
14820
|
if (parsed.version !== 2 || !parsed.agent || !parsed.body || !parsed.communityId) {
|
|
14048
14821
|
throw new Error(`invalid agent runtime record: ${path}`);
|
|
14049
14822
|
}
|
|
@@ -14061,7 +14834,7 @@ async function migrateRuntimeRecordAccessPolicy(path) {
|
|
|
14061
14834
|
return { runtime, migrated: true };
|
|
14062
14835
|
}
|
|
14063
14836
|
async function stageMonolithAgentRuntime(input) {
|
|
14064
|
-
const supervisorRoot = input.supervisorRoot ?
|
|
14837
|
+
const supervisorRoot = input.supervisorRoot ? resolve14(input.supervisorRoot) : defaultSupervisorRoot();
|
|
14065
14838
|
const configPath = runtimeConfigPath(supervisorRoot, input.agentIdentity.publicKey);
|
|
14066
14839
|
const configuredBaseUrl = input.monolithBaseUrl ?? DEFAULT_DAEMON_MONOLITH_BASE_URL;
|
|
14067
14840
|
const baseUrl = new URL(configuredBaseUrl).origin;
|
|
@@ -14106,9 +14879,9 @@ async function stageMonolithAgentRuntime(input) {
|
|
|
14106
14879
|
return { runtime, configPath };
|
|
14107
14880
|
}
|
|
14108
14881
|
async function runtimePaths(root) {
|
|
14109
|
-
const agents =
|
|
14110
|
-
const entries = await
|
|
14111
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) =>
|
|
14882
|
+
const agents = resolve14(root, "beeline", "agents");
|
|
14883
|
+
const entries = await readdir3(agents, { withFileTypes: true }).catch(() => []);
|
|
14884
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => resolve14(agents, entry.name, "runtime.json"));
|
|
14112
14885
|
}
|
|
14113
14886
|
async function findAgentRuntimeConfigPaths(env = process.env, _cwd = process.cwd()) {
|
|
14114
14887
|
return runtimePaths(defaultSupervisorRoot(env));
|
|
@@ -14117,13 +14890,13 @@ async function findRuntimeConfigPaths(cwd = process.cwd(), env = process.env) {
|
|
|
14117
14890
|
return findAgentRuntimeConfigPaths(env, cwd);
|
|
14118
14891
|
}
|
|
14119
14892
|
async function resolveRuntimeConfigPath(path) {
|
|
14120
|
-
return
|
|
14893
|
+
return resolve14(path);
|
|
14121
14894
|
}
|
|
14122
14895
|
async function selectRuntimeConfigPaths(options) {
|
|
14123
14896
|
const hostScope = true;
|
|
14124
14897
|
const configs = await options.findHostRuntimes(options.cwd);
|
|
14125
14898
|
const requestedPubkey = options.requestedPubkey;
|
|
14126
|
-
const paths = requestedPubkey ? configs.filter((path) =>
|
|
14899
|
+
const paths = requestedPubkey ? configs.filter((path) => dirname5(path).endsWith(requestedPubkey)) : [...new Set(configs)];
|
|
14127
14900
|
if (!paths.length)
|
|
14128
14901
|
throw new Error(options.noRuntimeMessage(hostScope));
|
|
14129
14902
|
if (options.requestedPubkey && paths.length > 1)
|
|
@@ -14132,7 +14905,7 @@ async function selectRuntimeConfigPaths(options) {
|
|
|
14132
14905
|
}
|
|
14133
14906
|
async function runtimeDaemonPid(configPath) {
|
|
14134
14907
|
try {
|
|
14135
|
-
const pid = Number((await
|
|
14908
|
+
const pid = Number((await readFile6(resolve14(dirname5(configPath), "daemon.pid"), "utf8")).trim());
|
|
14136
14909
|
if (!Number.isSafeInteger(pid) || pid <= 0)
|
|
14137
14910
|
return null;
|
|
14138
14911
|
process.kill(pid, 0);
|
|
@@ -14143,13 +14916,13 @@ async function runtimeDaemonPid(configPath) {
|
|
|
14143
14916
|
}
|
|
14144
14917
|
async function daemonIsThisRuntime(pid, configPath) {
|
|
14145
14918
|
try {
|
|
14146
|
-
const argv = (await
|
|
14919
|
+
const argv = (await readFile6(`/proc/${pid}/cmdline`, "utf8")).split("\0").filter(Boolean);
|
|
14147
14920
|
const flag = argv.lastIndexOf("--config");
|
|
14148
|
-
return flag > 0 && argv[flag - 1] === "daemon" &&
|
|
14921
|
+
return flag > 0 && argv[flag - 1] === "daemon" && resolve14(argv[flag + 1]) === resolve14(configPath);
|
|
14149
14922
|
} catch {
|
|
14150
14923
|
try {
|
|
14151
14924
|
const { stdout: stdout6 } = await execFileAsync("ps", ["-p", String(pid), "-o", "command="]);
|
|
14152
|
-
return stdout6.includes(" daemon ") && stdout6.includes(
|
|
14925
|
+
return stdout6.includes(" daemon ") && stdout6.includes(resolve14(configPath));
|
|
14153
14926
|
} catch {
|
|
14154
14927
|
return false;
|
|
14155
14928
|
}
|
|
@@ -14175,14 +14948,14 @@ async function stopRuntimeDaemon(path, opts = {}) {
|
|
|
14175
14948
|
throw new Error(`agent daemon ${pid} did not stop after ${timeout}ms`);
|
|
14176
14949
|
}
|
|
14177
14950
|
async function launchRuntimeDaemon(configPath, opts = {}) {
|
|
14178
|
-
const directory =
|
|
14179
|
-
await
|
|
14951
|
+
const directory = dirname5(configPath);
|
|
14952
|
+
await mkdir5(directory, { recursive: true, mode: 448 });
|
|
14180
14953
|
const foreground = opts.foreground === true;
|
|
14181
|
-
const output = foreground ? "inherit" : openSync(
|
|
14954
|
+
const output = foreground ? "inherit" : openSync(resolve14(directory, "daemon.log"), "a", 384);
|
|
14182
14955
|
const entrypoint = opts.entrypoint ?? process.argv[1];
|
|
14183
14956
|
if (!entrypoint)
|
|
14184
14957
|
throw new Error("cannot resolve daemon CLI entrypoint");
|
|
14185
|
-
const child = spawn2(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config",
|
|
14958
|
+
const child = spawn2(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve14(configPath)], {
|
|
14186
14959
|
cwd: directory,
|
|
14187
14960
|
env: opts.env ?? process.env,
|
|
14188
14961
|
detached: !foreground,
|
|
@@ -14199,9 +14972,9 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
|
|
|
14199
14972
|
}
|
|
14200
14973
|
async function removeAgentRuntime(runtime) {
|
|
14201
14974
|
const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
14202
|
-
const deletedRoot =
|
|
14203
|
-
await
|
|
14204
|
-
const target =
|
|
14975
|
+
const deletedRoot = resolve14(runtime.supervisorRoot, "beeline", "deleted-runtimes");
|
|
14976
|
+
await mkdir5(deletedRoot, { recursive: true, mode: 448 });
|
|
14977
|
+
const target = resolve14(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
|
|
14205
14978
|
await rename2(source, target);
|
|
14206
14979
|
return target;
|
|
14207
14980
|
}
|
|
@@ -14210,8 +14983,7 @@ async function removeAgentRuntime(runtime) {
|
|
|
14210
14983
|
var MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE = "Maintain your assigned identity and soul in every response, including when tools or permissions block the requested action.";
|
|
14211
14984
|
|
|
14212
14985
|
// apps/body/dist/monolith-corner-turn.js
|
|
14213
|
-
var execFileAsync2 = promisify2(
|
|
14214
|
-
var PULL_REQUEST_URL = /https:\/\/github\.com\/[^\s/]+\/[^\s/]+\/pull\/\d+/;
|
|
14986
|
+
var execFileAsync2 = promisify2(execFile3);
|
|
14215
14987
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
14216
14988
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
14217
14989
|
var TOOL_PATH_LIMIT = 12;
|
|
@@ -14230,9 +15002,6 @@ function serialized(value) {
|
|
|
14230
15002
|
function record(value) {
|
|
14231
15003
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
14232
15004
|
}
|
|
14233
|
-
function redactToolDetail(value) {
|
|
14234
|
-
return value.replace(/\b(["']?)(api[_-]?key|token|secret|password|passwd|authorization|credential|cookie|private[_-]?key)\1\s*[:=]\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|[^\s,}\]]+)/gi, '"$2": "[REDACTED]"').replace(/\b(?:export\s+)?[A-Za-z_][A-Za-z0-9_]*=(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\S+)/g, (assignment) => `${assignment.slice(0, assignment.indexOf("="))}=[REDACTED]`).replace(/\b(?:gh[pousr]_[A-Za-z0-9_]{12,}|github_pat_[A-Za-z0-9_]{12,})\b/gi, "[REDACTED]").replace(/\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]").replace(/\b(Bearer\s+)[^\s,]+/gi, "$1[REDACTED]").replace(/(--?(?:api[_-]?key|token|secret|password|authorization|credential|cookie)\s+)(?:"[^"]*"|'[^']*'|\S+)/gi, "$1[REDACTED]");
|
|
14235
|
-
}
|
|
14236
15005
|
function clampBytes(value, maxBytes) {
|
|
14237
15006
|
const clean4 = value.trim();
|
|
14238
15007
|
if (Buffer.byteLength(clean4) <= maxBytes)
|
|
@@ -14313,7 +15082,7 @@ function isSuccessfulCommit(call) {
|
|
|
14313
15082
|
return false;
|
|
14314
15083
|
return /\bgit\s+commit\b|\bcommit(?:ted)?\s+(?:changes|files?)\b/i.test(`${call.title ?? ""} ${serialized(call.rawInput)}`);
|
|
14315
15084
|
}
|
|
14316
|
-
async function cornerToolActivity(call, worktreePath) {
|
|
15085
|
+
async function cornerToolActivity(call, worktreePath, requestedBy) {
|
|
14317
15086
|
const operation = oneLine(call.kind ?? "") || "tool";
|
|
14318
15087
|
let title = oneLine(redactToolDetail(call.title ?? "")) || `${operation} tool`;
|
|
14319
15088
|
if (isSuccessfulCommit(call)) {
|
|
@@ -14336,6 +15105,7 @@ async function cornerToolActivity(call, worktreePath) {
|
|
|
14336
15105
|
status: resultStatus(call),
|
|
14337
15106
|
...argumentsSummary,
|
|
14338
15107
|
...output ? { output } : {},
|
|
15108
|
+
...requestedBy ? { requestedBy } : {},
|
|
14339
15109
|
...paths.length ? { files: paths.map((path) => ({ path })) } : {}
|
|
14340
15110
|
};
|
|
14341
15111
|
}
|
|
@@ -14348,6 +15118,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
14348
15118
|
agent;
|
|
14349
15119
|
client;
|
|
14350
15120
|
sessionId;
|
|
15121
|
+
/** The live session's environment, read back for pi's own turn record. */
|
|
15122
|
+
agentEnv = {};
|
|
14351
15123
|
turnIdentityInstructions = "";
|
|
14352
15124
|
busy = false;
|
|
14353
15125
|
forcedStop = false;
|
|
@@ -14355,9 +15127,17 @@ var MonolithCornerTurnLoop = class {
|
|
|
14355
15127
|
activityTail = Promise.resolve();
|
|
14356
15128
|
/** Session scratch directory attachments are downloaded into (`TMPDIR/beeline-attachments`). */
|
|
14357
15129
|
attachmentDir;
|
|
15130
|
+
/** The turn in flight and who asked for it, for ledger rows and the grant runner. */
|
|
15131
|
+
currentTurn;
|
|
15132
|
+
memberNames = /* @__PURE__ */ new Map();
|
|
14358
15133
|
constructor(options) {
|
|
14359
15134
|
this.options = options;
|
|
14360
15135
|
this.agent = runtimeIdentity(options.runtime.agent);
|
|
15136
|
+
options.grantRunner?.register(options.cornerId, {
|
|
15137
|
+
workspaceId: options.workspaceId,
|
|
15138
|
+
cwd: options.worktreePath,
|
|
15139
|
+
turn: () => this.currentTurn
|
|
15140
|
+
});
|
|
14361
15141
|
}
|
|
14362
15142
|
isBusy() {
|
|
14363
15143
|
return this.busy;
|
|
@@ -14376,11 +15156,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
14376
15156
|
this.client.sessionCancel(this.sessionId);
|
|
14377
15157
|
await this.options.scheduler.forceSuspend(this.options.cornerId);
|
|
14378
15158
|
}
|
|
14379
|
-
roster() {
|
|
14380
|
-
|
|
15159
|
+
async roster() {
|
|
15160
|
+
const roster = await this.options.api.execute("getWorkspaceRoster", {
|
|
14381
15161
|
agentId: this.agent.publicKey,
|
|
14382
15162
|
workspaceId: this.options.workspaceId
|
|
14383
15163
|
});
|
|
15164
|
+
this.memberNames = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
15165
|
+
return roster;
|
|
14384
15166
|
}
|
|
14385
15167
|
async activate() {
|
|
14386
15168
|
if (this.client?.isAlive && this.sessionId)
|
|
@@ -14392,30 +15174,32 @@ var MonolithCornerTurnLoop = class {
|
|
|
14392
15174
|
}),
|
|
14393
15175
|
this.roster()
|
|
14394
15176
|
]);
|
|
14395
|
-
await
|
|
15177
|
+
await mkdir6(this.options.worktreePath, { recursive: true });
|
|
15178
|
+
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
14396
15179
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
14397
15180
|
root: this.options.config.agentHomeRoot,
|
|
14398
15181
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
14399
|
-
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {}
|
|
15182
|
+
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
|
|
15183
|
+
...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl)
|
|
14400
15184
|
}) : {};
|
|
14401
15185
|
const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
|
|
14402
|
-
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
14403
15186
|
const agentEnv = {
|
|
14404
15187
|
...this.options.config.agentEnv,
|
|
14405
15188
|
...homeOverlay,
|
|
14406
15189
|
GH_TOKEN: this.options.githubToken,
|
|
14407
15190
|
GITHUB_TOKEN: this.options.githubToken
|
|
14408
15191
|
};
|
|
15192
|
+
this.agentEnv = agentEnv;
|
|
14409
15193
|
const agentArgs = agentArgsWithModelSelection({
|
|
14410
15194
|
kind: this.options.config.agentKind,
|
|
14411
15195
|
command,
|
|
14412
15196
|
args: this.options.config.agentArgs ?? []
|
|
14413
15197
|
}, selection);
|
|
14414
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
15198
|
+
const operatorHome = this.options.config.operatorHome ?? homedir6();
|
|
14415
15199
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
14416
15200
|
this.attachmentDir = tmpDir ? join4(tmpDir, "beeline-attachments") : void 0;
|
|
14417
15201
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
14418
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
15202
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir6(dir, { recursive: true })));
|
|
14419
15203
|
const spawnCommand = wrapAgentCommand({
|
|
14420
15204
|
bwrapPath: this.options.config.bwrapPath,
|
|
14421
15205
|
spec: {
|
|
@@ -14461,7 +15245,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
14461
15245
|
roomId: this.options.parentRoomId,
|
|
14462
15246
|
workspaceId: this.options.workspaceId,
|
|
14463
15247
|
cornerId: this.options.cornerId,
|
|
14464
|
-
attachRoot: this.options.worktreePath
|
|
15248
|
+
attachRoot: this.options.worktreePath,
|
|
15249
|
+
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
14465
15250
|
})
|
|
14466
15251
|
];
|
|
14467
15252
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
@@ -14506,8 +15291,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
14506
15291
|
}
|
|
14507
15292
|
};
|
|
14508
15293
|
}
|
|
14509
|
-
async prompt(requestId, trigger, attachments = []) {
|
|
15294
|
+
async prompt(requestId, trigger, attachments = [], requestedById) {
|
|
14510
15295
|
const { api, cornerId } = this.options;
|
|
15296
|
+
const requester = requestedById ? {
|
|
15297
|
+
pubkey: requestedById,
|
|
15298
|
+
...this.memberNames.get(requestedById) ? { name: this.memberNames.get(requestedById) } : {}
|
|
15299
|
+
} : void 0;
|
|
15300
|
+
this.currentTurn = { requestId, ...requester ? { requester } : {} };
|
|
14511
15301
|
await api.execute("postAgentTurnReceipt", {
|
|
14512
15302
|
agentId: this.agent.publicKey,
|
|
14513
15303
|
roomId: cornerId,
|
|
@@ -14526,6 +15316,9 @@ var MonolithCornerTurnLoop = class {
|
|
|
14526
15316
|
this.attachmentDir && attachments.length ? deliverAttachments(attachments, join4(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
|
|
14527
15317
|
]);
|
|
14528
15318
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
15319
|
+
const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
|
|
15320
|
+
if (requestedBy)
|
|
15321
|
+
this.currentTurn = { requestId, requester: requestedBy };
|
|
14529
15322
|
const transcript = conversation.items.slice(-120).map((message) => `${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`).join("\n");
|
|
14530
15323
|
const prompt = [
|
|
14531
15324
|
this.turnIdentityInstructions,
|
|
@@ -14578,7 +15371,7 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
|
|
|
14578
15371
|
return;
|
|
14579
15372
|
publishedToolCalls.add(key);
|
|
14580
15373
|
this.activityTail = this.activityTail.catch(() => void 0).then(async () => {
|
|
14581
|
-
const activity = await cornerToolActivity(call, this.options.worktreePath);
|
|
15374
|
+
const activity = await cornerToolActivity(call, this.options.worktreePath, requestedBy);
|
|
14582
15375
|
await api.execute("postAgentActivity", {
|
|
14583
15376
|
agentId: this.agent.publicKey,
|
|
14584
15377
|
roomId: cornerId,
|
|
@@ -14605,9 +15398,19 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
|
|
|
14605
15398
|
await this.activityTail;
|
|
14606
15399
|
await this.draftTail;
|
|
14607
15400
|
await this.activityTail;
|
|
14608
|
-
|
|
14609
|
-
if (!reply)
|
|
14610
|
-
|
|
15401
|
+
let reply = stripAgentReplyPreamble(result.agentText).trim();
|
|
15402
|
+
if (!reply) {
|
|
15403
|
+
const explained = await explainEmptyAgentTurn({
|
|
15404
|
+
agentLabel: this.options.config.agentCommand ?? this.options.config.agentBinary,
|
|
15405
|
+
agentEnv: this.agentEnv,
|
|
15406
|
+
sessionId,
|
|
15407
|
+
result
|
|
15408
|
+
});
|
|
15409
|
+
reply = explained.recoveredText ? stripAgentReplyPreamble(explained.recoveredText).trim() : "";
|
|
15410
|
+
if (!reply)
|
|
15411
|
+
throw new Error(explained.reason);
|
|
15412
|
+
console.warn(`[thin-core] corner ${cornerId} turn ${requestId}: ${explained.reason}`);
|
|
15413
|
+
}
|
|
14611
15414
|
const durableTail = narrationPostedChars > 0 ? stripAgentReplyPreamble(result.agentText.slice(narrationPostedChars)).trim() : reply;
|
|
14612
15415
|
if (durableTail) {
|
|
14613
15416
|
await api.execute("postRoomMessage", {
|
|
@@ -14617,17 +15420,6 @@ ${trigger}`, ...attachmentPromptLines(attachments, delivered)].join("\n"),
|
|
|
14617
15420
|
presentation: "message"
|
|
14618
15421
|
});
|
|
14619
15422
|
}
|
|
14620
|
-
const pullRequest = reply.match(PULL_REQUEST_URL)?.[0];
|
|
14621
|
-
const alreadyReady = conversation.items.some((item) => /\bPR ready for review\b/i.test(item.body));
|
|
14622
|
-
if (pullRequest && !alreadyReady) {
|
|
14623
|
-
await api.execute("postRoomMessage", {
|
|
14624
|
-
roomId: cornerId,
|
|
14625
|
-
requestId,
|
|
14626
|
-
text: `PR ready for review
|
|
14627
|
-
${pullRequest}`,
|
|
14628
|
-
presentation: "system"
|
|
14629
|
-
});
|
|
14630
|
-
}
|
|
14631
15423
|
await api.execute("retractAgentLiveOutput", {
|
|
14632
15424
|
agentId: this.agent.publicKey,
|
|
14633
15425
|
roomId: cornerId,
|
|
@@ -14648,11 +15440,13 @@ ${pullRequest}`,
|
|
|
14648
15440
|
roomId: cornerId,
|
|
14649
15441
|
requestId,
|
|
14650
15442
|
status: "failed",
|
|
14651
|
-
generationId: `${this.agent.publicKey}:${cornerId}
|
|
15443
|
+
generationId: `${this.agent.publicKey}:${cornerId}`,
|
|
15444
|
+
reason: distillTurnFailureReason(error)
|
|
14652
15445
|
});
|
|
14653
15446
|
throw error;
|
|
14654
15447
|
} finally {
|
|
14655
15448
|
this.busy = false;
|
|
15449
|
+
this.currentTurn = void 0;
|
|
14656
15450
|
}
|
|
14657
15451
|
}
|
|
14658
15452
|
async run() {
|
|
@@ -14685,7 +15479,14 @@ ${pullRequest}`,
|
|
|
14685
15479
|
});
|
|
14686
15480
|
if (!authority.member || authority.principalKind !== "human")
|
|
14687
15481
|
continue;
|
|
14688
|
-
await this.prompt(item.id, item.body, item.attachments);
|
|
15482
|
+
await this.prompt(item.id, item.body, item.attachments, item.authorId);
|
|
15483
|
+
pollWithoutWait = true;
|
|
15484
|
+
continue;
|
|
15485
|
+
}
|
|
15486
|
+
const grantDecision = item.type === "system" && item.mentionIds.includes(this.agent.publicKey) && parseGrantDecisionLine(item.body) !== void 0;
|
|
15487
|
+
if (grantDecision) {
|
|
15488
|
+
await this.prompt(item.id, `${item.body}
|
|
15489
|
+
This answers your grant request; resume the paused work. If approved and it is a command grant, run it with run_granted_command and the exact argv; if declined, try another way or say what you cannot do.`, [], item.authorId);
|
|
14689
15490
|
pollWithoutWait = true;
|
|
14690
15491
|
continue;
|
|
14691
15492
|
}
|
|
@@ -14707,6 +15508,7 @@ ${pullRequest}`,
|
|
|
14707
15508
|
}
|
|
14708
15509
|
}
|
|
14709
15510
|
} finally {
|
|
15511
|
+
this.options.grantRunner?.unregister(cornerId);
|
|
14710
15512
|
await this.options.scheduler.suspend(cornerId);
|
|
14711
15513
|
}
|
|
14712
15514
|
}
|
|
@@ -14726,13 +15528,13 @@ async function wait(ms, signal) {
|
|
|
14726
15528
|
}
|
|
14727
15529
|
|
|
14728
15530
|
// apps/body/dist/monolith-room-turn.js
|
|
14729
|
-
import { mkdir as
|
|
14730
|
-
import { homedir as
|
|
15531
|
+
import { mkdir as mkdir7 } from "node:fs/promises";
|
|
15532
|
+
import { homedir as homedir7 } from "node:os";
|
|
14731
15533
|
import { join as join5 } from "node:path";
|
|
14732
15534
|
|
|
14733
15535
|
// packages/api-contract/dist/scheduled-prompts.js
|
|
14734
15536
|
var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
|
|
14735
|
-
var
|
|
15537
|
+
var SCHEDULE_RAN_VERB = "ran a schedule for";
|
|
14736
15538
|
|
|
14737
15539
|
// apps/body/dist/monolith-room-turn.js
|
|
14738
15540
|
function isRoomMcpPermissionRequest(request) {
|
|
@@ -14744,14 +15546,25 @@ function roomPrincipalMayAddressAgent(authority, humanPermitted) {
|
|
|
14744
15546
|
return authority.member && (authority.principalKind === "agent" || authority.principalKind === "human" && humanPermitted);
|
|
14745
15547
|
}
|
|
14746
15548
|
function isScheduledPrompt(item, agentId) {
|
|
14747
|
-
return item.type === "system" && item.
|
|
15549
|
+
return item.type === "system" && item.systemEvent?.verb === SCHEDULE_RAN_VERB && item.mentionIds.includes(agentId);
|
|
15550
|
+
}
|
|
15551
|
+
function inboxItemPromptBody(item, agentId) {
|
|
15552
|
+
return isScheduledPrompt(item, agentId) ? item.systemEvent?.consequence ?? item.body : item.body;
|
|
15553
|
+
}
|
|
15554
|
+
function isGrantDecisionLine(item, agentId) {
|
|
15555
|
+
return item.type === "system" && item.mentionIds.includes(agentId) && parseGrantDecisionLine(item.body) !== void 0;
|
|
14748
15556
|
}
|
|
14749
15557
|
function inboxItemTriggersTurn(item, agentId) {
|
|
14750
15558
|
if (item.authorId === agentId)
|
|
14751
15559
|
return false;
|
|
14752
15560
|
if (!item.mentionIds.includes(agentId))
|
|
14753
15561
|
return false;
|
|
14754
|
-
return item.type === "message" || isScheduledPrompt(item, agentId);
|
|
15562
|
+
return item.type === "message" || isScheduledPrompt(item, agentId) || isGrantDecisionLine(item, agentId);
|
|
15563
|
+
}
|
|
15564
|
+
function pendingGrantToolCall(call) {
|
|
15565
|
+
if (!/(?:^|[._:/-])request_grant$/i.test(call.title ?? ""))
|
|
15566
|
+
return false;
|
|
15567
|
+
return /pending, card posted/i.test(typeof call.content === "string" ? call.content : JSON.stringify(call.content ?? ""));
|
|
14755
15568
|
}
|
|
14756
15569
|
function escapeRegExp(value) {
|
|
14757
15570
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
@@ -14789,6 +15602,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
14789
15602
|
agent;
|
|
14790
15603
|
client;
|
|
14791
15604
|
sessionId;
|
|
15605
|
+
/** The live session's environment, read back for pi's own turn record. */
|
|
15606
|
+
agentEnv = {};
|
|
14792
15607
|
busy = false;
|
|
14793
15608
|
turnInstructionPrefix = "";
|
|
14794
15609
|
draftTail = Promise.resolve();
|
|
@@ -14798,13 +15613,36 @@ var MonolithRoomTurnLoop = class {
|
|
|
14798
15613
|
attachmentDir;
|
|
14799
15614
|
/** Local copies already delivered this session, by message id, so transcript renders reuse them. */
|
|
14800
15615
|
deliveredAttachments = /* @__PURE__ */ new Map();
|
|
15616
|
+
/** Names from the latest roster read, for ledger bylines the runner writes. */
|
|
15617
|
+
memberNames = /* @__PURE__ */ new Map();
|
|
15618
|
+
/** The request id of the turn that paused on a grant card, until its decision arrives. */
|
|
15619
|
+
pausedOnGrantRequestId;
|
|
14801
15620
|
constructor(options) {
|
|
14802
15621
|
this.options = options;
|
|
14803
15622
|
this.agent = runtimeIdentity(options.runtime.agent);
|
|
15623
|
+
options.grantRunner?.register(options.roomId, {
|
|
15624
|
+
workspaceId: options.workspaceId,
|
|
15625
|
+
cwd: options.cwd,
|
|
15626
|
+
turn: () => this.currentTurnForRunner()
|
|
15627
|
+
});
|
|
14804
15628
|
}
|
|
14805
15629
|
isBusy() {
|
|
14806
15630
|
return this.busy;
|
|
14807
15631
|
}
|
|
15632
|
+
/** The turn a `request_grant` paused, if any (cleared when its decision resumes it). */
|
|
15633
|
+
pausedGrantRequestId() {
|
|
15634
|
+
return this.pausedOnGrantRequestId;
|
|
15635
|
+
}
|
|
15636
|
+
currentTurnForRunner() {
|
|
15637
|
+
const active = this.activeTurn;
|
|
15638
|
+
if (!active)
|
|
15639
|
+
return void 0;
|
|
15640
|
+
return { requestId: active.item.id, requester: this.requesterOf(active.item.authorId) };
|
|
15641
|
+
}
|
|
15642
|
+
requesterOf(authorId) {
|
|
15643
|
+
const name = this.memberNames.get(authorId);
|
|
15644
|
+
return { pubkey: authorId, ...name ? { name } : {} };
|
|
15645
|
+
}
|
|
14808
15646
|
currentPrincipalCanDrive(_workspaceId, principalId) {
|
|
14809
15647
|
return Promise.resolve(isSenderPermitted(this.options.config.accessPolicy ?? LEGACY_ACCESS_POLICY, principalId, this.options.config.accessOwnerPubkey, this.options.config.accessAllowlist));
|
|
14810
15648
|
}
|
|
@@ -14818,11 +15656,13 @@ var MonolithRoomTurnLoop = class {
|
|
|
14818
15656
|
this.client.sessionCancel(this.sessionId);
|
|
14819
15657
|
await this.options.scheduler.forceSuspend(this.options.roomId);
|
|
14820
15658
|
}
|
|
14821
|
-
roster() {
|
|
14822
|
-
|
|
15659
|
+
async roster() {
|
|
15660
|
+
const roster = await this.options.api.execute("getWorkspaceRoster", {
|
|
14823
15661
|
agentId: this.agent.publicKey,
|
|
14824
15662
|
workspaceId: this.options.workspaceId
|
|
14825
15663
|
});
|
|
15664
|
+
this.memberNames = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
15665
|
+
return roster;
|
|
14826
15666
|
}
|
|
14827
15667
|
/** Download a message's attachments into the session scratch directory once. */
|
|
14828
15668
|
async deliver(item) {
|
|
@@ -14847,25 +15687,27 @@ var MonolithRoomTurnLoop = class {
|
|
|
14847
15687
|
this.options.api.execute("getRoomRepositoryState", { roomId: this.options.roomId })
|
|
14848
15688
|
]);
|
|
14849
15689
|
const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
|
|
14850
|
-
await
|
|
15690
|
+
await mkdir7(this.options.cwd, { recursive: true });
|
|
15691
|
+
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
14851
15692
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
14852
15693
|
root: this.options.config.agentHomeRoot,
|
|
14853
15694
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
14854
|
-
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {}
|
|
15695
|
+
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
|
|
15696
|
+
...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl)
|
|
14855
15697
|
}) : {};
|
|
14856
15698
|
const command = this.options.config.agentCommand ?? this.options.config.agentBinary;
|
|
14857
|
-
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
14858
15699
|
const agentEnv = { ...this.options.config.agentEnv, ...homeOverlay };
|
|
15700
|
+
this.agentEnv = agentEnv;
|
|
14859
15701
|
const agentArgs = agentArgsWithModelSelection({
|
|
14860
15702
|
kind: this.options.config.agentKind,
|
|
14861
15703
|
command,
|
|
14862
15704
|
args: this.options.config.agentArgs ?? []
|
|
14863
15705
|
}, selection);
|
|
14864
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
15706
|
+
const operatorHome = this.options.config.operatorHome ?? homedir7();
|
|
14865
15707
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
14866
15708
|
this.attachmentDir = tmpDir ? join5(tmpDir, "beeline-attachments") : void 0;
|
|
14867
15709
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
14868
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
15710
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir7(dir, { recursive: true })));
|
|
14869
15711
|
const spawnCommand = wrapAgentCommand({
|
|
14870
15712
|
bwrapPath: this.options.config.bwrapPath,
|
|
14871
15713
|
spec: {
|
|
@@ -14899,7 +15741,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
14899
15741
|
roomId: this.options.roomId,
|
|
14900
15742
|
workspaceId: this.options.workspaceId,
|
|
14901
15743
|
attachRoot: this.options.cwd,
|
|
14902
|
-
directMessage
|
|
15744
|
+
directMessage,
|
|
15745
|
+
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
14903
15746
|
})
|
|
14904
15747
|
];
|
|
14905
15748
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
@@ -14987,6 +15830,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
14987
15830
|
const api = this.options.api;
|
|
14988
15831
|
this.busy = true;
|
|
14989
15832
|
try {
|
|
15833
|
+
if (!this.memberNames.has(item.authorId))
|
|
15834
|
+
await this.roster().catch(() => void 0);
|
|
14990
15835
|
await api.execute("postAgentTurnReceipt", {
|
|
14991
15836
|
agentId: this.agent.publicKey,
|
|
14992
15837
|
roomId: this.options.roomId,
|
|
@@ -14998,7 +15843,14 @@ var MonolithRoomTurnLoop = class {
|
|
|
14998
15843
|
agentId: this.agent.publicKey,
|
|
14999
15844
|
roomId: this.options.roomId,
|
|
15000
15845
|
requestId: item.id,
|
|
15001
|
-
activity: [
|
|
15846
|
+
activity: [
|
|
15847
|
+
{
|
|
15848
|
+
kind: "thinking",
|
|
15849
|
+
title: "Working",
|
|
15850
|
+
status: "in_progress",
|
|
15851
|
+
requestedBy: this.requesterOf(item.authorId)
|
|
15852
|
+
}
|
|
15853
|
+
]
|
|
15002
15854
|
});
|
|
15003
15855
|
await this.options.scheduler.run(this.options.roomId, this.lifecycle(), async () => {
|
|
15004
15856
|
const [conversation, roster, delivered] = await Promise.all([
|
|
@@ -15008,12 +15860,21 @@ var MonolithRoomTurnLoop = class {
|
|
|
15008
15860
|
]);
|
|
15009
15861
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
15010
15862
|
const transcript = conversation.items.filter((message) => message.type === "message" && message.id !== item.id && !active.steers.some((steerItem) => steerItem.id === message.id)).slice(-80).map((message) => roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id))).join("\n");
|
|
15863
|
+
const grantDecision = isGrantDecisionLine(item, this.agent.publicKey);
|
|
15864
|
+
const resumedRequestId = grantDecision ? this.pausedOnGrantRequestId : void 0;
|
|
15865
|
+
if (grantDecision)
|
|
15866
|
+
this.pausedOnGrantRequestId = void 0;
|
|
15011
15867
|
const prompt = [
|
|
15012
15868
|
this.turnInstructionPrefix,
|
|
15013
15869
|
transcript ? `Room conversation so far:
|
|
15014
15870
|
${transcript}` : "",
|
|
15015
15871
|
`Newest message from ${isScheduledPrompt(item, this.agent.publicKey) ? SCHEDULE_SCHEDULER_NAME : names.get(item.authorId) ?? item.authorId.slice(0, 12)}:`,
|
|
15016
|
-
roomMessagePrompt("", item.
|
|
15872
|
+
roomMessagePrompt("", inboxItemPromptBody(item, this.agent.publicKey), item.attachments, delivered),
|
|
15873
|
+
grantDecision ? [
|
|
15874
|
+
"This is the answer to your grant request; your paused work resumes now.",
|
|
15875
|
+
"If it was approved and it is a command grant, run it with run_granted_command and the exact argv.",
|
|
15876
|
+
"If it was declined, try another way or say plainly what you cannot do."
|
|
15877
|
+
].join(" ") : "",
|
|
15017
15878
|
[
|
|
15018
15879
|
"Write only the substantive Room message you want the human to read.",
|
|
15019
15880
|
"Do not repeat or paraphrase these instructions.",
|
|
@@ -15062,15 +15923,31 @@ ${transcript}` : "",
|
|
|
15062
15923
|
].join("\n\n");
|
|
15063
15924
|
}
|
|
15064
15925
|
active.phase = "finishing";
|
|
15926
|
+
if (result?.toolCalls.some((call) => pendingGrantToolCall(call))) {
|
|
15927
|
+
this.pausedOnGrantRequestId = item.id;
|
|
15928
|
+
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id} paused on a grant card`);
|
|
15929
|
+
} else if (resumedRequestId) {
|
|
15930
|
+
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by grant decision ${item.id}`);
|
|
15931
|
+
}
|
|
15065
15932
|
const openCornerCall = result?.toolCalls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
|
|
15066
15933
|
if (openCornerCall) {
|
|
15067
15934
|
console.log(`[thin-core] monolith Room ${this.options.roomId} tool call: ${openCornerCall.title}`);
|
|
15068
15935
|
this.options.onCornerOpened?.();
|
|
15069
15936
|
}
|
|
15070
15937
|
await this.draftTail;
|
|
15071
|
-
|
|
15072
|
-
if (!reply)
|
|
15073
|
-
|
|
15938
|
+
let reply = sanitizeAgentReply(result.agentText);
|
|
15939
|
+
if (!reply) {
|
|
15940
|
+
const explained = await explainEmptyAgentTurn({
|
|
15941
|
+
agentLabel: this.options.config.agentCommand ?? this.options.config.agentBinary,
|
|
15942
|
+
agentEnv: this.agentEnv,
|
|
15943
|
+
sessionId,
|
|
15944
|
+
result
|
|
15945
|
+
});
|
|
15946
|
+
reply = explained.recoveredText ? sanitizeAgentReply(explained.recoveredText) : "";
|
|
15947
|
+
if (!reply)
|
|
15948
|
+
throw new Error(explained.reason);
|
|
15949
|
+
console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${explained.reason}`);
|
|
15950
|
+
}
|
|
15074
15951
|
await api.execute("postRoomMessage", {
|
|
15075
15952
|
roomId: this.options.roomId,
|
|
15076
15953
|
requestId: item.id,
|
|
@@ -15099,7 +15976,8 @@ ${transcript}` : "",
|
|
|
15099
15976
|
roomId: this.options.roomId,
|
|
15100
15977
|
requestId: item.id,
|
|
15101
15978
|
status: "failed",
|
|
15102
|
-
generationId: `${this.agent.publicKey}:${this.options.roomId}
|
|
15979
|
+
generationId: `${this.agent.publicKey}:${this.options.roomId}`,
|
|
15980
|
+
reason: distillTurnFailureReason(error)
|
|
15103
15981
|
});
|
|
15104
15982
|
throw error;
|
|
15105
15983
|
} finally {
|
|
@@ -15138,7 +16016,7 @@ ${transcript}` : "",
|
|
|
15138
16016
|
for (const item of inbox.items) {
|
|
15139
16017
|
if (!inboxItemTriggersTurn(item, this.agent.publicKey))
|
|
15140
16018
|
continue;
|
|
15141
|
-
if (!isScheduledPrompt(item, this.agent.publicKey)) {
|
|
16019
|
+
if (!isScheduledPrompt(item, this.agent.publicKey) && !isGrantDecisionLine(item, this.agent.publicKey)) {
|
|
15142
16020
|
const authority = await api.execute("getRoomAuthority", {
|
|
15143
16021
|
roomId,
|
|
15144
16022
|
principalId: item.authorId
|
|
@@ -15168,6 +16046,7 @@ ${transcript}` : "",
|
|
|
15168
16046
|
}
|
|
15169
16047
|
} finally {
|
|
15170
16048
|
clearInterval(heartbeat);
|
|
16049
|
+
this.options.grantRunner?.unregister(roomId);
|
|
15171
16050
|
if (this.activeTurn?.phase === "prompting" && this.client && this.sessionId) {
|
|
15172
16051
|
this.client.sessionCancel(this.sessionId);
|
|
15173
16052
|
}
|
|
@@ -15547,7 +16426,7 @@ async function mapWithConcurrency(values, limit, visit) {
|
|
|
15547
16426
|
}
|
|
15548
16427
|
}));
|
|
15549
16428
|
}
|
|
15550
|
-
var execFileAsync3 = promisify3(
|
|
16429
|
+
var execFileAsync3 = promisify3(execFile4);
|
|
15551
16430
|
var RoomRuntimeCoordinator = class {
|
|
15552
16431
|
configPath;
|
|
15553
16432
|
baseConfig;
|
|
@@ -15567,6 +16446,9 @@ var RoomRuntimeCoordinator = class {
|
|
|
15567
16446
|
workspaceRemovalConfirmations = 0;
|
|
15568
16447
|
roomRemovalConfirmations = /* @__PURE__ */ new Map();
|
|
15569
16448
|
confirmationPending = false;
|
|
16449
|
+
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
16450
|
+
grantRunner;
|
|
16451
|
+
grantRunnerServer;
|
|
15570
16452
|
constructor(runtime, configPath, baseConfig, options) {
|
|
15571
16453
|
this.configPath = configPath;
|
|
15572
16454
|
this.baseConfig = baseConfig;
|
|
@@ -15576,6 +16458,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
15576
16458
|
this.runtime = runtime;
|
|
15577
16459
|
this.agent = runtimeIdentity(runtime.agent);
|
|
15578
16460
|
this.now = options.now ?? Date.now;
|
|
16461
|
+
this.grantRunner = new GrantCommandRunner({
|
|
16462
|
+
api: options.daemonApi,
|
|
16463
|
+
agentId: this.agent.publicKey
|
|
16464
|
+
});
|
|
16465
|
+
this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
|
|
15579
16466
|
this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
|
|
15580
16467
|
this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
|
|
15581
16468
|
this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
|
|
@@ -15602,7 +16489,15 @@ var RoomRuntimeCoordinator = class {
|
|
|
15602
16489
|
return this.reconcileHeartbeatMs;
|
|
15603
16490
|
}
|
|
15604
16491
|
isWorkspaceIdle() {
|
|
15605
|
-
return
|
|
16492
|
+
return this.activeTurnCount() === 0;
|
|
16493
|
+
}
|
|
16494
|
+
/** Turns executing right now. Serving a Room or corner with no turn in flight is idle. */
|
|
16495
|
+
activeTurnCount() {
|
|
16496
|
+
let count = 0;
|
|
16497
|
+
for (const room of this.running.values())
|
|
16498
|
+
if (room.body.isBusy())
|
|
16499
|
+
count += 1;
|
|
16500
|
+
return count;
|
|
15606
16501
|
}
|
|
15607
16502
|
quiesceForUpdateIfIdle() {
|
|
15608
16503
|
if (!this.isWorkspaceIdle())
|
|
@@ -15698,13 +16593,13 @@ var RoomRuntimeCoordinator = class {
|
|
|
15698
16593
|
return this.runtime.rooms.find((room) => room.channelId === roomId);
|
|
15699
16594
|
}
|
|
15700
16595
|
roomRoot(roomId) {
|
|
15701
|
-
return this.roomRecord(roomId)?.root ??
|
|
16596
|
+
return this.roomRecord(roomId)?.root ?? resolve15(dirname6(this.configPath), "rooms", roomId);
|
|
15702
16597
|
}
|
|
15703
16598
|
roomAgentHomeRoot(workspaceRoot, required = false) {
|
|
15704
16599
|
const flag = process.env.BUZZY_BODY_ROOM_HOME;
|
|
15705
16600
|
if (!required && flag === "0")
|
|
15706
16601
|
return void 0;
|
|
15707
|
-
const home =
|
|
16602
|
+
const home = resolve15(workspaceRoot, "agent-home");
|
|
15708
16603
|
if (!required && flag !== "1" && !existsSync4(home) && existsSync4(workspaceRoot))
|
|
15709
16604
|
return void 0;
|
|
15710
16605
|
try {
|
|
@@ -15721,19 +16616,30 @@ var RoomRuntimeCoordinator = class {
|
|
|
15721
16616
|
return {
|
|
15722
16617
|
...this.baseConfig,
|
|
15723
16618
|
workspaceRoot,
|
|
15724
|
-
agentPrivateRoot:
|
|
15725
|
-
agentMemoryRoot:
|
|
16619
|
+
agentPrivateRoot: resolve15(workspaceRoot, "agent-private"),
|
|
16620
|
+
agentMemoryRoot: resolve15(dirname6(this.configPath), "memory"),
|
|
16621
|
+
openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname6(this.configPath)),
|
|
15726
16622
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
15727
16623
|
};
|
|
15728
16624
|
}
|
|
16625
|
+
/** The loopback door for run_granted_command; started once, on first use. */
|
|
16626
|
+
grantRunnerEndpoint() {
|
|
16627
|
+
return this.grantRunnerServer.start().catch((error) => {
|
|
16628
|
+
console.error("[thin-core] grant runner unavailable; run_granted_command is off:", error);
|
|
16629
|
+
return void 0;
|
|
16630
|
+
});
|
|
16631
|
+
}
|
|
15729
16632
|
async startRoom(roomId) {
|
|
15730
16633
|
const controller = new AbortController();
|
|
15731
16634
|
const cwd = await this.materializeRoomCheckout(roomId);
|
|
16635
|
+
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
15732
16636
|
const startedAt = this.now();
|
|
15733
16637
|
const loop = new MonolithRoomTurnLoop({
|
|
15734
16638
|
roomId,
|
|
15735
16639
|
workspaceId: this.runtime.communityId,
|
|
15736
16640
|
cwd,
|
|
16641
|
+
grantRunner: this.grantRunner,
|
|
16642
|
+
...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
|
|
15737
16643
|
runtime: this.runtime,
|
|
15738
16644
|
config: this.roomConfig(roomId),
|
|
15739
16645
|
api: this.options.daemonApi,
|
|
@@ -15778,12 +16684,12 @@ var RoomRuntimeCoordinator = class {
|
|
|
15778
16684
|
return this.roomRoot(roomId);
|
|
15779
16685
|
const remote = roomCheckoutRemote(repository.remote);
|
|
15780
16686
|
const targetBranch = repository.targetBranch || "main";
|
|
15781
|
-
const checkoutId =
|
|
15782
|
-
const path =
|
|
15783
|
-
await
|
|
16687
|
+
const checkoutId = createHash2("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
16688
|
+
const path = resolve15(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
16689
|
+
await mkdir8(dirname6(path), { recursive: true, mode: 448 });
|
|
15784
16690
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
15785
16691
|
const env = token ? githubGitEnv(token.token) : process.env;
|
|
15786
|
-
if (!existsSync4(
|
|
16692
|
+
if (!existsSync4(resolve15(path, ".git"))) {
|
|
15787
16693
|
await execFileAsync3("git", ["clone", "--no-checkout", remote, path], {
|
|
15788
16694
|
env,
|
|
15789
16695
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -15845,9 +16751,12 @@ var RoomRuntimeCoordinator = class {
|
|
|
15845
16751
|
});
|
|
15846
16752
|
}
|
|
15847
16753
|
const controller = new AbortController();
|
|
16754
|
+
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
15848
16755
|
const startedAt = this.now();
|
|
15849
16756
|
const loop = new MonolithCornerTurnLoop({
|
|
15850
16757
|
cornerId: corner.cornerId,
|
|
16758
|
+
grantRunner: this.grantRunner,
|
|
16759
|
+
...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
|
|
15851
16760
|
parentRoomId: corner.parentRoomId,
|
|
15852
16761
|
workspaceId: this.runtime.communityId,
|
|
15853
16762
|
objective,
|
|
@@ -15900,13 +16809,13 @@ var RoomRuntimeCoordinator = class {
|
|
|
15900
16809
|
}
|
|
15901
16810
|
async materializeCornerWorktree(input) {
|
|
15902
16811
|
const remote = githubHttpsRemote(input.remote);
|
|
15903
|
-
const repositoryHash =
|
|
15904
|
-
const gitCommonDir =
|
|
15905
|
-
const path =
|
|
15906
|
-
await
|
|
15907
|
-
await
|
|
16812
|
+
const repositoryHash = createHash2("sha256").update(remote).digest("hex").slice(0, 24);
|
|
16813
|
+
const gitCommonDir = resolve15(this.runtime.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
16814
|
+
const path = resolve15(this.runtime.supervisorRoot, "beeline", "corners", input.cornerId);
|
|
16815
|
+
await mkdir8(dirname6(gitCommonDir), { recursive: true, mode: 448 });
|
|
16816
|
+
await mkdir8(dirname6(path), { recursive: true, mode: 448 });
|
|
15908
16817
|
const authEnv = githubGitEnv(input.token);
|
|
15909
|
-
if (!existsSync4(
|
|
16818
|
+
if (!existsSync4(resolve15(gitCommonDir, "HEAD"))) {
|
|
15910
16819
|
await execFileAsync3("git", ["clone", "--bare", remote, gitCommonDir], {
|
|
15911
16820
|
env: authEnv,
|
|
15912
16821
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -15919,7 +16828,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
15919
16828
|
"origin",
|
|
15920
16829
|
`+refs/heads/${input.targetBranch}:refs/remotes/origin/${input.targetBranch}`
|
|
15921
16830
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
|
|
15922
|
-
if (!existsSync4(
|
|
16831
|
+
if (!existsSync4(resolve15(path, ".git"))) {
|
|
15923
16832
|
await rm3(path, { recursive: true, force: true });
|
|
15924
16833
|
await execFileAsync3("git", [
|
|
15925
16834
|
`--git-dir=${gitCommonDir}`,
|
|
@@ -15956,7 +16865,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
15956
16865
|
`${this.agent.publicKey.slice(0, 16)}@users.noreply.github.com`
|
|
15957
16866
|
]);
|
|
15958
16867
|
const top = await execFileAsync3("git", ["-C", path, "rev-parse", "--show-toplevel"]);
|
|
15959
|
-
if (
|
|
16868
|
+
if (resolve15(top.stdout.trim()) !== resolve15(path)) {
|
|
15960
16869
|
throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
|
|
15961
16870
|
}
|
|
15962
16871
|
return { path, gitCommonDir };
|
|
@@ -16023,6 +16932,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
16023
16932
|
await Promise.allSettled(rooms.map((room) => room.body.forceRecoverRoom()));
|
|
16024
16933
|
await drained;
|
|
16025
16934
|
}
|
|
16935
|
+
await this.grantRunnerServer.close();
|
|
16026
16936
|
await this.scheduler.dispose();
|
|
16027
16937
|
}
|
|
16028
16938
|
};
|
|
@@ -16081,6 +16991,9 @@ var ThinDaemonCore = class {
|
|
|
16081
16991
|
isWorkspaceIdle() {
|
|
16082
16992
|
return this.roomRuntime.isWorkspaceIdle();
|
|
16083
16993
|
}
|
|
16994
|
+
activeTurnCount() {
|
|
16995
|
+
return this.roomRuntime.activeTurnCount();
|
|
16996
|
+
}
|
|
16084
16997
|
quiesceForUpdateIfIdle() {
|
|
16085
16998
|
return this.roomRuntime.quiesceForUpdateIfIdle();
|
|
16086
16999
|
}
|
|
@@ -16127,7 +17040,7 @@ var ThinDaemonCore = class {
|
|
|
16127
17040
|
};
|
|
16128
17041
|
|
|
16129
17042
|
// apps/body/dist/daemon-api-client.js
|
|
16130
|
-
import { resolve as
|
|
17043
|
+
import { resolve as resolve16 } from "node:path";
|
|
16131
17044
|
var DaemonApiError = class extends Error {
|
|
16132
17045
|
status;
|
|
16133
17046
|
retryable;
|
|
@@ -16186,8 +17099,8 @@ var DaemonApiClient = class {
|
|
|
16186
17099
|
};
|
|
16187
17100
|
async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
16188
17101
|
const runtime = await readRuntimeRecord(path);
|
|
16189
|
-
const expectedPath =
|
|
16190
|
-
if (
|
|
17102
|
+
const expectedPath = resolve16(runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey), "runtime.json");
|
|
17103
|
+
if (resolve16(path) !== expectedPath) {
|
|
16191
17104
|
throw new Error(`refusing daemon token exchange outside canonical runtime path: ${path}`);
|
|
16192
17105
|
}
|
|
16193
17106
|
const transport = runtime.transport;
|
|
@@ -16226,17 +17139,17 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
16226
17139
|
}
|
|
16227
17140
|
|
|
16228
17141
|
// apps/body/dist/start-command.js
|
|
16229
|
-
import { dirname as
|
|
17142
|
+
import { dirname as dirname8 } from "node:path";
|
|
16230
17143
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
16231
17144
|
|
|
16232
17145
|
// apps/body/dist/systemd.js
|
|
16233
|
-
import { execFile as
|
|
16234
|
-
import { mkdir as
|
|
16235
|
-
import { homedir as
|
|
16236
|
-
import { dirname as
|
|
17146
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
17147
|
+
import { mkdir as mkdir9, readFile as readFile7, writeFile as writeFile7 } from "node:fs/promises";
|
|
17148
|
+
import { homedir as homedir8 } from "node:os";
|
|
17149
|
+
import { dirname as dirname7, resolve as resolve17 } from "node:path";
|
|
16237
17150
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
16238
17151
|
import { promisify as promisify4 } from "node:util";
|
|
16239
|
-
var execFileAsync4 = promisify4(
|
|
17152
|
+
var execFileAsync4 = promisify4(execFile5);
|
|
16240
17153
|
var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
|
|
16241
17154
|
var DAEMON_DISTRESS_EXIT_STATUS = 77;
|
|
16242
17155
|
var UNKNOWN_AGENT_EXIT_STATUS = 79;
|
|
@@ -16274,10 +17187,10 @@ WantedBy=default.target
|
|
|
16274
17187
|
`;
|
|
16275
17188
|
}
|
|
16276
17189
|
function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
|
|
16277
|
-
const home = env.HOME?.trim() ||
|
|
16278
|
-
const expectedLibDir =
|
|
17190
|
+
const home = env.HOME?.trim() || homedir8();
|
|
17191
|
+
const expectedLibDir = resolve17(home, ".local", "lib", "beeline");
|
|
16279
17192
|
const expectedPrefix = `${expectedLibDir}/`;
|
|
16280
|
-
return
|
|
17193
|
+
return resolve17(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve17(invocationPath).startsWith(expectedPrefix);
|
|
16281
17194
|
}
|
|
16282
17195
|
function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
16283
17196
|
if (isCanonicalInstalledLauncher(env, invocationPath))
|
|
@@ -16285,8 +17198,8 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
|
16285
17198
|
throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
|
|
16286
17199
|
}
|
|
16287
17200
|
function systemdUserUnitPath(env = process.env) {
|
|
16288
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
16289
|
-
return
|
|
17201
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve17(homedir8(), ".config");
|
|
17202
|
+
return resolve17(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
|
|
16290
17203
|
}
|
|
16291
17204
|
var runSystemctl = async (args) => {
|
|
16292
17205
|
const result = await execFileAsync4("systemctl", ["--user", ...args], {
|
|
@@ -16302,10 +17215,10 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
16302
17215
|
assertCanonicalInstalledLauncher(env, options.invocationPath);
|
|
16303
17216
|
const path = systemdUserUnitPath(env);
|
|
16304
17217
|
const content = agentServiceUnit();
|
|
16305
|
-
const existing = await
|
|
17218
|
+
const existing = await readFile7(path, "utf8").catch(() => "");
|
|
16306
17219
|
if (existing !== content) {
|
|
16307
|
-
await
|
|
16308
|
-
await
|
|
17220
|
+
await mkdir9(dirname7(path), { recursive: true, mode: 448 });
|
|
17221
|
+
await writeFile7(path, content, { mode: 384 });
|
|
16309
17222
|
}
|
|
16310
17223
|
const run2 = options.run ?? runSystemctl;
|
|
16311
17224
|
await run2(["daemon-reload"]);
|
|
@@ -16443,7 +17356,7 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
16443
17356
|
continue;
|
|
16444
17357
|
}
|
|
16445
17358
|
const spinnerHandle = spinner();
|
|
16446
|
-
spinnerHandle.start(`Starting ${
|
|
17359
|
+
spinnerHandle.start(`Starting ${dirname8(path)}\u2026`);
|
|
16447
17360
|
try {
|
|
16448
17361
|
await startRuntime(path, spinnerHandle);
|
|
16449
17362
|
spinnerHandle.stop(import_picocolors.default.green("Started."));
|
|
@@ -16459,9 +17372,9 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
16459
17372
|
|
|
16460
17373
|
// apps/body/dist/connect-command.js
|
|
16461
17374
|
import { spawn as spawn5 } from "node:child_process";
|
|
16462
|
-
import { createHash as
|
|
16463
|
-
import { chmod as chmod4, mkdir as
|
|
16464
|
-
import { dirname as
|
|
17375
|
+
import { createHash as createHash4 } from "node:crypto";
|
|
17376
|
+
import { chmod as chmod4, mkdir as mkdir11, readFile as readFile9, unlink as unlink2, writeFile as writeFile9 } from "node:fs/promises";
|
|
17377
|
+
import { dirname as dirname10, resolve as resolve19 } from "node:path";
|
|
16465
17378
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
16466
17379
|
|
|
16467
17380
|
// packages/api-contract/dist/agent-pairing-code.js
|
|
@@ -16771,63 +17684,6 @@ async function pairDevice(grant, options = {}) {
|
|
|
16771
17684
|
return { runtime: activated.runtime, configPath: staged.configPath, pid };
|
|
16772
17685
|
}
|
|
16773
17686
|
|
|
16774
|
-
// apps/body/dist/provider-key-store.js
|
|
16775
|
-
import { chmod as chmod2, mkdir as mkdir8, readFile as readFile3, writeFile as writeFile5 } from "node:fs/promises";
|
|
16776
|
-
import { homedir as homedir8 } from "node:os";
|
|
16777
|
-
import { dirname as dirname7, resolve as resolve13 } from "node:path";
|
|
16778
|
-
var PROVIDER_KEY_ENV_VARS = {
|
|
16779
|
-
openrouter: "OPENROUTER_API_KEY",
|
|
16780
|
-
openai: "OPENAI_API_KEY",
|
|
16781
|
-
anthropic: "ANTHROPIC_API_KEY",
|
|
16782
|
-
google: "GOOGLE_API_KEY",
|
|
16783
|
-
xai: "XAI_API_KEY"
|
|
16784
|
-
};
|
|
16785
|
-
var GOOGLE_ENV_ALIAS = "GEMINI_API_KEY";
|
|
16786
|
-
function providerKeyStorePath(env = process.env) {
|
|
16787
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve13(homedir8(), ".config");
|
|
16788
|
-
return resolve13(configRoot, "beeline", "providers.json");
|
|
16789
|
-
}
|
|
16790
|
-
async function readProviderKeyStore(env = process.env) {
|
|
16791
|
-
const path = providerKeyStorePath(env);
|
|
16792
|
-
const raw = await readFile3(path, "utf8").catch(() => void 0);
|
|
16793
|
-
if (!raw)
|
|
16794
|
-
return {};
|
|
16795
|
-
try {
|
|
16796
|
-
const parsed = JSON.parse(raw);
|
|
16797
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
16798
|
-
return {};
|
|
16799
|
-
const entries = Object.entries(parsed).filter((entry) => entry[0] in PROVIDER_KEY_ENV_VARS && typeof entry[1] === "string" && entry[1].length > 0);
|
|
16800
|
-
return Object.fromEntries(entries);
|
|
16801
|
-
} catch {
|
|
16802
|
-
return {};
|
|
16803
|
-
}
|
|
16804
|
-
}
|
|
16805
|
-
async function readSavedProviderKey(provider, env = process.env) {
|
|
16806
|
-
return (await readProviderKeyStore(env))[provider];
|
|
16807
|
-
}
|
|
16808
|
-
async function saveProviderKey(provider, key, env = process.env) {
|
|
16809
|
-
const path = providerKeyStorePath(env);
|
|
16810
|
-
const store = { ...await readProviderKeyStore(env), [provider]: key };
|
|
16811
|
-
await mkdir8(dirname7(path), { recursive: true, mode: 448 });
|
|
16812
|
-
await writeFile5(path, `${JSON.stringify(store, null, 2)}
|
|
16813
|
-
`, { mode: 384 });
|
|
16814
|
-
await chmod2(path, 384);
|
|
16815
|
-
}
|
|
16816
|
-
function providerKeyFromEnvironment(provider, env = process.env) {
|
|
16817
|
-
const primary = env[PROVIDER_KEY_ENV_VARS[provider]]?.trim();
|
|
16818
|
-
if (primary)
|
|
16819
|
-
return primary;
|
|
16820
|
-
if (provider === "google")
|
|
16821
|
-
return env[GOOGLE_ENV_ALIAS]?.trim() || void 0;
|
|
16822
|
-
return void 0;
|
|
16823
|
-
}
|
|
16824
|
-
function maskProviderKey(key) {
|
|
16825
|
-
const trimmed = key.trim();
|
|
16826
|
-
if (trimmed.length <= 9)
|
|
16827
|
-
return "\u2026";
|
|
16828
|
-
return `${trimmed.slice(0, 6)}\u2026${trimmed.slice(-3)}`;
|
|
16829
|
-
}
|
|
16830
|
-
|
|
16831
17687
|
// apps/body/dist/connect-command.js
|
|
16832
17688
|
init_self_update();
|
|
16833
17689
|
init_self_update_manifest();
|
|
@@ -17044,7 +17900,7 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl) {
|
|
|
17044
17900
|
const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
|
|
17045
17901
|
if (!normalizedPairingCode)
|
|
17046
17902
|
throw new Error("invalid pairing code");
|
|
17047
|
-
const avatarSeed =
|
|
17903
|
+
const avatarSeed = createHash4("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
|
|
17048
17904
|
return jsonRequest(`${baseUrl}/auth/agent/connect`, {
|
|
17049
17905
|
pairing_code: normalizedPairingCode,
|
|
17050
17906
|
harness: selection.harness,
|
|
@@ -17069,7 +17925,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
17069
17925
|
});
|
|
17070
17926
|
await activateRelease(layout, releaseId);
|
|
17071
17927
|
return {
|
|
17072
|
-
binary:
|
|
17928
|
+
binary: resolve19(layout.binDir, "beeline"),
|
|
17073
17929
|
version: published.version ?? releaseId
|
|
17074
17930
|
};
|
|
17075
17931
|
}
|
|
@@ -17092,8 +17948,8 @@ function providerEnvironment(selection) {
|
|
|
17092
17948
|
};
|
|
17093
17949
|
}
|
|
17094
17950
|
async function writePrivateJson(path, value) {
|
|
17095
|
-
await
|
|
17096
|
-
await
|
|
17951
|
+
await mkdir11(dirname10(path), { recursive: true, mode: 448 });
|
|
17952
|
+
await writeFile9(path, `${JSON.stringify(value, null, 2)}
|
|
17097
17953
|
`, { mode: 384 });
|
|
17098
17954
|
await chmod4(path, 384);
|
|
17099
17955
|
}
|
|
@@ -17101,10 +17957,10 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
17101
17957
|
const values = providerEnvironment(selection);
|
|
17102
17958
|
if (Object.keys(values).length === 0)
|
|
17103
17959
|
return void 0;
|
|
17104
|
-
const path =
|
|
17105
|
-
await
|
|
17960
|
+
const path = resolve19(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
17961
|
+
await mkdir11(dirname10(path), { recursive: true, mode: 448 });
|
|
17106
17962
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
17107
|
-
await
|
|
17963
|
+
await writeFile9(path, `${contents}
|
|
17108
17964
|
`, { mode: 384 });
|
|
17109
17965
|
await chmod4(path, 384);
|
|
17110
17966
|
return path;
|
|
@@ -17158,7 +18014,7 @@ async function runConnectCommand(code, options = {}) {
|
|
|
17158
18014
|
const grant = await brassSpinner("Connecting to your Beeline Workspace\u2026", () => requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl), (connectedGrant) => `Connected to ${connectedGrant.workspace_name}`);
|
|
17159
18015
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
17160
18016
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
17161
|
-
const grantPath =
|
|
18017
|
+
const grantPath = resolve19(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
17162
18018
|
await writePrivateJson(grantPath, {
|
|
17163
18019
|
agentSecretKey: grant.agent_secret_key,
|
|
17164
18020
|
bodySecretKey: grant.body_secret_key,
|
|
@@ -17209,11 +18065,22 @@ async function runConnectFinishCommand(path) {
|
|
|
17209
18065
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
17210
18066
|
}
|
|
17211
18067
|
try {
|
|
17212
|
-
const grant = JSON.parse(await
|
|
18068
|
+
const grant = JSON.parse(await readFile9(resolve19(path), "utf8"));
|
|
17213
18069
|
if (!isDevicePairingGrant(grant))
|
|
17214
18070
|
throw new Error("device connection grant is invalid");
|
|
17215
|
-
await completeDevicePairing(grant);
|
|
17216
|
-
await unlink2(
|
|
18071
|
+
const connected = await completeDevicePairing(grant);
|
|
18072
|
+
await unlink2(resolve19(path));
|
|
18073
|
+
const providerEnv = grant.llmEnvFile ? await readFile9(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
18074
|
+
const model = openRouterModelId(grant.model, {
|
|
18075
|
+
OPENROUTER_API_KEY: /^OPENROUTER_API_KEY=\S/m.test(providerEnv) ? "set" : ""
|
|
18076
|
+
});
|
|
18077
|
+
if (model) {
|
|
18078
|
+
const decision2 = await resolveOpenRouterRouting({
|
|
18079
|
+
model,
|
|
18080
|
+
cacheDir: openRouterRoutingCacheDir(dirname10(connected.configPath))
|
|
18081
|
+
});
|
|
18082
|
+
console.log(decision2.line);
|
|
18083
|
+
}
|
|
17217
18084
|
} catch (error) {
|
|
17218
18085
|
throw new ConnectFailureError(connectPlainFailure(error));
|
|
17219
18086
|
}
|
|
@@ -17227,20 +18094,20 @@ init_self_update_manifest();
|
|
|
17227
18094
|
// apps/body/dist/managed-update.js
|
|
17228
18095
|
init_self_update();
|
|
17229
18096
|
import { spawn as spawn6 } from "node:child_process";
|
|
17230
|
-
import { mkdir as
|
|
17231
|
-
import { dirname as
|
|
18097
|
+
import { mkdir as mkdir13, rm as rm5, stat as stat2, writeFile as writeFile11 } from "node:fs/promises";
|
|
18098
|
+
import { dirname as dirname12, resolve as resolve21 } from "node:path";
|
|
17232
18099
|
|
|
17233
18100
|
// apps/body/dist/update-rollback-alert.js
|
|
17234
|
-
import { mkdir as
|
|
17235
|
-
import { dirname as
|
|
18101
|
+
import { mkdir as mkdir12, readFile as readFile10, rename as rename4, writeFile as writeFile10 } from "node:fs/promises";
|
|
18102
|
+
import { dirname as dirname11, resolve as resolve20 } from "node:path";
|
|
17236
18103
|
function updateRollbackAlertPath(runtimeDir) {
|
|
17237
|
-
return
|
|
18104
|
+
return resolve20(runtimeDir, "update-rollback-alert.json");
|
|
17238
18105
|
}
|
|
17239
18106
|
async function writeAlert(runtimeDir, alert) {
|
|
17240
18107
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
17241
18108
|
const staged = `${path}.${process.pid}.tmp`;
|
|
17242
|
-
await
|
|
17243
|
-
await
|
|
18109
|
+
await mkdir12(dirname11(path), { recursive: true });
|
|
18110
|
+
await writeFile10(staged, `${JSON.stringify(alert, null, 2)}
|
|
17244
18111
|
`, { mode: 384 });
|
|
17245
18112
|
await rename4(staged, path);
|
|
17246
18113
|
}
|
|
@@ -17252,7 +18119,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
|
|
|
17252
18119
|
}
|
|
17253
18120
|
async function readUpdateRollbackAlert(runtimeDir) {
|
|
17254
18121
|
try {
|
|
17255
|
-
const value = JSON.parse(await
|
|
18122
|
+
const value = JSON.parse(await readFile10(updateRollbackAlertPath(runtimeDir), "utf8"));
|
|
17256
18123
|
if (value.version !== 1 || typeof value.releaseId !== "string")
|
|
17257
18124
|
return void 0;
|
|
17258
18125
|
return value;
|
|
@@ -17277,13 +18144,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
17277
18144
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
17278
18145
|
async function withInstallLock(layout, work, options = {}) {
|
|
17279
18146
|
const now2 = options.now ?? Date.now;
|
|
17280
|
-
const lock =
|
|
18147
|
+
const lock = resolve21(layout.releasesRoot, ".state", "install.lock");
|
|
17281
18148
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
17282
|
-
await
|
|
18149
|
+
await mkdir13(dirname12(lock), { recursive: true });
|
|
17283
18150
|
for (; ; ) {
|
|
17284
18151
|
try {
|
|
17285
|
-
await
|
|
17286
|
-
await
|
|
18152
|
+
await mkdir13(lock);
|
|
18153
|
+
await writeFile11(resolve21(lock, "owner"), `${process.pid}
|
|
17287
18154
|
${now2()}
|
|
17288
18155
|
`, "utf8");
|
|
17289
18156
|
break;
|
|
@@ -17320,7 +18187,6 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
17320
18187
|
#requested = false;
|
|
17321
18188
|
#restartRequest;
|
|
17322
18189
|
#stagedReleaseId;
|
|
17323
|
-
#drainDeadlineLogged = false;
|
|
17324
18190
|
constructor(options) {
|
|
17325
18191
|
this.#layout = options.layout;
|
|
17326
18192
|
this.#loadedRelease = options.loadedRelease;
|
|
@@ -17407,7 +18273,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
17407
18273
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
17408
18274
|
const from = await readInstalledBundleIdentity({
|
|
17409
18275
|
...this.#layout,
|
|
17410
|
-
libDir:
|
|
18276
|
+
libDir: resolve21(this.#layout.releasesRoot, this.#loadedRelease)
|
|
17411
18277
|
}).catch(() => void 0) ?? {};
|
|
17412
18278
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
17413
18279
|
const record2 = {
|
|
@@ -17444,11 +18310,13 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
17444
18310
|
}
|
|
17445
18311
|
/**
|
|
17446
18312
|
* Resolve one handoff tick against the daemon's authoritative turn registry.
|
|
17447
|
-
*
|
|
17448
|
-
* `quiesceIfIdle` closes intake in the same
|
|
17449
|
-
* proves idle, so a new turn cannot race
|
|
17450
|
-
*
|
|
17451
|
-
*
|
|
18313
|
+
* Busy means a turn is executing right now; serving Rooms and corners with
|
|
18314
|
+
* no turn in flight is idle. `quiesceIfIdle` closes intake in the same
|
|
18315
|
+
* synchronous transition that proves idle, so a new turn cannot race
|
|
18316
|
+
* activation. A staged release stays inert while a turn runs — until the
|
|
18317
|
+
* absolute drain deadline, when the restart is forced (`forced: true`) and
|
|
18318
|
+
* the caller cancels the running turn; the convergence contract outranks a
|
|
18319
|
+
* stuck turn. `ManagedUpdateDrain` enforces that deadline with a timer.
|
|
17452
18320
|
*/
|
|
17453
18321
|
async restartRequest(quiesceIfIdle) {
|
|
17454
18322
|
const activeDrift = await this.check();
|
|
@@ -17457,12 +18325,11 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
17457
18325
|
const request = this.#restartRequest;
|
|
17458
18326
|
if (!request)
|
|
17459
18327
|
throw new Error("update drift was detected without an in-memory restart request");
|
|
18328
|
+
let forced = false;
|
|
17460
18329
|
if (!quiesceIfIdle()) {
|
|
17461
|
-
if (this.#now()
|
|
17462
|
-
|
|
17463
|
-
|
|
17464
|
-
}
|
|
17465
|
-
return { kind: "waiting", request };
|
|
18330
|
+
if (this.#now() < request.drainDeadlineAt)
|
|
18331
|
+
return { kind: "waiting", request };
|
|
18332
|
+
forced = true;
|
|
17466
18333
|
}
|
|
17467
18334
|
if (this.#stagedReleaseId) {
|
|
17468
18335
|
const stagedReleaseId = this.#stagedReleaseId;
|
|
@@ -17482,7 +18349,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
17482
18349
|
this.#restartRequest = void 0;
|
|
17483
18350
|
await this.#journalDrift(stagedReleaseId);
|
|
17484
18351
|
}
|
|
17485
|
-
return { kind: "restart", request: this.#restartRequest ?? request };
|
|
18352
|
+
return { kind: "restart", request: this.#restartRequest ?? request, forced };
|
|
17486
18353
|
}
|
|
17487
18354
|
};
|
|
17488
18355
|
async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, waiting = async () => void 0) {
|
|
@@ -17493,9 +18360,88 @@ async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, wa
|
|
|
17493
18360
|
await waiting(next.request);
|
|
17494
18361
|
return "waiting-for-idle";
|
|
17495
18362
|
}
|
|
17496
|
-
await restart(next.request);
|
|
18363
|
+
await restart(next.request, next.forced ? "forced" : "drained");
|
|
17497
18364
|
return "restarting";
|
|
17498
18365
|
}
|
|
18366
|
+
var UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS = 6e4;
|
|
18367
|
+
var ManagedUpdateDrain = class {
|
|
18368
|
+
#options;
|
|
18369
|
+
#now;
|
|
18370
|
+
#log;
|
|
18371
|
+
#setTimer;
|
|
18372
|
+
#clearTimer;
|
|
18373
|
+
#deadlineTimer;
|
|
18374
|
+
#waitLogTimer;
|
|
18375
|
+
#resolving;
|
|
18376
|
+
#restarting = false;
|
|
18377
|
+
constructor(options) {
|
|
18378
|
+
this.#options = options;
|
|
18379
|
+
this.#now = options.now ?? Date.now;
|
|
18380
|
+
this.#log = options.log ?? ((line) => console.log(line));
|
|
18381
|
+
this.#setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
|
|
18382
|
+
this.#clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
18383
|
+
}
|
|
18384
|
+
/** Called from every completed core progress tick. */
|
|
18385
|
+
tick() {
|
|
18386
|
+
return this.#resolve();
|
|
18387
|
+
}
|
|
18388
|
+
#resolve() {
|
|
18389
|
+
if (this.#restarting)
|
|
18390
|
+
return Promise.resolve("restarting");
|
|
18391
|
+
if (this.#resolving)
|
|
18392
|
+
return this.#resolving;
|
|
18393
|
+
this.#resolving = this.#step().finally(() => {
|
|
18394
|
+
this.#resolving = void 0;
|
|
18395
|
+
});
|
|
18396
|
+
return this.#resolving;
|
|
18397
|
+
}
|
|
18398
|
+
async #step() {
|
|
18399
|
+
const { update, quiesceIfIdle, activeTurnCount } = this.#options;
|
|
18400
|
+
let armed;
|
|
18401
|
+
const progress = await coordinateManagedUpdateHandoff(update, quiesceIfIdle, async (request, mode) => {
|
|
18402
|
+
this.#disarm();
|
|
18403
|
+
if (mode === "forced") {
|
|
18404
|
+
this.#log(`[thin-core] update restart forced: drain deadline reached with ${activeTurnCount()} active turn(s); cancelling them and restarting onto ${request.desiredRelease}`);
|
|
18405
|
+
}
|
|
18406
|
+
await this.#options.restart(request, mode);
|
|
18407
|
+
this.#restarting = true;
|
|
18408
|
+
}, async (request) => {
|
|
18409
|
+
armed = request;
|
|
18410
|
+
await this.#options.waiting?.(request);
|
|
18411
|
+
});
|
|
18412
|
+
if (armed && !this.#deadlineTimer)
|
|
18413
|
+
this.#arm(armed);
|
|
18414
|
+
return progress;
|
|
18415
|
+
}
|
|
18416
|
+
#arm(request) {
|
|
18417
|
+
this.#logWaiting(request);
|
|
18418
|
+
this.#scheduleWaitLog(request);
|
|
18419
|
+
this.#deadlineTimer = this.#setTimer(() => void this.#resolve().catch((error) => {
|
|
18420
|
+
this.#log(`[thin-core] update restart at the drain deadline failed; retrying on the next tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
18421
|
+
}), Math.max(0, request.drainDeadlineAt - this.#now()));
|
|
18422
|
+
}
|
|
18423
|
+
#scheduleWaitLog(request) {
|
|
18424
|
+
this.#waitLogTimer = this.#setTimer(() => {
|
|
18425
|
+
this.#waitLogTimer = void 0;
|
|
18426
|
+
if (this.#restarting)
|
|
18427
|
+
return;
|
|
18428
|
+
this.#logWaiting(request);
|
|
18429
|
+
this.#scheduleWaitLog(request);
|
|
18430
|
+
}, UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS);
|
|
18431
|
+
}
|
|
18432
|
+
#logWaiting(request) {
|
|
18433
|
+
const minutesLeft = Math.max(0, Math.ceil((request.drainDeadlineAt - this.#now()) / 6e4));
|
|
18434
|
+
this.#log(`[thin-core] update restart waiting: ${this.#options.activeTurnCount()} active turn(s); deadline in ${minutesLeft}m`);
|
|
18435
|
+
}
|
|
18436
|
+
#disarm() {
|
|
18437
|
+
if (this.#deadlineTimer !== void 0)
|
|
18438
|
+
this.#clearTimer(this.#deadlineTimer);
|
|
18439
|
+
if (this.#waitLogTimer !== void 0)
|
|
18440
|
+
this.#clearTimer(this.#waitLogTimer);
|
|
18441
|
+
this.#deadlineTimer = void 0;
|
|
18442
|
+
this.#waitLogTimer = void 0;
|
|
18443
|
+
}
|
|
18444
|
+
};
|
|
17499
18445
|
function numberEnv(env, name, fallback) {
|
|
17500
18446
|
const value = Number(env[name]);
|
|
17501
18447
|
return Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
@@ -17614,7 +18560,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
17614
18560
|
});
|
|
17615
18561
|
if (!accepted)
|
|
17616
18562
|
return false;
|
|
17617
|
-
await
|
|
18563
|
+
await writeFile11(resolve21(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
17618
18564
|
readyAt: (options.now ?? Date.now)(),
|
|
17619
18565
|
loadedRelease,
|
|
17620
18566
|
functionalProof: options.functionalProof
|
|
@@ -17819,16 +18765,16 @@ async function runUpdateCommand(args) {
|
|
|
17819
18765
|
init_self_update();
|
|
17820
18766
|
|
|
17821
18767
|
// apps/body/dist/daemon-failure.js
|
|
17822
|
-
import { mkdir as
|
|
17823
|
-
import { dirname as
|
|
18768
|
+
import { mkdir as mkdir14, readFile as readFile11, rename as rename5, rm as rm6, writeFile as writeFile12 } from "node:fs/promises";
|
|
18769
|
+
import { dirname as dirname13, resolve as resolve22 } from "node:path";
|
|
17824
18770
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
17825
18771
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
17826
18772
|
function daemonFailurePath(runtimeDir) {
|
|
17827
|
-
return
|
|
18773
|
+
return resolve22(runtimeDir, "daemon-distress.json");
|
|
17828
18774
|
}
|
|
17829
18775
|
async function readFailureRecord(runtimeDir) {
|
|
17830
18776
|
try {
|
|
17831
|
-
const value = JSON.parse(await
|
|
18777
|
+
const value = JSON.parse(await readFile11(daemonFailurePath(runtimeDir), "utf8"));
|
|
17832
18778
|
if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure) => typeof failure !== "number") || typeof value.lastError !== "string") {
|
|
17833
18779
|
return void 0;
|
|
17834
18780
|
}
|
|
@@ -17840,8 +18786,8 @@ async function readFailureRecord(runtimeDir) {
|
|
|
17840
18786
|
async function writeFailureRecord(runtimeDir, record2) {
|
|
17841
18787
|
const path = daemonFailurePath(runtimeDir);
|
|
17842
18788
|
const staged = `${path}.${process.pid}.tmp`;
|
|
17843
|
-
await
|
|
17844
|
-
await
|
|
18789
|
+
await mkdir14(dirname13(path), { recursive: true, mode: 448 });
|
|
18790
|
+
await writeFile12(staged, `${JSON.stringify(record2, null, 2)}
|
|
17845
18791
|
`, { mode: 384 });
|
|
17846
18792
|
await rename5(staged, path);
|
|
17847
18793
|
}
|
|
@@ -17863,9 +18809,9 @@ async function clearDaemonStartFailures(runtimeDir) {
|
|
|
17863
18809
|
}
|
|
17864
18810
|
|
|
17865
18811
|
// apps/body/dist/update-functional-probe.js
|
|
17866
|
-
import { mkdir as
|
|
18812
|
+
import { mkdir as mkdir15, rm as rm7 } from "node:fs/promises";
|
|
17867
18813
|
import { homedir as homedir10 } from "node:os";
|
|
17868
|
-
import { resolve as
|
|
18814
|
+
import { resolve as resolve23 } from "node:path";
|
|
17869
18815
|
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
17870
18816
|
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
17871
18817
|
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
@@ -17887,11 +18833,11 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17887
18833
|
if (input.sandboxRequired && !input.config.bwrapPath) {
|
|
17888
18834
|
throw new UpdateFunctionalProbeError("sandbox-unavailable", "the configured bubblewrap boundary did not pass its startup self-test");
|
|
17889
18835
|
}
|
|
17890
|
-
const root =
|
|
17891
|
-
const cwd =
|
|
17892
|
-
const homeRoot =
|
|
18836
|
+
const root = resolve23(input.runtimeDir, "update-functional-probe");
|
|
18837
|
+
const cwd = resolve23(root, "checkout");
|
|
18838
|
+
const homeRoot = resolve23(root, "agent-home");
|
|
17893
18839
|
await rm7(root, { recursive: true, force: true });
|
|
17894
|
-
await
|
|
18840
|
+
await mkdir15(cwd, { recursive: true, mode: 448 });
|
|
17895
18841
|
let client;
|
|
17896
18842
|
try {
|
|
17897
18843
|
const agentEnv = {
|
|
@@ -17901,7 +18847,8 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17901
18847
|
operatorHome: input.config.operatorHome ?? homedir10(),
|
|
17902
18848
|
sharedSkills: input.config.sharedSkills ?? [],
|
|
17903
18849
|
skillReleaseId: input.releaseId,
|
|
17904
|
-
failClosed: true
|
|
18850
|
+
failClosed: true,
|
|
18851
|
+
...openRouterRoutingInput({ ...input.config, openRouterRoutingCacheDir: openRouterRoutingCacheDir(input.runtimeDir) }, input.config.modelSelection)
|
|
17905
18852
|
})
|
|
17906
18853
|
};
|
|
17907
18854
|
const selectedAgent = {
|
|
@@ -17913,11 +18860,12 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17913
18860
|
command,
|
|
17914
18861
|
args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
|
|
17915
18862
|
};
|
|
18863
|
+
let modelAnswer = {};
|
|
17916
18864
|
if (input.config.bwrapPath) {
|
|
17917
18865
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
17918
18866
|
const operatorHome = input.config.operatorHome ?? homedir10();
|
|
17919
18867
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
17920
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
18868
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir15(dir, { recursive: true })));
|
|
17921
18869
|
spawnCommand = wrapAgentCommand({
|
|
17922
18870
|
bwrapPath: input.config.bwrapPath,
|
|
17923
18871
|
spec: {
|
|
@@ -17956,8 +18904,21 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17956
18904
|
}
|
|
17957
18905
|
try {
|
|
17958
18906
|
const served = await client.sessionPrompt(opened.sessionId, "Reply READY.", input.turnTimeoutMs ?? UPDATE_PROBE_TURN_TIMEOUT_MS);
|
|
17959
|
-
if (
|
|
17960
|
-
|
|
18907
|
+
if (served.agentText.trim()) {
|
|
18908
|
+
modelAnswer = { modelAnswer: "served" };
|
|
18909
|
+
} else {
|
|
18910
|
+
const explained = await explainEmptyAgentTurn({
|
|
18911
|
+
agentLabel: command,
|
|
18912
|
+
agentEnv,
|
|
18913
|
+
sessionId: opened.sessionId,
|
|
18914
|
+
result: served
|
|
18915
|
+
});
|
|
18916
|
+
const modelSide = isAccountOrProviderRefusal(explained.record) || explained.record?.kind === "empty";
|
|
18917
|
+
if (!modelSide) {
|
|
18918
|
+
throw new UpdateFunctionalProbeError("turn-failed", `the harness completed a session/prompt without an agent answer: ${explained.reason}`);
|
|
18919
|
+
}
|
|
18920
|
+
console.warn(`[body] update probe: the bundle reached the model boundary but the model answered nothing (${explained.reason}); that is the model's own answer on any release, so the probe passes without one`);
|
|
18921
|
+
modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: explained.reason };
|
|
17961
18922
|
}
|
|
17962
18923
|
} catch (error) {
|
|
17963
18924
|
throw new UpdateFunctionalProbeError("turn-failed", error instanceof Error ? error.message : String(error), { cause: error });
|
|
@@ -17972,7 +18933,8 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17972
18933
|
sandboxed: Boolean(input.config.bwrapPath),
|
|
17973
18934
|
sessionStarted: true,
|
|
17974
18935
|
turnCompleted: true,
|
|
17975
|
-
nativeTools: []
|
|
18936
|
+
nativeTools: [],
|
|
18937
|
+
...modelAnswer
|
|
17976
18938
|
};
|
|
17977
18939
|
} finally {
|
|
17978
18940
|
await client?.stop().catch(() => void 0);
|
|
@@ -17981,8 +18943,8 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
17981
18943
|
}
|
|
17982
18944
|
|
|
17983
18945
|
// apps/body/dist/release-status.js
|
|
17984
|
-
import { readFile as
|
|
17985
|
-
import { resolve as
|
|
18946
|
+
import { readFile as readFile12, readdir as readdir4, rename as rename6, writeFile as writeFile13 } from "node:fs/promises";
|
|
18947
|
+
import { resolve as resolve24 } from "node:path";
|
|
17986
18948
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
17987
18949
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
17988
18950
|
var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
|
|
@@ -17999,9 +18961,9 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
17999
18961
|
pid: options.pid ?? process.pid,
|
|
18000
18962
|
readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
18001
18963
|
};
|
|
18002
|
-
const target =
|
|
18964
|
+
const target = resolve24(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
|
|
18003
18965
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
18004
|
-
await
|
|
18966
|
+
await writeFile13(temporary, `${JSON.stringify(status, null, 2)}
|
|
18005
18967
|
`, { mode: 384 });
|
|
18006
18968
|
await rename6(temporary, target);
|
|
18007
18969
|
return status;
|
|
@@ -18042,7 +19004,7 @@ var DaemonExitError = class extends Error {
|
|
|
18042
19004
|
};
|
|
18043
19005
|
async function runStoredDaemon(pathOrPointer) {
|
|
18044
19006
|
const configPath = await resolveRuntimeConfigPath(pathOrPointer);
|
|
18045
|
-
daemonFailureRuntimeDir =
|
|
19007
|
+
daemonFailureRuntimeDir = dirname14(configPath);
|
|
18046
19008
|
const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
|
|
18047
19009
|
let runtime = accessMigration.runtime;
|
|
18048
19010
|
if (!runtime.transport) {
|
|
@@ -18054,7 +19016,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18054
19016
|
runtime = activated.runtime;
|
|
18055
19017
|
const daemonApi = activated.client;
|
|
18056
19018
|
const agent = runtimeAgentCommand(runtime);
|
|
18057
|
-
await
|
|
19019
|
+
await writeFile14(resolve25(dirname14(configPath), "daemon.pid"), `${process.pid}
|
|
18058
19020
|
`, { mode: 384 });
|
|
18059
19021
|
const env = {
|
|
18060
19022
|
...process.env,
|
|
@@ -18062,7 +19024,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18062
19024
|
BUZZ_DEV_MCP_BIN: runtime.mcpBinary
|
|
18063
19025
|
};
|
|
18064
19026
|
const config = loadBodyConfig({
|
|
18065
|
-
workspaceRoot:
|
|
19027
|
+
workspaceRoot: resolve25(dirname14(configPath), "workspace"),
|
|
18066
19028
|
llmEnvFile: runtime.llmEnvFile,
|
|
18067
19029
|
env,
|
|
18068
19030
|
agent
|
|
@@ -18097,7 +19059,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18097
19059
|
const stop = () => controller.abort();
|
|
18098
19060
|
process.once("SIGINT", stop);
|
|
18099
19061
|
process.once("SIGTERM", stop);
|
|
18100
|
-
const runtimeDir =
|
|
19062
|
+
const runtimeDir = dirname14(configPath);
|
|
18101
19063
|
const layout = beelineInstallLayout(process.env);
|
|
18102
19064
|
const notifier = new SystemdNotifier();
|
|
18103
19065
|
let rollbackAlertDrain;
|
|
@@ -18143,6 +19105,22 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18143
19105
|
let stoppingStatus = "daemon stopped";
|
|
18144
19106
|
try {
|
|
18145
19107
|
const core = new ThinDaemonCore(runtime, configPath, config, { daemonApi });
|
|
19108
|
+
const updateDrain = update ? new ManagedUpdateDrain({
|
|
19109
|
+
update,
|
|
19110
|
+
quiesceIfIdle: () => core.quiesceForUpdateIfIdle(),
|
|
19111
|
+
activeTurnCount: () => core.activeTurnCount(),
|
|
19112
|
+
restart: async ({ desiredRelease, drainDeadlineAt }, mode) => {
|
|
19113
|
+
if (mode === "forced")
|
|
19114
|
+
await core.prepareForForcedUpdateRestart();
|
|
19115
|
+
core.setDrainDeadlineAt(drainDeadlineAt);
|
|
19116
|
+
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; ${mode === "forced" ? "active work cancelled at the drain deadline" : "active work drained"}; intake quiesced; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`;
|
|
19117
|
+
await notifier.stopping(stoppingStatus);
|
|
19118
|
+
controller.abort();
|
|
19119
|
+
},
|
|
19120
|
+
waiting: async ({ desiredRelease, drainDeadlineAt }) => {
|
|
19121
|
+
await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`);
|
|
19122
|
+
}
|
|
19123
|
+
}) : void 0;
|
|
18146
19124
|
const result = await core.run({
|
|
18147
19125
|
signal: controller.signal,
|
|
18148
19126
|
onEstablished: async () => {
|
|
@@ -18166,28 +19144,26 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18166
19144
|
}
|
|
18167
19145
|
functionalProof = gate.proof;
|
|
18168
19146
|
pendingSuccessor = false;
|
|
18169
|
-
console.log(`[thin-core] successor functional probe passed on exact release ${loadedRelease}: ${functionalProof?.harness ?? "unknown"} session/new + turn`);
|
|
19147
|
+
console.log(`[thin-core] successor functional probe passed on exact release ${loadedRelease}: ${functionalProof?.harness ?? "unknown"} session/new + turn` + (functionalProof?.modelAnswer === "unavailable" ? ` (model answer unavailable: ${functionalProof.modelAnswerReason})` : ""));
|
|
18170
19148
|
}
|
|
18171
19149
|
await clearDaemonStartFailures(runtimeDir);
|
|
18172
19150
|
await writeDaemonReleaseStatus(runtimeDir, runtime.agent.publicKey, loadedReleaseIdentity);
|
|
18173
19151
|
await notifier.ready(`ready; loaded_release=${loadedRelease ?? "development"}`);
|
|
18174
19152
|
ready = true;
|
|
19153
|
+
void syncAgentModelCatalog({
|
|
19154
|
+
api: daemonApi,
|
|
19155
|
+
agent,
|
|
19156
|
+
agentEnv: config.agentEnv,
|
|
19157
|
+
agentId: runtime.agent.publicKey,
|
|
19158
|
+
workspaceId: runtime.communityId,
|
|
19159
|
+
runtimeDir,
|
|
19160
|
+
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {}
|
|
19161
|
+
});
|
|
18175
19162
|
},
|
|
18176
19163
|
onProgress: async (status) => {
|
|
18177
19164
|
void drainRollbackAlert(core.activeRoomIds()[0] ?? runtime.rooms[0]?.channelId);
|
|
18178
19165
|
await notifier.progress(`loaded_release=${loadedRelease ?? "development"}; ${status}`);
|
|
18179
|
-
|
|
18180
|
-
return;
|
|
18181
|
-
await coordinateManagedUpdateHandoff(update, () => core.quiesceForUpdateIfIdle(), async ({ desiredRelease, drainDeadlineAt }) => {
|
|
18182
|
-
if (Date.now() >= drainDeadlineAt)
|
|
18183
|
-
await core.prepareForForcedUpdateRestart();
|
|
18184
|
-
core.setDrainDeadlineAt(drainDeadlineAt);
|
|
18185
|
-
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; active work drained; intake quiesced; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`;
|
|
18186
|
-
await notifier.stopping(stoppingStatus);
|
|
18187
|
-
controller.abort();
|
|
18188
|
-
}, async ({ desiredRelease, drainDeadlineAt }) => {
|
|
18189
|
-
await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}; exit_deadline=${new Date(drainDeadlineAt).toISOString()}`);
|
|
18190
|
-
});
|
|
19166
|
+
await updateDrain?.tick();
|
|
18191
19167
|
}
|
|
18192
19168
|
});
|
|
18193
19169
|
if (result === "agent-removed") {
|
|
@@ -18209,8 +19185,8 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
18209
19185
|
throw error;
|
|
18210
19186
|
} finally {
|
|
18211
19187
|
await notifier.stopping(stoppingStatus).catch(() => void 0);
|
|
18212
|
-
const pidPath =
|
|
18213
|
-
const recorded = Number((await
|
|
19188
|
+
const pidPath = resolve25(dirname14(configPath), "daemon.pid");
|
|
19189
|
+
const recorded = Number((await readFile13(pidPath, "utf8").catch(() => "")).trim());
|
|
18214
19190
|
if (recorded === process.pid) {
|
|
18215
19191
|
await unlink3(pidPath).catch(() => void 0);
|
|
18216
19192
|
}
|
|
@@ -18266,14 +19242,14 @@ async function main() {
|
|
|
18266
19242
|
const agentPubkey = agentFlag >= 0 ? args[agentFlag + 1] : void 0;
|
|
18267
19243
|
if (!configPath && agentPubkey) {
|
|
18268
19244
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
18269
|
-
configPath = configs.find((candidate) =>
|
|
19245
|
+
configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
|
|
18270
19246
|
}
|
|
18271
19247
|
if (!configPath && agentPubkey) {
|
|
18272
19248
|
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
|
|
18273
19249
|
}
|
|
18274
19250
|
if (!configPath)
|
|
18275
19251
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
18276
|
-
await runStoredDaemon(
|
|
19252
|
+
await runStoredDaemon(resolve25(configPath));
|
|
18277
19253
|
return;
|
|
18278
19254
|
}
|
|
18279
19255
|
if (command === "update") {
|
|
@@ -18290,7 +19266,7 @@ async function main() {
|
|
|
18290
19266
|
if (!agentPubkey)
|
|
18291
19267
|
throw new Error("stop requires --agent <pubkey>");
|
|
18292
19268
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
18293
|
-
const configPath = configs.find((candidate) =>
|
|
19269
|
+
const configPath = configs.find((candidate) => dirname14(candidate).endsWith(agentPubkey));
|
|
18294
19270
|
if (!configPath)
|
|
18295
19271
|
throw new Error(`no stored runtime found for agent ${agentPubkey}`);
|
|
18296
19272
|
const runtime = await readRuntimeRecord(configPath);
|