usebeeline 0.0.91 → 0.0.94
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/README.md +5 -2
- package/dist/usebeeline.mjs +938 -280
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -2413,9 +2413,9 @@ var require_websocket = __commonJS({
|
|
|
2413
2413
|
var EventEmitter2 = __require("events");
|
|
2414
2414
|
var https = __require("https");
|
|
2415
2415
|
var http = __require("http");
|
|
2416
|
-
var
|
|
2416
|
+
var net2 = __require("net");
|
|
2417
2417
|
var tls = __require("tls");
|
|
2418
|
-
var { randomBytes: randomBytes7, createHash:
|
|
2418
|
+
var { randomBytes: randomBytes7, createHash: createHash9 } = __require("crypto");
|
|
2419
2419
|
var { Duplex, Readable } = __require("stream");
|
|
2420
2420
|
var { URL: URL2 } = __require("url");
|
|
2421
2421
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -3083,7 +3083,7 @@ var require_websocket = __commonJS({
|
|
|
3083
3083
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
3084
3084
|
return;
|
|
3085
3085
|
}
|
|
3086
|
-
const digest =
|
|
3086
|
+
const digest = createHash9("sha1").update(key + GUID).digest("base64");
|
|
3087
3087
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
3088
3088
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
3089
3089
|
return;
|
|
@@ -3157,12 +3157,12 @@ var require_websocket = __commonJS({
|
|
|
3157
3157
|
}
|
|
3158
3158
|
function netConnect(options) {
|
|
3159
3159
|
options.path = options.socketPath;
|
|
3160
|
-
return
|
|
3160
|
+
return net2.connect(options);
|
|
3161
3161
|
}
|
|
3162
3162
|
function tlsConnect(options) {
|
|
3163
3163
|
options.path = void 0;
|
|
3164
3164
|
if (!options.servername && options.servername !== "") {
|
|
3165
|
-
options.servername =
|
|
3165
|
+
options.servername = net2.isIP(options.host) ? "" : options.host;
|
|
3166
3166
|
}
|
|
3167
3167
|
return tls.connect(options);
|
|
3168
3168
|
}
|
|
@@ -3452,7 +3452,7 @@ var require_websocket_server = __commonJS({
|
|
|
3452
3452
|
var EventEmitter2 = __require("events");
|
|
3453
3453
|
var http = __require("http");
|
|
3454
3454
|
var { Duplex } = __require("stream");
|
|
3455
|
-
var { createHash:
|
|
3455
|
+
var { createHash: createHash9 } = __require("crypto");
|
|
3456
3456
|
var extension2 = require_extension();
|
|
3457
3457
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
3458
3458
|
var subprotocol2 = require_subprotocol();
|
|
@@ -3759,7 +3759,7 @@ var require_websocket_server = __commonJS({
|
|
|
3759
3759
|
);
|
|
3760
3760
|
}
|
|
3761
3761
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
3762
|
-
const digest =
|
|
3762
|
+
const digest = createHash9("sha1").update(key + GUID).digest("base64");
|
|
3763
3763
|
const headers = [
|
|
3764
3764
|
"HTTP/1.1 101 Switching Protocols",
|
|
3765
3765
|
"Upgrade: websocket",
|
|
@@ -3984,14 +3984,14 @@ __export(self_update_exports, {
|
|
|
3984
3984
|
writeUpdateAttemptFixture: () => writeUpdateAttemptFixture,
|
|
3985
3985
|
writeUpdateState: () => writeUpdateState
|
|
3986
3986
|
});
|
|
3987
|
-
import { createHash as
|
|
3988
|
-
import { constants as
|
|
3989
|
-
import { access, chmod as
|
|
3987
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
3988
|
+
import { constants as fsConstants2 } from "node:fs";
|
|
3989
|
+
import { access, chmod as chmod5, lstat as lstat3, mkdir as mkdir15, open, readFile as readFile10, rename as rename4, rm as rm6, symlink as symlink3, writeFile as writeFile10 } from "node:fs/promises";
|
|
3990
3990
|
import { spawn as spawn4 } from "node:child_process";
|
|
3991
3991
|
import { homedir as homedir9 } from "node:os";
|
|
3992
|
-
import { dirname as
|
|
3992
|
+
import { dirname as dirname11, join as join9, resolve as resolve22 } from "node:path";
|
|
3993
3993
|
function anchorLayout(rawLibDir) {
|
|
3994
|
-
const libDir =
|
|
3994
|
+
const libDir = resolve22(rawLibDir);
|
|
3995
3995
|
const segments = libDir.split(/[/\\]/);
|
|
3996
3996
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
3997
3997
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -4007,9 +4007,9 @@ function anchorLayout(rawLibDir) {
|
|
|
4007
4007
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
4008
4008
|
// defect that made activateRelease write its stable forwarders where
|
|
4009
4009
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
4010
|
-
binDir:
|
|
4010
|
+
binDir: resolve22(libDir, "../../bin"),
|
|
4011
4011
|
libDir,
|
|
4012
|
-
releasesRoot:
|
|
4012
|
+
releasesRoot: resolve22(libDir, `../${RELEASES_SEGMENT}`)
|
|
4013
4013
|
};
|
|
4014
4014
|
}
|
|
4015
4015
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -4020,7 +4020,7 @@ function beelineInstallLayout(env = process.env) {
|
|
|
4020
4020
|
}
|
|
4021
4021
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
4022
4022
|
const home = env.HOME?.trim() || homedir9();
|
|
4023
|
-
return anchorLayout(
|
|
4023
|
+
return anchorLayout(resolve22(home, ".local", "lib", "beeline"));
|
|
4024
4024
|
}
|
|
4025
4025
|
function discoveredBeelineInstallLayout(env = process.env) {
|
|
4026
4026
|
const explicitAnchor = env.BEELINE_INSTALL_LIB_DIR?.trim();
|
|
@@ -4028,7 +4028,7 @@ function discoveredBeelineInstallLayout(env = process.env) {
|
|
|
4028
4028
|
return anchorLayout(explicitAnchor);
|
|
4029
4029
|
const explicitBinDir = env.BEELINE_INSTALL_DIR?.trim();
|
|
4030
4030
|
if (explicitBinDir)
|
|
4031
|
-
return anchorLayout(
|
|
4031
|
+
return anchorLayout(resolve22(dirname11(resolve22(explicitBinDir)), "lib", "beeline"));
|
|
4032
4032
|
return defaultBeelineInstallLayout(env);
|
|
4033
4033
|
}
|
|
4034
4034
|
function hostPlatformKey() {
|
|
@@ -4039,13 +4039,13 @@ function hostPlatformKey() {
|
|
|
4039
4039
|
return `${os}-${arch}`;
|
|
4040
4040
|
}
|
|
4041
4041
|
function bundleJsonCandidates(bundleDir) {
|
|
4042
|
-
return [
|
|
4042
|
+
return [join9(bundleDir, "lib", "beeline", "bundle.json"), join9(bundleDir, "bundle.json")];
|
|
4043
4043
|
}
|
|
4044
4044
|
async function readBundleJson(bundleDir) {
|
|
4045
4045
|
let raw;
|
|
4046
4046
|
for (const candidate of bundleJsonCandidates(bundleDir)) {
|
|
4047
4047
|
try {
|
|
4048
|
-
raw = await
|
|
4048
|
+
raw = await readFile10(candidate, "utf8");
|
|
4049
4049
|
break;
|
|
4050
4050
|
} catch {
|
|
4051
4051
|
}
|
|
@@ -4063,17 +4063,17 @@ async function readBundleJson(bundleDir) {
|
|
|
4063
4063
|
}
|
|
4064
4064
|
}
|
|
4065
4065
|
function updateStatePath(layout) {
|
|
4066
|
-
return
|
|
4066
|
+
return join9(layout.releasesRoot, ".state", "update-state.json");
|
|
4067
4067
|
}
|
|
4068
4068
|
async function readUpdateState(layout) {
|
|
4069
4069
|
try {
|
|
4070
|
-
return JSON.parse(await
|
|
4070
|
+
return JSON.parse(await readFile10(updateStatePath(layout), "utf8"));
|
|
4071
4071
|
} catch {
|
|
4072
4072
|
return {};
|
|
4073
4073
|
}
|
|
4074
4074
|
}
|
|
4075
4075
|
async function writeUpdateState(layout, state) {
|
|
4076
|
-
await
|
|
4076
|
+
await mkdir15(join9(layout.releasesRoot, ".state"), { recursive: true });
|
|
4077
4077
|
await writeFile10(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
|
|
4078
4078
|
`, "utf8");
|
|
4079
4079
|
}
|
|
@@ -4089,7 +4089,7 @@ function sanitizeReleaseId(id) {
|
|
|
4089
4089
|
}
|
|
4090
4090
|
async function pathKind(path) {
|
|
4091
4091
|
try {
|
|
4092
|
-
const info = await
|
|
4092
|
+
const info = await lstat3(path);
|
|
4093
4093
|
if (info.isSymbolicLink())
|
|
4094
4094
|
return "symlink";
|
|
4095
4095
|
return info.isDirectory() ? "directory" : "missing";
|
|
@@ -4103,9 +4103,9 @@ async function activeReleaseId(layout) {
|
|
|
4103
4103
|
return "legacy";
|
|
4104
4104
|
if (kind === "missing")
|
|
4105
4105
|
return void 0;
|
|
4106
|
-
const { readlink } = await import("node:fs/promises");
|
|
4106
|
+
const { readlink: readlink2 } = await import("node:fs/promises");
|
|
4107
4107
|
try {
|
|
4108
|
-
const target = await
|
|
4108
|
+
const target = await readlink2(layout.libDir);
|
|
4109
4109
|
return sanitizeReleaseId(target.split("/").pop() ?? target);
|
|
4110
4110
|
} catch {
|
|
4111
4111
|
return void 0;
|
|
@@ -4155,12 +4155,12 @@ function run(command, args, timeoutMs) {
|
|
|
4155
4155
|
});
|
|
4156
4156
|
}
|
|
4157
4157
|
function entrypointCandidates(bundleDir) {
|
|
4158
|
-
return [
|
|
4158
|
+
return [join9(bundleDir, BUNDLE_ENTRYPOINT), join9(bundleDir, "beeline-cli.mjs")];
|
|
4159
4159
|
}
|
|
4160
4160
|
async function resolveBundleEntrypoint(bundleDir) {
|
|
4161
4161
|
for (const candidate of entrypointCandidates(bundleDir)) {
|
|
4162
4162
|
try {
|
|
4163
|
-
await access(candidate,
|
|
4163
|
+
await access(candidate, fsConstants2.F_OK);
|
|
4164
4164
|
return candidate;
|
|
4165
4165
|
} catch {
|
|
4166
4166
|
}
|
|
@@ -4177,18 +4177,18 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4177
4177
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
4178
4178
|
const log2 = opts.logger ?? ((line) => console.log(`[body] self-update: ${line}`));
|
|
4179
4179
|
const releaseId = sanitizeReleaseId(published.commit ?? published.version ?? `release-${Date.now()}`);
|
|
4180
|
-
const releaseDir =
|
|
4181
|
-
const okMarker =
|
|
4180
|
+
const releaseDir = join9(layout.releasesRoot, releaseId);
|
|
4181
|
+
const okMarker = join9(releaseDir, ".stage-ok");
|
|
4182
4182
|
let previouslyVerified = false;
|
|
4183
4183
|
try {
|
|
4184
|
-
const recorded = await
|
|
4184
|
+
const recorded = await readFile10(okMarker, "utf8");
|
|
4185
4185
|
if (recorded.trim() === published.sha256)
|
|
4186
4186
|
return releaseId;
|
|
4187
4187
|
previouslyVerified = true;
|
|
4188
4188
|
} catch {
|
|
4189
4189
|
}
|
|
4190
|
-
await
|
|
4191
|
-
const tempArchive =
|
|
4190
|
+
await mkdir15(releaseDir, { recursive: true });
|
|
4191
|
+
const tempArchive = join9(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
|
|
4192
4192
|
try {
|
|
4193
4193
|
log2(`downloading ${published.file}`);
|
|
4194
4194
|
const response = await fetchImpl(archiveUrlFor(manifestUrl, published.file), {
|
|
@@ -4197,7 +4197,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4197
4197
|
if (!response.ok || !response.body) {
|
|
4198
4198
|
throw new Error(`downloading ${published.file} failed: HTTP ${response.status}`);
|
|
4199
4199
|
}
|
|
4200
|
-
const hash =
|
|
4200
|
+
const hash = createHash7("sha256");
|
|
4201
4201
|
const chunks = [];
|
|
4202
4202
|
for await (const chunk of response.body) {
|
|
4203
4203
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
@@ -4228,13 +4228,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4228
4228
|
throw new Error(`extracting bundle failed: ${extract2.stderr}`);
|
|
4229
4229
|
for (const relative3 of requiredBundlePaths()) {
|
|
4230
4230
|
try {
|
|
4231
|
-
await access(
|
|
4231
|
+
await access(join9(releaseDir, relative3), fsConstants2.F_OK);
|
|
4232
4232
|
} catch {
|
|
4233
4233
|
throw new Error(`staged bundle is missing ${relative3}`);
|
|
4234
4234
|
}
|
|
4235
4235
|
}
|
|
4236
4236
|
if (opts.smokeTestCli !== false) {
|
|
4237
|
-
const probe = await run(process.execPath, [
|
|
4237
|
+
const probe = await run(process.execPath, [join9(releaseDir, BUNDLE_ENTRYPOINT), "--version"], 6e4);
|
|
4238
4238
|
if (probe.status !== 0) {
|
|
4239
4239
|
throw new Error(`staged bundle failed its startup smoke test (--version exited ${probe.status})${probe.stderr ? `: ${probe.stderr.trim()}` : ""}`);
|
|
4240
4240
|
}
|
|
@@ -4245,10 +4245,10 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4245
4245
|
return releaseId;
|
|
4246
4246
|
} catch (error) {
|
|
4247
4247
|
if (!previouslyVerified)
|
|
4248
|
-
await
|
|
4248
|
+
await rm6(releaseDir, { recursive: true, force: true });
|
|
4249
4249
|
throw error;
|
|
4250
4250
|
} finally {
|
|
4251
|
-
await
|
|
4251
|
+
await rm6(tempArchive, { force: true });
|
|
4252
4252
|
}
|
|
4253
4253
|
}
|
|
4254
4254
|
function forwarderScript(tool) {
|
|
@@ -4268,45 +4268,45 @@ function forwarderScript(tool) {
|
|
|
4268
4268
|
async function replaceFile(path, contents, mode) {
|
|
4269
4269
|
const temp = `${path}.new-${process.pid}`;
|
|
4270
4270
|
await writeFile10(temp, contents, { mode });
|
|
4271
|
-
await
|
|
4272
|
-
await
|
|
4271
|
+
await chmod5(temp, mode);
|
|
4272
|
+
await rename4(temp, path);
|
|
4273
4273
|
}
|
|
4274
4274
|
async function activateRelease(layout, releaseId) {
|
|
4275
|
-
const releaseDir =
|
|
4276
|
-
await access(
|
|
4277
|
-
await
|
|
4278
|
-
await
|
|
4275
|
+
const releaseDir = join9(layout.releasesRoot, releaseId);
|
|
4276
|
+
await access(join9(releaseDir, BUNDLE_ENTRYPOINT), fsConstants2.F_OK);
|
|
4277
|
+
await mkdir15(layout.releasesRoot, { recursive: true });
|
|
4278
|
+
await mkdir15(layout.binDir, { recursive: true });
|
|
4279
4279
|
let previousReleaseId = await activeReleaseId(layout);
|
|
4280
4280
|
const kind = await pathKind(layout.libDir);
|
|
4281
4281
|
if (kind === "directory") {
|
|
4282
4282
|
const legacyIdentity = await readBundleJson(layout.libDir);
|
|
4283
4283
|
const legacyId = sanitizeReleaseId(legacyIdentity?.commit ?? legacyIdentity?.version ?? `legacy-${Date.now()}`);
|
|
4284
|
-
const legacyDir =
|
|
4284
|
+
const legacyDir = join9(layout.releasesRoot, legacyId);
|
|
4285
4285
|
try {
|
|
4286
|
-
await access(legacyDir,
|
|
4286
|
+
await access(legacyDir, fsConstants2.F_OK);
|
|
4287
4287
|
previousReleaseId = `${legacyId}-${Date.now()}`;
|
|
4288
|
-
await
|
|
4289
|
-
await normalizeLegacyBundleShape(
|
|
4288
|
+
await rename4(layout.libDir, join9(layout.releasesRoot, previousReleaseId));
|
|
4289
|
+
await normalizeLegacyBundleShape(join9(layout.releasesRoot, previousReleaseId));
|
|
4290
4290
|
} catch {
|
|
4291
|
-
await
|
|
4291
|
+
await rename4(layout.libDir, legacyDir);
|
|
4292
4292
|
await normalizeLegacyBundleShape(legacyDir);
|
|
4293
4293
|
previousReleaseId = legacyId;
|
|
4294
4294
|
}
|
|
4295
4295
|
}
|
|
4296
4296
|
const tempLink = `${layout.libDir}.new-${process.pid}`;
|
|
4297
|
-
await
|
|
4298
|
-
await
|
|
4299
|
-
await
|
|
4300
|
-
await fsyncDir(
|
|
4297
|
+
await rm6(tempLink, { force: true });
|
|
4298
|
+
await symlink3(join9("beeline-releases", releaseId), tempLink);
|
|
4299
|
+
await rename4(tempLink, layout.libDir);
|
|
4300
|
+
await fsyncDir(dirname11(layout.libDir));
|
|
4301
4301
|
await writeBinForwarders(layout, releaseDir);
|
|
4302
4302
|
return { previousReleaseId };
|
|
4303
4303
|
}
|
|
4304
4304
|
async function normalizeLegacyBundleShape(bundleDir) {
|
|
4305
|
-
const innerLib =
|
|
4305
|
+
const innerLib = join9(bundleDir, "lib", "beeline");
|
|
4306
4306
|
let anyFlat = false;
|
|
4307
4307
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
4308
4308
|
try {
|
|
4309
|
-
await access(
|
|
4309
|
+
await access(join9(bundleDir, name), fsConstants2.F_OK);
|
|
4310
4310
|
anyFlat = true;
|
|
4311
4311
|
break;
|
|
4312
4312
|
} catch {
|
|
@@ -4314,14 +4314,14 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
4314
4314
|
}
|
|
4315
4315
|
if (!anyFlat)
|
|
4316
4316
|
return;
|
|
4317
|
-
await
|
|
4317
|
+
await mkdir15(innerLib, { recursive: true });
|
|
4318
4318
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
4319
4319
|
try {
|
|
4320
|
-
await access(
|
|
4320
|
+
await access(join9(innerLib, name), fsConstants2.F_OK);
|
|
4321
4321
|
continue;
|
|
4322
4322
|
} catch {
|
|
4323
4323
|
}
|
|
4324
|
-
await
|
|
4324
|
+
await rename4(join9(bundleDir, name), join9(innerLib, name)).catch(() => void 0);
|
|
4325
4325
|
}
|
|
4326
4326
|
}
|
|
4327
4327
|
async function writeBinForwarders(layout, activeBundleRoot) {
|
|
@@ -4330,13 +4330,13 @@ async function writeBinForwarders(layout, activeBundleRoot) {
|
|
|
4330
4330
|
...Object.entries(FORWARDER_ALIASES).map(([alias, tool]) => [alias, tool])
|
|
4331
4331
|
];
|
|
4332
4332
|
for (const [name, tool] of entries) {
|
|
4333
|
-
const target =
|
|
4333
|
+
const target = join9(activeBundleRoot, "bin", tool);
|
|
4334
4334
|
try {
|
|
4335
|
-
await access(target,
|
|
4335
|
+
await access(target, fsConstants2.X_OK);
|
|
4336
4336
|
} catch {
|
|
4337
4337
|
continue;
|
|
4338
4338
|
}
|
|
4339
|
-
await replaceFile(
|
|
4339
|
+
await replaceFile(join9(layout.binDir, name), forwarderScript(tool), 493);
|
|
4340
4340
|
}
|
|
4341
4341
|
}
|
|
4342
4342
|
async function repairInstallForwarders(layout, opts = {}) {
|
|
@@ -4345,7 +4345,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
4345
4345
|
const forwarderHealthy = async (name, tool) => {
|
|
4346
4346
|
let current;
|
|
4347
4347
|
try {
|
|
4348
|
-
current = await
|
|
4348
|
+
current = await readFile10(join9(layout.binDir, name), "utf8");
|
|
4349
4349
|
} catch {
|
|
4350
4350
|
current = void 0;
|
|
4351
4351
|
}
|
|
@@ -4358,29 +4358,29 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
4358
4358
|
}
|
|
4359
4359
|
if (healthy)
|
|
4360
4360
|
return false;
|
|
4361
|
-
await
|
|
4361
|
+
await mkdir15(layout.binDir, { recursive: true });
|
|
4362
4362
|
await writeBinForwarders(layout, layout.libDir);
|
|
4363
4363
|
opts.logger?.(`[body] self-update: repaired <prefix>/bin forwarders to follow the active-bundle anchor (${layout.libDir})`);
|
|
4364
4364
|
return true;
|
|
4365
4365
|
}
|
|
4366
4366
|
async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
4367
|
-
const releaseDir =
|
|
4367
|
+
const releaseDir = join9(layout.releasesRoot, previousReleaseId);
|
|
4368
4368
|
const entrypoint = await resolveBundleEntrypoint(releaseDir);
|
|
4369
4369
|
if (!entrypoint) {
|
|
4370
4370
|
throw new Error(`release ${previousReleaseId} has no runnable CLI entrypoint`);
|
|
4371
4371
|
}
|
|
4372
4372
|
const tempLink = `${layout.libDir}.rollback-${process.pid}`;
|
|
4373
|
-
await
|
|
4374
|
-
await
|
|
4375
|
-
await
|
|
4376
|
-
await fsyncDir(
|
|
4373
|
+
await rm6(tempLink, { force: true });
|
|
4374
|
+
await symlink3(join9("beeline-releases", previousReleaseId), tempLink);
|
|
4375
|
+
await rename4(tempLink, layout.libDir);
|
|
4376
|
+
await fsyncDir(dirname11(layout.libDir));
|
|
4377
4377
|
}
|
|
4378
4378
|
function updateAttemptPath(layout) {
|
|
4379
|
-
return
|
|
4379
|
+
return join9(layout.releasesRoot, ".state", "update-attempt.json");
|
|
4380
4380
|
}
|
|
4381
4381
|
async function readUpdateAttempt(layout) {
|
|
4382
4382
|
try {
|
|
4383
|
-
const raw = JSON.parse(await
|
|
4383
|
+
const raw = JSON.parse(await readFile10(updateAttemptPath(layout), "utf8"));
|
|
4384
4384
|
if (raw.version !== 1 || typeof raw.appliedAt !== "number" || typeof raw.confirmBy !== "number" || typeof raw.releaseId !== "string" || !["pending", "confirmed", "reverted"].includes(raw.status)) {
|
|
4385
4385
|
return void 0;
|
|
4386
4386
|
}
|
|
@@ -4390,12 +4390,12 @@ async function readUpdateAttempt(layout) {
|
|
|
4390
4390
|
}
|
|
4391
4391
|
}
|
|
4392
4392
|
async function writeUpdateAttempt(layout, record2) {
|
|
4393
|
-
await
|
|
4393
|
+
await mkdir15(join9(layout.releasesRoot, ".state"), { recursive: true });
|
|
4394
4394
|
const path = updateAttemptPath(layout);
|
|
4395
4395
|
const staged = `${path}.${process.pid}.tmp`;
|
|
4396
4396
|
await writeFile10(staged, `${JSON.stringify(record2, null, 2)}
|
|
4397
4397
|
`, { mode: 384 });
|
|
4398
|
-
await
|
|
4398
|
+
await rename4(staged, path);
|
|
4399
4399
|
}
|
|
4400
4400
|
async function replaceUpdateAttempt(layout, record2) {
|
|
4401
4401
|
await writeUpdateAttempt(layout, record2);
|
|
@@ -4654,9 +4654,22 @@ var init_self_update = __esm({
|
|
|
4654
4654
|
}
|
|
4655
4655
|
});
|
|
4656
4656
|
|
|
4657
|
+
// apps/body/dist/network-family-bootstrap.js
|
|
4658
|
+
import * as net from "node:net";
|
|
4659
|
+
var NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS = 5e3;
|
|
4660
|
+
function configureNetworkFamilyDefaults(network = net) {
|
|
4661
|
+
if (typeof network.setDefaultAutoSelectFamilyAttemptTimeout === "function") {
|
|
4662
|
+
network.setDefaultAutoSelectFamilyAttemptTimeout(NETWORK_FAMILY_ATTEMPT_TIMEOUT_MS);
|
|
4663
|
+
return "timeout";
|
|
4664
|
+
}
|
|
4665
|
+
network.setDefaultAutoSelectFamily?.(false);
|
|
4666
|
+
return "autoselection-disabled";
|
|
4667
|
+
}
|
|
4668
|
+
configureNetworkFamilyDefaults();
|
|
4669
|
+
|
|
4657
4670
|
// apps/body/dist/cli.js
|
|
4658
|
-
import { dirname as
|
|
4659
|
-
import { readFile as
|
|
4671
|
+
import { dirname as dirname17, resolve as resolve30 } from "node:path";
|
|
4672
|
+
import { readFile as readFile15, unlink as unlink5, writeFile as writeFile16 } from "node:fs/promises";
|
|
4660
4673
|
import { stdin as stdin4, stdout as stdout5 } from "node:process";
|
|
4661
4674
|
|
|
4662
4675
|
// node_modules/@clack/core/dist/index.mjs
|
|
@@ -7527,7 +7540,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7527
7540
|
const current = this.activeRunIds.get(sessionId);
|
|
7528
7541
|
if (current)
|
|
7529
7542
|
return Promise.resolve(current);
|
|
7530
|
-
return new Promise((
|
|
7543
|
+
return new Promise((resolve31, reject) => {
|
|
7531
7544
|
const onUpdate = (update) => {
|
|
7532
7545
|
if (update.sessionId !== sessionId)
|
|
7533
7546
|
return;
|
|
@@ -7535,7 +7548,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
7535
7548
|
if (!runId)
|
|
7536
7549
|
return;
|
|
7537
7550
|
cleanup();
|
|
7538
|
-
|
|
7551
|
+
resolve31(runId);
|
|
7539
7552
|
};
|
|
7540
7553
|
const timer = setTimeout(() => {
|
|
7541
7554
|
cleanup();
|
|
@@ -7619,13 +7632,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
7619
7632
|
}
|
|
7620
7633
|
const id = this.nextId++;
|
|
7621
7634
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
7622
|
-
return new Promise((
|
|
7635
|
+
return new Promise((resolve31, reject) => {
|
|
7623
7636
|
const timer = setTimeout(() => {
|
|
7624
7637
|
this.pending.delete(id);
|
|
7625
7638
|
reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
|
|
7626
7639
|
}, timeoutMs);
|
|
7627
7640
|
this.pending.set(id, {
|
|
7628
|
-
resolve:
|
|
7641
|
+
resolve: resolve31,
|
|
7629
7642
|
reject,
|
|
7630
7643
|
timer,
|
|
7631
7644
|
method,
|
|
@@ -8010,7 +8023,24 @@ async function withAgentModelCatalog(agent, agentEnv, selection, inspect, limits
|
|
|
8010
8023
|
}
|
|
8011
8024
|
}
|
|
8012
8025
|
async function fetchAgentModelCatalog(agent, agentEnv, selection, limits = {}) {
|
|
8013
|
-
return withAgentModelCatalog(agent, agentEnv, selection, async ({ raw, catalog }) => ({
|
|
8026
|
+
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => ({
|
|
8027
|
+
raw,
|
|
8028
|
+
catalog: await filterModelChoicesByLiveValidation(client, sessionId, catalog)
|
|
8029
|
+
}), limits);
|
|
8030
|
+
}
|
|
8031
|
+
async function filterModelChoicesByLiveValidation(client, sessionId, catalog) {
|
|
8032
|
+
const modelAxis = catalog.find((axis) => axis.category === "model");
|
|
8033
|
+
if (!modelAxis)
|
|
8034
|
+
return catalog;
|
|
8035
|
+
const available = [];
|
|
8036
|
+
for (const choice of modelAxis.options) {
|
|
8037
|
+
try {
|
|
8038
|
+
await applyAgentModelSelection(client, sessionId, catalog, { model: choice.id });
|
|
8039
|
+
available.push(choice);
|
|
8040
|
+
} catch {
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
return catalog.map((axis) => axis === modelAxis ? { ...axis, options: available } : axis);
|
|
8014
8044
|
}
|
|
8015
8045
|
async function validateAgentModelSelection(agent, agentEnv, selection) {
|
|
8016
8046
|
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => {
|
|
@@ -8066,8 +8096,12 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
8066
8096
|
import { resolve as resolve5 } from "node:path";
|
|
8067
8097
|
var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
|
|
8068
8098
|
var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
|
|
8069
|
-
function modelCatalogHash(options, selection) {
|
|
8070
|
-
return createHash("sha256").update(JSON.stringify({
|
|
8099
|
+
function modelCatalogHash(options, selection, startupUnavailable) {
|
|
8100
|
+
return createHash("sha256").update(JSON.stringify({
|
|
8101
|
+
options,
|
|
8102
|
+
selection: selection ?? null,
|
|
8103
|
+
startupUnavailable: startupUnavailable ?? null
|
|
8104
|
+
})).digest("hex");
|
|
8071
8105
|
}
|
|
8072
8106
|
async function withTimeout(work, timeoutMs, what) {
|
|
8073
8107
|
let timer;
|
|
@@ -8093,8 +8127,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
8093
8127
|
...configuration.model ? { model: configuration.model } : {},
|
|
8094
8128
|
...configuration.effort ? { effort: configuration.effort } : {}
|
|
8095
8129
|
} : input.runtimeSelection;
|
|
8096
|
-
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
8097
|
-
const hash = modelCatalogHash(catalog, selection);
|
|
8130
|
+
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, input.startupUnavailable ? void 0 : selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
8131
|
+
const hash = modelCatalogHash(catalog, selection, input.startupUnavailable);
|
|
8098
8132
|
const previous = await readFile(hashPath, "utf8").catch(() => "");
|
|
8099
8133
|
if (previous.trim() === hash)
|
|
8100
8134
|
return "unchanged";
|
|
@@ -8103,7 +8137,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
8103
8137
|
workspaceId: input.workspaceId,
|
|
8104
8138
|
// `fetchAgentModelCatalog` already applied the category allow-list.
|
|
8105
8139
|
options: catalog,
|
|
8106
|
-
...selection ? { selection } : {}
|
|
8140
|
+
...selection ? { selection } : {},
|
|
8141
|
+
...input.startupUnavailable ? { unavailable: input.startupUnavailable } : {}
|
|
8107
8142
|
});
|
|
8108
8143
|
await writeFile(hashPath, `${hash}
|
|
8109
8144
|
`, { mode: 384 });
|
|
@@ -17164,10 +17199,10 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
17164
17199
|
|
|
17165
17200
|
// apps/body/dist/room-runtime.js
|
|
17166
17201
|
import { execFile as execFile5 } from "node:child_process";
|
|
17167
|
-
import { createHash as
|
|
17202
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
17168
17203
|
import { existsSync as existsSync4, mkdirSync } from "node:fs";
|
|
17169
|
-
import { mkdir as
|
|
17170
|
-
import { dirname as
|
|
17204
|
+
import { mkdir as mkdir13, rm as rm5 } from "node:fs/promises";
|
|
17205
|
+
import { dirname as dirname8, resolve as resolve20 } from "node:path";
|
|
17171
17206
|
import { promisify as promisify4 } from "node:util";
|
|
17172
17207
|
|
|
17173
17208
|
// apps/body/dist/grant-runner.js
|
|
@@ -17449,7 +17484,7 @@ var KNOWN_CREDENTIAL_MASK_PATHS = [
|
|
|
17449
17484
|
".git-credentials",
|
|
17450
17485
|
".secrets.env"
|
|
17451
17486
|
];
|
|
17452
|
-
function credentialMaskPaths(extraPaths, home = homedir3(),
|
|
17487
|
+
function credentialMaskPaths(extraPaths, home = homedir3(), stat4 = (path) => {
|
|
17453
17488
|
try {
|
|
17454
17489
|
const info = lstatSync2(path);
|
|
17455
17490
|
return { isDirectory: info.isDirectory() };
|
|
@@ -17468,7 +17503,7 @@ function credentialMaskPaths(extraPaths, home = homedir3(), stat3 = (path) => {
|
|
|
17468
17503
|
if (seen.has(path))
|
|
17469
17504
|
continue;
|
|
17470
17505
|
seen.add(path);
|
|
17471
|
-
const info =
|
|
17506
|
+
const info = stat4(path);
|
|
17472
17507
|
if (!info) {
|
|
17473
17508
|
if (required.has(path))
|
|
17474
17509
|
masks.push({ path, kind: "dir", create: true });
|
|
@@ -18137,13 +18172,13 @@ async function runServerCommandIntake(options) {
|
|
|
18137
18172
|
}
|
|
18138
18173
|
}
|
|
18139
18174
|
options.onPoll?.();
|
|
18140
|
-
const reconcile = await new Promise((
|
|
18175
|
+
const reconcile = await new Promise((resolve31) => {
|
|
18141
18176
|
const done = (needed) => {
|
|
18142
18177
|
if (timer)
|
|
18143
18178
|
clearTimeout(timer);
|
|
18144
18179
|
signal?.removeEventListener("abort", aborted);
|
|
18145
18180
|
wake = void 0;
|
|
18146
|
-
|
|
18181
|
+
resolve31(needed);
|
|
18147
18182
|
};
|
|
18148
18183
|
const aborted = () => done(false);
|
|
18149
18184
|
wake = done;
|
|
@@ -18169,10 +18204,10 @@ async function runServerCommandIntake(options) {
|
|
|
18169
18204
|
|
|
18170
18205
|
// apps/body/dist/monolith-corner-turn.js
|
|
18171
18206
|
import { execFile as execFile4 } from "node:child_process";
|
|
18172
|
-
import { createHash as
|
|
18173
|
-
import { mkdir as
|
|
18207
|
+
import { createHash as createHash5 } from "node:crypto";
|
|
18208
|
+
import { mkdir as mkdir12 } from "node:fs/promises";
|
|
18174
18209
|
import { homedir as homedir7 } from "node:os";
|
|
18175
|
-
import { join as
|
|
18210
|
+
import { join as join8 } from "node:path";
|
|
18176
18211
|
import { promisify as promisify3 } from "node:util";
|
|
18177
18212
|
|
|
18178
18213
|
// apps/body/dist/agent-home.js
|
|
@@ -18201,6 +18236,7 @@ function isAgentPairingCode(value) {
|
|
|
18201
18236
|
|
|
18202
18237
|
// apps/body/dist/beeline-skill.js
|
|
18203
18238
|
var USING_BEELINE_SKILL_NAME = "using-beeline";
|
|
18239
|
+
var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
|
|
18204
18240
|
var BEELINE_ROOM_CAPABILITIES = [
|
|
18205
18241
|
"The repository filesystem is read-only in this Room session.",
|
|
18206
18242
|
"You may address any Room member, including another agent, by writing @name in your reply; the server routes that mention to them. Each turn prompt lists the Room members and the exact spelling that tags each one - use those spellings, and never guess or reuse one from an older message.",
|
|
@@ -18265,6 +18301,86 @@ description: How to answer inside a Beeline Room.
|
|
|
18265
18301
|
You are answering inside a Room whose filesystem is read-only. ${BEELINE_ROOM_CAPABILITIES}
|
|
18266
18302
|
`;
|
|
18267
18303
|
}
|
|
18304
|
+
function beelineReviewSkillMarkdown(releaseId) {
|
|
18305
|
+
return `---
|
|
18306
|
+
name: beeline-review
|
|
18307
|
+
description: Review a corner pull request against its objective and the Beeline merge gate.
|
|
18308
|
+
---
|
|
18309
|
+
|
|
18310
|
+
<!-- beeline-release: ${releaseId} -->
|
|
18311
|
+
|
|
18312
|
+
# Beeline pull-request review
|
|
18313
|
+
|
|
18314
|
+
Follow these steps in order. Do not skip or reorder them.
|
|
18315
|
+
|
|
18316
|
+
## 1. Isolate the revision
|
|
18317
|
+
|
|
18318
|
+
- Run \`gh pr view N --json headRefOid,files\` and record \`headRefOid\`.
|
|
18319
|
+
- Run \`gh pr diff N\`.
|
|
18320
|
+
- Check out that exact head in a new scratch git worktree. Never use the author's worktree.
|
|
18321
|
+
- Review and test only the recorded revision. If the head moves, start over.
|
|
18322
|
+
|
|
18323
|
+
## 2. P0 - OBJECTIVE FULFILLED, DEMONSTRATED
|
|
18324
|
+
|
|
18325
|
+
- Quote the corner objective verbatim.
|
|
18326
|
+
- Derive its end-user story in one sentence: \`a user who does X sees Y\`.
|
|
18327
|
+
- Make Y happen against the built PR head: run the app or affected service and perform X.
|
|
18328
|
+
- If no interactive surface is reachable, run the narrowest test or script that exercises the exact user path and prints the observable Y.
|
|
18329
|
+
- Record the command and the observed Y.
|
|
18330
|
+
- A unit test of an inner function, a log line, \`the code looks right\`, or any other proxy does not count.
|
|
18331
|
+
- If the user-visible Y cannot be produced, FAIL now. Nothing below can rescue the review.
|
|
18332
|
+
- State whether the diff fulfills that objective and only that objective.
|
|
18333
|
+
|
|
18334
|
+
## 3. Empirical pass second
|
|
18335
|
+
|
|
18336
|
+
- Run the repository typecheck and tests touched by the diff.
|
|
18337
|
+
- If the objective names a user path, exercise that path.
|
|
18338
|
+
- Record every command and exit code.
|
|
18339
|
+
- A review with no executed command is invalid and must FAIL.
|
|
18340
|
+
|
|
18341
|
+
## 4. Adversarial pass
|
|
18342
|
+
|
|
18343
|
+
- For every changed function, name one concrete input or sequence that breaks it.
|
|
18344
|
+
- If none is found, write \`none found\` for that function.
|
|
18345
|
+
|
|
18346
|
+
## 5. Verify before reporting
|
|
18347
|
+
|
|
18348
|
+
- Confirm every finding by reading the exact line or by running a command.
|
|
18349
|
+
- Put unconfirmed concerns under plausible findings. They never block.
|
|
18350
|
+
|
|
18351
|
+
## 6. Bloat guard
|
|
18352
|
+
|
|
18353
|
+
- Compare net lines with the objective.
|
|
18354
|
+
- FAIL backwards-compatibility shims, dual paths, feature flags, or abstractions with one caller.
|
|
18355
|
+
- FAIL machinery the objective did not ask for.
|
|
18356
|
+
|
|
18357
|
+
## 7. Security and data
|
|
18358
|
+
|
|
18359
|
+
- Check credentials, authorization boundaries, and destructive migrations.
|
|
18360
|
+
|
|
18361
|
+
## 8. Gate and verdict
|
|
18362
|
+
|
|
18363
|
+
- The merge gate is open only when \`pr_checks_status\` reports checks=passed, held=false, approvalPending=false.
|
|
18364
|
+
- Green \`gh pr checks\` alone never opens the gate.
|
|
18365
|
+
- Always use this exact verdict shape:
|
|
18366
|
+
|
|
18367
|
+
\`objective quoted:\`
|
|
18368
|
+
\`user story:\`
|
|
18369
|
+
\`how Y was demonstrated (or FAIL):\`
|
|
18370
|
+
\`commands run + results:\`
|
|
18371
|
+
\`critical findings (block):\`
|
|
18372
|
+
\`plausible findings (do not block):\`
|
|
18373
|
+
\`net lines:\`
|
|
18374
|
+
\`decision: PASS|FAIL\`
|
|
18375
|
+
|
|
18376
|
+
Then take exactly one action:
|
|
18377
|
+
|
|
18378
|
+
- FAIL: reply \`@author\` with the confirmed findings; do not merge.
|
|
18379
|
+
- PASS with pending checks: reply \`approved pending checks <reviewed sha>\` and stop.
|
|
18380
|
+
- PASS with the gate open and your yolo on: run \`gh pr merge --squash --match-head-commit <reviewed sha> N\`.
|
|
18381
|
+
- PASS with the gate open and your yolo off: reply \`approved <reviewed sha>\` and stop.
|
|
18382
|
+
`;
|
|
18383
|
+
}
|
|
18268
18384
|
|
|
18269
18385
|
// apps/body/dist/external-mcp-capabilities.js
|
|
18270
18386
|
var SQUIRE_MCP_VERSION = "1.1.12";
|
|
@@ -18862,7 +18978,10 @@ var SHARED_CREDENTIALS = [
|
|
|
18862
18978
|
{ dir: "pi", source: ".pi/agent/auth.json", target: "auth.json" }
|
|
18863
18979
|
];
|
|
18864
18980
|
var GOOSE_SHARED_CONFIG_FILES = ["config.yaml", "secrets.yaml"];
|
|
18865
|
-
var BEELINE_DEFAULT_SKILL_NAMES = [
|
|
18981
|
+
var BEELINE_DEFAULT_SKILL_NAMES = [
|
|
18982
|
+
BEELINE_REVIEW_SKILL_NAME,
|
|
18983
|
+
USING_BEELINE_SKILL_NAME
|
|
18984
|
+
];
|
|
18866
18985
|
var OPERATOR_SKILL_SOURCE_DIRS = [
|
|
18867
18986
|
".agents/skills",
|
|
18868
18987
|
".claude/skills",
|
|
@@ -18941,7 +19060,8 @@ async function prepareRoomAgentHome(input) {
|
|
|
18941
19060
|
}
|
|
18942
19061
|
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting) {
|
|
18943
19062
|
const managedSkills = [
|
|
18944
|
-
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) }
|
|
19063
|
+
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
|
|
19064
|
+
{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }
|
|
18945
19065
|
];
|
|
18946
19066
|
const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
|
|
18947
19067
|
await provisionManagedSkillsDir(resolve13(root, skillDir, "skills"), managedSkills, shared, sharedSkills.length === 0);
|
|
@@ -18995,7 +19115,7 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
18995
19115
|
console.warn("[body] operator MCP passthrough failed for claude:", error);
|
|
18996
19116
|
}
|
|
18997
19117
|
try {
|
|
18998
|
-
const settings2 = { permissions: { allow: ["WebSearch"] } };
|
|
19118
|
+
const settings2 = { permissions: { allow: ["WebSearch", "WebFetch"] } };
|
|
18999
19119
|
await writeIsolatedHarnessFile(resolve13(root, "claude", "settings.json"), `${JSON.stringify(settings2, null, 2)}
|
|
19000
19120
|
`);
|
|
19001
19121
|
} catch (error) {
|
|
@@ -19559,18 +19679,6 @@ function sanitizeAgentReply(message) {
|
|
|
19559
19679
|
}
|
|
19560
19680
|
return lines.slice(1).join("\n").trim();
|
|
19561
19681
|
}
|
|
19562
|
-
var CORNER_OPEN_ECHO = /^(?:(?:ok(?:ay)?|done|sure|great|alright)[,.!:]?\s+)?(?:i(?:'ve| have|'ll| will|'m| am)?\s+)?(?:just\s+)?open(?:ed|ing)?\s+(?:up\s+)?(?:a\s+|the\s+|your\s+)?(?:new\s+|write(?:-enabled)?\s+|repository\s+)*corner\b/iu;
|
|
19563
|
-
var CORNER_OPEN_ECHO_MAX_CHARS = 320;
|
|
19564
|
-
function stripCornerOpenEcho(message) {
|
|
19565
|
-
const visible = message.trim();
|
|
19566
|
-
if (!visible)
|
|
19567
|
-
return "";
|
|
19568
|
-
const paragraphs = visible.split(/\n\s*\n/);
|
|
19569
|
-
const first = paragraphs[0].trim();
|
|
19570
|
-
if (first.length > CORNER_OPEN_ECHO_MAX_CHARS || !CORNER_OPEN_ECHO.test(first))
|
|
19571
|
-
return visible;
|
|
19572
|
-
return paragraphs.slice(1).join("\n\n").trim();
|
|
19573
|
-
}
|
|
19574
19682
|
var STATUS_FILLER = new Set("a an the this that it its is are was were be been being has have had do does did now still remains remain remaining ready for review reviewing pr pull request all every both and or so to of in on at as with by no not nothing new further more else needed needs need required action actions change changes changed status state update updates updated waiting wait awaiting pending running progress idle done ok okay good great well fine confirmed confirm already again yet just currently right i we will can should would github remote upstream branch head latest current report reported noted note see looks look like everything nothing appears appear seems seem there here which what from without any".split(/\s+/));
|
|
19575
19683
|
var STATUS_SYNONYMS = {
|
|
19576
19684
|
passed: ["pass", "passed", "passing", "passes", "green", "succeeded", "success", "successful", "successfully"],
|
|
@@ -19629,6 +19737,12 @@ var AgentTurnStream = class {
|
|
|
19629
19737
|
inFlight;
|
|
19630
19738
|
/** Closed lanes publish nothing more, so the answer never queues behind a draft. */
|
|
19631
19739
|
closed = false;
|
|
19740
|
+
/**
|
|
19741
|
+
* The retract this lane already sent. A settled turn that throws afterwards
|
|
19742
|
+
* reaches the same retract a second time, and one empty lane is the whole
|
|
19743
|
+
* point: asking twice would only be a second write saying what is already so.
|
|
19744
|
+
*/
|
|
19745
|
+
retraction;
|
|
19632
19746
|
constructor(options) {
|
|
19633
19747
|
this.options = options;
|
|
19634
19748
|
}
|
|
@@ -19688,14 +19802,45 @@ var AgentTurnStream = class {
|
|
|
19688
19802
|
this.closed = true;
|
|
19689
19803
|
this.pending = void 0;
|
|
19690
19804
|
}
|
|
19805
|
+
/**
|
|
19806
|
+
* Dissolve the draft, publishing nothing.
|
|
19807
|
+
*
|
|
19808
|
+
* Every ending uses this: the settle below calls it once the durable reply is
|
|
19809
|
+
* on the wire, and a turn that THROWS calls it directly. A throw never
|
|
19810
|
+
* reaches a settle, and `close()` alone only stops future writes — the last
|
|
19811
|
+
* snapshot stays live on the page, so a turn the Room has already reported
|
|
19812
|
+
* stopped or failed keeps a half-written answer visibly in progress under it.
|
|
19813
|
+
*/
|
|
19814
|
+
async retract() {
|
|
19815
|
+
this.close();
|
|
19816
|
+
this.retraction ??= (async () => {
|
|
19817
|
+
await this.inFlight;
|
|
19818
|
+
const { api, agentId, roomId, requestId, label } = this.options;
|
|
19819
|
+
await api.execute("retractAgentLiveOutput", {
|
|
19820
|
+
agentId,
|
|
19821
|
+
roomId,
|
|
19822
|
+
turnId: requestId,
|
|
19823
|
+
kind: "draft"
|
|
19824
|
+
}).catch((error) => console.error(`[thin-core] ${label} draft retract failed:`, error));
|
|
19825
|
+
})();
|
|
19826
|
+
await this.retraction;
|
|
19827
|
+
}
|
|
19691
19828
|
/**
|
|
19692
19829
|
* Post the durable reply under the turn's request id and dissolve the draft.
|
|
19693
19830
|
* An empty reply settles through the turn receipt instead, and the lane is
|
|
19694
19831
|
* retracted either way.
|
|
19832
|
+
*
|
|
19833
|
+
* The durable reply is this turn's answer and its last word. The draft lane
|
|
19834
|
+
* is presentation, so a refused retract is logged like a refused draft and
|
|
19835
|
+
* the turn still settles complete: raising here failed a turn that had
|
|
19836
|
+
* already answered, which posts a `failed` receipt and inscribes "<agent>
|
|
19837
|
+
* could not answer" UNDER the answer the reader is looking at. The reader
|
|
19838
|
+
* loses nothing by it either — the phone ends a retracted draft on the
|
|
19839
|
+
* turn's own complete receipt (`visibleLiveOverlays`).
|
|
19695
19840
|
*/
|
|
19696
19841
|
async settle(reply, fields = {}, onReplyPosted) {
|
|
19697
19842
|
this.close();
|
|
19698
|
-
const { api,
|
|
19843
|
+
const { api, roomId, requestId } = this.options;
|
|
19699
19844
|
if (reply) {
|
|
19700
19845
|
const posted = await api.execute("postRoomMessage", {
|
|
19701
19846
|
roomId,
|
|
@@ -19706,13 +19851,7 @@ var AgentTurnStream = class {
|
|
|
19706
19851
|
});
|
|
19707
19852
|
onReplyPosted?.(posted);
|
|
19708
19853
|
}
|
|
19709
|
-
await this.
|
|
19710
|
-
await api.execute("retractAgentLiveOutput", {
|
|
19711
|
-
agentId,
|
|
19712
|
-
roomId,
|
|
19713
|
-
turnId: requestId,
|
|
19714
|
-
kind: "draft"
|
|
19715
|
-
});
|
|
19854
|
+
await this.retract();
|
|
19716
19855
|
}
|
|
19717
19856
|
};
|
|
19718
19857
|
|
|
@@ -19722,13 +19861,18 @@ function redactToolDetail(value) {
|
|
|
19722
19861
|
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]");
|
|
19723
19862
|
}
|
|
19724
19863
|
function distillTurnFailureReason(error) {
|
|
19864
|
+
if (error instanceof ModelSelectionUnavailableError) {
|
|
19865
|
+
return { text: "model selection unavailable", kind: "model-selection-unavailable" };
|
|
19866
|
+
}
|
|
19725
19867
|
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);
|
|
19726
19868
|
const firstLine = raw.split(/\r?\n/).map((line) => line.trim()).find((line) => line && !/^at\s/.test(line)) ?? "";
|
|
19727
19869
|
const stripped = firstLine.replace(/^(?:[A-Za-z]*Error|Error):\s*/, "").replace(/\s+/g, " ");
|
|
19728
19870
|
const clean4 = redactToolDetail(stripped).trim();
|
|
19729
19871
|
if (!clean4)
|
|
19730
|
-
return "turn failed";
|
|
19731
|
-
return
|
|
19872
|
+
return { text: "turn failed" };
|
|
19873
|
+
return {
|
|
19874
|
+
text: clean4.length > TURN_FAILURE_REASON_MAX ? `${clean4.slice(0, TURN_FAILURE_REASON_MAX - 1)}\u2026` : clean4
|
|
19875
|
+
};
|
|
19732
19876
|
}
|
|
19733
19877
|
|
|
19734
19878
|
// apps/body/dist/tool-call-failure.js
|
|
@@ -19762,6 +19906,9 @@ function toolCallText(content) {
|
|
|
19762
19906
|
function isFailedToolCall(call) {
|
|
19763
19907
|
return /^(?:failed|error|denied|rejected)$/i.test(call.status ?? "");
|
|
19764
19908
|
}
|
|
19909
|
+
function isCompletedToolCall(call) {
|
|
19910
|
+
return /^completed$/i.test(call.status ?? "");
|
|
19911
|
+
}
|
|
19765
19912
|
function toolCallFailureLine(call) {
|
|
19766
19913
|
if (!isFailedToolCall(call))
|
|
19767
19914
|
return void 0;
|
|
@@ -19772,14 +19919,17 @@ function toolCallFailureLine(call) {
|
|
|
19772
19919
|
|
|
19773
19920
|
// apps/body/dist/session-config-fingerprint.js
|
|
19774
19921
|
function sessionConfigFingerprint(input) {
|
|
19775
|
-
|
|
19922
|
+
const fingerprint = [
|
|
19776
19923
|
input.model ?? "",
|
|
19777
19924
|
input.effort ?? "",
|
|
19778
19925
|
input.soul?.name ?? "",
|
|
19779
19926
|
input.soul?.instructions ?? "",
|
|
19780
19927
|
input.agentName ?? "",
|
|
19781
19928
|
input.yoloMode ?? false
|
|
19782
|
-
]
|
|
19929
|
+
];
|
|
19930
|
+
if (input.reviewerHandle !== void 0)
|
|
19931
|
+
fingerprint.push(input.reviewerHandle);
|
|
19932
|
+
return JSON.stringify(fingerprint);
|
|
19783
19933
|
}
|
|
19784
19934
|
|
|
19785
19935
|
// apps/body/dist/pi-mcp-bridge.js
|
|
@@ -20792,16 +20942,299 @@ process.exit(result.status ?? 1);
|
|
|
20792
20942
|
await chmod3(path, 448);
|
|
20793
20943
|
}
|
|
20794
20944
|
|
|
20945
|
+
// apps/body/dist/warm-node-modules.js
|
|
20946
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "node:crypto";
|
|
20947
|
+
import { constants as fsConstants } from "node:fs";
|
|
20948
|
+
import { chmod as chmod4, copyFile as copyFile2, link, lstat as lstat2, mkdir as mkdir10, readdir as readdir5, readFile as readFile8, readlink, rename as rename3, rm as rm4, stat as stat2, symlink as symlink2, utimes } from "node:fs/promises";
|
|
20949
|
+
import { dirname as dirname7, join as join6, resolve as resolve19 } from "node:path";
|
|
20950
|
+
var STORE_FORMAT = "v1";
|
|
20951
|
+
var STAGING_PREFIX = ".beeline-warm-";
|
|
20952
|
+
var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
20953
|
+
var WARM_STORE_MAX_ENTRIES = 3;
|
|
20954
|
+
function sharedNpmCacheDir(supervisorRoot) {
|
|
20955
|
+
return resolve19(supervisorRoot, "beeline", "npm-cache");
|
|
20956
|
+
}
|
|
20957
|
+
function warmNodeModulesStoreDir(supervisorRoot) {
|
|
20958
|
+
return resolve19(supervisorRoot, "beeline", "node-modules");
|
|
20959
|
+
}
|
|
20960
|
+
function isPlanRefusal(value) {
|
|
20961
|
+
return "failure" in value;
|
|
20962
|
+
}
|
|
20963
|
+
async function readWarmPlan(worktreePath) {
|
|
20964
|
+
const lockfile = await readFile8(resolve19(worktreePath, "package-lock.json")).catch(() => void 0);
|
|
20965
|
+
if (!lockfile)
|
|
20966
|
+
return { failure: "no-lockfile" };
|
|
20967
|
+
const packages = parseLockfilePackages(lockfile);
|
|
20968
|
+
if (!packages)
|
|
20969
|
+
return { failure: "no-lockfile" };
|
|
20970
|
+
const escaping = Object.keys(packages).find((path) => namesInstalledPackage(path) && !isContainedPath(path));
|
|
20971
|
+
if (escaping)
|
|
20972
|
+
return { failure: "unsafe-lockfile", detail: escaping };
|
|
20973
|
+
const trees = nodeModulesTreePaths(packages);
|
|
20974
|
+
if (trees.length === 0)
|
|
20975
|
+
return { failure: "no-lockfile" };
|
|
20976
|
+
const unsafe = trees.find((tree) => !isContainedTreePath(tree));
|
|
20977
|
+
if (unsafe)
|
|
20978
|
+
return { failure: "unsafe-lockfile", detail: unsafe };
|
|
20979
|
+
const key = createHash4("sha256").update(`${STORE_FORMAT}\0${process.platform}\0${process.arch}\0${process.versions.modules}\0`).update(lockfile).digest("hex").slice(0, 32);
|
|
20980
|
+
return { key, trees };
|
|
20981
|
+
}
|
|
20982
|
+
function nodeModulesTreePaths(packages) {
|
|
20983
|
+
const trees = /* @__PURE__ */ new Set();
|
|
20984
|
+
for (const path of Object.keys(packages)) {
|
|
20985
|
+
if (!namesInstalledPackage(path))
|
|
20986
|
+
continue;
|
|
20987
|
+
const segments = path.split("/");
|
|
20988
|
+
trees.add(segments.slice(0, segments.indexOf("node_modules") + 1).join("/"));
|
|
20989
|
+
}
|
|
20990
|
+
return [...trees].sort();
|
|
20991
|
+
}
|
|
20992
|
+
function namesInstalledPackage(path) {
|
|
20993
|
+
const segments = path.split("/");
|
|
20994
|
+
const index = segments.indexOf("node_modules");
|
|
20995
|
+
return index >= 0 && index < segments.length - 1;
|
|
20996
|
+
}
|
|
20997
|
+
async function seedWarmNodeModules(input) {
|
|
20998
|
+
const plan = await readWarmPlan(input.worktreePath);
|
|
20999
|
+
if (isPlanRefusal(plan)) {
|
|
21000
|
+
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
21001
|
+
}
|
|
21002
|
+
for (const tree of plan.trees) {
|
|
21003
|
+
if (await pathExists(resolve19(input.worktreePath, tree))) {
|
|
21004
|
+
return { reason: "present", key: plan.key };
|
|
21005
|
+
}
|
|
21006
|
+
}
|
|
21007
|
+
const entry = resolve19(input.storeRoot, plan.key);
|
|
21008
|
+
if (!await isDirectory(entry))
|
|
21009
|
+
return { reason: "cold", key: plan.key };
|
|
21010
|
+
const [storeDevice, checkoutDevice] = await Promise.all([
|
|
21011
|
+
deviceOf(entry),
|
|
21012
|
+
deviceOf(input.worktreePath)
|
|
21013
|
+
]);
|
|
21014
|
+
if (storeDevice === void 0 || storeDevice !== checkoutDevice) {
|
|
21015
|
+
return { reason: "cross-device", key: plan.key };
|
|
21016
|
+
}
|
|
21017
|
+
const staging = resolve19(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID4()}`);
|
|
21018
|
+
const placed = [];
|
|
21019
|
+
const now2 = (input.now ?? Date.now)();
|
|
21020
|
+
try {
|
|
21021
|
+
await sweepStaleStaging(input.storeRoot, now2);
|
|
21022
|
+
await utimes(entry, now2 / 1e3, now2 / 1e3).catch(() => void 0);
|
|
21023
|
+
for (const tree of plan.trees) {
|
|
21024
|
+
await cloneTree(resolve19(entry, tree), resolve19(staging, tree), seedFile);
|
|
21025
|
+
}
|
|
21026
|
+
for (const tree of plan.trees) {
|
|
21027
|
+
const target = resolve19(input.worktreePath, tree);
|
|
21028
|
+
await mkdir10(dirname7(target), { recursive: true });
|
|
21029
|
+
await rename3(resolve19(staging, tree), target);
|
|
21030
|
+
placed.push(target);
|
|
21031
|
+
}
|
|
21032
|
+
return { reason: "seeded", key: plan.key };
|
|
21033
|
+
} catch (error) {
|
|
21034
|
+
for (const target of placed) {
|
|
21035
|
+
await rm4(target, { recursive: true, force: true }).catch(() => void 0);
|
|
21036
|
+
}
|
|
21037
|
+
return { reason: "failed", key: plan.key, detail: describe(error) };
|
|
21038
|
+
} finally {
|
|
21039
|
+
await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
21040
|
+
}
|
|
21041
|
+
}
|
|
21042
|
+
async function harvestWarmNodeModules(input) {
|
|
21043
|
+
const plan = await readWarmPlan(input.worktreePath);
|
|
21044
|
+
if (isPlanRefusal(plan)) {
|
|
21045
|
+
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
21046
|
+
}
|
|
21047
|
+
const entry = resolve19(input.storeRoot, plan.key);
|
|
21048
|
+
if (await pathExists(entry))
|
|
21049
|
+
return { reason: "already-warm", key: plan.key };
|
|
21050
|
+
for (const tree of plan.trees) {
|
|
21051
|
+
if (!await isDirectory(resolve19(input.worktreePath, tree))) {
|
|
21052
|
+
return { reason: "no-node-modules", key: plan.key, detail: tree };
|
|
21053
|
+
}
|
|
21054
|
+
}
|
|
21055
|
+
const missing = await missingInstalledPackages(input.worktreePath);
|
|
21056
|
+
if (missing.length > 0) {
|
|
21057
|
+
return {
|
|
21058
|
+
reason: "incomplete",
|
|
21059
|
+
key: plan.key,
|
|
21060
|
+
detail: `${missing.length} absent, e.g. ${missing.slice(0, 3).join(", ")}`
|
|
21061
|
+
};
|
|
21062
|
+
}
|
|
21063
|
+
const staging = resolve19(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID4()}`);
|
|
21064
|
+
try {
|
|
21065
|
+
await mkdir10(input.storeRoot, { recursive: true, mode: 493 });
|
|
21066
|
+
await sweepStaleStaging(input.storeRoot, (input.now ?? Date.now)());
|
|
21067
|
+
for (const tree of plan.trees) {
|
|
21068
|
+
await cloneTree(resolve19(input.worktreePath, tree), resolve19(staging, tree), harvestFile);
|
|
21069
|
+
}
|
|
21070
|
+
await input.onCopied?.();
|
|
21071
|
+
if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
|
|
21072
|
+
return { reason: "changed", key: plan.key };
|
|
21073
|
+
}
|
|
21074
|
+
await rename3(staging, entry);
|
|
21075
|
+
await pruneWarmStore(input.storeRoot, WARM_STORE_MAX_ENTRIES);
|
|
21076
|
+
return { reason: "stored", key: plan.key };
|
|
21077
|
+
} catch (error) {
|
|
21078
|
+
if (await pathExists(entry))
|
|
21079
|
+
return { reason: "already-warm", key: plan.key };
|
|
21080
|
+
return { reason: "failed", key: plan.key, detail: describe(error) };
|
|
21081
|
+
} finally {
|
|
21082
|
+
await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
21083
|
+
}
|
|
21084
|
+
}
|
|
21085
|
+
async function stagedTreeIsPublishable(worktreePath, staging, key) {
|
|
21086
|
+
const settled = await readWarmPlan(worktreePath);
|
|
21087
|
+
if (isPlanRefusal(settled) || settled.key !== key)
|
|
21088
|
+
return false;
|
|
21089
|
+
const hidden = join6("node_modules", ".package-lock.json");
|
|
21090
|
+
const [copied, current] = await Promise.all([
|
|
21091
|
+
readFile8(resolve19(staging, hidden)).catch(() => void 0),
|
|
21092
|
+
readFile8(resolve19(worktreePath, hidden)).catch(() => void 0)
|
|
21093
|
+
]);
|
|
21094
|
+
if (!copied || !current || !copied.equals(current))
|
|
21095
|
+
return false;
|
|
21096
|
+
return (await missingInstalledPackages(worktreePath, staging)).length === 0;
|
|
21097
|
+
}
|
|
21098
|
+
async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
21099
|
+
const wanted = parseLockfilePackages(await readFile8(resolve19(worktreePath, "package-lock.json")).catch(() => void 0));
|
|
21100
|
+
const installed = parseLockfilePackages(await readFile8(resolve19(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
|
|
21101
|
+
if (!wanted)
|
|
21102
|
+
return ["package-lock.json is unreadable"];
|
|
21103
|
+
if (!installed)
|
|
21104
|
+
return ["node_modules/.package-lock.json is absent"];
|
|
21105
|
+
const required = [];
|
|
21106
|
+
const missing = [];
|
|
21107
|
+
for (const [path, entry] of Object.entries(wanted)) {
|
|
21108
|
+
if (!namesInstalledPackage(path))
|
|
21109
|
+
continue;
|
|
21110
|
+
if (entry.link === true || entry.optional === true)
|
|
21111
|
+
continue;
|
|
21112
|
+
if (entry.os !== void 0 || entry.cpu !== void 0)
|
|
21113
|
+
continue;
|
|
21114
|
+
if (!isContainedPath(path))
|
|
21115
|
+
missing.push(path);
|
|
21116
|
+
else
|
|
21117
|
+
required.push(path);
|
|
21118
|
+
}
|
|
21119
|
+
await mapWithLimit(required, INTEGRITY_READ_CONCURRENCY, async (path) => {
|
|
21120
|
+
if (!(path in installed) || !await isInstalledPackage(resolve19(treeRoot, path))) {
|
|
21121
|
+
missing.push(path);
|
|
21122
|
+
}
|
|
21123
|
+
});
|
|
21124
|
+
return missing.sort();
|
|
21125
|
+
}
|
|
21126
|
+
async function isInstalledPackage(path) {
|
|
21127
|
+
return stat2(join6(path, "package.json")).then((info) => info.isFile(), () => false);
|
|
21128
|
+
}
|
|
21129
|
+
var INTEGRITY_READ_CONCURRENCY = 64;
|
|
21130
|
+
async function mapWithLimit(values, limit, visit) {
|
|
21131
|
+
let cursor3 = 0;
|
|
21132
|
+
await Promise.all(Array.from({ length: Math.min(limit, values.length) }, async () => {
|
|
21133
|
+
while (cursor3 < values.length)
|
|
21134
|
+
await visit(values[cursor3++]);
|
|
21135
|
+
}));
|
|
21136
|
+
}
|
|
21137
|
+
function parseLockfilePackages(source) {
|
|
21138
|
+
if (!source)
|
|
21139
|
+
return void 0;
|
|
21140
|
+
try {
|
|
21141
|
+
const parsed = JSON.parse(source.toString("utf8"));
|
|
21142
|
+
const packages = parsed.packages;
|
|
21143
|
+
if (!packages || typeof packages !== "object" || Array.isArray(packages))
|
|
21144
|
+
return void 0;
|
|
21145
|
+
return packages;
|
|
21146
|
+
} catch {
|
|
21147
|
+
return void 0;
|
|
21148
|
+
}
|
|
21149
|
+
}
|
|
21150
|
+
function isContainedPath(value) {
|
|
21151
|
+
if (!value)
|
|
21152
|
+
return false;
|
|
21153
|
+
return value.split("/").every((segment) => segment.length > 0 && segment !== "." && segment !== ".." && !segment.includes("\\"));
|
|
21154
|
+
}
|
|
21155
|
+
function isContainedTreePath(value) {
|
|
21156
|
+
return isContainedPath(value) && value.split("/").pop() === "node_modules";
|
|
21157
|
+
}
|
|
21158
|
+
async function cloneTree(source, target, file, topLevel = true) {
|
|
21159
|
+
await mkdir10(target, { recursive: true, mode: 493 });
|
|
21160
|
+
for (const entry of await readdir5(source, { withFileTypes: true })) {
|
|
21161
|
+
const from = join6(source, entry.name);
|
|
21162
|
+
const to = join6(target, entry.name);
|
|
21163
|
+
if (entry.isSymbolicLink()) {
|
|
21164
|
+
await symlink2(await readlink(from), to);
|
|
21165
|
+
continue;
|
|
21166
|
+
}
|
|
21167
|
+
if (entry.isDirectory()) {
|
|
21168
|
+
await cloneTree(from, to, file, false);
|
|
21169
|
+
continue;
|
|
21170
|
+
}
|
|
21171
|
+
if (entry.isFile())
|
|
21172
|
+
await file(from, to, topLevel);
|
|
21173
|
+
}
|
|
21174
|
+
}
|
|
21175
|
+
var harvestFile = async (source, target) => {
|
|
21176
|
+
await copyFile2(source, target, fsConstants.COPYFILE_FICLONE);
|
|
21177
|
+
const info = await stat2(source);
|
|
21178
|
+
await chmod4(target, info.mode & 4095 & ~146);
|
|
21179
|
+
};
|
|
21180
|
+
var seedFile = async (source, target, topLevel) => {
|
|
21181
|
+
if (!topLevel) {
|
|
21182
|
+
await link(source, target);
|
|
21183
|
+
return;
|
|
21184
|
+
}
|
|
21185
|
+
await copyFile2(source, target);
|
|
21186
|
+
await chmod4(target, 420);
|
|
21187
|
+
};
|
|
21188
|
+
async function pruneWarmStore(storeRoot, keep) {
|
|
21189
|
+
const names = (await readdir5(storeRoot).catch(() => [])).filter((name) => !name.startsWith(STAGING_PREFIX));
|
|
21190
|
+
const entries = [];
|
|
21191
|
+
for (const name of names) {
|
|
21192
|
+
const info = await lstat2(join6(storeRoot, name)).catch(() => void 0);
|
|
21193
|
+
if (info?.isDirectory())
|
|
21194
|
+
entries.push({ name, usedAt: info.mtimeMs });
|
|
21195
|
+
}
|
|
21196
|
+
const dropped = entries.sort((a2, b) => b.usedAt - a2.usedAt || a2.name.localeCompare(b.name)).slice(keep);
|
|
21197
|
+
for (const entry of dropped) {
|
|
21198
|
+
await rm4(join6(storeRoot, entry.name), { recursive: true, force: true }).catch(() => void 0);
|
|
21199
|
+
}
|
|
21200
|
+
return dropped.map((entry) => entry.name);
|
|
21201
|
+
}
|
|
21202
|
+
async function sweepStaleStaging(storeRoot, now2) {
|
|
21203
|
+
const entries = await readdir5(storeRoot).catch(() => []);
|
|
21204
|
+
for (const name of entries) {
|
|
21205
|
+
if (!name.startsWith(STAGING_PREFIX))
|
|
21206
|
+
continue;
|
|
21207
|
+
const path = join6(storeRoot, name);
|
|
21208
|
+
const info = await lstat2(path).catch(() => void 0);
|
|
21209
|
+
if (!info || now2 - info.mtimeMs < STAGING_SWEEP_MS)
|
|
21210
|
+
continue;
|
|
21211
|
+
await rm4(path, { recursive: true, force: true }).catch(() => void 0);
|
|
21212
|
+
}
|
|
21213
|
+
}
|
|
21214
|
+
async function pathExists(path) {
|
|
21215
|
+
return lstat2(path).then(() => true, () => false);
|
|
21216
|
+
}
|
|
21217
|
+
async function deviceOf(path) {
|
|
21218
|
+
return stat2(path).then((info) => info.dev, () => void 0);
|
|
21219
|
+
}
|
|
21220
|
+
async function isDirectory(path) {
|
|
21221
|
+
return stat2(path).then((info) => info.isDirectory(), () => false);
|
|
21222
|
+
}
|
|
21223
|
+
function describe(error) {
|
|
21224
|
+
return error instanceof Error ? error.message : String(error);
|
|
21225
|
+
}
|
|
21226
|
+
|
|
20795
21227
|
// apps/body/dist/monolith-room-turn.js
|
|
20796
|
-
import { mkdir as
|
|
21228
|
+
import { mkdir as mkdir11 } from "node:fs/promises";
|
|
20797
21229
|
import { homedir as homedir6 } from "node:os";
|
|
20798
|
-
import { join as
|
|
21230
|
+
import { join as join7 } from "node:path";
|
|
20799
21231
|
|
|
20800
21232
|
// packages/api-contract/dist/scheduled-prompts.js
|
|
20801
21233
|
var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
|
|
20802
21234
|
var SCHEDULE_RAN_VERB = "ran a schedule for";
|
|
20803
21235
|
|
|
20804
21236
|
// apps/body/dist/monolith-room-turn.js
|
|
21237
|
+
var ROOM_PROMPT_INACTIVITY_TIMEOUT_MS = 18e4;
|
|
20805
21238
|
function isRoomMcpPermissionRequest(request, mountedServers = ROOM_MOUNTED_MCP_SERVERS) {
|
|
20806
21239
|
if (isSquireMcpPermissionRequest(request))
|
|
20807
21240
|
return false;
|
|
@@ -20997,7 +21430,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
20997
21430
|
const cached = this.deliveredAttachments.get(item.id);
|
|
20998
21431
|
if (cached)
|
|
20999
21432
|
return cached;
|
|
21000
|
-
const delivered = await deliverAttachments(item.attachments,
|
|
21433
|
+
const delivered = await deliverAttachments(item.attachments, join7(this.attachmentDir, item.id.replace(/[^\w-]/g, "_")), this.options.fetchImpl);
|
|
21001
21434
|
this.deliveredAttachments.set(item.id, withoutImageData(delivered));
|
|
21002
21435
|
return delivered;
|
|
21003
21436
|
}
|
|
@@ -21064,7 +21497,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21064
21497
|
agentName: self?.name ?? this.agent.name
|
|
21065
21498
|
});
|
|
21066
21499
|
const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
|
|
21067
|
-
await
|
|
21500
|
+
await mkdir11(this.options.cwd, { recursive: true });
|
|
21068
21501
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
21069
21502
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
21070
21503
|
root: this.options.config.agentHomeRoot,
|
|
@@ -21090,14 +21523,14 @@ var MonolithRoomTurnLoop = class {
|
|
|
21090
21523
|
}, selection);
|
|
21091
21524
|
const operatorHome = this.options.config.operatorHome ?? homedir6();
|
|
21092
21525
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
21093
|
-
this.attachmentDir = tmpDir ?
|
|
21526
|
+
this.attachmentDir = tmpDir ? join7(tmpDir, "beeline-attachments") : void 0;
|
|
21094
21527
|
this.sessionScratchDir = tmpDir;
|
|
21095
21528
|
this.sessionStateDirs = stateDirs;
|
|
21096
21529
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
21097
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
21530
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir11(dir, { recursive: true })));
|
|
21098
21531
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
21099
21532
|
if (attachScratchRoot)
|
|
21100
|
-
await
|
|
21533
|
+
await mkdir11(attachScratchRoot, { recursive: true });
|
|
21101
21534
|
const spawnCommand = wrapAgentCommand({
|
|
21102
21535
|
bwrapPath: this.options.config.bwrapPath,
|
|
21103
21536
|
spec: {
|
|
@@ -21287,6 +21720,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21287
21720
|
const api = this.options.api;
|
|
21288
21721
|
this.busy = true;
|
|
21289
21722
|
const trace = this.beginTurnTrace(item.id);
|
|
21723
|
+
let liveStream;
|
|
21290
21724
|
try {
|
|
21291
21725
|
if (!this.memberNames.has(item.authorId))
|
|
21292
21726
|
await this.roster().catch(() => void 0);
|
|
@@ -21353,6 +21787,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21353
21787
|
requestId: item.id,
|
|
21354
21788
|
label: `monolith Room ${this.options.roomId}`
|
|
21355
21789
|
});
|
|
21790
|
+
liveStream = stream;
|
|
21356
21791
|
const runPrompt = async () => {
|
|
21357
21792
|
let nextPrompt = promptWithImages(buildPrompt(), attachmentImageBlocks(delivered, this.acceptsImages()));
|
|
21358
21793
|
let result2;
|
|
@@ -21361,7 +21796,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21361
21796
|
try {
|
|
21362
21797
|
stream.beginRun();
|
|
21363
21798
|
trace.promptSent();
|
|
21364
|
-
result2 = await this.client.sessionPrompt(this.sessionId, nextPrompt,
|
|
21799
|
+
result2 = await this.client.sessionPrompt(this.sessionId, nextPrompt, ROOM_PROMPT_INACTIVITY_TIMEOUT_MS, (delta, full) => {
|
|
21365
21800
|
trace.firstModelOutput();
|
|
21366
21801
|
stream.onChunk(delta, full);
|
|
21367
21802
|
}, void 0, (calls) => trace.toolCalls(calls));
|
|
@@ -21390,7 +21825,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21390
21825
|
};
|
|
21391
21826
|
let result = await runPrompt();
|
|
21392
21827
|
trace.promptSettled();
|
|
21393
|
-
let
|
|
21828
|
+
let openCornerCall = openCornerToolCall(result.toolCalls);
|
|
21829
|
+
let cornerOpened = openedACorner(openCornerCall);
|
|
21830
|
+
let explained = cornerOpened ? void 0 : await this.explainEmpty(result);
|
|
21394
21831
|
if (explained && shouldRetryEmptyTurn(explained)) {
|
|
21395
21832
|
const silent = this.servingProviders();
|
|
21396
21833
|
const next = await this.repinNextProvider(trace, explained.reason);
|
|
@@ -21398,7 +21835,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21398
21835
|
console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${turnFailureReasonWithProvider(explained.reason, silent)}; retrying on ${next}`);
|
|
21399
21836
|
result = await runPrompt();
|
|
21400
21837
|
trace.promptSettled();
|
|
21401
|
-
|
|
21838
|
+
openCornerCall = openCornerToolCall(result.toolCalls);
|
|
21839
|
+
cornerOpened = openedACorner(openCornerCall);
|
|
21840
|
+
explained = cornerOpened ? void 0 : await this.explainEmpty(result);
|
|
21402
21841
|
}
|
|
21403
21842
|
}
|
|
21404
21843
|
if (active.cancelled) {
|
|
@@ -21412,10 +21851,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21412
21851
|
} else if (resumedRequestId) {
|
|
21413
21852
|
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${resumedRequestId} resumed by grant decision ${item.id}`);
|
|
21414
21853
|
}
|
|
21415
|
-
const openCornerCall = result.toolCalls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
|
|
21416
21854
|
if (openCornerCall) {
|
|
21417
21855
|
console.log(`[thin-core] monolith Room ${this.options.roomId} tool call: ${openCornerCall.title} (${openCornerCall.status ?? "no status"})`);
|
|
21418
|
-
if (
|
|
21856
|
+
if (cornerOpened)
|
|
21419
21857
|
this.options.onCornerOpened?.();
|
|
21420
21858
|
}
|
|
21421
21859
|
for (const call of result?.toolCalls ?? []) {
|
|
@@ -21433,8 +21871,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
21433
21871
|
}
|
|
21434
21872
|
console.warn(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: ${explained.reason}`);
|
|
21435
21873
|
}
|
|
21436
|
-
if (
|
|
21437
|
-
reply =
|
|
21874
|
+
if (cornerOpened) {
|
|
21875
|
+
reply = "";
|
|
21438
21876
|
}
|
|
21439
21877
|
await trace.measure("publish", () => stream.settle(reply, reply ? {
|
|
21440
21878
|
triggerMessageId: item.id
|
|
@@ -21455,6 +21893,9 @@ var MonolithRoomTurnLoop = class {
|
|
|
21455
21893
|
await trace.finish("cancelled");
|
|
21456
21894
|
return;
|
|
21457
21895
|
}
|
|
21896
|
+
await liveStream?.retract().catch((retractError) => {
|
|
21897
|
+
console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
|
|
21898
|
+
});
|
|
21458
21899
|
const reason = distillTurnFailureReason(error);
|
|
21459
21900
|
await api.execute("postAgentTurnReceipt", {
|
|
21460
21901
|
agentId: this.agent.publicKey,
|
|
@@ -21462,9 +21903,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
21462
21903
|
requestId: item.id,
|
|
21463
21904
|
status: "failed",
|
|
21464
21905
|
generationId: this.commandContext.generationId,
|
|
21465
|
-
reason
|
|
21906
|
+
reason: reason.text,
|
|
21907
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
21466
21908
|
});
|
|
21467
|
-
await trace.finish("failed", reason);
|
|
21909
|
+
await trace.finish("failed", reason.text);
|
|
21468
21910
|
throw error;
|
|
21469
21911
|
} finally {
|
|
21470
21912
|
this.busy = false;
|
|
@@ -21507,6 +21949,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
21507
21949
|
}
|
|
21508
21950
|
}
|
|
21509
21951
|
};
|
|
21952
|
+
function openCornerToolCall(calls) {
|
|
21953
|
+
return calls.find((call) => /(?:^|[._:/-])open_corner$/i.test(call.title ?? ""));
|
|
21954
|
+
}
|
|
21955
|
+
function openedACorner(call) {
|
|
21956
|
+
return !!call && isCompletedToolCall(call);
|
|
21957
|
+
}
|
|
21510
21958
|
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true) {
|
|
21511
21959
|
const message = body.trim() || "(shared attachments)";
|
|
21512
21960
|
const rendered = author ? `${author}: ${message}` : message;
|
|
@@ -21518,9 +21966,65 @@ var execFileAsync3 = promisify3(execFile4);
|
|
|
21518
21966
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
21519
21967
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
21520
21968
|
var TOOL_PATH_LIMIT = 12;
|
|
21521
|
-
function cornerMergeInstruction(yoloMode) {
|
|
21969
|
+
function cornerMergeInstruction(yoloMode, reviewerHandle) {
|
|
21970
|
+
if (reviewerHandle)
|
|
21971
|
+
return `Commit, push, open the pull request, reply with the PR URL and then @${reviewerHandle} please review; never merge this PR yourself. If you asked any other agent to review in this corner, do not merge until they answer.`;
|
|
21522
21972
|
return yoloMode ? "Yolo is on: when the gate passes, merge this pull request with gh." : "Yolo is off: never merge; wait for explicit human approval in the app.";
|
|
21523
21973
|
}
|
|
21974
|
+
function cornerReviewerInstruction(input) {
|
|
21975
|
+
if (!input.reviewerHandle || !input.agentHandle || input.openedByAgent || input.agentHandle.replace(/^@/, "") !== input.reviewerHandle.replace(/^@/, ""))
|
|
21976
|
+
return void 0;
|
|
21977
|
+
const author = input.authorHandle?.replace(/^@/, "") || "author";
|
|
21978
|
+
const number = input.pullRequestNumber ?? "N";
|
|
21979
|
+
return `Review PR #${number} with the beeline-review skill. If it fails, reply @${author} with the findings. If it passes and the gate (pr_checks_status: checks=passed, held=false, approvalPending=false) is open and YOUR yolo is on, merge with gh pr merge --squash --match-head-commit <sha you reviewed>; if your yolo is off, reply approved <sha> and stop; if checks are pending, reply approved pending checks <sha> and stop.`;
|
|
21980
|
+
}
|
|
21981
|
+
var CORNER_AUTHOR_CONTRACT = `The objective text is the user's ask. Keep it verbatim in your head and do not reinterpret it.
|
|
21982
|
+
Before any code, write its end-user story in one sentence: "a person who does X sees Y".
|
|
21983
|
+
If the objective reports a defect, reproduce it first at the layer where it lives: the command, request, or tap sequence, and what was observed.
|
|
21984
|
+
Do not write a fix before you have seen the defect. Turn the reproduction into the regression test.
|
|
21985
|
+
Before opening the pull request, produce Y against the built change: run the app or affected service from your branch and perform X.
|
|
21986
|
+
If no interactive surface is reachable, run the narrowest test or script that exercises the exact user path and prints the observable Y.
|
|
21987
|
+
A unit test of an inner function, a log line, or reading the code is not a demonstration.
|
|
21988
|
+
The pull request body MUST contain two sections with exactly these headings: ## Reproduced and ## Demonstrated.
|
|
21989
|
+
Under ## Reproduced, give the steps or command and what was observed; write "not a defect report" for feature work.
|
|
21990
|
+
Under ## Demonstrated, give the command or steps that produced Y and what was observed.
|
|
21991
|
+
A pull request without both sections is not deliverable and the Room's reviewer will fail it.
|
|
21992
|
+
Change only what the objective asks. No unrequested features, flags, compatibility shims, or refactors.`;
|
|
21993
|
+
var CORNER_DELIVERY_NUDGE = "Before ending this turn, inspect the repository state and finish delivering the work: commit and push the intended changes and open the pull request if one does not exist. Decide yourself whether any remaining dirty work belongs to the objective; do not discard it merely to make the worktree clean. The pull request body must carry ## Reproduced and ## Demonstrated; if they are missing, add them before ending the turn.";
|
|
21994
|
+
var CORNER_YOLO_MERGE_NUDGE = 'Yolo is on. Check the server merge gate with pr_checks_status now and, if checks="passed", held=false, and approvalPending=false, merge this pull request with gh. Otherwise stop without merging.';
|
|
21995
|
+
function isCornerChecksTurn(trigger, restates) {
|
|
21996
|
+
return Boolean(restates) || /\b(?:passed|failed) a check\b/i.test(trigger);
|
|
21997
|
+
}
|
|
21998
|
+
async function cornerUndeliveredRepositoryState(worktreePath, featureBranch, targetBranch) {
|
|
21999
|
+
try {
|
|
22000
|
+
const [{ stdout: status }, ahead] = await Promise.all([
|
|
22001
|
+
execFileAsync3("git", ["-C", worktreePath, "status", "--porcelain=v1"]),
|
|
22002
|
+
featureBranch ? firstResolvedRemoteRef(worktreePath, [featureBranch, targetBranch]).then((remoteRef) => remoteRef ? execFileAsync3("git", [
|
|
22003
|
+
"-C",
|
|
22004
|
+
worktreePath,
|
|
22005
|
+
"rev-list",
|
|
22006
|
+
"--count",
|
|
22007
|
+
`${remoteRef}..HEAD`
|
|
22008
|
+
]).then(({ stdout: stdout6 }) => Number.parseInt(stdout6.trim(), 10) || 0) : 0).catch(() => 0) : Promise.resolve(0)
|
|
22009
|
+
]);
|
|
22010
|
+
const dirty = status.trimEnd();
|
|
22011
|
+
return dirty || ahead > 0 ? `${dirty}
|
|
22012
|
+
ahead:${ahead}` : void 0;
|
|
22013
|
+
} catch {
|
|
22014
|
+
return void 0;
|
|
22015
|
+
}
|
|
22016
|
+
}
|
|
22017
|
+
async function firstResolvedRemoteRef(worktreePath, branches) {
|
|
22018
|
+
for (const branch of branches) {
|
|
22019
|
+
if (!branch)
|
|
22020
|
+
continue;
|
|
22021
|
+
const ref = `refs/remotes/origin/${branch}`;
|
|
22022
|
+
const resolved = await execFileAsync3("git", ["-C", worktreePath, "rev-parse", "--verify", ref]).then(() => true).catch(() => false);
|
|
22023
|
+
if (resolved)
|
|
22024
|
+
return ref;
|
|
22025
|
+
}
|
|
22026
|
+
return void 0;
|
|
22027
|
+
}
|
|
21524
22028
|
function oneLine(value) {
|
|
21525
22029
|
return value.replace(/\s+/g, " ").trim();
|
|
21526
22030
|
}
|
|
@@ -21606,7 +22110,7 @@ function toolArguments(call) {
|
|
|
21606
22110
|
return input ? { input: clampBytes(redactToolDetail(input), TOOL_ARGUMENT_MAX_BYTES) } : {};
|
|
21607
22111
|
}
|
|
21608
22112
|
function toolCallKey(call, index) {
|
|
21609
|
-
return call.id ? `id-${
|
|
22113
|
+
return call.id ? `id-${createHash5("sha256").update(call.id).digest("hex")}` : `tool-${index}`;
|
|
21610
22114
|
}
|
|
21611
22115
|
function toolCallSettled(call) {
|
|
21612
22116
|
if (call.resultReceived)
|
|
@@ -21669,6 +22173,14 @@ var MonolithCornerTurnLoop = class {
|
|
|
21669
22173
|
modelTakesImages;
|
|
21670
22174
|
/** The one provider re-pinned after an empty completion, until the session ends. */
|
|
21671
22175
|
pinnedProviderOverride;
|
|
22176
|
+
/** The merge authority baked into the current session. */
|
|
22177
|
+
yoloMode = false;
|
|
22178
|
+
/** The live parent-Room reviewer baked into the current session. */
|
|
22179
|
+
reviewerHandle;
|
|
22180
|
+
/** The role-specific second-chance instruction for this session. */
|
|
22181
|
+
cornerTurnEndNudge = CORNER_DELIVERY_NUDGE;
|
|
22182
|
+
/** Repository state already given a delivery reminder, until that state changes. */
|
|
22183
|
+
lastDeliveryNudgeState;
|
|
21672
22184
|
turnIdentityInstructions = "";
|
|
21673
22185
|
busy = false;
|
|
21674
22186
|
forcedStop = false;
|
|
@@ -21693,6 +22205,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
21693
22205
|
* by the same replay window the inbox already de-duplicates over.
|
|
21694
22206
|
*/
|
|
21695
22207
|
stoppedTurns = /* @__PURE__ */ new Set();
|
|
22208
|
+
/** In-flight warm-store harvest, awaited at shutdown and never by a turn. */
|
|
22209
|
+
harvest;
|
|
21696
22210
|
constructor(options) {
|
|
21697
22211
|
this.options = options;
|
|
21698
22212
|
this.agent = runtimeIdentity(options.runtime.agent);
|
|
@@ -21782,7 +22296,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
21782
22296
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
21783
22297
|
soul: configuration.soul ?? self?.soul,
|
|
21784
22298
|
agentName: self?.name ?? this.agent.name,
|
|
21785
|
-
yoloMode: configuration.yoloMode
|
|
22299
|
+
yoloMode: configuration.yoloMode,
|
|
22300
|
+
reviewerHandle: configuration.reviewerHandle
|
|
21786
22301
|
});
|
|
21787
22302
|
}
|
|
21788
22303
|
async activate(trace) {
|
|
@@ -21802,9 +22317,31 @@ var MonolithCornerTurnLoop = class {
|
|
|
21802
22317
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
21803
22318
|
soul: configuration.soul ?? self?.soul,
|
|
21804
22319
|
agentName: self?.name ?? this.agent.name,
|
|
21805
|
-
yoloMode: configuration.yoloMode
|
|
22320
|
+
yoloMode: configuration.yoloMode,
|
|
22321
|
+
reviewerHandle: configuration.reviewerHandle
|
|
21806
22322
|
});
|
|
21807
|
-
|
|
22323
|
+
this.yoloMode = configuration.yoloMode;
|
|
22324
|
+
this.reviewerHandle = configuration.reviewerHandle;
|
|
22325
|
+
const opener = this.options.openedBy ? roster.members.find((member) => member.identityId === this.options.openedBy) : void 0;
|
|
22326
|
+
const reviewerInput = {
|
|
22327
|
+
reviewerHandle: configuration.reviewerHandle,
|
|
22328
|
+
agentHandle: self?.handle,
|
|
22329
|
+
authorHandle: opener?.handle,
|
|
22330
|
+
openedByAgent: !this.options.openedBy || this.options.openedBy === this.agent.publicKey,
|
|
22331
|
+
yoloMode: configuration.yoloMode
|
|
22332
|
+
};
|
|
22333
|
+
let reviewerInstruction = cornerReviewerInstruction(reviewerInput);
|
|
22334
|
+
if (reviewerInstruction) {
|
|
22335
|
+
const restore = await this.options.api.execute("getCornerRestoreState", {
|
|
22336
|
+
cornerId: this.options.cornerId
|
|
22337
|
+
});
|
|
22338
|
+
reviewerInstruction = cornerReviewerInstruction({
|
|
22339
|
+
...reviewerInput,
|
|
22340
|
+
pullRequestNumber: restore.lifecycle?.pr?.number
|
|
22341
|
+
});
|
|
22342
|
+
}
|
|
22343
|
+
this.cornerTurnEndNudge = reviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
|
|
22344
|
+
await mkdir12(this.options.worktreePath, { recursive: true });
|
|
21808
22345
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
21809
22346
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
21810
22347
|
root: this.options.config.agentHomeRoot,
|
|
@@ -21836,10 +22373,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
21836
22373
|
inheritedPath: this.options.config.agentEnv.PATH ?? process.env.PATH
|
|
21837
22374
|
});
|
|
21838
22375
|
}
|
|
22376
|
+
const npmCacheDir = sharedNpmCacheDir(this.options.runtime.supervisorRoot);
|
|
22377
|
+
await mkdir12(npmCacheDir, { recursive: true, mode: 448 });
|
|
21839
22378
|
const agentEnv = {
|
|
21840
22379
|
...this.options.config.agentEnv,
|
|
21841
22380
|
...homeOverlay,
|
|
21842
|
-
...githubEnv
|
|
22381
|
+
...githubEnv,
|
|
22382
|
+
npm_config_cache: npmCacheDir
|
|
21843
22383
|
};
|
|
21844
22384
|
this.agentEnv = agentEnv;
|
|
21845
22385
|
const agentArgs = agentArgsWithModelSelection({
|
|
@@ -21849,13 +22389,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
21849
22389
|
}, selection);
|
|
21850
22390
|
const operatorHome = this.options.config.operatorHome ?? homedir7();
|
|
21851
22391
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
21852
|
-
this.attachmentDir = tmpDir ?
|
|
22392
|
+
this.attachmentDir = tmpDir ? join8(tmpDir, "beeline-attachments") : void 0;
|
|
21853
22393
|
this.sessionScratchDir = tmpDir;
|
|
21854
22394
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
21855
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
22395
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir12(dir, { recursive: true })));
|
|
21856
22396
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
21857
22397
|
if (attachScratchRoot)
|
|
21858
|
-
await
|
|
22398
|
+
await mkdir12(attachScratchRoot, { recursive: true });
|
|
21859
22399
|
const spawnCommand = wrapAgentCommand({
|
|
21860
22400
|
bwrapPath: this.options.config.bwrapPath,
|
|
21861
22401
|
spec: {
|
|
@@ -21867,7 +22407,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
21867
22407
|
harnessStateDirs: stateDirs,
|
|
21868
22408
|
harnessHomeStateDirs: homeStateDirs,
|
|
21869
22409
|
...tmpDir ? { tmpDir } : {},
|
|
21870
|
-
|
|
22410
|
+
additionalWritablePaths: [
|
|
22411
|
+
...attachScratchRoot ? [attachScratchRoot] : [],
|
|
22412
|
+
// The shared npm cache. npm writes to its cache on every install,
|
|
22413
|
+
// and this one is deliberately outside the per-corner home so the
|
|
22414
|
+
// download is paid once per host rather than once per corner.
|
|
22415
|
+
npmCacheDir
|
|
22416
|
+
],
|
|
21871
22417
|
maskPaths: credentialMaskPaths(this.options.config.sandboxMaskPaths, operatorHome)
|
|
21872
22418
|
},
|
|
21873
22419
|
command,
|
|
@@ -21896,7 +22442,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
21896
22442
|
// the corner harness without inheriting any host credentials.
|
|
21897
22443
|
env: [
|
|
21898
22444
|
{ name: "GH_TOKEN", value: repository.githubToken },
|
|
21899
|
-
{ name: "GITHUB_TOKEN", value: repository.githubToken }
|
|
22445
|
+
{ name: "GITHUB_TOKEN", value: repository.githubToken },
|
|
22446
|
+
// Its shell tool runs the same installs the harness does, and
|
|
22447
|
+
// a sanitized env would otherwise send them to npm's default
|
|
22448
|
+
// per-corner cache.
|
|
22449
|
+
{ name: "npm_config_cache", value: npmCacheDir }
|
|
21900
22450
|
]
|
|
21901
22451
|
}
|
|
21902
22452
|
] : [],
|
|
@@ -21934,8 +22484,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
21934
22484
|
...repository ? [
|
|
21935
22485
|
`You are in an isolated git worktree on ${repository.featureBranch}, targeting ${repository.targetBranch}.`,
|
|
21936
22486
|
`Commit and push only ${repository.featureBranch}; never force-push or write to ${repository.targetBranch}. Before pushing, rebase on origin/${repository.featureBranch}; resolve conflicts autonomously, realigning to that remote branch and redoing the objective if needed, then rerun affected tests. Open the pull request with gh.`,
|
|
21937
|
-
|
|
21938
|
-
|
|
22487
|
+
...reviewerInstruction ? [reviewerInstruction] : [
|
|
22488
|
+
configuration.reviewerHandle ? `Once the pull request exists, reply with its full URL, then @${configuration.reviewerHandle} please review, and end the turn; do not check or wait for CI.` : 'Once the pull request exists, reply only with its full URL and end the turn; do not check or wait for CI. On a later checks turn, call pr_checks_status. Merge only when checks="passed", held=false, and approvalPending=false; only a later explicit human resume clears a hold.',
|
|
22489
|
+
CORNER_AUTHOR_CONTRACT,
|
|
22490
|
+
cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
|
|
22491
|
+
],
|
|
21939
22492
|
"Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
|
|
21940
22493
|
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. When approval is pending, wait for the server close request. Never merge another pull request."
|
|
21941
22494
|
] : [
|
|
@@ -22056,7 +22609,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
22056
22609
|
const [conversation, roster, delivered] = await trace.measure("context-fetch", () => Promise.all([
|
|
22057
22610
|
api.execute("getRoomConversation", { roomId: cornerId, limit: 200 }),
|
|
22058
22611
|
this.roster(),
|
|
22059
|
-
this.attachmentDir && attachments.length ? deliverAttachments(attachments,
|
|
22612
|
+
this.attachmentDir && attachments.length ? deliverAttachments(attachments, join8(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
|
|
22060
22613
|
]));
|
|
22061
22614
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
22062
22615
|
const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
|
|
@@ -22158,7 +22711,7 @@ ${trigger}`,
|
|
|
22158
22711
|
publishToolCalls(calls, false);
|
|
22159
22712
|
await this.activityTail;
|
|
22160
22713
|
};
|
|
22161
|
-
const runPrompt = async () => {
|
|
22714
|
+
const runPrompt = async (prompt = buildPrompt()) => {
|
|
22162
22715
|
activityAttempt += 1;
|
|
22163
22716
|
publishedToolCalls.clear();
|
|
22164
22717
|
observedToolCalls.clear();
|
|
@@ -22170,7 +22723,7 @@ ${trigger}`,
|
|
|
22170
22723
|
narrationRunBoundary = 0;
|
|
22171
22724
|
currentNarrationRun = "";
|
|
22172
22725
|
trace.promptSent();
|
|
22173
|
-
return this.client.sessionPrompt(this.sessionId, promptWithImages(
|
|
22726
|
+
return this.client.sessionPrompt(this.sessionId, promptWithImages(prompt, attachmentImageBlocks(delivered, this.acceptsImages())), 12e4, (delta, full, currentRun, runs) => {
|
|
22174
22727
|
trace.firstModelOutput();
|
|
22175
22728
|
if (runs) {
|
|
22176
22729
|
completedNarrationRuns = runs.slice(narrationRunBoundary);
|
|
@@ -22202,6 +22755,19 @@ ${trigger}`,
|
|
|
22202
22755
|
explained = await this.explainEmpty(result);
|
|
22203
22756
|
}
|
|
22204
22757
|
}
|
|
22758
|
+
const checksTurn = isCornerChecksTurn(trigger, restates);
|
|
22759
|
+
const deliveryState = !checksTurn && this.options.repository ? await cornerUndeliveredRepositoryState(this.options.worktreePath, this.options.repository.featureBranch, this.options.repository.targetBranch) : void 0;
|
|
22760
|
+
const needsDeliveryNudge = deliveryState !== void 0 && deliveryState !== this.lastDeliveryNudgeState;
|
|
22761
|
+
let replyBeforeNudge = "";
|
|
22762
|
+
if (!explained && (needsDeliveryNudge || checksTurn && (this.yoloMode || Boolean(this.reviewerHandle)))) {
|
|
22763
|
+
if (needsDeliveryNudge)
|
|
22764
|
+
this.lastDeliveryNudgeState = deliveryState;
|
|
22765
|
+
replyBeforeNudge = durableReplyText(result.agentText);
|
|
22766
|
+
await flushToolCalls(result.toolCalls, "");
|
|
22767
|
+
result = await runPrompt(this.reviewerHandle ? this.cornerTurnEndNudge : checksTurn ? CORNER_YOLO_MERGE_NUDGE : CORNER_DELIVERY_NUDGE);
|
|
22768
|
+
trace.promptSettled();
|
|
22769
|
+
explained = await this.explainEmpty(result);
|
|
22770
|
+
}
|
|
22205
22771
|
if (this.stoppedTurns.has(requestId)) {
|
|
22206
22772
|
stream.close();
|
|
22207
22773
|
throw new TurnStoppedError("turn stopped by the requester");
|
|
@@ -22209,6 +22775,7 @@ ${trigger}`,
|
|
|
22209
22775
|
let reply = durableReplyText(result.agentText);
|
|
22210
22776
|
if (!reply && explained?.recoveredText)
|
|
22211
22777
|
reply = durableReplyText(explained.recoveredText);
|
|
22778
|
+
reply = [replyBeforeNudge, reply].filter(Boolean).join("\n\n");
|
|
22212
22779
|
await flushToolCalls(result.toolCalls, reply);
|
|
22213
22780
|
for (const call of result.toolCalls) {
|
|
22214
22781
|
const failure = toolCallFailureLine(call);
|
|
@@ -22249,15 +22816,47 @@ ${trigger}`,
|
|
|
22249
22816
|
requestId,
|
|
22250
22817
|
status: "failed",
|
|
22251
22818
|
generationId: this.commandContext.generationId,
|
|
22252
|
-
reason
|
|
22819
|
+
reason: reason.text,
|
|
22820
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
22253
22821
|
});
|
|
22254
|
-
await trace.finish("failed", reason);
|
|
22822
|
+
await trace.finish("failed", reason.text);
|
|
22255
22823
|
throw error;
|
|
22256
22824
|
} finally {
|
|
22257
22825
|
this.busy = false;
|
|
22258
22826
|
this.currentTurn = void 0;
|
|
22827
|
+
this.harvestWarmNodeModules();
|
|
22259
22828
|
}
|
|
22260
22829
|
}
|
|
22830
|
+
/**
|
|
22831
|
+
* Offer this worktree's `node_modules` to the host-wide warm store.
|
|
22832
|
+
*
|
|
22833
|
+
* A turn's end is the moment the tree is most likely to be a finished
|
|
22834
|
+
* install, and the store keeps only the first complete tree per lockfile —
|
|
22835
|
+
* so after the first corner on a lockfile this is one `stat` that answers
|
|
22836
|
+
* `already-warm`. The copy that the first one does is deliberately NOT
|
|
22837
|
+
* awaited by the turn: nobody in the Room is waiting on a cache, and the
|
|
22838
|
+
* only thing that waits is shutdown, so a half-copied tree is never left
|
|
22839
|
+
* where a later corner could read it as complete.
|
|
22840
|
+
*/
|
|
22841
|
+
harvestWarmNodeModules() {
|
|
22842
|
+
if (this.harvest || !this.options.repository)
|
|
22843
|
+
return;
|
|
22844
|
+
const { cornerId, worktreePath, runtime } = this.options;
|
|
22845
|
+
const run2 = harvestWarmNodeModules({
|
|
22846
|
+
worktreePath,
|
|
22847
|
+
storeRoot: warmNodeModulesStoreDir(runtime.supervisorRoot)
|
|
22848
|
+
}).then((outcome) => {
|
|
22849
|
+
if (outcome.reason === "already-warm" || outcome.reason === "no-lockfile")
|
|
22850
|
+
return;
|
|
22851
|
+
console.log(`[thin-core] corner ${cornerId} warm node_modules harvest: ${outcome.reason}${outcome.detail ? ` (${outcome.detail})` : ""}`);
|
|
22852
|
+
}).catch((error) => {
|
|
22853
|
+
console.error(`[thin-core] corner ${cornerId} warm node_modules harvest failed:`, error);
|
|
22854
|
+
}).finally(() => {
|
|
22855
|
+
if (this.harvest === run2)
|
|
22856
|
+
this.harvest = void 0;
|
|
22857
|
+
});
|
|
22858
|
+
this.harvest = run2;
|
|
22859
|
+
}
|
|
22261
22860
|
/**
|
|
22262
22861
|
* Bring this worktree onto the corner's branch as GitHub currently has it,
|
|
22263
22862
|
* before any work is done on top of it.
|
|
@@ -22304,6 +22903,7 @@ ${trigger}`,
|
|
|
22304
22903
|
const state = await api.execute("getCornerRestoreState", { cornerId });
|
|
22305
22904
|
if (!state.closeRequested)
|
|
22306
22905
|
return false;
|
|
22906
|
+
await this.harvest;
|
|
22307
22907
|
await this.options.onCloseRequested();
|
|
22308
22908
|
return true;
|
|
22309
22909
|
},
|
|
@@ -22312,6 +22912,7 @@ ${trigger}`,
|
|
|
22312
22912
|
} finally {
|
|
22313
22913
|
this.options.grantRunner?.unregister(cornerId);
|
|
22314
22914
|
await this.options.scheduler.suspend(cornerId);
|
|
22915
|
+
await this.harvest;
|
|
22315
22916
|
}
|
|
22316
22917
|
}
|
|
22317
22918
|
};
|
|
@@ -22732,13 +23333,13 @@ function shouldPostInitialCornerWorkingState(restore, isOpener = true) {
|
|
|
22732
23333
|
}
|
|
22733
23334
|
async function materializeCornerWorktree(input) {
|
|
22734
23335
|
const remote = roomCheckoutRemote(input.remote);
|
|
22735
|
-
const repositoryHash =
|
|
22736
|
-
const gitCommonDir =
|
|
22737
|
-
const path =
|
|
22738
|
-
await
|
|
22739
|
-
await
|
|
23336
|
+
const repositoryHash = createHash6("sha256").update(remote).digest("hex").slice(0, 24);
|
|
23337
|
+
const gitCommonDir = resolve20(input.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
23338
|
+
const path = resolve20(input.supervisorRoot, "beeline", "corners", input.cornerId);
|
|
23339
|
+
await mkdir13(dirname8(gitCommonDir), { recursive: true, mode: 448 });
|
|
23340
|
+
await mkdir13(dirname8(path), { recursive: true, mode: 448 });
|
|
22740
23341
|
const authEnv = githubGitEnv(input.token);
|
|
22741
|
-
if (!existsSync4(
|
|
23342
|
+
if (!existsSync4(resolve20(gitCommonDir, "HEAD"))) {
|
|
22742
23343
|
await execFileAsync4("git", ["clone", "--bare", remote, gitCommonDir], {
|
|
22743
23344
|
env: authEnv,
|
|
22744
23345
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -22757,8 +23358,8 @@ async function materializeCornerWorktree(input) {
|
|
|
22757
23358
|
"origin",
|
|
22758
23359
|
`+refs/heads/${input.featureBranch}:refs/remotes/origin/${input.featureBranch}`
|
|
22759
23360
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 }).then(() => true, () => false);
|
|
22760
|
-
if (!existsSync4(
|
|
22761
|
-
await
|
|
23361
|
+
if (!existsSync4(resolve20(path, ".git"))) {
|
|
23362
|
+
await rm5(path, { recursive: true, force: true });
|
|
22762
23363
|
await execFileAsync4("git", [
|
|
22763
23364
|
`--git-dir=${gitCommonDir}`,
|
|
22764
23365
|
"worktree",
|
|
@@ -22768,6 +23369,13 @@ async function materializeCornerWorktree(input) {
|
|
|
22768
23369
|
path,
|
|
22769
23370
|
restored ? `refs/remotes/origin/${input.featureBranch}` : `refs/remotes/origin/${input.targetBranch}`
|
|
22770
23371
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
|
|
23372
|
+
const seed = await seedWarmNodeModules({
|
|
23373
|
+
worktreePath: path,
|
|
23374
|
+
storeRoot: warmNodeModulesStoreDir(input.supervisorRoot)
|
|
23375
|
+
});
|
|
23376
|
+
if (seed.reason !== "no-lockfile") {
|
|
23377
|
+
console.log(`[thin-core] corner ${input.cornerId} warm node_modules: ${seed.reason}${seed.detail ? ` (${seed.detail})` : ""}`);
|
|
23378
|
+
}
|
|
22771
23379
|
}
|
|
22772
23380
|
await execFileAsync4("git", [
|
|
22773
23381
|
`--git-dir=${gitCommonDir}`,
|
|
@@ -22801,7 +23409,7 @@ async function materializeCornerWorktree(input) {
|
|
|
22801
23409
|
`${input.committer.publicKey.slice(0, 16)}@users.noreply.github.com`
|
|
22802
23410
|
]);
|
|
22803
23411
|
const top = await execFileAsync4("git", ["-C", path, "rev-parse", "--show-toplevel"]);
|
|
22804
|
-
if (
|
|
23412
|
+
if (resolve20(top.stdout.trim()) !== resolve20(path)) {
|
|
22805
23413
|
throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
|
|
22806
23414
|
}
|
|
22807
23415
|
return { path, gitCommonDir };
|
|
@@ -22867,11 +23475,11 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
22867
23475
|
}
|
|
22868
23476
|
var execFileAsync4 = promisify4(execFile5);
|
|
22869
23477
|
async function removeCornerScratchWorkspace(input) {
|
|
22870
|
-
const expected =
|
|
22871
|
-
if (
|
|
23478
|
+
const expected = resolve20(input.roomRoot, "scratch");
|
|
23479
|
+
if (resolve20(input.scratchPath) !== expected) {
|
|
22872
23480
|
throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
|
|
22873
23481
|
}
|
|
22874
|
-
await
|
|
23482
|
+
await rm5(expected, { recursive: true, force: true });
|
|
22875
23483
|
}
|
|
22876
23484
|
var RoomRuntimeCoordinator = class {
|
|
22877
23485
|
configPath;
|
|
@@ -23061,13 +23669,13 @@ var RoomRuntimeCoordinator = class {
|
|
|
23061
23669
|
return this.runtime.rooms.find((room) => room.channelId === roomId);
|
|
23062
23670
|
}
|
|
23063
23671
|
roomRoot(roomId) {
|
|
23064
|
-
return this.roomRecord(roomId)?.root ??
|
|
23672
|
+
return this.roomRecord(roomId)?.root ?? resolve20(dirname8(this.configPath), "rooms", roomId);
|
|
23065
23673
|
}
|
|
23066
23674
|
roomAgentHomeRoot(workspaceRoot, required = false) {
|
|
23067
23675
|
const flag = process.env.BUZZY_BODY_ROOM_HOME;
|
|
23068
23676
|
if (!required && flag === "0")
|
|
23069
23677
|
return void 0;
|
|
23070
|
-
const home =
|
|
23678
|
+
const home = resolve20(workspaceRoot, "agent-home");
|
|
23071
23679
|
if (!required && flag !== "1" && !existsSync4(home) && existsSync4(workspaceRoot))
|
|
23072
23680
|
return void 0;
|
|
23073
23681
|
try {
|
|
@@ -23083,10 +23691,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
23083
23691
|
return {
|
|
23084
23692
|
...this.baseConfig,
|
|
23085
23693
|
workspaceRoot,
|
|
23086
|
-
agentPrivateRoot:
|
|
23087
|
-
agentMemoryRoot:
|
|
23088
|
-
openRouterRoutingCacheDir: openRouterRoutingCacheDir(
|
|
23089
|
-
turnTraceDir: turnTraceDirectory(
|
|
23694
|
+
agentPrivateRoot: resolve20(workspaceRoot, "agent-private"),
|
|
23695
|
+
agentMemoryRoot: resolve20(dirname8(this.configPath), "memory"),
|
|
23696
|
+
openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname8(this.configPath)),
|
|
23697
|
+
turnTraceDir: turnTraceDirectory(dirname8(this.configPath)),
|
|
23090
23698
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
23091
23699
|
};
|
|
23092
23700
|
}
|
|
@@ -23152,12 +23760,12 @@ var RoomRuntimeCoordinator = class {
|
|
|
23152
23760
|
return this.roomRoot(roomId);
|
|
23153
23761
|
const remote = roomCheckoutRemote(repository.remote);
|
|
23154
23762
|
const targetBranch = repository.targetBranch || "main";
|
|
23155
|
-
const checkoutId =
|
|
23156
|
-
const path =
|
|
23157
|
-
await
|
|
23763
|
+
const checkoutId = createHash6("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
23764
|
+
const path = resolve20(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
23765
|
+
await mkdir13(dirname8(path), { recursive: true, mode: 448 });
|
|
23158
23766
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
23159
23767
|
const env = token ? githubGitEnv(token.token) : process.env;
|
|
23160
|
-
if (!existsSync4(
|
|
23768
|
+
if (!existsSync4(resolve20(path, ".git"))) {
|
|
23161
23769
|
await execFileAsync4("git", ["clone", "--no-checkout", remote, path], {
|
|
23162
23770
|
env,
|
|
23163
23771
|
maxBuffer: 4 * 1024 * 1024
|
|
@@ -23210,9 +23818,9 @@ var RoomRuntimeCoordinator = class {
|
|
|
23210
23818
|
featureBranch,
|
|
23211
23819
|
token: granted.token
|
|
23212
23820
|
}) : void 0;
|
|
23213
|
-
const workspacePath = worktree?.path ??
|
|
23821
|
+
const workspacePath = worktree?.path ?? resolve20(this.roomRoot(corner.cornerId), "scratch");
|
|
23214
23822
|
if (!worktree)
|
|
23215
|
-
await
|
|
23823
|
+
await mkdir13(workspacePath, { recursive: true, mode: 448 });
|
|
23216
23824
|
const isOpener = !corner.openedBy || corner.openedBy === this.agent.publicKey;
|
|
23217
23825
|
if (worktree && shouldPostInitialCornerWorkingState(restore, isOpener)) {
|
|
23218
23826
|
await this.options.daemonApi.execute("postCornerRemoteState", {
|
|
@@ -23316,13 +23924,15 @@ var RoomRuntimeCoordinator = class {
|
|
|
23316
23924
|
const asked = [...conversation.items].reverse().find((item) => item.type === "message" && item.mentionIds.includes(this.agent.publicKey));
|
|
23317
23925
|
if (!asked)
|
|
23318
23926
|
return;
|
|
23927
|
+
const reason = distillTurnFailureReason(error);
|
|
23319
23928
|
await this.options.daemonApi.execute("postAgentTurnReceipt", {
|
|
23320
23929
|
agentId: this.agent.publicKey,
|
|
23321
23930
|
roomId: cornerId,
|
|
23322
23931
|
requestId: asked.id,
|
|
23323
23932
|
status: "failed",
|
|
23324
23933
|
generationId: `${this.agent.publicKey}:${cornerId}`,
|
|
23325
|
-
reason:
|
|
23934
|
+
reason: reason.text,
|
|
23935
|
+
...reason.kind ? { reasonKind: reason.kind } : {}
|
|
23326
23936
|
});
|
|
23327
23937
|
} catch (reportError) {
|
|
23328
23938
|
console.error(`[thin-core] corner ${cornerId} start-failure report failed:`, reportError);
|
|
@@ -23595,9 +24205,9 @@ var ThinDaemonCore = class {
|
|
|
23595
24205
|
|
|
23596
24206
|
// apps/body/dist/systemd.js
|
|
23597
24207
|
import { execFile as execFile6 } from "node:child_process";
|
|
23598
|
-
import { mkdir as
|
|
24208
|
+
import { mkdir as mkdir14, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
23599
24209
|
import { homedir as homedir8 } from "node:os";
|
|
23600
|
-
import { dirname as
|
|
24210
|
+
import { dirname as dirname9, resolve as resolve21 } from "node:path";
|
|
23601
24211
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
23602
24212
|
import { promisify as promisify5 } from "node:util";
|
|
23603
24213
|
var execFileAsync5 = promisify5(execFile6);
|
|
@@ -23639,9 +24249,9 @@ WantedBy=default.target
|
|
|
23639
24249
|
}
|
|
23640
24250
|
function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
|
|
23641
24251
|
const home = env.HOME?.trim() || homedir8();
|
|
23642
|
-
const expectedLibDir =
|
|
24252
|
+
const expectedLibDir = resolve21(home, ".local", "lib", "beeline");
|
|
23643
24253
|
const expectedPrefix = `${expectedLibDir}/`;
|
|
23644
|
-
return
|
|
24254
|
+
return resolve21(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve21(invocationPath).startsWith(expectedPrefix);
|
|
23645
24255
|
}
|
|
23646
24256
|
function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
23647
24257
|
if (isCanonicalInstalledLauncher(env, invocationPath))
|
|
@@ -23649,8 +24259,8 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
|
23649
24259
|
throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
|
|
23650
24260
|
}
|
|
23651
24261
|
function systemdUserUnitPath(env = process.env) {
|
|
23652
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
23653
|
-
return
|
|
24262
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve21(homedir8(), ".config");
|
|
24263
|
+
return resolve21(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
|
|
23654
24264
|
}
|
|
23655
24265
|
var runSystemctl = async (args) => {
|
|
23656
24266
|
const result = await execFileAsync5("systemctl", ["--user", ...args], {
|
|
@@ -23666,9 +24276,9 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
23666
24276
|
assertCanonicalInstalledLauncher(env, options.invocationPath);
|
|
23667
24277
|
const path = systemdUserUnitPath(env);
|
|
23668
24278
|
const content = agentServiceUnit();
|
|
23669
|
-
const existing = await
|
|
24279
|
+
const existing = await readFile9(path, "utf8").catch(() => "");
|
|
23670
24280
|
if (existing !== content) {
|
|
23671
|
-
await
|
|
24281
|
+
await mkdir14(dirname9(path), { recursive: true, mode: 448 });
|
|
23672
24282
|
await writeFile9(path, content, { mode: 384 });
|
|
23673
24283
|
}
|
|
23674
24284
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -23749,7 +24359,7 @@ async function retireRemovedAgent(runtime, options = {}) {
|
|
|
23749
24359
|
}
|
|
23750
24360
|
|
|
23751
24361
|
// apps/body/dist/start-command.js
|
|
23752
|
-
import { dirname as
|
|
24362
|
+
import { dirname as dirname10 } from "node:path";
|
|
23753
24363
|
var import_picocolors = __toESM(require_picocolors(), 1);
|
|
23754
24364
|
var DEFAULT_RESTART_DRAIN_TIMEOUT_MS = 30 * 6e4;
|
|
23755
24365
|
var RESTART_WAIT_REPORT_INTERVAL_MS = 3e4;
|
|
@@ -23824,7 +24434,7 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
23824
24434
|
continue;
|
|
23825
24435
|
}
|
|
23826
24436
|
const spinnerHandle = spinner();
|
|
23827
|
-
spinnerHandle.start(`Starting ${
|
|
24437
|
+
spinnerHandle.start(`Starting ${dirname10(path)}\u2026`);
|
|
23828
24438
|
try {
|
|
23829
24439
|
await startRuntime(path, spinnerHandle);
|
|
23830
24440
|
spinnerHandle.stop(import_picocolors.default.green("Started."));
|
|
@@ -23840,9 +24450,9 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
23840
24450
|
|
|
23841
24451
|
// apps/body/dist/connect-command.js
|
|
23842
24452
|
import { spawn as spawn5 } from "node:child_process";
|
|
23843
|
-
import { createHash as
|
|
23844
|
-
import { chmod as
|
|
23845
|
-
import { dirname as
|
|
24453
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
24454
|
+
import { chmod as chmod6, mkdir as mkdir16, readFile as readFile11, unlink as unlink2, writeFile as writeFile11 } from "node:fs/promises";
|
|
24455
|
+
import { dirname as dirname12, resolve as resolve23 } from "node:path";
|
|
23846
24456
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
23847
24457
|
|
|
23848
24458
|
// apps/body/dist/clack-support.js
|
|
@@ -24109,7 +24719,11 @@ async function pairDevice(grant, options = {}) {
|
|
|
24109
24719
|
...grant.llmEnvFile ? { llmEnvFile: grant.llmEnvFile } : {},
|
|
24110
24720
|
agent: selectedAgent
|
|
24111
24721
|
});
|
|
24112
|
-
|
|
24722
|
+
const modelSelection = {
|
|
24723
|
+
model: grant.model,
|
|
24724
|
+
...grant.effort ? { effort: grant.effort } : {}
|
|
24725
|
+
};
|
|
24726
|
+
await (options.validateSelection ?? validateAgentModelSelection)(selectedAgent, localConfig.agentEnv, modelSelection);
|
|
24113
24727
|
const agentIdentity = identityFromKey(grant.agentSecretKey, grant.agentName);
|
|
24114
24728
|
const staged = await stageMonolithAgentRuntime({
|
|
24115
24729
|
workspaceId: grant.workspaceId,
|
|
@@ -24121,7 +24735,7 @@ async function pairDevice(grant, options = {}) {
|
|
|
24121
24735
|
agentKind: selectedAgent.kind,
|
|
24122
24736
|
agentCommand: selectedAgent.command,
|
|
24123
24737
|
agentArgs: selectedAgent.args,
|
|
24124
|
-
modelSelection
|
|
24738
|
+
modelSelection,
|
|
24125
24739
|
...grant.accessPolicy ? { accessPolicy: grant.accessPolicy } : {},
|
|
24126
24740
|
mcpBinary: localConfig.mcpBinary,
|
|
24127
24741
|
agentIdentity,
|
|
@@ -24236,6 +24850,12 @@ var clackPrompts = {
|
|
|
24236
24850
|
}), "Connection cancelled.");
|
|
24237
24851
|
}
|
|
24238
24852
|
};
|
|
24853
|
+
var CONNECT_EFFORT_CATEGORIES = ["effort", "reasoning_effort", "thought_level"];
|
|
24854
|
+
function connectEffortPickerFromAxes(axes, rawAxes = axes) {
|
|
24855
|
+
const isEffort = (axis2) => CONNECT_EFFORT_CATEGORIES.includes(axis2.category) && axis2.options.length > 0;
|
|
24856
|
+
const axis = axes.find(isEffort) ?? rawAxes.find(isEffort);
|
|
24857
|
+
return axis ? { currentValue: axis.currentValue, options: axis.options } : void 0;
|
|
24858
|
+
}
|
|
24239
24859
|
function connectModelPickerFromAxes(axes, fallbackModel, harness, rawAxes = axes) {
|
|
24240
24860
|
const filtered = axes.find((axis) => axis.category === "model");
|
|
24241
24861
|
if (filtered?.options.length) {
|
|
@@ -24253,13 +24873,18 @@ function connectModelPickerFromAxes(axes, fallbackModel, harness, rawAxes = axes
|
|
|
24253
24873
|
}
|
|
24254
24874
|
async function loadConnectModelCatalog(input) {
|
|
24255
24875
|
const agent = resolveAgentCommand({ kind: input.harness });
|
|
24876
|
+
const model = input.model ?? defaultConnectModel(input.harness, input.provider);
|
|
24256
24877
|
const catalog = await fetchAgentModelCatalog(agent, providerEnvironment({
|
|
24257
24878
|
harness: input.harness,
|
|
24258
24879
|
...input.provider ? { provider: input.provider } : {},
|
|
24259
24880
|
...input.apiKey ? { apiKey: input.apiKey } : {},
|
|
24260
|
-
model
|
|
24261
|
-
}), void 0, input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs });
|
|
24262
|
-
|
|
24881
|
+
model
|
|
24882
|
+
}), input.model ? { model: input.model } : void 0, input.timeoutMs === void 0 ? {} : { timeoutMs: input.timeoutMs });
|
|
24883
|
+
const effort = connectEffortPickerFromAxes(catalog.catalog, catalog.raw);
|
|
24884
|
+
return {
|
|
24885
|
+
...connectModelPickerFromAxes(catalog.catalog, defaultConnectModel(input.harness, input.provider), input.harness, catalog.raw),
|
|
24886
|
+
...effort ? { effort } : {}
|
|
24887
|
+
};
|
|
24263
24888
|
}
|
|
24264
24889
|
var CONNECT_PROBE_TIMEOUT_MS = 12e3;
|
|
24265
24890
|
function connectProbeFoundModels(catalog) {
|
|
@@ -24293,6 +24918,33 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
|
|
|
24293
24918
|
});
|
|
24294
24919
|
return picked.trim();
|
|
24295
24920
|
};
|
|
24921
|
+
const askEffort = async (catalog2, forHarness, model2, forProvider, forKey) => {
|
|
24922
|
+
if (!catalog2.effort)
|
|
24923
|
+
return void 0;
|
|
24924
|
+
let axis = catalog2.effort;
|
|
24925
|
+
if (model2 !== catalog2.currentValue) {
|
|
24926
|
+
const reread = await loadModels({
|
|
24927
|
+
harness: forHarness,
|
|
24928
|
+
model: model2,
|
|
24929
|
+
...forProvider ? { provider: forProvider } : {},
|
|
24930
|
+
...forKey ? { apiKey: forKey } : {},
|
|
24931
|
+
timeoutMs: CONNECT_PROBE_TIMEOUT_MS
|
|
24932
|
+
}).catch(() => void 0);
|
|
24933
|
+
axis = reread?.effort ?? axis;
|
|
24934
|
+
}
|
|
24935
|
+
if (!axis.options.length)
|
|
24936
|
+
return void 0;
|
|
24937
|
+
const picked = await prompts.select({
|
|
24938
|
+
message: brass("Choose reasoning effort"),
|
|
24939
|
+
options: axis.options.map((choice) => ({
|
|
24940
|
+
value: choice.id,
|
|
24941
|
+
label: choice.name ?? choice.id,
|
|
24942
|
+
...choice.id === axis.currentValue ? { hint: "current" } : {}
|
|
24943
|
+
})),
|
|
24944
|
+
...axis.options.some((choice) => choice.id === axis.currentValue) ? { initialValue: axis.currentValue } : {}
|
|
24945
|
+
});
|
|
24946
|
+
return picked.trim();
|
|
24947
|
+
};
|
|
24296
24948
|
const probe = async (candidate) => {
|
|
24297
24949
|
try {
|
|
24298
24950
|
const catalog2 = await loadModels({
|
|
@@ -24325,10 +24977,9 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
|
|
|
24325
24977
|
sole: harness === sole,
|
|
24326
24978
|
...configured.currentValue ? { model: configured.currentValue } : {}
|
|
24327
24979
|
}));
|
|
24328
|
-
|
|
24329
|
-
|
|
24330
|
-
|
|
24331
|
-
};
|
|
24980
|
+
const model2 = await askModel(configured, harness);
|
|
24981
|
+
const effort2 = await askEffort(configured, harness, model2);
|
|
24982
|
+
return { harness, model: model2, ...effort2 ? { effort: effort2 } : {} };
|
|
24332
24983
|
}
|
|
24333
24984
|
let provider;
|
|
24334
24985
|
let apiKey;
|
|
@@ -24387,11 +25038,13 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
|
|
|
24387
25038
|
...apiKey ? { apiKey: apiKey.trim() } : {}
|
|
24388
25039
|
});
|
|
24389
25040
|
const model = await askModel(catalog, harness, provider);
|
|
25041
|
+
const effort = await askEffort(catalog, harness, model, provider, apiKey?.trim());
|
|
24390
25042
|
return {
|
|
24391
25043
|
harness,
|
|
24392
25044
|
...provider ? { provider } : {},
|
|
24393
25045
|
...apiKey ? { apiKey: apiKey.trim() } : {},
|
|
24394
|
-
model
|
|
25046
|
+
model,
|
|
25047
|
+
...effort ? { effort } : {}
|
|
24395
25048
|
};
|
|
24396
25049
|
}
|
|
24397
25050
|
async function jsonRequest(url, body, fetchImpl) {
|
|
@@ -24416,12 +25069,13 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl) {
|
|
|
24416
25069
|
const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
|
|
24417
25070
|
if (!normalizedPairingCode)
|
|
24418
25071
|
throw new Error("invalid pairing code");
|
|
24419
|
-
const avatarSeed =
|
|
25072
|
+
const avatarSeed = createHash8("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
|
|
24420
25073
|
return jsonRequest(`${baseUrl}/auth/agent/connect`, {
|
|
24421
25074
|
pairing_code: normalizedPairingCode,
|
|
24422
25075
|
harness: selection.harness,
|
|
24423
25076
|
...selection.provider ? { provider: selection.provider } : {},
|
|
24424
25077
|
model: selection.model,
|
|
25078
|
+
...selection.effort ? { effort: selection.effort } : {},
|
|
24425
25079
|
avatar_seed: avatarSeed,
|
|
24426
25080
|
// This wizard always finishes the join itself (`finishConnectedAgentPairing`,
|
|
24427
25081
|
// called once the rename prompt below settles), so the claim must not
|
|
@@ -24463,7 +25117,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
24463
25117
|
});
|
|
24464
25118
|
await activateRelease(layout, releaseId);
|
|
24465
25119
|
return {
|
|
24466
|
-
binary:
|
|
25120
|
+
binary: resolve23(layout.binDir, "beeline"),
|
|
24467
25121
|
version: published.version ?? releaseId
|
|
24468
25122
|
};
|
|
24469
25123
|
}
|
|
@@ -24486,21 +25140,21 @@ function providerEnvironment(selection) {
|
|
|
24486
25140
|
};
|
|
24487
25141
|
}
|
|
24488
25142
|
async function writePrivateJson(path, value) {
|
|
24489
|
-
await
|
|
25143
|
+
await mkdir16(dirname12(path), { recursive: true, mode: 448 });
|
|
24490
25144
|
await writeFile11(path, `${JSON.stringify(value, null, 2)}
|
|
24491
25145
|
`, { mode: 384 });
|
|
24492
|
-
await
|
|
25146
|
+
await chmod6(path, 384);
|
|
24493
25147
|
}
|
|
24494
25148
|
async function writeProviderEnv(selection, agentPubkey) {
|
|
24495
25149
|
const values = providerEnvironment(selection);
|
|
24496
25150
|
if (Object.keys(values).length === 0)
|
|
24497
25151
|
return void 0;
|
|
24498
|
-
const path =
|
|
24499
|
-
await
|
|
25152
|
+
const path = resolve23(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
25153
|
+
await mkdir16(dirname12(path), { recursive: true, mode: 448 });
|
|
24500
25154
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
24501
25155
|
await writeFile11(path, `${contents}
|
|
24502
25156
|
`, { mode: 384 });
|
|
24503
|
-
await
|
|
25157
|
+
await chmod6(path, 384);
|
|
24504
25158
|
return path;
|
|
24505
25159
|
}
|
|
24506
25160
|
async function runInstalledFinish(binary, grantPath) {
|
|
@@ -24561,13 +25215,16 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
|
|
|
24561
25215
|
await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
|
|
24562
25216
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
24563
25217
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
24564
|
-
const grantPath =
|
|
25218
|
+
const grantPath = resolve23(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
24565
25219
|
await writePrivateJson(grantPath, {
|
|
24566
25220
|
agentSecretKey: grant.agent_secret_key,
|
|
24567
25221
|
bodySecretKey: grant.body_secret_key,
|
|
24568
25222
|
agentName: grant.agent_name,
|
|
24569
25223
|
harness: grant.harness,
|
|
24570
25224
|
model: grant.model,
|
|
25225
|
+
// The server echoes back what it stored; the wizard's own answer is the
|
|
25226
|
+
// fallback for a server too old to carry the effort through.
|
|
25227
|
+
...grant.effort ?? selection.effort ? { effort: grant.effort ?? selection.effort } : {},
|
|
24571
25228
|
soul: grant.soul,
|
|
24572
25229
|
workspaceId: grant.workspace_id,
|
|
24573
25230
|
workspaceName: grant.workspace_name,
|
|
@@ -24623,7 +25280,7 @@ function isDevicePairingGrant(value) {
|
|
|
24623
25280
|
} catch {
|
|
24624
25281
|
return false;
|
|
24625
25282
|
}
|
|
24626
|
-
return typeof grant.agentSecretKey === "string" && /^[0-9a-f]{64}$/.test(grant.agentSecretKey) && typeof grant.bodySecretKey === "string" && /^[0-9a-f]{64}$/.test(grant.bodySecretKey) && typeof grant.agentName === "string" && CONNECT_HARNESSES.includes(grant.harness) && typeof grant.model === "string" && typeof grant.soul === "string" && typeof grant.workspaceId === "string" && typeof grant.workspaceName === "string" && typeof grant.pairedBy === "string" && /^[0-9a-f]{64}$/.test(grant.pairedBy) && typeof grant.monolithBaseUrl === "string" && grant.monolithBaseUrl === monolithOrigin && /^https?:$/.test(new URL(monolithOrigin).protocol) && typeof grant.daemonExchangeToken === "string" && /^bde_[A-Za-z0-9_-]{43}$/.test(grant.daemonExchangeToken);
|
|
25283
|
+
return typeof grant.agentSecretKey === "string" && /^[0-9a-f]{64}$/.test(grant.agentSecretKey) && typeof grant.bodySecretKey === "string" && /^[0-9a-f]{64}$/.test(grant.bodySecretKey) && typeof grant.agentName === "string" && CONNECT_HARNESSES.includes(grant.harness) && typeof grant.model === "string" && (grant.effort === void 0 || typeof grant.effort === "string") && typeof grant.soul === "string" && typeof grant.workspaceId === "string" && typeof grant.workspaceName === "string" && typeof grant.pairedBy === "string" && /^[0-9a-f]{64}$/.test(grant.pairedBy) && typeof grant.monolithBaseUrl === "string" && grant.monolithBaseUrl === monolithOrigin && /^https?:$/.test(new URL(monolithOrigin).protocol) && typeof grant.daemonExchangeToken === "string" && /^bde_[A-Za-z0-9_-]{43}$/.test(grant.daemonExchangeToken);
|
|
24627
25284
|
}
|
|
24628
25285
|
function connectPlainFailure(error) {
|
|
24629
25286
|
const raw = error instanceof Error ? error.message : String(error);
|
|
@@ -24643,18 +25300,18 @@ async function runConnectFinishCommand(path) {
|
|
|
24643
25300
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
24644
25301
|
}
|
|
24645
25302
|
try {
|
|
24646
|
-
const grant = JSON.parse(await
|
|
25303
|
+
const grant = JSON.parse(await readFile11(resolve23(path), "utf8"));
|
|
24647
25304
|
if (!isDevicePairingGrant(grant))
|
|
24648
25305
|
throw new Error("device connection grant is invalid");
|
|
24649
25306
|
const connected = await completeDevicePairing(grant);
|
|
24650
|
-
await unlink2(
|
|
24651
|
-
const providerEnv = grant.llmEnvFile ? await
|
|
25307
|
+
await unlink2(resolve23(path));
|
|
25308
|
+
const providerEnv = grant.llmEnvFile ? await readFile11(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
24652
25309
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
24653
25310
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
24654
25311
|
if (model) {
|
|
24655
25312
|
const decision2 = await resolveOpenRouterRouting({
|
|
24656
25313
|
model,
|
|
24657
|
-
cacheDir: openRouterRoutingCacheDir(
|
|
25314
|
+
cacheDir: openRouterRoutingCacheDir(dirname12(connected.configPath)),
|
|
24658
25315
|
...apiKey ? { apiKey } : {},
|
|
24659
25316
|
probeTimeoutMs: 1e4
|
|
24660
25317
|
});
|
|
@@ -24676,24 +25333,24 @@ init_self_update_manifest();
|
|
|
24676
25333
|
// apps/body/dist/managed-update.js
|
|
24677
25334
|
init_self_update();
|
|
24678
25335
|
import { spawn as spawn6 } from "node:child_process";
|
|
24679
|
-
import { mkdir as
|
|
24680
|
-
import { dirname as
|
|
25336
|
+
import { mkdir as mkdir18, rm as rm7, stat as stat3, writeFile as writeFile13 } from "node:fs/promises";
|
|
25337
|
+
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
24681
25338
|
|
|
24682
25339
|
// apps/body/dist/update-rollback-alert.js
|
|
24683
|
-
import { mkdir as
|
|
24684
|
-
import { dirname as
|
|
25340
|
+
import { mkdir as mkdir17, readFile as readFile12, rename as rename5, unlink as unlink3, writeFile as writeFile12 } from "node:fs/promises";
|
|
25341
|
+
import { dirname as dirname13, resolve as resolve24 } from "node:path";
|
|
24685
25342
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
24686
25343
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
24687
25344
|
function updateRollbackAlertPath(runtimeDir) {
|
|
24688
|
-
return
|
|
25345
|
+
return resolve24(runtimeDir, "update-rollback-alert.json");
|
|
24689
25346
|
}
|
|
24690
25347
|
async function writeAlert(runtimeDir, alert) {
|
|
24691
25348
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
24692
25349
|
const staged = `${path}.${process.pid}.tmp`;
|
|
24693
|
-
await
|
|
25350
|
+
await mkdir17(dirname13(path), { recursive: true });
|
|
24694
25351
|
await writeFile12(staged, `${JSON.stringify(alert, null, 2)}
|
|
24695
25352
|
`, { mode: 384 });
|
|
24696
|
-
await
|
|
25353
|
+
await rename5(staged, path);
|
|
24697
25354
|
}
|
|
24698
25355
|
async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()) {
|
|
24699
25356
|
const existing = await readUpdateRollbackAlert(runtimeDir);
|
|
@@ -24703,7 +25360,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
|
|
|
24703
25360
|
}
|
|
24704
25361
|
async function readUpdateRollbackAlert(runtimeDir) {
|
|
24705
25362
|
try {
|
|
24706
|
-
const value = JSON.parse(await
|
|
25363
|
+
const value = JSON.parse(await readFile12(updateRollbackAlertPath(runtimeDir), "utf8"));
|
|
24707
25364
|
if (value.version !== 1 || typeof value.releaseId !== "string")
|
|
24708
25365
|
return void 0;
|
|
24709
25366
|
return value;
|
|
@@ -24754,22 +25411,22 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
24754
25411
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
24755
25412
|
async function withInstallLock(layout, work, options = {}) {
|
|
24756
25413
|
const now2 = options.now ?? Date.now;
|
|
24757
|
-
const lock =
|
|
25414
|
+
const lock = resolve25(layout.releasesRoot, ".state", "install.lock");
|
|
24758
25415
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
24759
|
-
await
|
|
25416
|
+
await mkdir18(dirname14(lock), { recursive: true });
|
|
24760
25417
|
for (; ; ) {
|
|
24761
25418
|
try {
|
|
24762
|
-
await
|
|
24763
|
-
await writeFile13(
|
|
25419
|
+
await mkdir18(lock);
|
|
25420
|
+
await writeFile13(resolve25(lock, "owner"), `${process.pid}
|
|
24764
25421
|
${now2()}
|
|
24765
25422
|
`, "utf8");
|
|
24766
25423
|
break;
|
|
24767
25424
|
} catch (error) {
|
|
24768
25425
|
if (error.code !== "EEXIST")
|
|
24769
25426
|
throw error;
|
|
24770
|
-
const age = now2() - await
|
|
25427
|
+
const age = now2() - await stat3(lock).then((value) => value.mtimeMs).catch(() => now2());
|
|
24771
25428
|
if (age > LOCK_STALE_MS) {
|
|
24772
|
-
await
|
|
25429
|
+
await rm7(lock, { recursive: true, force: true });
|
|
24773
25430
|
continue;
|
|
24774
25431
|
}
|
|
24775
25432
|
if (now2() >= deadline)
|
|
@@ -24780,7 +25437,7 @@ ${now2()}
|
|
|
24780
25437
|
try {
|
|
24781
25438
|
return await work();
|
|
24782
25439
|
} finally {
|
|
24783
|
-
await
|
|
25440
|
+
await rm7(lock, { recursive: true, force: true });
|
|
24784
25441
|
}
|
|
24785
25442
|
}
|
|
24786
25443
|
var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
@@ -24883,7 +25540,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
24883
25540
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
24884
25541
|
const from = await readInstalledBundleIdentity({
|
|
24885
25542
|
...this.#layout,
|
|
24886
|
-
libDir:
|
|
25543
|
+
libDir: resolve25(this.#layout.releasesRoot, this.#loadedRelease)
|
|
24887
25544
|
}).catch(() => void 0) ?? {};
|
|
24888
25545
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
24889
25546
|
const record2 = {
|
|
@@ -25170,7 +25827,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
25170
25827
|
});
|
|
25171
25828
|
if (!accepted)
|
|
25172
25829
|
return false;
|
|
25173
|
-
await writeFile13(
|
|
25830
|
+
await writeFile13(resolve25(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
25174
25831
|
readyAt: (options.now ?? Date.now)(),
|
|
25175
25832
|
loadedRelease,
|
|
25176
25833
|
functionalProof: options.functionalProof
|
|
@@ -25398,16 +26055,16 @@ async function runUpdateCommand(args) {
|
|
|
25398
26055
|
init_self_update();
|
|
25399
26056
|
|
|
25400
26057
|
// apps/body/dist/daemon-failure.js
|
|
25401
|
-
import { mkdir as
|
|
25402
|
-
import { dirname as
|
|
26058
|
+
import { mkdir as mkdir19, readFile as readFile13, rename as rename6, rm as rm8, writeFile as writeFile14 } from "node:fs/promises";
|
|
26059
|
+
import { dirname as dirname15, resolve as resolve26 } from "node:path";
|
|
25403
26060
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
25404
26061
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
25405
26062
|
function daemonFailurePath(runtimeDir) {
|
|
25406
|
-
return
|
|
26063
|
+
return resolve26(runtimeDir, "daemon-distress.json");
|
|
25407
26064
|
}
|
|
25408
26065
|
async function readFailureRecord(runtimeDir) {
|
|
25409
26066
|
try {
|
|
25410
|
-
const value = JSON.parse(await
|
|
26067
|
+
const value = JSON.parse(await readFile13(daemonFailurePath(runtimeDir), "utf8"));
|
|
25411
26068
|
if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure) => typeof failure !== "number") || typeof value.lastError !== "string") {
|
|
25412
26069
|
return void 0;
|
|
25413
26070
|
}
|
|
@@ -25419,10 +26076,10 @@ async function readFailureRecord(runtimeDir) {
|
|
|
25419
26076
|
async function writeFailureRecord(runtimeDir, record2) {
|
|
25420
26077
|
const path = daemonFailurePath(runtimeDir);
|
|
25421
26078
|
const staged = `${path}.${process.pid}.tmp`;
|
|
25422
|
-
await
|
|
26079
|
+
await mkdir19(dirname15(path), { recursive: true, mode: 448 });
|
|
25423
26080
|
await writeFile14(staged, `${JSON.stringify(record2, null, 2)}
|
|
25424
26081
|
`, { mode: 384 });
|
|
25425
|
-
await
|
|
26082
|
+
await rename6(staged, path);
|
|
25426
26083
|
}
|
|
25427
26084
|
async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
25428
26085
|
const prior = await readFailureRecord(runtimeDir);
|
|
@@ -25438,13 +26095,13 @@ async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
|
25438
26095
|
return { distressed, count: failures.length, path: daemonFailurePath(runtimeDir) };
|
|
25439
26096
|
}
|
|
25440
26097
|
async function clearDaemonStartFailures(runtimeDir) {
|
|
25441
|
-
await
|
|
26098
|
+
await rm8(daemonFailurePath(runtimeDir), { force: true });
|
|
25442
26099
|
}
|
|
25443
26100
|
|
|
25444
26101
|
// apps/body/dist/update-functional-probe.js
|
|
25445
|
-
import { mkdir as
|
|
26102
|
+
import { mkdir as mkdir20, rm as rm9 } from "node:fs/promises";
|
|
25446
26103
|
import { homedir as homedir10 } from "node:os";
|
|
25447
|
-
import { resolve as
|
|
26104
|
+
import { resolve as resolve27 } from "node:path";
|
|
25448
26105
|
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
25449
26106
|
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
25450
26107
|
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
@@ -25538,11 +26195,11 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
25538
26195
|
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
25539
26196
|
};
|
|
25540
26197
|
}
|
|
25541
|
-
const root = input.probeRoot ??
|
|
25542
|
-
const cwd =
|
|
25543
|
-
const homeRoot =
|
|
25544
|
-
await
|
|
25545
|
-
await
|
|
26198
|
+
const root = input.probeRoot ?? resolve27(input.runtimeDir, "update-functional-probe");
|
|
26199
|
+
const cwd = resolve27(root, "checkout");
|
|
26200
|
+
const homeRoot = resolve27(root, "agent-home");
|
|
26201
|
+
await rm9(root, { recursive: true, force: true });
|
|
26202
|
+
await mkdir20(cwd, { recursive: true, mode: 448 });
|
|
25546
26203
|
let client;
|
|
25547
26204
|
try {
|
|
25548
26205
|
const agentEnv = {
|
|
@@ -25572,7 +26229,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
25572
26229
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
25573
26230
|
const operatorHome = input.config.operatorHome ?? homedir10();
|
|
25574
26231
|
const homeStateDirs = harnessHomeStateDirs(command, agentEnv.HOME ?? operatorHome);
|
|
25575
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
26232
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir20(dir, { recursive: true })));
|
|
25576
26233
|
spawnCommand = wrapAgentCommand({
|
|
25577
26234
|
bwrapPath: input.config.bwrapPath,
|
|
25578
26235
|
spec: {
|
|
@@ -25716,13 +26373,13 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
25716
26373
|
};
|
|
25717
26374
|
} finally {
|
|
25718
26375
|
await client?.stop().catch(() => void 0);
|
|
25719
|
-
await
|
|
26376
|
+
await rm9(root, { recursive: true, force: true }).catch(() => void 0);
|
|
25720
26377
|
}
|
|
25721
26378
|
}
|
|
25722
26379
|
|
|
25723
26380
|
// apps/body/dist/current-release-probe.js
|
|
25724
26381
|
import { spawn as spawn7 } from "node:child_process";
|
|
25725
|
-
import { dirname as
|
|
26382
|
+
import { dirname as dirname16, join as join10 } from "node:path";
|
|
25726
26383
|
init_self_update();
|
|
25727
26384
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
25728
26385
|
var UPDATE_PROBE_COMMAND = "update-probe";
|
|
@@ -25765,13 +26422,13 @@ function outcomeFromReport(report) {
|
|
|
25765
26422
|
}
|
|
25766
26423
|
}
|
|
25767
26424
|
async function probeReleaseInSubprocess(input) {
|
|
25768
|
-
const bundleDir =
|
|
26425
|
+
const bundleDir = join10(input.layout.releasesRoot, input.releaseId);
|
|
25769
26426
|
const entrypoint = await resolveBundleEntrypoint(bundleDir);
|
|
25770
26427
|
if (!entrypoint) {
|
|
25771
26428
|
return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
|
|
25772
26429
|
}
|
|
25773
26430
|
const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
|
|
25774
|
-
return new Promise((
|
|
26431
|
+
return new Promise((resolve31) => {
|
|
25775
26432
|
const child = spawn7(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
25776
26433
|
let stdout6 = "";
|
|
25777
26434
|
let stderr = "";
|
|
@@ -25781,7 +26438,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
25781
26438
|
return;
|
|
25782
26439
|
settled = true;
|
|
25783
26440
|
clearTimeout(timer);
|
|
25784
|
-
|
|
26441
|
+
resolve31(outcome);
|
|
25785
26442
|
};
|
|
25786
26443
|
const timer = setTimeout(() => {
|
|
25787
26444
|
child.kill("SIGKILL");
|
|
@@ -25828,7 +26485,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
25828
26485
|
const runtime = await readRuntimeRecord(configPath);
|
|
25829
26486
|
const agent = runtimeAgentCommand(runtime);
|
|
25830
26487
|
const config = loadBodyConfig({
|
|
25831
|
-
workspaceRoot:
|
|
26488
|
+
workspaceRoot: join10(dirname16(configPath), "workspace"),
|
|
25832
26489
|
llmEnvFile: runtime.llmEnvFile,
|
|
25833
26490
|
env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
|
|
25834
26491
|
agent
|
|
@@ -25846,7 +26503,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
25846
26503
|
}
|
|
25847
26504
|
const layout = beelineInstallLayout(env);
|
|
25848
26505
|
const releaseId = (layout && await activeReleaseId(layout).catch(() => void 0)) ?? "unknown";
|
|
25849
|
-
const runtimeDir =
|
|
26506
|
+
const runtimeDir = dirname16(configPath);
|
|
25850
26507
|
const outcome = await probeOutcome(() => (options.probe ?? runUpdateFunctionalProbe)({
|
|
25851
26508
|
config,
|
|
25852
26509
|
runtimeDir,
|
|
@@ -25854,15 +26511,15 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
25854
26511
|
sandboxRequired: runtime.sandbox !== "off",
|
|
25855
26512
|
sandboxUnavailableDetail: sandbox.advisory,
|
|
25856
26513
|
// The successor's probe still holds `<runtimeDir>/update-functional-probe`.
|
|
25857
|
-
probeRoot:
|
|
26514
|
+
probeRoot: join10(runtimeDir, "current-release-probe")
|
|
25858
26515
|
}));
|
|
25859
26516
|
const report = outcome.kind === "served" ? { probe: "served" } : outcome.kind === "refused" ? { probe: "refused", status: outcome.status, reason: outcome.reason } : outcome.kind === "sandbox-unavailable" ? { probe: "sandbox-unavailable", reason: outcome.reason } : { probe: "failed", reason: outcome.reason };
|
|
25860
26517
|
write(JSON.stringify(report));
|
|
25861
26518
|
}
|
|
25862
26519
|
|
|
25863
26520
|
// apps/body/dist/release-status.js
|
|
25864
|
-
import { readFile as
|
|
25865
|
-
import { resolve as
|
|
26521
|
+
import { readFile as readFile14, readdir as readdir6, rename as rename7, writeFile as writeFile15 } from "node:fs/promises";
|
|
26522
|
+
import { resolve as resolve28 } from "node:path";
|
|
25866
26523
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
25867
26524
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
25868
26525
|
var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
|
|
@@ -25879,17 +26536,17 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
25879
26536
|
pid: options.pid ?? process.pid,
|
|
25880
26537
|
readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
25881
26538
|
};
|
|
25882
|
-
const target =
|
|
26539
|
+
const target = resolve28(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
|
|
25883
26540
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
25884
26541
|
await writeFile15(temporary, `${JSON.stringify(status, null, 2)}
|
|
25885
26542
|
`, { mode: 384 });
|
|
25886
|
-
await
|
|
26543
|
+
await rename7(temporary, target);
|
|
25887
26544
|
return status;
|
|
25888
26545
|
}
|
|
25889
26546
|
|
|
25890
26547
|
// apps/body/dist/scratch-sweep.js
|
|
25891
|
-
import { lstat as
|
|
25892
|
-
import { resolve as
|
|
26548
|
+
import { lstat as lstat4, readdir as readdir7, rmdir, unlink as unlink4 } from "node:fs/promises";
|
|
26549
|
+
import { resolve as resolve29 } from "node:path";
|
|
25893
26550
|
var DEFAULT_SCRATCH_TTL_HOURS = 72;
|
|
25894
26551
|
var NEVER_SWEEP_SUBDIR_NAMES = new Set(HOME_SUBDIRS.filter((name) => name !== "tmp"));
|
|
25895
26552
|
function scratchTtlMs(env = process.env) {
|
|
@@ -25898,10 +26555,10 @@ function scratchTtlMs(env = process.env) {
|
|
|
25898
26555
|
return (Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_SCRATCH_TTL_HOURS) * 60 * 60 * 1e3;
|
|
25899
26556
|
}
|
|
25900
26557
|
async function discoverAttachScratchRoots(runtimeDir) {
|
|
25901
|
-
const roomsDir =
|
|
26558
|
+
const roomsDir = resolve29(runtimeDir, "rooms");
|
|
25902
26559
|
let entries;
|
|
25903
26560
|
try {
|
|
25904
|
-
entries = await
|
|
26561
|
+
entries = await readdir7(roomsDir, { withFileTypes: true });
|
|
25905
26562
|
} catch {
|
|
25906
26563
|
return [];
|
|
25907
26564
|
}
|
|
@@ -25909,8 +26566,8 @@ async function discoverAttachScratchRoots(runtimeDir) {
|
|
|
25909
26566
|
for (const entry of entries) {
|
|
25910
26567
|
if (!entry.isDirectory())
|
|
25911
26568
|
continue;
|
|
25912
|
-
const home =
|
|
25913
|
-
const stats = await
|
|
26569
|
+
const home = resolve29(roomsDir, entry.name, "agent-home");
|
|
26570
|
+
const stats = await lstat4(home).catch(() => void 0);
|
|
25914
26571
|
if (stats?.isDirectory())
|
|
25915
26572
|
roots.push(home);
|
|
25916
26573
|
}
|
|
@@ -25919,7 +26576,7 @@ async function discoverAttachScratchRoots(runtimeDir) {
|
|
|
25919
26576
|
async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
|
|
25920
26577
|
let entries;
|
|
25921
26578
|
try {
|
|
25922
|
-
entries = await
|
|
26579
|
+
entries = await readdir7(dir, { withFileTypes: true });
|
|
25923
26580
|
} catch {
|
|
25924
26581
|
return { removedFiles: 0, removedBytes: 0 };
|
|
25925
26582
|
}
|
|
@@ -25928,15 +26585,15 @@ async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
|
|
|
25928
26585
|
for (const entry of entries) {
|
|
25929
26586
|
if (protectNamesHere && NEVER_SWEEP_SUBDIR_NAMES.has(entry.name))
|
|
25930
26587
|
continue;
|
|
25931
|
-
const path =
|
|
25932
|
-
const stats = await
|
|
26588
|
+
const path = resolve29(dir, entry.name);
|
|
26589
|
+
const stats = await lstat4(path).catch(() => void 0);
|
|
25933
26590
|
if (!stats || stats.isSymbolicLink())
|
|
25934
26591
|
continue;
|
|
25935
26592
|
if (stats.isDirectory()) {
|
|
25936
26593
|
const nested = await removeStaleFiles(path, cutoffMs, false);
|
|
25937
26594
|
removedFiles += nested.removedFiles;
|
|
25938
26595
|
removedBytes += nested.removedBytes;
|
|
25939
|
-
const remaining = await
|
|
26596
|
+
const remaining = await readdir7(path).catch(() => void 0);
|
|
25940
26597
|
if (remaining && remaining.length === 0)
|
|
25941
26598
|
await rmdir(path).catch(() => void 0);
|
|
25942
26599
|
continue;
|
|
@@ -25954,7 +26611,7 @@ async function sweepAttachScratchRoots(roots, ttlMs, now2 = Date.now()) {
|
|
|
25954
26611
|
let removedFiles = 0;
|
|
25955
26612
|
let removedBytes = 0;
|
|
25956
26613
|
for (const root of roots) {
|
|
25957
|
-
const stats = await
|
|
26614
|
+
const stats = await lstat4(root).catch(() => void 0);
|
|
25958
26615
|
if (!stats?.isDirectory())
|
|
25959
26616
|
continue;
|
|
25960
26617
|
const result = await removeStaleFiles(root, cutoffMs, true);
|
|
@@ -26015,7 +26672,7 @@ var DaemonExitError = class extends Error {
|
|
|
26015
26672
|
};
|
|
26016
26673
|
async function runStoredDaemon(pathOrPointer) {
|
|
26017
26674
|
const configPath = await resolveRuntimeConfigPath(pathOrPointer);
|
|
26018
|
-
daemonFailureRuntimeDir =
|
|
26675
|
+
daemonFailureRuntimeDir = dirname17(configPath);
|
|
26019
26676
|
const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
|
|
26020
26677
|
let runtime = accessMigration.runtime;
|
|
26021
26678
|
if (!runtime.transport) {
|
|
@@ -26027,7 +26684,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26027
26684
|
runtime = activated.runtime;
|
|
26028
26685
|
const daemonApi = activated.client;
|
|
26029
26686
|
const agent = runtimeAgentCommand(runtime);
|
|
26030
|
-
await writeFile16(
|
|
26687
|
+
await writeFile16(resolve30(dirname17(configPath), "daemon.pid"), `${process.pid}
|
|
26031
26688
|
`, { mode: 384 });
|
|
26032
26689
|
const env = {
|
|
26033
26690
|
...process.env,
|
|
@@ -26035,7 +26692,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26035
26692
|
BUZZ_DEV_MCP_BIN: runtime.mcpBinary
|
|
26036
26693
|
};
|
|
26037
26694
|
const config = loadBodyConfig({
|
|
26038
|
-
workspaceRoot:
|
|
26695
|
+
workspaceRoot: resolve30(dirname17(configPath), "workspace"),
|
|
26039
26696
|
llmEnvFile: runtime.llmEnvFile,
|
|
26040
26697
|
env,
|
|
26041
26698
|
agent
|
|
@@ -26070,7 +26727,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26070
26727
|
const stop = () => controller.abort();
|
|
26071
26728
|
process.once("SIGINT", stop);
|
|
26072
26729
|
process.once("SIGTERM", stop);
|
|
26073
|
-
const runtimeDir =
|
|
26730
|
+
const runtimeDir = dirname17(configPath);
|
|
26074
26731
|
const layout = beelineInstallLayout(process.env);
|
|
26075
26732
|
const notifier = new SystemdNotifier();
|
|
26076
26733
|
let rollbackAlertDrain;
|
|
@@ -26186,7 +26843,8 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26186
26843
|
agentId: runtime.agent.publicKey,
|
|
26187
26844
|
workspaceId: runtime.communityId,
|
|
26188
26845
|
runtimeDir,
|
|
26189
|
-
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {}
|
|
26846
|
+
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {},
|
|
26847
|
+
...config.modelUnavailable ? { startupUnavailable: config.modelUnavailable.unavailable.label } : {}
|
|
26190
26848
|
});
|
|
26191
26849
|
},
|
|
26192
26850
|
onProgress: async (status) => {
|
|
@@ -26217,8 +26875,8 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26217
26875
|
} finally {
|
|
26218
26876
|
clearInterval(scratchSweepTimer);
|
|
26219
26877
|
await notifier.stopping(stoppingStatus).catch(() => void 0);
|
|
26220
|
-
const pidPath =
|
|
26221
|
-
const recorded = Number((await
|
|
26878
|
+
const pidPath = resolve30(dirname17(configPath), "daemon.pid");
|
|
26879
|
+
const recorded = Number((await readFile15(pidPath, "utf8").catch(() => "")).trim());
|
|
26222
26880
|
if (recorded === process.pid) {
|
|
26223
26881
|
await unlink5(pidPath).catch(() => void 0);
|
|
26224
26882
|
}
|
|
@@ -26245,7 +26903,7 @@ async function main() {
|
|
|
26245
26903
|
const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
|
|
26246
26904
|
if (!configPath || !roomId)
|
|
26247
26905
|
throw new Error("corner-read-token requires --config and --room");
|
|
26248
|
-
const activated = await activateDaemonTransport(
|
|
26906
|
+
const activated = await activateDaemonTransport(resolve30(configPath));
|
|
26249
26907
|
if (!activated)
|
|
26250
26908
|
throw new Error("corner-read-token requires monolith transport");
|
|
26251
26909
|
const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
|
|
@@ -26294,14 +26952,14 @@ async function main() {
|
|
|
26294
26952
|
const agentPubkey = agentFlag >= 0 ? args[agentFlag + 1] : void 0;
|
|
26295
26953
|
if (!configPath && agentPubkey) {
|
|
26296
26954
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
26297
|
-
configPath = configs.find((candidate) =>
|
|
26955
|
+
configPath = configs.find((candidate) => dirname17(candidate).endsWith(agentPubkey));
|
|
26298
26956
|
}
|
|
26299
26957
|
if (!configPath && agentPubkey) {
|
|
26300
26958
|
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
|
|
26301
26959
|
}
|
|
26302
26960
|
if (!configPath)
|
|
26303
26961
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
26304
|
-
await runStoredDaemon(
|
|
26962
|
+
await runStoredDaemon(resolve30(configPath));
|
|
26305
26963
|
return;
|
|
26306
26964
|
}
|
|
26307
26965
|
if (command === "update") {
|
|
@@ -26322,7 +26980,7 @@ async function main() {
|
|
|
26322
26980
|
if (!agentPubkey)
|
|
26323
26981
|
throw new Error("stop requires --agent <pubkey>");
|
|
26324
26982
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
26325
|
-
const configPath = configs.find((candidate) =>
|
|
26983
|
+
const configPath = configs.find((candidate) => dirname17(candidate).endsWith(agentPubkey));
|
|
26326
26984
|
if (!configPath)
|
|
26327
26985
|
throw new Error(`no stored runtime found for agent ${agentPubkey}`);
|
|
26328
26986
|
const runtime = await readRuntimeRecord(configPath);
|