usebeeline 0.0.121 → 0.0.123
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 +11 -1
- package/dist/usebeeline.mjs +1191 -705
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -11315,12 +11315,12 @@ __export(self_update_exports, {
|
|
|
11315
11315
|
});
|
|
11316
11316
|
import { createHash as createHash8 } from "node:crypto";
|
|
11317
11317
|
import { constants as fsConstants2 } from "node:fs";
|
|
11318
|
-
import { access, chmod as chmod6, lstat as lstat4, mkdir as
|
|
11318
|
+
import { access as access2, chmod as chmod6, lstat as lstat4, mkdir as mkdir18, open, readFile as readFile11, rename as rename6, rm as rm6, symlink as symlink3, writeFile as writeFile11 } from "node:fs/promises";
|
|
11319
11319
|
import { spawn as spawn8 } from "node:child_process";
|
|
11320
11320
|
import { homedir as homedir12 } from "node:os";
|
|
11321
|
-
import { dirname as
|
|
11321
|
+
import { dirname as dirname16, join as join13, resolve as resolve27 } from "node:path";
|
|
11322
11322
|
function anchorLayout(rawLibDir) {
|
|
11323
|
-
const libDir =
|
|
11323
|
+
const libDir = resolve27(rawLibDir);
|
|
11324
11324
|
const segments = libDir.split(/[/\\]/);
|
|
11325
11325
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
11326
11326
|
if (idx >= 2 && segments[idx - 1] === "lib") {
|
|
@@ -11336,9 +11336,9 @@ function anchorLayout(rawLibDir) {
|
|
|
11336
11336
|
// prefix's bin, NOT <prefix>/lib/bin — deriving it one level up was the
|
|
11337
11337
|
// defect that made activateRelease write its stable forwarders where
|
|
11338
11338
|
// nothing executed them, leaving stale raw wrappers in <prefix>/bin.
|
|
11339
|
-
binDir:
|
|
11339
|
+
binDir: resolve27(libDir, "../../bin"),
|
|
11340
11340
|
libDir,
|
|
11341
|
-
releasesRoot:
|
|
11341
|
+
releasesRoot: resolve27(libDir, `../${RELEASES_SEGMENT}`)
|
|
11342
11342
|
};
|
|
11343
11343
|
}
|
|
11344
11344
|
function beelineInstallLayout(env = process.env) {
|
|
@@ -11350,18 +11350,18 @@ function beelineInstallLayout(env = process.env) {
|
|
|
11350
11350
|
function releaseIdFromPath(path) {
|
|
11351
11351
|
if (!path?.trim())
|
|
11352
11352
|
return void 0;
|
|
11353
|
-
const segments =
|
|
11353
|
+
const segments = resolve27(path.trim()).split(/[/\\]/);
|
|
11354
11354
|
const idx = segments.lastIndexOf(RELEASES_SEGMENT);
|
|
11355
11355
|
const id = idx >= 0 ? segments[idx + 1] : void 0;
|
|
11356
11356
|
return id ? sanitizeReleaseId(id) : void 0;
|
|
11357
11357
|
}
|
|
11358
11358
|
function runningReleaseId(layout, env = process.env, invocationPath = process.argv[1]) {
|
|
11359
|
-
const releasesRoot =
|
|
11360
|
-
const anchor =
|
|
11359
|
+
const releasesRoot = resolve27(layout.releasesRoot);
|
|
11360
|
+
const anchor = resolve27(layout.libDir);
|
|
11361
11361
|
for (const raw of [env.BEELINE_LIB_DIR, invocationPath]) {
|
|
11362
11362
|
if (!raw?.trim())
|
|
11363
11363
|
continue;
|
|
11364
|
-
const resolved =
|
|
11364
|
+
const resolved = resolve27(raw.trim());
|
|
11365
11365
|
if (resolved !== anchor && !resolved.startsWith(`${releasesRoot}/`))
|
|
11366
11366
|
continue;
|
|
11367
11367
|
const id = releaseIdFromPath(resolved);
|
|
@@ -11372,7 +11372,7 @@ function runningReleaseId(layout, env = process.env, invocationPath = process.ar
|
|
|
11372
11372
|
}
|
|
11373
11373
|
function defaultBeelineInstallLayout(env = process.env) {
|
|
11374
11374
|
const home = env.HOME?.trim() || homedir12();
|
|
11375
|
-
return anchorLayout(
|
|
11375
|
+
return anchorLayout(resolve27(home, ".local", "lib", "beeline"));
|
|
11376
11376
|
}
|
|
11377
11377
|
function discoveredBeelineInstallLayout(env = process.env) {
|
|
11378
11378
|
const explicitAnchor = env.BEELINE_INSTALL_LIB_DIR?.trim();
|
|
@@ -11380,7 +11380,7 @@ function discoveredBeelineInstallLayout(env = process.env) {
|
|
|
11380
11380
|
return anchorLayout(explicitAnchor);
|
|
11381
11381
|
const explicitBinDir = env.BEELINE_INSTALL_DIR?.trim();
|
|
11382
11382
|
if (explicitBinDir)
|
|
11383
|
-
return anchorLayout(
|
|
11383
|
+
return anchorLayout(resolve27(dirname16(resolve27(explicitBinDir)), "lib", "beeline"));
|
|
11384
11384
|
return defaultBeelineInstallLayout(env);
|
|
11385
11385
|
}
|
|
11386
11386
|
function hostPlatformKey() {
|
|
@@ -11397,7 +11397,7 @@ async function readBundleJson(bundleDir) {
|
|
|
11397
11397
|
let raw;
|
|
11398
11398
|
for (const candidate of bundleJsonCandidates(bundleDir)) {
|
|
11399
11399
|
try {
|
|
11400
|
-
raw = await
|
|
11400
|
+
raw = await readFile11(candidate, "utf8");
|
|
11401
11401
|
break;
|
|
11402
11402
|
} catch {
|
|
11403
11403
|
}
|
|
@@ -11419,13 +11419,13 @@ function updateStatePath(layout) {
|
|
|
11419
11419
|
}
|
|
11420
11420
|
async function readUpdateState(layout) {
|
|
11421
11421
|
try {
|
|
11422
|
-
return JSON.parse(await
|
|
11422
|
+
return JSON.parse(await readFile11(updateStatePath(layout), "utf8"));
|
|
11423
11423
|
} catch {
|
|
11424
11424
|
return {};
|
|
11425
11425
|
}
|
|
11426
11426
|
}
|
|
11427
11427
|
async function writeUpdateState(layout, state) {
|
|
11428
|
-
await
|
|
11428
|
+
await mkdir18(join13(layout.releasesRoot, ".state"), { recursive: true });
|
|
11429
11429
|
await writeFile11(updateStatePath(layout), `${JSON.stringify(state, null, 2)}
|
|
11430
11430
|
`, "utf8");
|
|
11431
11431
|
}
|
|
@@ -11512,7 +11512,7 @@ function entrypointCandidates(bundleDir) {
|
|
|
11512
11512
|
async function resolveBundleEntrypoint(bundleDir) {
|
|
11513
11513
|
for (const candidate of entrypointCandidates(bundleDir)) {
|
|
11514
11514
|
try {
|
|
11515
|
-
await
|
|
11515
|
+
await access2(candidate, fsConstants2.F_OK);
|
|
11516
11516
|
return candidate;
|
|
11517
11517
|
} catch {
|
|
11518
11518
|
}
|
|
@@ -11520,10 +11520,7 @@ async function resolveBundleEntrypoint(bundleDir) {
|
|
|
11520
11520
|
return void 0;
|
|
11521
11521
|
}
|
|
11522
11522
|
function requiredBundlePaths() {
|
|
11523
|
-
return [
|
|
11524
|
-
BUNDLE_ENTRYPOINT,
|
|
11525
|
-
PI_MCP_ADAPTER_ENTRYPOINT
|
|
11526
|
-
];
|
|
11523
|
+
return [BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, CODEGRAPH_ENTRYPOINT];
|
|
11527
11524
|
}
|
|
11528
11525
|
async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
11529
11526
|
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
@@ -11533,13 +11530,13 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
11533
11530
|
const okMarker = join13(releaseDir, ".stage-ok");
|
|
11534
11531
|
let previouslyVerified = false;
|
|
11535
11532
|
try {
|
|
11536
|
-
const recorded = await
|
|
11533
|
+
const recorded = await readFile11(okMarker, "utf8");
|
|
11537
11534
|
if (recorded.trim() === published.sha256)
|
|
11538
11535
|
return releaseId;
|
|
11539
11536
|
previouslyVerified = true;
|
|
11540
11537
|
} catch {
|
|
11541
11538
|
}
|
|
11542
|
-
await
|
|
11539
|
+
await mkdir18(releaseDir, { recursive: true });
|
|
11543
11540
|
const tempArchive = join13(layout.releasesRoot, `.download-${releaseId}-${process.pid}.tar.gz`);
|
|
11544
11541
|
try {
|
|
11545
11542
|
log2(`downloading ${published.file}`);
|
|
@@ -11580,7 +11577,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
11580
11577
|
throw new Error(`extracting bundle failed: ${extract2.stderr}`);
|
|
11581
11578
|
for (const relative3 of requiredBundlePaths()) {
|
|
11582
11579
|
try {
|
|
11583
|
-
await
|
|
11580
|
+
await access2(join13(releaseDir, relative3), fsConstants2.F_OK);
|
|
11584
11581
|
} catch {
|
|
11585
11582
|
throw new Error(`staged bundle is missing ${relative3}`);
|
|
11586
11583
|
}
|
|
@@ -11625,9 +11622,9 @@ async function replaceFile(path, contents, mode) {
|
|
|
11625
11622
|
}
|
|
11626
11623
|
async function activateRelease(layout, releaseId) {
|
|
11627
11624
|
const releaseDir = join13(layout.releasesRoot, releaseId);
|
|
11628
|
-
await
|
|
11629
|
-
await
|
|
11630
|
-
await
|
|
11625
|
+
await access2(join13(releaseDir, BUNDLE_ENTRYPOINT), fsConstants2.F_OK);
|
|
11626
|
+
await mkdir18(layout.releasesRoot, { recursive: true });
|
|
11627
|
+
await mkdir18(layout.binDir, { recursive: true });
|
|
11631
11628
|
let previousReleaseId = await activeReleaseId(layout);
|
|
11632
11629
|
const kind = await pathKind(layout.libDir);
|
|
11633
11630
|
if (kind === "directory") {
|
|
@@ -11635,7 +11632,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
11635
11632
|
const legacyId = sanitizeReleaseId(legacyIdentity?.commit ?? legacyIdentity?.version ?? `legacy-${Date.now()}`);
|
|
11636
11633
|
const legacyDir = join13(layout.releasesRoot, legacyId);
|
|
11637
11634
|
try {
|
|
11638
|
-
await
|
|
11635
|
+
await access2(legacyDir, fsConstants2.F_OK);
|
|
11639
11636
|
previousReleaseId = `${legacyId}-${Date.now()}`;
|
|
11640
11637
|
await rename6(layout.libDir, join13(layout.releasesRoot, previousReleaseId));
|
|
11641
11638
|
await normalizeLegacyBundleShape(join13(layout.releasesRoot, previousReleaseId));
|
|
@@ -11649,7 +11646,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
11649
11646
|
await rm6(tempLink, { force: true });
|
|
11650
11647
|
await symlink3(join13("beeline-releases", releaseId), tempLink);
|
|
11651
11648
|
await rename6(tempLink, layout.libDir);
|
|
11652
|
-
await fsyncDir(
|
|
11649
|
+
await fsyncDir(dirname16(layout.libDir));
|
|
11653
11650
|
await writeBinForwarders(layout, releaseDir);
|
|
11654
11651
|
return { previousReleaseId };
|
|
11655
11652
|
}
|
|
@@ -11658,7 +11655,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
11658
11655
|
let anyFlat = false;
|
|
11659
11656
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
11660
11657
|
try {
|
|
11661
|
-
await
|
|
11658
|
+
await access2(join13(bundleDir, name), fsConstants2.F_OK);
|
|
11662
11659
|
anyFlat = true;
|
|
11663
11660
|
break;
|
|
11664
11661
|
} catch {
|
|
@@ -11666,10 +11663,10 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
11666
11663
|
}
|
|
11667
11664
|
if (!anyFlat)
|
|
11668
11665
|
return;
|
|
11669
|
-
await
|
|
11666
|
+
await mkdir18(innerLib, { recursive: true });
|
|
11670
11667
|
for (const name of LEGACY_FLAT_BUNDLE_FILES) {
|
|
11671
11668
|
try {
|
|
11672
|
-
await
|
|
11669
|
+
await access2(join13(innerLib, name), fsConstants2.F_OK);
|
|
11673
11670
|
continue;
|
|
11674
11671
|
} catch {
|
|
11675
11672
|
}
|
|
@@ -11684,7 +11681,7 @@ async function writeBinForwarders(layout, activeBundleRoot) {
|
|
|
11684
11681
|
for (const [name, tool] of entries) {
|
|
11685
11682
|
const target = join13(activeBundleRoot, "bin", tool);
|
|
11686
11683
|
try {
|
|
11687
|
-
await
|
|
11684
|
+
await access2(target, fsConstants2.X_OK);
|
|
11688
11685
|
} catch {
|
|
11689
11686
|
continue;
|
|
11690
11687
|
}
|
|
@@ -11697,7 +11694,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
11697
11694
|
const forwarderHealthy = async (name, tool) => {
|
|
11698
11695
|
let current;
|
|
11699
11696
|
try {
|
|
11700
|
-
current = await
|
|
11697
|
+
current = await readFile11(join13(layout.binDir, name), "utf8");
|
|
11701
11698
|
} catch {
|
|
11702
11699
|
current = void 0;
|
|
11703
11700
|
}
|
|
@@ -11710,7 +11707,7 @@ async function repairInstallForwarders(layout, opts = {}) {
|
|
|
11710
11707
|
}
|
|
11711
11708
|
if (healthy)
|
|
11712
11709
|
return false;
|
|
11713
|
-
await
|
|
11710
|
+
await mkdir18(layout.binDir, { recursive: true });
|
|
11714
11711
|
await writeBinForwarders(layout, layout.libDir);
|
|
11715
11712
|
opts.logger?.(`[body] self-update: repaired <prefix>/bin forwarders to follow the active-bundle anchor (${layout.libDir})`);
|
|
11716
11713
|
return true;
|
|
@@ -11725,14 +11722,14 @@ async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
|
11725
11722
|
await rm6(tempLink, { force: true });
|
|
11726
11723
|
await symlink3(join13("beeline-releases", previousReleaseId), tempLink);
|
|
11727
11724
|
await rename6(tempLink, layout.libDir);
|
|
11728
|
-
await fsyncDir(
|
|
11725
|
+
await fsyncDir(dirname16(layout.libDir));
|
|
11729
11726
|
}
|
|
11730
11727
|
function updateAttemptPath(layout) {
|
|
11731
11728
|
return join13(layout.releasesRoot, ".state", "update-attempt.json");
|
|
11732
11729
|
}
|
|
11733
11730
|
async function readUpdateAttempt(layout) {
|
|
11734
11731
|
try {
|
|
11735
|
-
const raw = JSON.parse(await
|
|
11732
|
+
const raw = JSON.parse(await readFile11(updateAttemptPath(layout), "utf8"));
|
|
11736
11733
|
if (raw.version !== 1 || typeof raw.appliedAt !== "number" || typeof raw.confirmBy !== "number" || typeof raw.releaseId !== "string" || !["pending", "confirmed", "reverted"].includes(raw.status)) {
|
|
11737
11734
|
return void 0;
|
|
11738
11735
|
}
|
|
@@ -11742,7 +11739,7 @@ async function readUpdateAttempt(layout) {
|
|
|
11742
11739
|
}
|
|
11743
11740
|
}
|
|
11744
11741
|
async function writeUpdateAttempt(layout, record3) {
|
|
11745
|
-
await
|
|
11742
|
+
await mkdir18(join13(layout.releasesRoot, ".state"), { recursive: true });
|
|
11746
11743
|
const path = updateAttemptPath(layout);
|
|
11747
11744
|
const staged = `${path}.${process.pid}.tmp`;
|
|
11748
11745
|
await writeFile11(staged, `${JSON.stringify(record3, null, 2)}
|
|
@@ -11790,7 +11787,7 @@ function describeIdentity(identity) {
|
|
|
11790
11787
|
parts.push(identity.commit.slice(0, 12));
|
|
11791
11788
|
return parts.join(" ") || "unknown";
|
|
11792
11789
|
}
|
|
11793
|
-
var RELEASES_SEGMENT, BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, FORWARDER_TOOLS, FORWARDER_ALIASES, LEGACY_FLAT_BUNDLE_FILES, DEFAULT_UPDATE_CONFIRM_WINDOW_MS, SelfUpdateManager;
|
|
11790
|
+
var RELEASES_SEGMENT, BUNDLE_ENTRYPOINT, PI_MCP_ADAPTER_ENTRYPOINT, CODEGRAPH_ENTRYPOINT, FORWARDER_TOOLS, FORWARDER_ALIASES, LEGACY_FLAT_BUNDLE_FILES, DEFAULT_UPDATE_CONFIRM_WINDOW_MS, SelfUpdateManager;
|
|
11794
11791
|
var init_self_update = __esm({
|
|
11795
11792
|
"apps/body/dist/self-update.js"() {
|
|
11796
11793
|
"use strict";
|
|
@@ -11798,7 +11795,14 @@ var init_self_update = __esm({
|
|
|
11798
11795
|
RELEASES_SEGMENT = "beeline-releases";
|
|
11799
11796
|
BUNDLE_ENTRYPOINT = "lib/beeline/beeline-cli.mjs";
|
|
11800
11797
|
PI_MCP_ADAPTER_ENTRYPOINT = "lib/beeline/pi-mcp-adapter.mjs";
|
|
11801
|
-
|
|
11798
|
+
CODEGRAPH_ENTRYPOINT = "lib/beeline/codegraph/bin/codegraph";
|
|
11799
|
+
FORWARDER_TOOLS = [
|
|
11800
|
+
"beeline",
|
|
11801
|
+
"buzz-agent",
|
|
11802
|
+
"buzz-dev-mcp",
|
|
11803
|
+
"beeline-readonly-mcp",
|
|
11804
|
+
"codegraph"
|
|
11805
|
+
];
|
|
11802
11806
|
FORWARDER_ALIASES = {
|
|
11803
11807
|
usebeeline: "beeline"
|
|
11804
11808
|
};
|
|
@@ -12020,7 +12024,7 @@ function configureNetworkFamilyDefaults(network = net) {
|
|
|
12020
12024
|
configureNetworkFamilyDefaults();
|
|
12021
12025
|
|
|
12022
12026
|
// apps/body/dist/cli.js
|
|
12023
|
-
import { dirname as
|
|
12027
|
+
import { dirname as dirname23, resolve as resolve35 } from "node:path";
|
|
12024
12028
|
import { stdin as stdin4, stdout as stdout5 } from "node:process";
|
|
12025
12029
|
|
|
12026
12030
|
// node_modules/@clack/core/dist/index.mjs
|
|
@@ -13469,7 +13473,7 @@ function parseCursorModelsOutput(output) {
|
|
|
13469
13473
|
};
|
|
13470
13474
|
}
|
|
13471
13475
|
async function enumerateCursorModels(env = process.env) {
|
|
13472
|
-
return new Promise((
|
|
13476
|
+
return new Promise((resolve36) => {
|
|
13473
13477
|
const child = spawn("cursor-agent", ["models"], {
|
|
13474
13478
|
env,
|
|
13475
13479
|
stdio: ["ignore", "pipe", "ignore"]
|
|
@@ -13483,11 +13487,11 @@ async function enumerateCursorModels(env = process.env) {
|
|
|
13483
13487
|
});
|
|
13484
13488
|
child.on("error", () => {
|
|
13485
13489
|
clearTimeout(timer);
|
|
13486
|
-
|
|
13490
|
+
resolve36(void 0);
|
|
13487
13491
|
});
|
|
13488
13492
|
child.on("close", (code) => {
|
|
13489
13493
|
clearTimeout(timer);
|
|
13490
|
-
|
|
13494
|
+
resolve36(code === 0 ? parseCursorModelsOutput(output) : void 0);
|
|
13491
13495
|
});
|
|
13492
13496
|
});
|
|
13493
13497
|
}
|
|
@@ -13914,13 +13918,13 @@ var CursorAcpServer = class {
|
|
|
13914
13918
|
consumeLine(line);
|
|
13915
13919
|
});
|
|
13916
13920
|
try {
|
|
13917
|
-
const { code, signal } = await new Promise((
|
|
13921
|
+
const { code, signal } = await new Promise((resolve36) => {
|
|
13918
13922
|
let settled = false;
|
|
13919
13923
|
const finish = (exitCode, exitSignal) => {
|
|
13920
13924
|
if (settled)
|
|
13921
13925
|
return;
|
|
13922
13926
|
settled = true;
|
|
13923
|
-
|
|
13927
|
+
resolve36({ code: exitCode, signal: exitSignal });
|
|
13924
13928
|
};
|
|
13925
13929
|
child.once("error", (error) => {
|
|
13926
13930
|
spawnError = error instanceof Error ? error.message : String(error);
|
|
@@ -14364,7 +14368,10 @@ function resolveReadonlyMcpCommand(env = process.env) {
|
|
|
14364
14368
|
throw new Error("read-only tools unavailable: beeline-readonly-mcp was not found. Reinstall Beeline or set BEELINE_READONLY_MCP_BIN / BEELINE_READONLY_MCP_SCRIPT");
|
|
14365
14369
|
}
|
|
14366
14370
|
function resolveCodegraphCommand(env = process.env) {
|
|
14367
|
-
const
|
|
14371
|
+
const bundledBinary = commandInRunningBundle("codegraph");
|
|
14372
|
+
if (bundledBinary)
|
|
14373
|
+
return bundledBinary;
|
|
14374
|
+
const configured = env.BEELINE_CODEGRAPH_BIN ?? env.BUZZ_CODEGRAPH_BIN;
|
|
14368
14375
|
if (configured) {
|
|
14369
14376
|
try {
|
|
14370
14377
|
accessSync2(configured, constants2.X_OK);
|
|
@@ -15172,6 +15179,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15172
15179
|
osSandbox;
|
|
15173
15180
|
permissionHandler;
|
|
15174
15181
|
permissionAllowlist;
|
|
15182
|
+
commandsHandler;
|
|
15175
15183
|
constructor(opts) {
|
|
15176
15184
|
super();
|
|
15177
15185
|
const command = opts.agentCommand ?? opts.agentBinary;
|
|
@@ -15188,6 +15196,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15188
15196
|
this.osSandbox = opts.osSandbox ?? false;
|
|
15189
15197
|
this.permissionHandler = opts.permissionHandler;
|
|
15190
15198
|
this.permissionAllowlist = opts.permissionAllowlist;
|
|
15199
|
+
this.commandsHandler = opts.onCommands;
|
|
15191
15200
|
}
|
|
15192
15201
|
async start(timeoutMs = 6e4) {
|
|
15193
15202
|
if (this.alive)
|
|
@@ -15562,6 +15571,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15562
15571
|
if (params.update.sessionUpdate === "available_commands_update") {
|
|
15563
15572
|
const commands = parseAvailableCommands(params.update.availableCommands);
|
|
15564
15573
|
this.sessionCommands.set(params.sessionId, commands);
|
|
15574
|
+
this.commandsHandler?.(commands);
|
|
15565
15575
|
this.emit("commands", { sessionId: params.sessionId, commands });
|
|
15566
15576
|
}
|
|
15567
15577
|
const toolCallId = params.update.toolCallId;
|
|
@@ -15609,7 +15619,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15609
15619
|
const current = this.activeRunIds.get(sessionId);
|
|
15610
15620
|
if (current)
|
|
15611
15621
|
return Promise.resolve(current);
|
|
15612
|
-
return new Promise((
|
|
15622
|
+
return new Promise((resolve36, reject) => {
|
|
15613
15623
|
const onUpdate = (update) => {
|
|
15614
15624
|
if (update.sessionId !== sessionId)
|
|
15615
15625
|
return;
|
|
@@ -15617,7 +15627,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
15617
15627
|
if (!runId)
|
|
15618
15628
|
return;
|
|
15619
15629
|
cleanup();
|
|
15620
|
-
|
|
15630
|
+
resolve36(runId);
|
|
15621
15631
|
};
|
|
15622
15632
|
const timer = setTimeout(() => {
|
|
15623
15633
|
cleanup();
|
|
@@ -15701,13 +15711,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
15701
15711
|
}
|
|
15702
15712
|
const id = this.nextId++;
|
|
15703
15713
|
const payload = { jsonrpc: "2.0", id, method, params };
|
|
15704
|
-
return new Promise((
|
|
15714
|
+
return new Promise((resolve36, reject) => {
|
|
15705
15715
|
const timer = setTimeout(() => {
|
|
15706
15716
|
this.pending.delete(id);
|
|
15707
15717
|
reject(new AcpRequestTimeoutError(method, timeoutMs, this.stderrTail, Boolean(onStart), detail));
|
|
15708
15718
|
}, timeoutMs);
|
|
15709
15719
|
this.pending.set(id, {
|
|
15710
|
-
resolve:
|
|
15720
|
+
resolve: resolve36,
|
|
15711
15721
|
reject,
|
|
15712
15722
|
timer,
|
|
15713
15723
|
method,
|
|
@@ -15995,39 +16005,28 @@ function modelSelectionTargets(selection) {
|
|
|
15995
16005
|
}
|
|
15996
16006
|
];
|
|
15997
16007
|
}
|
|
15998
|
-
function assertModelSelectionAdvertised(advertisedOptions, selection) {
|
|
15999
|
-
for (const target of modelSelectionTargets(selection)) {
|
|
16000
|
-
if (!target.value)
|
|
16001
|
-
continue;
|
|
16002
|
-
const axis = advertisedOptions.find((option) => target.categories.includes(option.category));
|
|
16003
|
-
if (!axis) {
|
|
16004
|
-
continue;
|
|
16005
|
-
}
|
|
16006
|
-
assertModelConfigAxisAllowed(axis.id, advertisedOptions);
|
|
16007
|
-
if (!axis.options.some((choice) => choice.id === target.value)) {
|
|
16008
|
-
throw new ModelSelectionUnavailableError({
|
|
16009
|
-
label: target.label,
|
|
16010
|
-
value: target.value,
|
|
16011
|
-
reason: "not-advertised"
|
|
16012
|
-
});
|
|
16013
|
-
}
|
|
16014
|
-
}
|
|
16015
|
-
}
|
|
16016
16008
|
function assertModelConfigAxisAllowed(configId, advertisedOptions) {
|
|
16017
16009
|
const axis = advertisedOptions.find((option) => option.id === configId);
|
|
16018
16010
|
if (!axis || !isAllowedAgentModelConfigCategory(axis.category)) {
|
|
16019
16011
|
throw new DisallowedModelConfigOptionError(configId);
|
|
16020
16012
|
}
|
|
16021
16013
|
}
|
|
16022
|
-
async function
|
|
16023
|
-
|
|
16014
|
+
async function applyAgentModelSelectionWithUpdatedCatalog(client, sessionId, advertisedOptions, selection) {
|
|
16015
|
+
let currentOptions = advertisedOptions;
|
|
16024
16016
|
for (const target of modelSelectionTargets(selection)) {
|
|
16025
16017
|
if (!target.value)
|
|
16026
16018
|
continue;
|
|
16027
|
-
const axis =
|
|
16019
|
+
const axis = currentOptions.find((option) => target.categories.includes(option.category));
|
|
16028
16020
|
if (!axis)
|
|
16029
16021
|
continue;
|
|
16030
|
-
assertModelConfigAxisAllowed(axis.id,
|
|
16022
|
+
assertModelConfigAxisAllowed(axis.id, currentOptions);
|
|
16023
|
+
if (!axis.options.some((choice) => choice.id === target.value)) {
|
|
16024
|
+
throw new ModelSelectionUnavailableError({
|
|
16025
|
+
label: target.label,
|
|
16026
|
+
value: target.value,
|
|
16027
|
+
reason: "not-advertised"
|
|
16028
|
+
});
|
|
16029
|
+
}
|
|
16031
16030
|
try {
|
|
16032
16031
|
if (axis.id === GROK_SESSION_MODEL_AXIS_ID) {
|
|
16033
16032
|
if (!client.setModel)
|
|
@@ -16038,7 +16037,10 @@ async function applyAgentModelSelection(client, sessionId, advertisedOptions, se
|
|
|
16038
16037
|
throw new Error(`Grok started with reasoning effort "${axis.currentValue ?? "unknown"}", not "${target.value}"`);
|
|
16039
16038
|
}
|
|
16040
16039
|
} else {
|
|
16041
|
-
await client.setConfigOption(sessionId, axis.id, target.value);
|
|
16040
|
+
const updated = await client.setConfigOption(sessionId, axis.id, target.value);
|
|
16041
|
+
const refreshed = filterAllowedModelConfigOptions(parseAdvertisedConfigOptions(updated, selection.model));
|
|
16042
|
+
if (refreshed.length > 0)
|
|
16043
|
+
currentOptions = refreshed;
|
|
16042
16044
|
}
|
|
16043
16045
|
} catch (error) {
|
|
16044
16046
|
throw new ModelSelectionUnavailableError({
|
|
@@ -16049,6 +16051,10 @@ async function applyAgentModelSelection(client, sessionId, advertisedOptions, se
|
|
|
16049
16051
|
});
|
|
16050
16052
|
}
|
|
16051
16053
|
}
|
|
16054
|
+
return currentOptions;
|
|
16055
|
+
}
|
|
16056
|
+
async function applyAgentModelSelection(client, sessionId, advertisedOptions, selection) {
|
|
16057
|
+
await applyAgentModelSelectionWithUpdatedCatalog(client, sessionId, advertisedOptions, selection);
|
|
16052
16058
|
}
|
|
16053
16059
|
|
|
16054
16060
|
// apps/body/dist/model-catalog.js
|
|
@@ -16099,27 +16105,34 @@ async function withAgentModelCatalog(agent, agentEnv, selection, inspect, limits
|
|
|
16099
16105
|
async function fetchAgentModelCatalog(agent, agentEnv, selection, limits = {}) {
|
|
16100
16106
|
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => ({
|
|
16101
16107
|
raw,
|
|
16102
|
-
catalog: await filterModelChoicesByLiveValidation(client, sessionId, catalog)
|
|
16108
|
+
catalog: await filterModelChoicesByLiveValidation(client, sessionId, catalog, selection?.model)
|
|
16103
16109
|
}), limits);
|
|
16104
16110
|
}
|
|
16105
|
-
async function filterModelChoicesByLiveValidation(client, sessionId, catalog) {
|
|
16111
|
+
async function filterModelChoicesByLiveValidation(client, sessionId, catalog, preferredModelId) {
|
|
16106
16112
|
const modelAxis = catalog.find((axis) => axis.category === "model");
|
|
16107
16113
|
if (!modelAxis)
|
|
16108
16114
|
return catalog;
|
|
16109
16115
|
const available = [];
|
|
16116
|
+
const preferred = preferredModelId ?? modelAxis.currentValue;
|
|
16117
|
+
let preferredCatalog;
|
|
16110
16118
|
for (const choice of modelAxis.options) {
|
|
16111
16119
|
try {
|
|
16112
|
-
await
|
|
16120
|
+
const updated = await applyAgentModelSelectionWithUpdatedCatalog(client, sessionId, catalog, {
|
|
16121
|
+
model: choice.id
|
|
16122
|
+
});
|
|
16113
16123
|
available.push(choice);
|
|
16124
|
+
if (choice.id === preferred)
|
|
16125
|
+
preferredCatalog = updated;
|
|
16114
16126
|
} catch {
|
|
16115
16127
|
}
|
|
16116
16128
|
}
|
|
16117
|
-
|
|
16129
|
+
const effective = preferredCatalog ?? catalog;
|
|
16130
|
+
return effective.map((axis) => axis.category === "model" ? { ...axis, options: available } : axis);
|
|
16118
16131
|
}
|
|
16119
16132
|
async function validateAgentModelSelection(agent, agentEnv, selection) {
|
|
16120
16133
|
return withAgentModelCatalog(agent, agentEnv, selection, async ({ client, sessionId, raw, catalog }) => {
|
|
16121
|
-
await
|
|
16122
|
-
return { raw, catalog };
|
|
16134
|
+
const applied = await applyAgentModelSelectionWithUpdatedCatalog(client, sessionId, catalog, selection);
|
|
16135
|
+
return { raw, catalog: applied };
|
|
16123
16136
|
});
|
|
16124
16137
|
}
|
|
16125
16138
|
|
|
@@ -16205,7 +16218,7 @@ async function syncAgentModelCatalog(input) {
|
|
|
16205
16218
|
const effectiveCatalog = input.startupUnavailable ? catalog : withEffectiveCurrentValues(catalog, selection);
|
|
16206
16219
|
const hash = modelCatalogHash(effectiveCatalog, selection, input.startupUnavailable);
|
|
16207
16220
|
const previous = await readFile(hashPath, "utf8").catch(() => "");
|
|
16208
|
-
if (previous.trim() === hash)
|
|
16221
|
+
if (!input.force && previous.trim() === hash)
|
|
16209
16222
|
return "unchanged";
|
|
16210
16223
|
await input.api.execute("postAgentModelCatalog", {
|
|
16211
16224
|
agentId: input.agentId,
|
|
@@ -16226,8 +16239,8 @@ async function syncAgentModelCatalog(input) {
|
|
|
16226
16239
|
}
|
|
16227
16240
|
|
|
16228
16241
|
// apps/body/dist/connector-google.js
|
|
16229
|
-
import { mkdirSync as mkdirSync2, readFileSync as
|
|
16230
|
-
import { dirname as
|
|
16242
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync } from "node:fs";
|
|
16243
|
+
import { dirname as dirname4, join as join3 } from "node:path";
|
|
16231
16244
|
|
|
16232
16245
|
// apps/body/dist/google-workspace-client.js
|
|
16233
16246
|
function credentialsTokenSource(credentials) {
|
|
@@ -16601,14 +16614,170 @@ function googleWorkspaceClient(tokenSource, transport = defaultGoogleApiTranspor
|
|
|
16601
16614
|
};
|
|
16602
16615
|
}
|
|
16603
16616
|
|
|
16617
|
+
// apps/body/dist/connector-google.js
|
|
16618
|
+
var GOOGLE_TOOL_SCOPES = {
|
|
16619
|
+
"google-gmail": [
|
|
16620
|
+
"https://www.googleapis.com/auth/gmail.send",
|
|
16621
|
+
"https://www.googleapis.com/auth/gmail.readonly",
|
|
16622
|
+
"https://www.googleapis.com/auth/gmail.compose"
|
|
16623
|
+
],
|
|
16624
|
+
"google-calendar": ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
16625
|
+
"google-drive": ["https://www.googleapis.com/auth/drive.readonly"],
|
|
16626
|
+
"google-youtube": [
|
|
16627
|
+
"https://www.googleapis.com/auth/youtube.readonly",
|
|
16628
|
+
"https://www.googleapis.com/auth/yt-analytics.readonly"
|
|
16629
|
+
]
|
|
16630
|
+
};
|
|
16631
|
+
function isGoogleToolConnectorType(type) {
|
|
16632
|
+
return type in GOOGLE_TOOL_SCOPES;
|
|
16633
|
+
}
|
|
16634
|
+
async function readGoogleCredentialsFromVault(mcp) {
|
|
16635
|
+
if (!mcp)
|
|
16636
|
+
return { source: "unavailable", reason: "no Trusty Squire connector is paired" };
|
|
16637
|
+
let raw;
|
|
16638
|
+
try {
|
|
16639
|
+
raw = await mcp.call("google_oauth_credentials", {});
|
|
16640
|
+
} catch (error) {
|
|
16641
|
+
const detail = describe(error);
|
|
16642
|
+
return {
|
|
16643
|
+
source: "unavailable",
|
|
16644
|
+
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${detail})`
|
|
16645
|
+
};
|
|
16646
|
+
}
|
|
16647
|
+
const record3 = raw && typeof raw === "object" ? raw : {};
|
|
16648
|
+
const inner = record3.credentials && typeof record3.credentials === "object" ? record3.credentials : record3;
|
|
16649
|
+
const accessToken = inner.accessToken ?? inner.access_token;
|
|
16650
|
+
if (!accessToken || typeof accessToken !== "string") {
|
|
16651
|
+
return { source: "unavailable", reason: "Squire returned a Google grant without an access token" };
|
|
16652
|
+
}
|
|
16653
|
+
return {
|
|
16654
|
+
source: "squire",
|
|
16655
|
+
credentials: {
|
|
16656
|
+
accessToken,
|
|
16657
|
+
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
16658
|
+
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
16659
|
+
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
16660
|
+
}
|
|
16661
|
+
};
|
|
16662
|
+
}
|
|
16663
|
+
function manualGoogleCredentialsSearchPaths(home) {
|
|
16664
|
+
return [join3(home, "google-credentials.json")];
|
|
16665
|
+
}
|
|
16666
|
+
function loadManualGoogleCredentials(home, env = process.env) {
|
|
16667
|
+
if (env.BEELINE_GOOGLE_ACCESS_TOKEN) {
|
|
16668
|
+
return {
|
|
16669
|
+
source: "manual",
|
|
16670
|
+
credentials: { accessToken: env.BEELINE_GOOGLE_ACCESS_TOKEN }
|
|
16671
|
+
};
|
|
16672
|
+
}
|
|
16673
|
+
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
16674
|
+
try {
|
|
16675
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
16676
|
+
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
16677
|
+
return {
|
|
16678
|
+
source: "manual",
|
|
16679
|
+
credentials: {
|
|
16680
|
+
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
16681
|
+
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
16682
|
+
expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : void 0,
|
|
16683
|
+
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
16684
|
+
}
|
|
16685
|
+
};
|
|
16686
|
+
}
|
|
16687
|
+
} catch {
|
|
16688
|
+
}
|
|
16689
|
+
}
|
|
16690
|
+
return {
|
|
16691
|
+
source: "manual-missing",
|
|
16692
|
+
reason: `no Google credentials found. Put a google-credentials.json (OAuth access token) at ${manualGoogleCredentialsSearchPaths(home)[0]} or connect your Google account in Trusty Squire first.`
|
|
16693
|
+
};
|
|
16694
|
+
}
|
|
16695
|
+
function persistManualGoogleCredentials(home, credentials) {
|
|
16696
|
+
const path = manualGoogleCredentialsSearchPaths(home)[0];
|
|
16697
|
+
mkdirSync2(dirname4(path), { recursive: true, mode: 448 });
|
|
16698
|
+
writeFileSync(path, `${JSON.stringify({
|
|
16699
|
+
accessToken: credentials.accessToken,
|
|
16700
|
+
...credentials.refreshToken ? { refreshToken: credentials.refreshToken } : {},
|
|
16701
|
+
...credentials.expiresAt ? { expiresAt: credentials.expiresAt } : {},
|
|
16702
|
+
...credentials.accountEmail ? { accountEmail: credentials.accountEmail } : {}
|
|
16703
|
+
})}
|
|
16704
|
+
`, { encoding: "utf8", mode: 384 });
|
|
16705
|
+
return path;
|
|
16706
|
+
}
|
|
16707
|
+
var step = (label, status, extra) => ({
|
|
16708
|
+
label,
|
|
16709
|
+
status,
|
|
16710
|
+
...extra
|
|
16711
|
+
});
|
|
16712
|
+
function outputTail(text2, maxChars = 800) {
|
|
16713
|
+
const trimmed = text2.trim();
|
|
16714
|
+
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
16715
|
+
}
|
|
16716
|
+
async function installGoogleTool(options) {
|
|
16717
|
+
const steps = [step("helper reached", "done")];
|
|
16718
|
+
const emit = () => options.onProgress?.([...steps]);
|
|
16719
|
+
const push = (next) => {
|
|
16720
|
+
steps.push(next);
|
|
16721
|
+
emit();
|
|
16722
|
+
};
|
|
16723
|
+
const fail = (label, reason, output) => {
|
|
16724
|
+
push(step(label, "failed", { reason, ...output ? { output } : {} }));
|
|
16725
|
+
return { status: "error", steps, errorMessage: reason };
|
|
16726
|
+
};
|
|
16727
|
+
emit();
|
|
16728
|
+
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
16729
|
+
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
16730
|
+
}
|
|
16731
|
+
push(step("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
16732
|
+
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
16733
|
+
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
16734
|
+
if (oneClick.source === "squire")
|
|
16735
|
+
return oneClick;
|
|
16736
|
+
return loadManualGoogleCredentials(options.home, options.env);
|
|
16737
|
+
})();
|
|
16738
|
+
if (!("credentials" in resolved)) {
|
|
16739
|
+
const reason = resolved.reason;
|
|
16740
|
+
steps[1] = step("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
16741
|
+
emit();
|
|
16742
|
+
return { status: "error", steps, errorMessage: reason };
|
|
16743
|
+
}
|
|
16744
|
+
steps[1] = step("Google credentials resolved", "done", {
|
|
16745
|
+
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
16746
|
+
});
|
|
16747
|
+
emit();
|
|
16748
|
+
const client = options.client ?? googleWorkspaceClient(refreshableTokenSource(resolved.credentials, options.env?.BEELINE_GOOGLE_CLIENT_ID, options.env?.BEELINE_GOOGLE_CLIENT_SECRET));
|
|
16749
|
+
push(step("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
16750
|
+
const verify = await client.verify();
|
|
16751
|
+
if (!verify.ok) {
|
|
16752
|
+
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
16753
|
+
}
|
|
16754
|
+
steps[2] = step("authorized with Google", "done", {
|
|
16755
|
+
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
16756
|
+
});
|
|
16757
|
+
emit();
|
|
16758
|
+
push(step("tools enabled", "done", {
|
|
16759
|
+
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
16760
|
+
}));
|
|
16761
|
+
persistManualGoogleCredentials(options.home, resolved.credentials);
|
|
16762
|
+
return {
|
|
16763
|
+
status: "connected",
|
|
16764
|
+
steps,
|
|
16765
|
+
...verify.account ? { signedInAs: verify.account } : {}
|
|
16766
|
+
};
|
|
16767
|
+
}
|
|
16768
|
+
function describe(error) {
|
|
16769
|
+
return error instanceof Error ? error.message : String(error);
|
|
16770
|
+
}
|
|
16771
|
+
|
|
16604
16772
|
// apps/body/dist/connector-squire.js
|
|
16605
16773
|
import { execFile, spawn as spawn5 } from "node:child_process";
|
|
16606
16774
|
import { createHash as createHash2 } from "node:crypto";
|
|
16607
|
-
import { lstatSync as lstatSync3, readFileSync as
|
|
16775
|
+
import { lstatSync as lstatSync3, readFileSync as readFileSync4, realpathSync, rmSync } from "node:fs";
|
|
16608
16776
|
import { homedir as homedir3, hostname, tmpdir as tmpdir2 } from "node:os";
|
|
16609
|
-
import { basename as basename4, dirname as
|
|
16777
|
+
import { basename as basename4, dirname as dirname5, join as join4, resolve as resolve7 } from "node:path";
|
|
16610
16778
|
var SQUIRE_MCP_NAME = "@trusty-squire/mcp";
|
|
16611
|
-
var
|
|
16779
|
+
var SQUIRE_CONNECT_VERSION = "1.1.16-rc.4";
|
|
16780
|
+
var SQUIRE_CONNECT_PACKAGE = `${SQUIRE_MCP_NAME}@${SQUIRE_CONNECT_VERSION}`;
|
|
16612
16781
|
var activeConnectSession;
|
|
16613
16782
|
function squireConnectSession() {
|
|
16614
16783
|
return activeConnectSession;
|
|
@@ -16644,9 +16813,9 @@ function squireProfilePathIdentity(profileDir) {
|
|
|
16644
16813
|
let candidate = absolute;
|
|
16645
16814
|
for (; ; ) {
|
|
16646
16815
|
try {
|
|
16647
|
-
return
|
|
16816
|
+
return join4(realpathSync.native(candidate), ...suffix.reverse());
|
|
16648
16817
|
} catch {
|
|
16649
|
-
const parent =
|
|
16818
|
+
const parent = dirname5(candidate);
|
|
16650
16819
|
if (parent === candidate)
|
|
16651
16820
|
return absolute;
|
|
16652
16821
|
suffix.push(basename4(candidate));
|
|
@@ -16656,20 +16825,14 @@ function squireProfilePathIdentity(profileDir) {
|
|
|
16656
16825
|
}
|
|
16657
16826
|
function squireProfileLockPath(profileDir = squireChromeProfileDir(), lockRoot = tmpdir2()) {
|
|
16658
16827
|
const digest = createHash2("sha256").update(squireProfilePathIdentity(profileDir)).digest("hex").slice(0, 24);
|
|
16659
|
-
return
|
|
16660
|
-
}
|
|
16661
|
-
var SQUIRE_BROWSER_BUSY_GOOGLE_REASON = "Trusty Squire is still using the browser \u2014 connect Trusty Squire first";
|
|
16662
|
-
function isSquireBrowserSessionFailure(text2) {
|
|
16663
|
-
if (typeof text2 !== "string" || text2.length === 0)
|
|
16664
|
-
return false;
|
|
16665
|
-
return /Trusty Squire[\s\S]{0,80}browser/i.test(text2) || /browser[\s\S]{0,80}Trusty Squire/i.test(text2);
|
|
16828
|
+
return join4(lockRoot, `trusty-squire-profile-${digest}.lock`);
|
|
16666
16829
|
}
|
|
16667
16830
|
function squireForeignClaimAction(owner) {
|
|
16668
16831
|
return `Trusty Squire's browser is in use by another process (pid ${owner.pid}). Finish or close that Trusty Squire session, then press Connect again.`;
|
|
16669
16832
|
}
|
|
16670
16833
|
function readLinuxStartTime(pid) {
|
|
16671
16834
|
try {
|
|
16672
|
-
const stat5 =
|
|
16835
|
+
const stat5 = readFileSync4(`/proc/${pid}/stat`, "utf8");
|
|
16673
16836
|
const close = stat5.lastIndexOf(")");
|
|
16674
16837
|
return close < 0 ? void 0 : stat5.slice(close + 2).split(" ")[19];
|
|
16675
16838
|
} catch {
|
|
@@ -16688,8 +16851,8 @@ function lockOwnerIsAlive(owner) {
|
|
|
16688
16851
|
}
|
|
16689
16852
|
function readLockFileOwner(lockPath) {
|
|
16690
16853
|
try {
|
|
16691
|
-
const target = lstatSync3(lockPath).isDirectory() ?
|
|
16692
|
-
const parsed = JSON.parse(
|
|
16854
|
+
const target = lstatSync3(lockPath).isDirectory() ? join4(lockPath, "owner.json") : lockPath;
|
|
16855
|
+
const parsed = JSON.parse(readFileSync4(target, "utf8"));
|
|
16693
16856
|
if (typeof parsed.host !== "string" || typeof parsed.pid !== "number")
|
|
16694
16857
|
return void 0;
|
|
16695
16858
|
return {
|
|
@@ -16727,10 +16890,10 @@ function reclaimSquireProfileClaim(options) {
|
|
|
16727
16890
|
options?.log?.(`cleared dead trusty-squire on-disk browser claim (pid ${owner.pid} gone)`);
|
|
16728
16891
|
return { kind: "reclaimed-dead", owner };
|
|
16729
16892
|
}
|
|
16730
|
-
var defaultShellRunner = (command, args) => new Promise((
|
|
16893
|
+
var defaultShellRunner = (command, args) => new Promise((resolve36) => {
|
|
16731
16894
|
execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
|
|
16732
16895
|
const code = error?.code;
|
|
16733
|
-
|
|
16896
|
+
resolve36({
|
|
16734
16897
|
code: typeof code === "number" ? code : error ? 1 : 0,
|
|
16735
16898
|
stdout: String(stdout6 ?? ""),
|
|
16736
16899
|
stderr: String(stderr ?? "")
|
|
@@ -16755,7 +16918,7 @@ function killConnectTree(child) {
|
|
|
16755
16918
|
}
|
|
16756
16919
|
child.kill();
|
|
16757
16920
|
}
|
|
16758
|
-
var defaultStreamedRunner = (command, args, env) => new Promise((
|
|
16921
|
+
var defaultStreamedRunner = (command, args, env) => new Promise((resolve36) => {
|
|
16759
16922
|
const child = spawn5(command, args, {
|
|
16760
16923
|
stdio: ["ignore", "pipe", "pipe"],
|
|
16761
16924
|
env: env ?? squireConnectProcessEnv(),
|
|
@@ -16764,10 +16927,12 @@ var defaultStreamedRunner = (command, args, env) => new Promise((resolve35) => {
|
|
|
16764
16927
|
let stdout6 = "";
|
|
16765
16928
|
let stderr = "";
|
|
16766
16929
|
let resolved = false;
|
|
16930
|
+
let consumed = 0;
|
|
16931
|
+
let report;
|
|
16767
16932
|
const safetyTimer = setTimeout(() => {
|
|
16768
16933
|
if (!resolved) {
|
|
16769
16934
|
resolved = true;
|
|
16770
|
-
|
|
16935
|
+
resolve36({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
|
|
16771
16936
|
} });
|
|
16772
16937
|
}
|
|
16773
16938
|
killConnectTree(child);
|
|
@@ -16785,128 +16950,163 @@ var defaultStreamedRunner = (command, args, env) => new Promise((resolve35) => {
|
|
|
16785
16950
|
const finish = (result) => {
|
|
16786
16951
|
if (!resolved) {
|
|
16787
16952
|
resolved = true;
|
|
16788
|
-
|
|
16953
|
+
resolve36(result);
|
|
16789
16954
|
}
|
|
16790
16955
|
};
|
|
16791
16956
|
const settle = (result) => {
|
|
16792
16957
|
clearTimeout(safetyTimer);
|
|
16793
16958
|
finish(result);
|
|
16794
16959
|
};
|
|
16795
|
-
const
|
|
16796
|
-
const
|
|
16797
|
-
|
|
16798
|
-
|
|
16960
|
+
const consumeReports = (final) => {
|
|
16961
|
+
const pending = stdout6.slice(consumed);
|
|
16962
|
+
const lastNewline = pending.lastIndexOf("\n");
|
|
16963
|
+
if (lastNewline < 0 && !final)
|
|
16964
|
+
return;
|
|
16965
|
+
const complete = lastNewline < 0 ? pending : pending.slice(0, lastNewline);
|
|
16966
|
+
for (const line of complete.split("\n")) {
|
|
16967
|
+
if (line.trim() === "")
|
|
16968
|
+
continue;
|
|
16969
|
+
const parsed = parseConnectReport(line);
|
|
16970
|
+
if (parsed)
|
|
16971
|
+
report = parsed;
|
|
16972
|
+
}
|
|
16973
|
+
consumed += lastNewline < 0 ? pending.length : lastNewline + 1;
|
|
16974
|
+
};
|
|
16975
|
+
const publishSignIn = () => {
|
|
16976
|
+
consumeReports(false);
|
|
16977
|
+
if (report && isPublishableConnectReport(report)) {
|
|
16978
|
+
finish({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort });
|
|
16799
16979
|
}
|
|
16800
16980
|
};
|
|
16801
|
-
const finalSignIn = () => parseConnectOutput(`${stdout6}
|
|
16802
|
-
`) ?? parseConnectOutput(`${stderr}
|
|
16803
|
-
`);
|
|
16804
16981
|
child.stdout?.on("data", (chunk) => {
|
|
16805
16982
|
stdout6 += String(chunk);
|
|
16806
|
-
|
|
16983
|
+
publishSignIn();
|
|
16807
16984
|
});
|
|
16808
16985
|
child.stderr?.on("data", (chunk) => {
|
|
16809
16986
|
stderr += String(chunk);
|
|
16810
|
-
checkOutput();
|
|
16811
16987
|
});
|
|
16812
16988
|
child.on("close", () => {
|
|
16989
|
+
consumeReports(true);
|
|
16813
16990
|
settle({
|
|
16814
16991
|
stdout: stdout6,
|
|
16815
16992
|
stderr,
|
|
16816
16993
|
pid: child.pid ?? void 0,
|
|
16817
|
-
|
|
16994
|
+
report,
|
|
16818
16995
|
abort: () => {
|
|
16819
16996
|
}
|
|
16820
16997
|
});
|
|
16821
16998
|
});
|
|
16822
16999
|
child.on("error", () => {
|
|
16823
|
-
settle({ stdout: stdout6, stderr, pid: child.pid ?? void 0,
|
|
17000
|
+
settle({ stdout: stdout6, stderr, pid: child.pid ?? void 0, report, abort: () => {
|
|
16824
17001
|
} });
|
|
16825
17002
|
});
|
|
16826
17003
|
});
|
|
16827
|
-
var
|
|
17004
|
+
var step2 = (label, status, reason) => ({
|
|
16828
17005
|
label,
|
|
16829
17006
|
status,
|
|
16830
17007
|
...reason ? { reason } : {}
|
|
16831
17008
|
});
|
|
16832
|
-
function
|
|
16833
|
-
|
|
16834
|
-
|
|
16835
|
-
|
|
16836
|
-
|
|
16837
|
-
return
|
|
16838
|
-
|
|
16839
|
-
|
|
16840
|
-
|
|
17009
|
+
function isRecord(value) {
|
|
17010
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
17011
|
+
}
|
|
17012
|
+
function connectAccount(raw) {
|
|
17013
|
+
if (!isRecord(raw) || typeof raw.id !== "string")
|
|
17014
|
+
return null;
|
|
17015
|
+
return {
|
|
17016
|
+
id: raw.id,
|
|
17017
|
+
// An absent or unreadable `providers` is `null` (could not look), never
|
|
17018
|
+
// `[]` (looked and found nothing).
|
|
17019
|
+
providers: Array.isArray(raw.providers) ? raw.providers.filter((provider) => typeof provider === "string") : null
|
|
17020
|
+
};
|
|
16841
17021
|
}
|
|
16842
|
-
function
|
|
17022
|
+
function parseConnectReport(line) {
|
|
17023
|
+
let parsed;
|
|
16843
17024
|
try {
|
|
16844
|
-
|
|
16845
|
-
const host = parsed.hostname.toLowerCase();
|
|
16846
|
-
if (host === "developers.cloudflare.com")
|
|
16847
|
-
return true;
|
|
16848
|
-
if (host === "github.com" && parsed.pathname.toLowerCase().startsWith("/npm/cli"))
|
|
16849
|
-
return true;
|
|
16850
|
-
return host === "npmjs.com" || host === "www.npmjs.com";
|
|
17025
|
+
parsed = JSON.parse(line);
|
|
16851
17026
|
} catch {
|
|
16852
|
-
return
|
|
17027
|
+
return void 0;
|
|
16853
17028
|
}
|
|
17029
|
+
if (!isRecord(parsed) || typeof parsed.state !== "string")
|
|
17030
|
+
return void 0;
|
|
17031
|
+
return {
|
|
17032
|
+
state: parsed.state,
|
|
17033
|
+
terminal: parsed.terminal === true,
|
|
17034
|
+
reason: typeof parsed.reason === "string" ? parsed.reason : null,
|
|
17035
|
+
sign_in_url: typeof parsed.sign_in_url === "string" ? parsed.sign_in_url : null,
|
|
17036
|
+
account: connectAccount(parsed.account),
|
|
17037
|
+
holder: parsed.holder,
|
|
17038
|
+
browser_location: parsed.browser_location
|
|
17039
|
+
};
|
|
16854
17040
|
}
|
|
16855
|
-
|
|
16856
|
-
|
|
16857
|
-
|
|
16858
|
-
const
|
|
16859
|
-
|
|
16860
|
-
|
|
16861
|
-
|
|
16862
|
-
|
|
16863
|
-
|
|
16864
|
-
|
|
16865
|
-
|
|
16866
|
-
|
|
16867
|
-
|
|
16868
|
-
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
17041
|
+
function isOutstandingSignIn(report) {
|
|
17042
|
+
if (report.terminal || report.state !== "needs-sign-in" || !report.sign_in_url)
|
|
17043
|
+
return false;
|
|
17044
|
+
const location = report.browser_location;
|
|
17045
|
+
if (!isRecord(location) || typeof location.kind !== "string")
|
|
17046
|
+
return false;
|
|
17047
|
+
return location.kind !== "none" && location.kind !== "unreachable";
|
|
17048
|
+
}
|
|
17049
|
+
function isPublishableConnectReport(report) {
|
|
17050
|
+
return report.terminal || isOutstandingSignIn(report);
|
|
17051
|
+
}
|
|
17052
|
+
var CONNECT_REASON_LINES = {
|
|
17053
|
+
provider_session_missing: "the bot's Chrome profile has no live provider session",
|
|
17054
|
+
requested_provider_missing: "the provider sign-in you asked to refresh did not complete",
|
|
17055
|
+
account_mismatch: "this machine is bound to a different account",
|
|
17056
|
+
profile_unverifiable: "the bot's Chrome profile could not be verified",
|
|
17057
|
+
install_expired: "the sign-in page expired before it was used",
|
|
17058
|
+
cached_cookie_evidence: "the shared profile already carried the session",
|
|
17059
|
+
run_failed: "the connect run failed before it could finish"
|
|
17060
|
+
};
|
|
17061
|
+
function holderLine(raw) {
|
|
17062
|
+
if (!isRecord(raw))
|
|
17063
|
+
return void 0;
|
|
17064
|
+
if (raw.kind === "other") {
|
|
17065
|
+
const pid = typeof raw.pid === "number" ? raw.pid : void 0;
|
|
17066
|
+
return pid === void 0 ? "another Trusty Squire session is using the browser" : squireForeignClaimAction({ host: hostname(), pid, startTime: null });
|
|
16872
17067
|
}
|
|
16873
|
-
if (
|
|
16874
|
-
|
|
16875
|
-
return
|
|
17068
|
+
if (raw.kind === "unknown")
|
|
17069
|
+
return "another process may be using the browser";
|
|
17070
|
+
return void 0;
|
|
16876
17071
|
}
|
|
16877
|
-
function
|
|
16878
|
-
|
|
16879
|
-
|
|
16880
|
-
|
|
16881
|
-
|
|
16882
|
-
if (framed) {
|
|
16883
|
-
box.push(framed[1] ?? "");
|
|
16884
|
-
continue;
|
|
16885
|
-
}
|
|
16886
|
-
if (box.length > 0 && BOX_BORDER.test(row))
|
|
16887
|
-
lines.push(...rejoinBoxRows(box));
|
|
16888
|
-
box = [];
|
|
16889
|
-
lines.push(row);
|
|
17072
|
+
function connectBlockedLine(report) {
|
|
17073
|
+
if (report.state === "connected" || isOutstandingSignIn(report))
|
|
17074
|
+
return void 0;
|
|
17075
|
+
if (report.reason && CONNECT_REASON_LINES[report.reason]) {
|
|
17076
|
+
return CONNECT_REASON_LINES[report.reason];
|
|
16890
17077
|
}
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
16894
|
-
const urls = unframeBoxedOutput(output).match(/https:\/\/[^\s"'<>]+(?=[\s"'<>])/g) ?? [];
|
|
16895
|
-
const preferred = urls.find(isKnownCeremonySurface);
|
|
16896
|
-
if (preferred)
|
|
16897
|
-
return { method: "streamed-page", url: preferred };
|
|
16898
|
-
const fallback = urls.find((url) => !isForeignUrl(url));
|
|
16899
|
-
if (fallback) {
|
|
16900
|
-
console.log(`[body] squire connect: no known ceremony surface matched; using fallback URL ${fallback}`);
|
|
16901
|
-
return { method: "streamed-page", url: fallback };
|
|
17078
|
+
const location = isRecord(report.browser_location) ? report.browser_location : void 0;
|
|
17079
|
+
if (location?.kind === "unreachable") {
|
|
17080
|
+
return "the sign-in page could not be shown on this machine";
|
|
16902
17081
|
}
|
|
16903
|
-
|
|
16904
|
-
|
|
16905
|
-
|
|
16906
|
-
|
|
17082
|
+
if (report.state === "needs-sign-in") {
|
|
17083
|
+
return "the connect run ended before the sign-in was completed";
|
|
17084
|
+
}
|
|
17085
|
+
const holder = holderLine(report.holder);
|
|
17086
|
+
if (holder)
|
|
17087
|
+
return holder;
|
|
17088
|
+
return `Trusty Squire reported the connect state "${report.state}"`;
|
|
16907
17089
|
}
|
|
16908
|
-
function
|
|
16909
|
-
|
|
17090
|
+
function connectBrowserLocation(raw) {
|
|
17091
|
+
if (!isRecord(raw))
|
|
17092
|
+
return void 0;
|
|
17093
|
+
switch (raw.kind) {
|
|
17094
|
+
case "host_screen":
|
|
17095
|
+
return { kind: "host_screen" };
|
|
17096
|
+
case "virtual":
|
|
17097
|
+
return typeof raw.url === "string" ? { kind: "virtual", url: raw.url } : void 0;
|
|
17098
|
+
case "unreachable":
|
|
17099
|
+
return {
|
|
17100
|
+
kind: "unreachable",
|
|
17101
|
+
reason: typeof raw.reason === "string" ? raw.reason : "the sign-in page could not be shown"
|
|
17102
|
+
};
|
|
17103
|
+
case "none":
|
|
17104
|
+
return { kind: "none" };
|
|
17105
|
+
case "unknown":
|
|
17106
|
+
return typeof raw.reason === "string" ? { kind: "unknown", reason: raw.reason } : void 0;
|
|
17107
|
+
default:
|
|
17108
|
+
return void 0;
|
|
17109
|
+
}
|
|
16910
17110
|
}
|
|
16911
17111
|
async function installedSquireVersion(run2, spec = SQUIRE_CONNECT_PACKAGE, preferOnline = false) {
|
|
16912
17112
|
const probe = await run2("npx", [
|
|
@@ -16919,7 +17119,7 @@ async function installedSquireVersion(run2, spec = SQUIRE_CONNECT_PACKAGE, prefe
|
|
|
16919
17119
|
return version;
|
|
16920
17120
|
}
|
|
16921
17121
|
async function currentSquireRelease(run2) {
|
|
16922
|
-
const probe = await run2("npm", ["view",
|
|
17122
|
+
const probe = await run2("npm", ["view", SQUIRE_CONNECT_PACKAGE, "version"]);
|
|
16923
17123
|
if (probe.code !== 0)
|
|
16924
17124
|
return void 0;
|
|
16925
17125
|
return probe.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0];
|
|
@@ -16951,9 +17151,6 @@ async function resolveSquireConnectSpec(run2) {
|
|
|
16951
17151
|
reResolved: true
|
|
16952
17152
|
};
|
|
16953
17153
|
}
|
|
16954
|
-
function parseSignedInAs(output) {
|
|
16955
|
-
return output.match(/signed in as ([^\s,;]+)/i)?.[1];
|
|
16956
|
-
}
|
|
16957
17154
|
async function installSquire(options) {
|
|
16958
17155
|
const profileDir = options.profileDir ?? squireChromeProfileDir();
|
|
16959
17156
|
const lockRoot = options.lockRoot ?? tmpdir2();
|
|
@@ -16961,14 +17158,14 @@ async function installSquire(options) {
|
|
|
16961
17158
|
const streamRun = options.streamRun ?? defaultStreamedRunner;
|
|
16962
17159
|
const log2 = options.log ?? (() => {
|
|
16963
17160
|
});
|
|
16964
|
-
const steps = [
|
|
17161
|
+
const steps = [step2("helper reached", "done")];
|
|
16965
17162
|
const emit = () => options.onProgress?.([...steps]);
|
|
16966
17163
|
const push = (next) => {
|
|
16967
17164
|
steps.push(next);
|
|
16968
17165
|
emit();
|
|
16969
17166
|
};
|
|
16970
17167
|
const fail = (reason) => {
|
|
16971
|
-
steps.push(
|
|
17168
|
+
steps.push(step2("waiting for sign-in", "pending"));
|
|
16972
17169
|
emit();
|
|
16973
17170
|
return { status: "error", steps, errorMessage: reason };
|
|
16974
17171
|
};
|
|
@@ -16982,62 +17179,62 @@ async function installSquire(options) {
|
|
|
16982
17179
|
...previousPid !== void 0 ? { ourPids: [previousPid] } : {}
|
|
16983
17180
|
});
|
|
16984
17181
|
if (claim.kind === "blocked-foreign") {
|
|
16985
|
-
push(
|
|
17182
|
+
push(step2("trusty-squire installed", "failed", claim.action));
|
|
16986
17183
|
return fail(claim.action);
|
|
16987
17184
|
}
|
|
16988
17185
|
const resolution = await resolveSquireConnectSpec(run2);
|
|
16989
17186
|
if (resolution.reResolved) {
|
|
16990
17187
|
log2(`trusty-squire stale copy ${resolution.resolvedVersion ?? "unknown"} re-resolved against current release ${resolution.currentRelease}`);
|
|
16991
17188
|
}
|
|
16992
|
-
const install = await streamRun("npx", [...resolution.npxArgs, "connect", "--target=codex"], squireConnectProcessEnv(profileDir));
|
|
16993
|
-
const
|
|
16994
|
-
${install.stderr}`);
|
|
16995
|
-
if (!install.signIn && !alreadyConnected) {
|
|
16996
|
-
const stderr = withoutForceReloginHint(install.stderr);
|
|
16997
|
-
let stepReason = stderr || "connect printed no sign-in URL";
|
|
16998
|
-
let failReason = stderr || "the trusty-squire connect command printed no sign-in surface";
|
|
16999
|
-
if (isSquireBrowserSessionFailure(stderr)) {
|
|
17000
|
-
const again = reclaimSquireProfileClaim({ log: log2, profileDir, lockRoot });
|
|
17001
|
-
if (again.kind === "blocked-foreign") {
|
|
17002
|
-
stepReason = again.action;
|
|
17003
|
-
failReason = again.action;
|
|
17004
|
-
}
|
|
17005
|
-
}
|
|
17006
|
-
push(step("trusty-squire installed", "failed", stepReason));
|
|
17007
|
-
return fail(failReason);
|
|
17008
|
-
}
|
|
17189
|
+
const install = await streamRun("npx", [...resolution.npxArgs, "connect", "--target=codex", "--json"], squireConnectProcessEnv(profileDir));
|
|
17190
|
+
const report = install.report;
|
|
17009
17191
|
const version = resolution.resolvedVersion;
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17192
|
+
if (!report) {
|
|
17193
|
+
const reason = "Trusty Squire did not report a connect result";
|
|
17194
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17195
|
+
return fail(reason);
|
|
17196
|
+
}
|
|
17197
|
+
if (report.state === "needs-sign-in" && !report.sign_in_url) {
|
|
17198
|
+
const reason = "Trusty Squire reported a sign-in without a page";
|
|
17199
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17200
|
+
return fail(reason);
|
|
17201
|
+
}
|
|
17202
|
+
if (report.state !== "connected" && !isOutstandingSignIn(report)) {
|
|
17203
|
+
const reason = connectBlockedLine(report) ?? "Trusty Squire could not connect";
|
|
17204
|
+
push(step2("trusty-squire installed", "failed", reason));
|
|
17205
|
+
return fail(reason);
|
|
17206
|
+
}
|
|
17207
|
+
push(step2(`trusty-squire${version ? ` ${version}` : ""} installed`, "done"));
|
|
17208
|
+
const location = connectBrowserLocation(report.browser_location);
|
|
17209
|
+
const signIn = isOutstandingSignIn(report) && report.sign_in_url ? {
|
|
17210
|
+
method: "streamed-page",
|
|
17211
|
+
url: report.sign_in_url,
|
|
17212
|
+
...location ? { browserLocation: location } : {}
|
|
17213
|
+
} : void 0;
|
|
17214
|
+
push(step2("waiting for sign-in", signIn ? "pending" : "done"));
|
|
17015
17215
|
const pair = await pairSquire(options.mcp, options.workspaceId);
|
|
17016
17216
|
if (!pair.ok) {
|
|
17017
|
-
push(
|
|
17217
|
+
push(step2("paired to workspace", "failed", pair.reason));
|
|
17018
17218
|
return {
|
|
17019
17219
|
status: "installing",
|
|
17020
17220
|
steps,
|
|
17021
|
-
signIn,
|
|
17022
|
-
...version ? { squireVersion: version } : {}
|
|
17023
|
-
...signedInAs ? { signedInAs } : {}
|
|
17221
|
+
...signIn ? { signIn } : {},
|
|
17222
|
+
...version ? { squireVersion: version } : {}
|
|
17024
17223
|
};
|
|
17025
17224
|
}
|
|
17026
|
-
push(
|
|
17225
|
+
push(step2("paired to workspace", "done"));
|
|
17027
17226
|
if (signIn) {
|
|
17028
17227
|
return {
|
|
17029
17228
|
status: "installing",
|
|
17030
17229
|
steps,
|
|
17031
17230
|
signIn,
|
|
17032
|
-
...version ? { squireVersion: version } : {}
|
|
17033
|
-
...signedInAs ? { signedInAs } : {}
|
|
17231
|
+
...version ? { squireVersion: version } : {}
|
|
17034
17232
|
};
|
|
17035
17233
|
}
|
|
17036
17234
|
return {
|
|
17037
17235
|
status: "connected",
|
|
17038
17236
|
steps,
|
|
17039
|
-
...version ? { squireVersion: version } : {}
|
|
17040
|
-
...signedInAs ? { signedInAs } : {}
|
|
17237
|
+
...version ? { squireVersion: version } : {}
|
|
17041
17238
|
};
|
|
17042
17239
|
}
|
|
17043
17240
|
async function pairSquire(mcp, _workspaceId) {
|
|
@@ -17122,169 +17319,6 @@ async function revokeGrants(mcp, ref) {
|
|
|
17122
17319
|
return { revoked, failed };
|
|
17123
17320
|
}
|
|
17124
17321
|
|
|
17125
|
-
// apps/body/dist/connector-google.js
|
|
17126
|
-
var GOOGLE_TOOL_SCOPES = {
|
|
17127
|
-
"google-gmail": [
|
|
17128
|
-
"https://www.googleapis.com/auth/gmail.send",
|
|
17129
|
-
"https://www.googleapis.com/auth/gmail.readonly",
|
|
17130
|
-
"https://www.googleapis.com/auth/gmail.compose"
|
|
17131
|
-
],
|
|
17132
|
-
"google-calendar": ["https://www.googleapis.com/auth/calendar.events", "https://www.googleapis.com/auth/calendar.readonly"],
|
|
17133
|
-
"google-drive": ["https://www.googleapis.com/auth/drive.readonly"],
|
|
17134
|
-
"google-youtube": [
|
|
17135
|
-
"https://www.googleapis.com/auth/youtube.readonly",
|
|
17136
|
-
"https://www.googleapis.com/auth/yt-analytics.readonly"
|
|
17137
|
-
]
|
|
17138
|
-
};
|
|
17139
|
-
function isGoogleToolConnectorType(type) {
|
|
17140
|
-
return type in GOOGLE_TOOL_SCOPES;
|
|
17141
|
-
}
|
|
17142
|
-
async function readGoogleCredentialsFromVault(mcp) {
|
|
17143
|
-
if (!mcp)
|
|
17144
|
-
return { source: "unavailable", reason: "no Trusty Squire connector is paired" };
|
|
17145
|
-
let raw;
|
|
17146
|
-
try {
|
|
17147
|
-
raw = await mcp.call("google_oauth_credentials", {});
|
|
17148
|
-
} catch (error) {
|
|
17149
|
-
const detail = describe(error);
|
|
17150
|
-
if (isSquireBrowserSessionFailure(detail)) {
|
|
17151
|
-
return {
|
|
17152
|
-
source: "unavailable",
|
|
17153
|
-
reason: SQUIRE_BROWSER_BUSY_GOOGLE_REASON
|
|
17154
|
-
};
|
|
17155
|
-
}
|
|
17156
|
-
return {
|
|
17157
|
-
source: "unavailable",
|
|
17158
|
-
reason: `Squire has no Google OAuth grant on record yet (connect your Google account in Squire first: ${detail})`
|
|
17159
|
-
};
|
|
17160
|
-
}
|
|
17161
|
-
const record3 = raw && typeof raw === "object" ? raw : {};
|
|
17162
|
-
const inner = record3.credentials && typeof record3.credentials === "object" ? record3.credentials : record3;
|
|
17163
|
-
const accessToken = inner.accessToken ?? inner.access_token;
|
|
17164
|
-
if (!accessToken || typeof accessToken !== "string") {
|
|
17165
|
-
return { source: "unavailable", reason: "Squire returned a Google grant without an access token" };
|
|
17166
|
-
}
|
|
17167
|
-
return {
|
|
17168
|
-
source: "squire",
|
|
17169
|
-
credentials: {
|
|
17170
|
-
accessToken,
|
|
17171
|
-
refreshToken: typeof record3.refreshToken === "string" ? record3.refreshToken : typeof record3.refresh_token === "string" ? record3.refresh_token : void 0,
|
|
17172
|
-
expiresAt: typeof record3.expiresAt === "number" ? record3.expiresAt : void 0,
|
|
17173
|
-
accountEmail: typeof record3.accountEmail === "string" ? record3.accountEmail : typeof record3.email === "string" ? record3.email : void 0
|
|
17174
|
-
}
|
|
17175
|
-
};
|
|
17176
|
-
}
|
|
17177
|
-
function manualGoogleCredentialsSearchPaths(home) {
|
|
17178
|
-
return [join4(home, "google-credentials.json")];
|
|
17179
|
-
}
|
|
17180
|
-
function loadManualGoogleCredentials(home, env = process.env) {
|
|
17181
|
-
if (env.BEELINE_GOOGLE_ACCESS_TOKEN) {
|
|
17182
|
-
return {
|
|
17183
|
-
source: "manual",
|
|
17184
|
-
credentials: { accessToken: env.BEELINE_GOOGLE_ACCESS_TOKEN }
|
|
17185
|
-
};
|
|
17186
|
-
}
|
|
17187
|
-
for (const path of manualGoogleCredentialsSearchPaths(home)) {
|
|
17188
|
-
try {
|
|
17189
|
-
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
17190
|
-
if (typeof parsed.accessToken === "string" || typeof parsed.access_token === "string") {
|
|
17191
|
-
return {
|
|
17192
|
-
source: "manual",
|
|
17193
|
-
credentials: {
|
|
17194
|
-
accessToken: parsed.accessToken ?? parsed.access_token,
|
|
17195
|
-
refreshToken: typeof parsed.refreshToken === "string" ? parsed.refreshToken : void 0,
|
|
17196
|
-
expiresAt: typeof parsed.expiresAt === "number" ? parsed.expiresAt : void 0,
|
|
17197
|
-
accountEmail: typeof parsed.accountEmail === "string" ? parsed.accountEmail : void 0
|
|
17198
|
-
}
|
|
17199
|
-
};
|
|
17200
|
-
}
|
|
17201
|
-
} catch {
|
|
17202
|
-
}
|
|
17203
|
-
}
|
|
17204
|
-
return {
|
|
17205
|
-
source: "manual-missing",
|
|
17206
|
-
reason: `no Google credentials found. Put a google-credentials.json (OAuth access token) at ${manualGoogleCredentialsSearchPaths(home)[0]} or connect your Google account in Trusty Squire first.`
|
|
17207
|
-
};
|
|
17208
|
-
}
|
|
17209
|
-
function persistManualGoogleCredentials(home, credentials) {
|
|
17210
|
-
const path = manualGoogleCredentialsSearchPaths(home)[0];
|
|
17211
|
-
mkdirSync2(dirname5(path), { recursive: true, mode: 448 });
|
|
17212
|
-
writeFileSync(path, `${JSON.stringify({
|
|
17213
|
-
accessToken: credentials.accessToken,
|
|
17214
|
-
...credentials.refreshToken ? { refreshToken: credentials.refreshToken } : {},
|
|
17215
|
-
...credentials.expiresAt ? { expiresAt: credentials.expiresAt } : {},
|
|
17216
|
-
...credentials.accountEmail ? { accountEmail: credentials.accountEmail } : {}
|
|
17217
|
-
})}
|
|
17218
|
-
`, { encoding: "utf8", mode: 384 });
|
|
17219
|
-
return path;
|
|
17220
|
-
}
|
|
17221
|
-
var step2 = (label, status, extra) => ({
|
|
17222
|
-
label,
|
|
17223
|
-
status,
|
|
17224
|
-
...extra
|
|
17225
|
-
});
|
|
17226
|
-
function outputTail(text2, maxChars = 800) {
|
|
17227
|
-
const trimmed = text2.trim();
|
|
17228
|
-
return trimmed.length <= maxChars ? trimmed : `\u2026${trimmed.slice(-maxChars)}`;
|
|
17229
|
-
}
|
|
17230
|
-
async function installGoogleTool(options) {
|
|
17231
|
-
const steps = [step2("helper reached", "done")];
|
|
17232
|
-
const emit = () => options.onProgress?.([...steps]);
|
|
17233
|
-
const push = (next) => {
|
|
17234
|
-
steps.push(next);
|
|
17235
|
-
emit();
|
|
17236
|
-
};
|
|
17237
|
-
const fail = (label, reason, output) => {
|
|
17238
|
-
push(step2(label, "failed", { reason, ...output ? { output } : {} }));
|
|
17239
|
-
return { status: "error", steps, errorMessage: reason };
|
|
17240
|
-
};
|
|
17241
|
-
emit();
|
|
17242
|
-
if (!isGoogleToolConnectorType(options.connectorType)) {
|
|
17243
|
-
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
17244
|
-
}
|
|
17245
|
-
push(step2("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
17246
|
-
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
17247
|
-
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
17248
|
-
if (oneClick.source === "squire")
|
|
17249
|
-
return oneClick;
|
|
17250
|
-
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
17251
|
-
return oneClick;
|
|
17252
|
-
return loadManualGoogleCredentials(options.home, options.env);
|
|
17253
|
-
})();
|
|
17254
|
-
if (!("credentials" in resolved)) {
|
|
17255
|
-
const reason = resolved.reason;
|
|
17256
|
-
steps[1] = step2("Google credentials resolved", "failed", { reason, output: outputTail(reason) });
|
|
17257
|
-
emit();
|
|
17258
|
-
return { status: "error", steps, errorMessage: reason };
|
|
17259
|
-
}
|
|
17260
|
-
steps[1] = step2("Google credentials resolved", "done", {
|
|
17261
|
-
output: resolved.source === "squire" ? "one-click: read the Google grant from Trusty Squire" : "manual: local google-credentials.json"
|
|
17262
|
-
});
|
|
17263
|
-
emit();
|
|
17264
|
-
const client = options.client ?? googleWorkspaceClient(refreshableTokenSource(resolved.credentials, options.env?.BEELINE_GOOGLE_CLIENT_ID, options.env?.BEELINE_GOOGLE_CLIENT_SECRET));
|
|
17265
|
-
push(step2("authorized with Google", "running", { output: "verifying the grant with Google\u2026" }));
|
|
17266
|
-
const verify = await client.verify();
|
|
17267
|
-
if (!verify.ok) {
|
|
17268
|
-
return fail("authorized with Google", verify.reason, outputTail(verify.reason));
|
|
17269
|
-
}
|
|
17270
|
-
steps[2] = step2("authorized with Google", "done", {
|
|
17271
|
-
...verify.account ? { output: `signed in as ${verify.account}` } : {}
|
|
17272
|
-
});
|
|
17273
|
-
emit();
|
|
17274
|
-
push(step2("tools enabled", "done", {
|
|
17275
|
-
output: `${GOOGLE_TOOL_SCOPES[options.connectorType].length} Google scopes granted`
|
|
17276
|
-
}));
|
|
17277
|
-
persistManualGoogleCredentials(options.home, resolved.credentials);
|
|
17278
|
-
return {
|
|
17279
|
-
status: "connected",
|
|
17280
|
-
steps,
|
|
17281
|
-
...verify.account ? { signedInAs: verify.account } : {}
|
|
17282
|
-
};
|
|
17283
|
-
}
|
|
17284
|
-
function describe(error) {
|
|
17285
|
-
return error instanceof Error ? error.message : String(error);
|
|
17286
|
-
}
|
|
17287
|
-
|
|
17288
17322
|
// apps/body/dist/squire-mcp-client.js
|
|
17289
17323
|
import { spawn as spawn6 } from "node:child_process";
|
|
17290
17324
|
import { homedir as homedir4 } from "node:os";
|
|
@@ -17378,12 +17412,12 @@ var StdioSquireMcpClient = class {
|
|
|
17378
17412
|
const child = this.child;
|
|
17379
17413
|
if (!child)
|
|
17380
17414
|
return Promise.reject(new Error("Squire MCP session is not running"));
|
|
17381
|
-
return new Promise((
|
|
17415
|
+
return new Promise((resolve36, reject) => {
|
|
17382
17416
|
const timer = setTimeout(() => {
|
|
17383
17417
|
this.pending.delete(id);
|
|
17384
17418
|
reject(new Error(`${method} timed out`));
|
|
17385
17419
|
}, method === "initialize" ? INITIALIZE_TIMEOUT_MS : CALL_TIMEOUT_MS);
|
|
17386
|
-
this.pending.set(id, { resolve:
|
|
17420
|
+
this.pending.set(id, { resolve: resolve36, reject, timer });
|
|
17387
17421
|
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
17388
17422
|
`);
|
|
17389
17423
|
});
|
|
@@ -17426,7 +17460,8 @@ function defaultSquireMcpClient() {
|
|
|
17426
17460
|
}
|
|
17427
17461
|
|
|
17428
17462
|
// apps/body/dist/connector-assignments.js
|
|
17429
|
-
var CONNECTOR_POLL_INTERVAL_MS =
|
|
17463
|
+
var CONNECTOR_POLL_INTERVAL_MS = 5 * 6e4;
|
|
17464
|
+
var CONNECT_WATCH_INTERVAL_MS = 2e3;
|
|
17430
17465
|
var CEREMONY_EXPIRED = "the Trusty Squire sign-in page expired before it was used \xB7 tap Retry to open a new one";
|
|
17431
17466
|
var ConnectorAssignmentLoop = class {
|
|
17432
17467
|
agentId;
|
|
@@ -17441,6 +17476,8 @@ var ConnectorAssignmentLoop = class {
|
|
|
17441
17476
|
schedule;
|
|
17442
17477
|
cancel;
|
|
17443
17478
|
timer;
|
|
17479
|
+
/** Armed only while this helper owns a live sign-in ceremony. */
|
|
17480
|
+
connectWatch;
|
|
17444
17481
|
started = false;
|
|
17445
17482
|
stopped = false;
|
|
17446
17483
|
/** One install at a time per connector; other polls skip it. */
|
|
@@ -17476,12 +17513,56 @@ var ConnectorAssignmentLoop = class {
|
|
|
17476
17513
|
void this.runOnce();
|
|
17477
17514
|
this.timer = this.schedule(() => this.poll(), this.intervalMs);
|
|
17478
17515
|
}
|
|
17479
|
-
|
|
17480
|
-
|
|
17481
|
-
if (this.
|
|
17482
|
-
|
|
17483
|
-
|
|
17516
|
+
/** Event-driven drain. The interval stays the recovery net. */
|
|
17517
|
+
wake() {
|
|
17518
|
+
if (this.stopped)
|
|
17519
|
+
return;
|
|
17520
|
+
void this.runOnce();
|
|
17521
|
+
}
|
|
17522
|
+
stop() {
|
|
17523
|
+
this.stopped = true;
|
|
17524
|
+
if (this.timer !== void 0) {
|
|
17525
|
+
this.cancel(this.timer);
|
|
17526
|
+
this.timer = void 0;
|
|
17527
|
+
}
|
|
17528
|
+
if (this.connectWatch !== void 0) {
|
|
17529
|
+
this.cancel(this.connectWatch);
|
|
17530
|
+
this.connectWatch = void 0;
|
|
17531
|
+
}
|
|
17532
|
+
}
|
|
17533
|
+
/**
|
|
17534
|
+
* Watch the sign-in this helper is holding open. The phone paints
|
|
17535
|
+
* `installing` for the whole ceremony and only a drain that reaches
|
|
17536
|
+
* Squire's already-connected short-circuit flips the row to `connected`;
|
|
17537
|
+
* nothing on the wire announces that the human finished, so the connect
|
|
17538
|
+
* process exiting is the signal. Drain on that, and the row settles on the
|
|
17539
|
+
* same cadence it always has instead of waiting out the recovery poll.
|
|
17540
|
+
*/
|
|
17541
|
+
watchConnectSignIn() {
|
|
17542
|
+
if (this.stopped || this.connectWatch !== void 0)
|
|
17543
|
+
return;
|
|
17544
|
+
if (!this.connectCeremonyLive())
|
|
17545
|
+
return;
|
|
17546
|
+
this.connectWatch = this.schedule(() => this.checkConnectSignIn(), CONNECT_WATCH_INTERVAL_MS);
|
|
17547
|
+
}
|
|
17548
|
+
/** A ceremony of ours still worth waiting on: claimed, unspent, alive. */
|
|
17549
|
+
connectCeremonyLive() {
|
|
17550
|
+
const claim = squireConnectSession();
|
|
17551
|
+
if (!claim)
|
|
17552
|
+
return false;
|
|
17553
|
+
if (Date.now() - claim.claimedAt >= CONNECT_TIMEOUT_MS)
|
|
17554
|
+
return false;
|
|
17555
|
+
return isProcessAlive(claim.pid);
|
|
17556
|
+
}
|
|
17557
|
+
checkConnectSignIn() {
|
|
17558
|
+
this.connectWatch = void 0;
|
|
17559
|
+
if (this.stopped)
|
|
17560
|
+
return;
|
|
17561
|
+
if (this.connectCeremonyLive()) {
|
|
17562
|
+
this.connectWatch = this.schedule(() => this.checkConnectSignIn(), CONNECT_WATCH_INTERVAL_MS);
|
|
17563
|
+
return;
|
|
17484
17564
|
}
|
|
17565
|
+
void this.runOnce();
|
|
17485
17566
|
}
|
|
17486
17567
|
/** One interval tick: poll, then re-arm. */
|
|
17487
17568
|
poll() {
|
|
@@ -17548,8 +17629,6 @@ var ConnectorAssignmentLoop = class {
|
|
|
17548
17629
|
const oneClick = await readGoogleCredentialsFromVault(this.squire());
|
|
17549
17630
|
if (oneClick.source === "squire")
|
|
17550
17631
|
return oneClick;
|
|
17551
|
-
if (isSquireBrowserSessionFailure(oneClick.reason))
|
|
17552
|
-
return oneClick;
|
|
17553
17632
|
} catch (error) {
|
|
17554
17633
|
this.log(`google one-click grant lookup failed: ${describe2(error)}`);
|
|
17555
17634
|
}
|
|
@@ -17605,6 +17684,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
17605
17684
|
if (claim && !spent && isProcessAlive(claim.pid)) {
|
|
17606
17685
|
if (!await this.rearmedByHuman(connectorId)) {
|
|
17607
17686
|
this.log(`trusty-squire connect still waiting for sign-in (pid ${String(claim.pid)}); leaving it alone`);
|
|
17687
|
+
this.watchConnectSignIn();
|
|
17608
17688
|
return;
|
|
17609
17689
|
}
|
|
17610
17690
|
this.log("trusty-squire re-pair requested; superseding the connect holding the browser");
|
|
@@ -17646,8 +17726,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
17646
17726
|
await this.api.execute("installConnector", {
|
|
17647
17727
|
agentId: this.agentId,
|
|
17648
17728
|
connectorId,
|
|
17649
|
-
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17650
|
-
...result.signedInAs ? { signedInAs: result.signedInAs } : {}
|
|
17729
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17651
17730
|
});
|
|
17652
17731
|
await this.reportVault(connectorId);
|
|
17653
17732
|
return;
|
|
@@ -17657,9 +17736,9 @@ var ConnectorAssignmentLoop = class {
|
|
|
17657
17736
|
connectorId,
|
|
17658
17737
|
steps: result.steps,
|
|
17659
17738
|
signIn: result.signIn ?? null,
|
|
17660
|
-
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17661
|
-
...result.signedInAs ? { signedInAs: result.signedInAs } : {}
|
|
17739
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {}
|
|
17662
17740
|
});
|
|
17741
|
+
this.watchConnectSignIn();
|
|
17663
17742
|
}
|
|
17664
17743
|
/** This connector's own row, or undefined when the server cannot answer. */
|
|
17665
17744
|
async connectorRow(connectorId) {
|
|
@@ -23759,9 +23838,9 @@ function isHex32(input) {
|
|
|
23759
23838
|
return true;
|
|
23760
23839
|
}
|
|
23761
23840
|
var verifiedSymbol = /* @__PURE__ */ Symbol("verified");
|
|
23762
|
-
var
|
|
23841
|
+
var isRecord2 = (obj) => obj instanceof Object;
|
|
23763
23842
|
function validateEvent(event) {
|
|
23764
|
-
if (!
|
|
23843
|
+
if (!isRecord2(event))
|
|
23765
23844
|
return false;
|
|
23766
23845
|
if (typeof event.kind !== "number")
|
|
23767
23846
|
return false;
|
|
@@ -26685,6 +26764,15 @@ var AGENT_REMOVED_CODE = "agent_removed";
|
|
|
26685
26764
|
function isAgentRemovedError(error) {
|
|
26686
26765
|
return error instanceof DaemonApiError && error.status === 403 && error.code === AGENT_REMOVED_CODE;
|
|
26687
26766
|
}
|
|
26767
|
+
function membershipChange(event) {
|
|
26768
|
+
return {
|
|
26769
|
+
...typeof event.roomId === "string" && event.roomId ? { roomId: event.roomId } : {},
|
|
26770
|
+
...typeof event.parentRoomId === "string" ? { parentRoomId: event.parentRoomId } : {},
|
|
26771
|
+
...typeof event.openedBy === "string" ? { openedBy: event.openedBy } : {},
|
|
26772
|
+
...event.archived === true ? { archived: true } : {},
|
|
26773
|
+
...event.removed === true ? { removed: true } : {}
|
|
26774
|
+
};
|
|
26775
|
+
}
|
|
26688
26776
|
function endpoint(origin, path) {
|
|
26689
26777
|
return new URL(path, `${origin}/`).toString();
|
|
26690
26778
|
}
|
|
@@ -26712,6 +26800,8 @@ var DaemonApiClient = class {
|
|
|
26712
26800
|
roomsChangedListener;
|
|
26713
26801
|
configChangedListener;
|
|
26714
26802
|
hiccupRestartListener;
|
|
26803
|
+
connectorAssignmentListener;
|
|
26804
|
+
cornerCompleteListener;
|
|
26715
26805
|
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
|
|
26716
26806
|
this.baseUrl = baseUrl;
|
|
26717
26807
|
this.daemonToken = daemonToken;
|
|
@@ -26759,8 +26849,8 @@ var DaemonApiClient = class {
|
|
|
26759
26849
|
};
|
|
26760
26850
|
}
|
|
26761
26851
|
/** Register the one listener invoked when the server reports this agent's
|
|
26762
|
-
* Room/corner memberships changed —
|
|
26763
|
-
*
|
|
26852
|
+
* Room/corner memberships changed — a scoped event applies incrementally;
|
|
26853
|
+
* an unscoped wake (reconnect) still runs the recovery reconcile. */
|
|
26764
26854
|
setRoomsChangedListener(listener) {
|
|
26765
26855
|
this.roomsChangedListener = listener;
|
|
26766
26856
|
}
|
|
@@ -26774,6 +26864,14 @@ var DaemonApiClient = class {
|
|
|
26774
26864
|
setHiccupRestartListener(listener) {
|
|
26775
26865
|
this.hiccupRestartListener = listener;
|
|
26776
26866
|
}
|
|
26867
|
+
/** Connect / pending_ops drain wake. Never a catalog. */
|
|
26868
|
+
setConnectorAssignmentListener(listener) {
|
|
26869
|
+
this.connectorAssignmentListener = listener;
|
|
26870
|
+
}
|
|
26871
|
+
/** corner-complete on the subscribed corner — close now, poll is recovery. */
|
|
26872
|
+
setCornerCompleteListener(listener) {
|
|
26873
|
+
this.cornerCompleteListener = listener;
|
|
26874
|
+
}
|
|
26777
26875
|
updateLiveCursor(roomId, cursor3) {
|
|
26778
26876
|
const room = this.liveRooms.get(roomId);
|
|
26779
26877
|
if (room && cursor3)
|
|
@@ -26815,6 +26913,7 @@ var DaemonApiClient = class {
|
|
|
26815
26913
|
for (const roomId of this.liveRooms.keys())
|
|
26816
26914
|
this.sendLiveSubscription(roomId);
|
|
26817
26915
|
this.roomsChangedListener?.();
|
|
26916
|
+
this.connectorAssignmentListener?.();
|
|
26818
26917
|
};
|
|
26819
26918
|
socket.onmessage = (message) => {
|
|
26820
26919
|
let value;
|
|
@@ -26827,7 +26926,15 @@ var DaemonApiClient = class {
|
|
|
26827
26926
|
return;
|
|
26828
26927
|
const event = value;
|
|
26829
26928
|
if (event.type === "rooms-changed") {
|
|
26830
|
-
this.roomsChangedListener?.();
|
|
26929
|
+
this.roomsChangedListener?.(membershipChange(event));
|
|
26930
|
+
return;
|
|
26931
|
+
}
|
|
26932
|
+
if (event.type === "corner-complete" && typeof event.roomId === "string") {
|
|
26933
|
+
this.cornerCompleteListener?.(event.roomId);
|
|
26934
|
+
return;
|
|
26935
|
+
}
|
|
26936
|
+
if (event.type === "connector-assignment") {
|
|
26937
|
+
this.connectorAssignmentListener?.();
|
|
26831
26938
|
return;
|
|
26832
26939
|
}
|
|
26833
26940
|
if (event.type === "config-changed") {
|
|
@@ -26954,12 +27061,12 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
26954
27061
|
}
|
|
26955
27062
|
|
|
26956
27063
|
// apps/body/dist/room-runtime.js
|
|
26957
|
-
import { execFile as
|
|
27064
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
26958
27065
|
import { createHash as createHash7 } from "node:crypto";
|
|
26959
27066
|
import { existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
|
|
26960
|
-
import { mkdir as
|
|
26961
|
-
import { dirname as
|
|
26962
|
-
import { promisify as
|
|
27067
|
+
import { mkdir as mkdir15, rm as rm5 } from "node:fs/promises";
|
|
27068
|
+
import { dirname as dirname13, resolve as resolve24 } from "node:path";
|
|
27069
|
+
import { promisify as promisify5 } from "node:util";
|
|
26963
27070
|
|
|
26964
27071
|
// apps/body/dist/grant-runner.js
|
|
26965
27072
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -27297,9 +27404,10 @@ function sandboxMountPlan(spec) {
|
|
|
27297
27404
|
...spec.additionalWritablePaths ?? [],
|
|
27298
27405
|
...harnessState
|
|
27299
27406
|
] : (
|
|
27300
|
-
// A Room
|
|
27301
|
-
// state,
|
|
27302
|
-
//
|
|
27407
|
+
// A Room keeps source files read-only. Its explicit capabilities are
|
|
27408
|
+
// limited to harness state, agent-private paths, and release-owned
|
|
27409
|
+
// generated state such as the repository's .codegraph index; callers
|
|
27410
|
+
// must name each path. /tmp remains private.
|
|
27303
27411
|
[...spec.additionalWritablePaths ?? [], ...harnessState]
|
|
27304
27412
|
));
|
|
27305
27413
|
const tmpRestores = normalize([
|
|
@@ -27426,8 +27534,8 @@ function detectBwrapSandbox(options = {}) {
|
|
|
27426
27534
|
});
|
|
27427
27535
|
const result = run2(probe.command, probe.args);
|
|
27428
27536
|
if (result.status !== 0) {
|
|
27429
|
-
const detail = (result.stderr ?? "").trim().split("\n").pop() ?? `exit ${result.status}`;
|
|
27430
|
-
const appArmorRemediation = /No permissions to create new namespace|setting up uid map: Permission denied|userns_create/i.test(result.stderr ?? "") ? " Ubuntu AppArmor may be blocking unprivileged user namespaces.
|
|
27537
|
+
const detail = (result.stderr ?? "").trim().split("\n").filter(Boolean).pop() ?? `exit ${result.status}`;
|
|
27538
|
+
const appArmorRemediation = /No permissions to create (?:a )?new namespace|setting up uid map: Permission denied|userns_create/i.test(result.stderr ?? "") ? " Ubuntu AppArmor may be blocking unprivileged user namespaces. If the agent's user service inherited an AppArmor profile, reinstall the Beeline user unit and restart the agent; the unit explicitly transitions to unconfined before /usr/bin/bwrap enters Ubuntu's bwrap profile." : "";
|
|
27431
27539
|
return {
|
|
27432
27540
|
advisory: `harness OS sandbox UNAVAILABLE: ${bwrapPath} self-test failed (${detail}); ACP children run unconfined and the Room read-only rule rests on the permission handler alone.${appArmorRemediation}`
|
|
27433
27541
|
};
|
|
@@ -27501,6 +27609,7 @@ var GRANT_COMMAND_OUTPUT_CAP_BYTES = 64 * 1024;
|
|
|
27501
27609
|
var ARGV_MAX_WORDS = 256;
|
|
27502
27610
|
var ARGV_WORD_MAX_LENGTH = 4096;
|
|
27503
27611
|
var WRITE_REFUSED = /Read-only file system|EROFS/;
|
|
27612
|
+
var SANDBOX_STARTED_SCRIPT = ['printf "%s\\n" "$1" >&2', "shift", 'exec "$@"'].join("\n");
|
|
27504
27613
|
var ROOM_SANDBOX_UNAVAILABLE = "this Room cannot run granted commands: a Room promises a read-only filesystem and that promise is enforced by bubblewrap, which is not usable on this host. Open a corner with open_corner and run it there, where writes belong.";
|
|
27505
27614
|
var ROOM_WRITE_REFUSED_NOTE = "[beeline] the Room filesystem is read-only outside your scratch directory, so this write was refused by the kernel. Open a corner with open_corner to change files.";
|
|
27506
27615
|
function operatorSecretResolver(env = process.env) {
|
|
@@ -27624,7 +27733,9 @@ var GrantCommandRunner = class {
|
|
|
27624
27733
|
...Object.fromEntries(secrets)
|
|
27625
27734
|
};
|
|
27626
27735
|
const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
|
|
27627
|
-
const
|
|
27736
|
+
const runsOnHost = surfaceAllows(policy.surface, "run-host-command");
|
|
27737
|
+
const sandboxMarker = runsOnHost ? void 0 : `[beeline-sandbox-started:${randomBytes6(16).toString("hex")}]`;
|
|
27738
|
+
const spawn13 = runsOnHost ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv, sandboxMarker);
|
|
27628
27739
|
const outcome = await new Promise((resolveRun) => {
|
|
27629
27740
|
const child = execFile3(spawn13.command, spawn13.args, {
|
|
27630
27741
|
cwd: room.cwd,
|
|
@@ -27645,7 +27756,13 @@ var GrantCommandRunner = class {
|
|
|
27645
27756
|
});
|
|
27646
27757
|
});
|
|
27647
27758
|
});
|
|
27648
|
-
const
|
|
27759
|
+
const sandboxStarted = sandboxMarker ? outcome.output.includes(sandboxMarker) : true;
|
|
27760
|
+
if (sandboxMarker && sandboxStarted) {
|
|
27761
|
+
outcome.output = outcome.output.replace(`${sandboxMarker}
|
|
27762
|
+
`, "").replace(sandboxMarker, "");
|
|
27763
|
+
}
|
|
27764
|
+
const writeRefused = !runsOnHost && WRITE_REFUSED.test(outcome.output);
|
|
27765
|
+
const sandboxFailure = !runsOnHost && !sandboxStarted;
|
|
27649
27766
|
const output = capOutput(scrubSecrets(writeRefused ? `${outcome.output.trimEnd()}
|
|
27650
27767
|
${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
27651
27768
|
const turn = room.turn();
|
|
@@ -27653,7 +27770,7 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
27653
27770
|
pubkey: grant.requestedBy,
|
|
27654
27771
|
...grant.requestedByName ? { name: grant.requestedByName } : {}
|
|
27655
27772
|
};
|
|
27656
|
-
const status = outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
|
|
27773
|
+
const status = sandboxFailure ? "sandbox failed" : outcome.timedOut ? "timed out" : outcome.exitCode === null ? "error" : `exit ${outcome.exitCode}`;
|
|
27657
27774
|
await this.options.api.execute("postAgentActivity", {
|
|
27658
27775
|
agentId: this.options.agentId,
|
|
27659
27776
|
roomId: input.roomId,
|
|
@@ -27677,7 +27794,8 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
27677
27794
|
...outcome.signal ? { signal: outcome.signal } : {},
|
|
27678
27795
|
timedOut: outcome.timedOut,
|
|
27679
27796
|
output,
|
|
27680
|
-
...writeRefused ? { writeRefused: true } : {}
|
|
27797
|
+
...writeRefused ? { writeRefused: true } : {},
|
|
27798
|
+
...sandboxFailure ? { sandboxFailure: true } : {}
|
|
27681
27799
|
};
|
|
27682
27800
|
}
|
|
27683
27801
|
/**
|
|
@@ -27719,7 +27837,7 @@ function scriptCandidates(cwd, scratch, argv) {
|
|
|
27719
27837
|
];
|
|
27720
27838
|
return [...new Set(paths)];
|
|
27721
27839
|
}
|
|
27722
|
-
function roomSandboxCommand(policy, cwd, argv) {
|
|
27840
|
+
function roomSandboxCommand(policy, cwd, argv, sandboxMarker) {
|
|
27723
27841
|
if (!policy.bwrapPath)
|
|
27724
27842
|
throw new Error(ROOM_SANDBOX_UNAVAILABLE);
|
|
27725
27843
|
return wrapAgentCommand({
|
|
@@ -27734,8 +27852,8 @@ function roomSandboxCommand(policy, cwd, argv) {
|
|
|
27734
27852
|
...policy.scratch ? { tmpDir: policy.scratch } : {},
|
|
27735
27853
|
...policy.maskPaths ? { maskPaths: policy.maskPaths } : {}
|
|
27736
27854
|
},
|
|
27737
|
-
command:
|
|
27738
|
-
args: argv
|
|
27855
|
+
command: "/bin/sh",
|
|
27856
|
+
args: ["-c", SANDBOX_STARTED_SCRIPT, "beeline-grant-sandbox", sandboxMarker, ...argv]
|
|
27739
27857
|
});
|
|
27740
27858
|
}
|
|
27741
27859
|
var GrantRunnerServer = class {
|
|
@@ -28050,13 +28168,13 @@ async function runServerCommandIntake(options) {
|
|
|
28050
28168
|
}
|
|
28051
28169
|
}
|
|
28052
28170
|
options.onPoll?.();
|
|
28053
|
-
const reconcile = await new Promise((
|
|
28171
|
+
const reconcile = await new Promise((resolve36) => {
|
|
28054
28172
|
const done = (needed) => {
|
|
28055
28173
|
if (timer)
|
|
28056
28174
|
clearTimeout(timer);
|
|
28057
28175
|
signal?.removeEventListener("abort", aborted);
|
|
28058
28176
|
wake = void 0;
|
|
28059
|
-
|
|
28177
|
+
resolve36(needed);
|
|
28060
28178
|
};
|
|
28061
28179
|
const aborted = () => done(false);
|
|
28062
28180
|
wake = done;
|
|
@@ -28081,12 +28199,12 @@ async function runServerCommandIntake(options) {
|
|
|
28081
28199
|
}
|
|
28082
28200
|
|
|
28083
28201
|
// apps/body/dist/monolith-corner-turn.js
|
|
28084
|
-
import { execFile as
|
|
28202
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
28085
28203
|
import { createHash as createHash6 } from "node:crypto";
|
|
28086
|
-
import { mkdir as
|
|
28204
|
+
import { mkdir as mkdir14 } from "node:fs/promises";
|
|
28087
28205
|
import { homedir as homedir10 } from "node:os";
|
|
28088
28206
|
import { join as join12 } from "node:path";
|
|
28089
|
-
import { promisify as
|
|
28207
|
+
import { promisify as promisify4 } from "node:util";
|
|
28090
28208
|
|
|
28091
28209
|
// apps/body/dist/agent-home.js
|
|
28092
28210
|
var import_yaml2 = __toESM(require_dist(), 1);
|
|
@@ -28896,6 +29014,9 @@ import { basename as basename5, dirname as dirname10, join as join8, relative as
|
|
|
28896
29014
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
28897
29015
|
import { resolve as resolve13 } from "node:path";
|
|
28898
29016
|
|
|
29017
|
+
// packages/api-contract/dist/phone-types.js
|
|
29018
|
+
var MESSAGE_REACTION_EMOJIS = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F389}", "\u{1F440}", "\u2705"];
|
|
29019
|
+
|
|
28899
29020
|
// packages/api-contract/dist/workbench.js
|
|
28900
29021
|
var CONNECTABLE_CONNECTOR_KINDS = [
|
|
28901
29022
|
"trusty-squire",
|
|
@@ -28942,11 +29063,13 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
28942
29063
|
"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.",
|
|
28943
29064
|
"Tag another agent only when you need something from them: a question, a handoff, a task. Never tag to acknowledge, agree, or say you are ready. If nothing is actionable, do not reply.",
|
|
28944
29065
|
"Tag the user only when you need a decision or input, or when the task they asked for is finished. Never tag for progress, acknowledgement, or questions the transcript already answers.",
|
|
29066
|
+
"If shell access is blocked, continue with read-only inspection instead of retrying it: call beeline-readonly-mcp.search_text to find code and beeline-readonly-mcp.read_file to read it. Use CodeGraph first when it is available for indexed code relationships.",
|
|
28945
29067
|
"Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
|
|
28946
29068
|
BEELINE_AMBIENT_CONNECTOR_CAPABILITY,
|
|
28947
29069
|
"Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
|
|
28948
29070
|
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture. To put a real photograph in an artifact, call beeline-agent fetch_image with the photo URL; it writes the bytes to your session scratch and returns the path, mime, and size \u2014 read them, base64-encode, and embed as a data: URL. The validator still refuses every http(s) image reference, and drawing an SVG stand-in is not a photograph.",
|
|
28949
29071
|
"To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
|
|
29072
|
+
`To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
|
|
28950
29073
|
`To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant. choice-answered, choice-skipped, and poll-closed start a new input turn for the asking agent; they do not resume a paused grant.`,
|
|
28951
29074
|
"If you need reach outside the sandbox, call beeline-agent request_grant. If you already know discrete options, call beeline-agent ask_choice (one human, optional) or open_poll (every human in this Room, required deadline). A poll is refused below two electors and above fifty, and in a DM. A plurality is a fact, never permission to deploy, delete, merge, or spend. Open-ended asks stay tagged prose. Never put Always / Once / No on a preference.",
|
|
28952
29075
|
"To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
|
|
@@ -28966,6 +29089,7 @@ var BEELINE_DM_CAPABILITIES = [
|
|
|
28966
29089
|
"Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
|
|
28967
29090
|
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture. To put a real photograph in an artifact, call beeline-agent fetch_image with the photo URL; it writes the bytes to your session scratch and returns the path, mime, and size \u2014 read them, base64-encode, and embed as a data: URL. The validator still refuses every http(s) image reference, and drawing an SVG stand-in is not a photograph.",
|
|
28968
29091
|
"Tag the person only when you need a decision or input, or when the task they asked for is finished.",
|
|
29092
|
+
`To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
|
|
28969
29093
|
"If you already know discrete options, call beeline-agent ask_choice. A pick is a preference, never sandbox, spend, or merge authority. open_poll is refused here: one human is a question.",
|
|
28970
29094
|
"Never claim an action or reply happened unless the prompt or a tool result proves it."
|
|
28971
29095
|
].join(" ");
|
|
@@ -30807,12 +30931,22 @@ function harnessStateDirsFromEnv(env) {
|
|
|
30807
30931
|
return { stateDirs, ...tmp ? { tmpDir: resolve16(tmp) } : {} };
|
|
30808
30932
|
}
|
|
30809
30933
|
|
|
30934
|
+
// apps/body/dist/agent-command-catalog.js
|
|
30935
|
+
function agentCommandCatalogPublisher(input) {
|
|
30936
|
+
return (commands) => {
|
|
30937
|
+
void input.api.execute("postAgentCommands", {
|
|
30938
|
+
agentId: input.agentId,
|
|
30939
|
+
workspaceId: input.workspaceId,
|
|
30940
|
+
commands
|
|
30941
|
+
}).catch(input.report ?? ((error) => console.error("[body] agent command catalog publish failed:", error)));
|
|
30942
|
+
};
|
|
30943
|
+
}
|
|
30944
|
+
|
|
30810
30945
|
// apps/body/dist/attachment-delivery.js
|
|
30811
30946
|
import { mkdir as mkdir7, writeFile as writeFile8 } from "node:fs/promises";
|
|
30812
30947
|
import { basename as basename6, extname, join as join9 } from "node:path";
|
|
30813
30948
|
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
30814
|
-
var
|
|
30815
|
-
var EXPIRED_REASON = `expired: attachments are kept for ${MEDIA_TTL_HOURS} hours and these bytes are past that window`;
|
|
30949
|
+
var EXPIRED_REASON = "expired: these attachment bytes are past their retention window";
|
|
30816
30950
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
30817
30951
|
var BoundedSizeError = class extends Error {
|
|
30818
30952
|
bytes;
|
|
@@ -31449,14 +31583,18 @@ export default async function (pi) {
|
|
|
31449
31583
|
label: labelOf(tool.name),
|
|
31450
31584
|
description: typeof tool.description === 'string' ? tool.description : tool.name,
|
|
31451
31585
|
parameters: schema,
|
|
31452
|
-
async execute(
|
|
31586
|
+
async execute(toolCallId, params, signal) {
|
|
31453
31587
|
const result = await callServer(
|
|
31454
31588
|
server,
|
|
31455
31589
|
{
|
|
31456
31590
|
jsonrpc: '2.0',
|
|
31457
31591
|
id: 1,
|
|
31458
31592
|
method: 'tools/call',
|
|
31459
|
-
params: {
|
|
31593
|
+
params: {
|
|
31594
|
+
name: tool.name,
|
|
31595
|
+
arguments: params ?? {},
|
|
31596
|
+
_meta: { beelineToolCallId: toolCallId },
|
|
31597
|
+
},
|
|
31460
31598
|
},
|
|
31461
31599
|
signal,
|
|
31462
31600
|
);
|
|
@@ -31951,6 +32089,7 @@ function beelineAgentMcpServer(config, api, context) {
|
|
|
31951
32089
|
{ name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
|
|
31952
32090
|
{ name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
|
|
31953
32091
|
...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
|
|
32092
|
+
...context.agentMayCloseCorner ? [{ name: "BEELINE_CORNER_AGENT_CLOSE", value: "1" }] : [],
|
|
31954
32093
|
...context.reviewer ? [{ name: "BEELINE_CORNER_REVIEWER", value: "1" }] : [],
|
|
31955
32094
|
...context.attachRoot ? [{ name: "BEELINE_ATTACH_ROOT", value: context.attachRoot }] : [],
|
|
31956
32095
|
...context.attachScratchRoot ? [{ name: "BEELINE_ATTACH_SCRATCH_ROOT", value: context.attachScratchRoot }] : [],
|
|
@@ -31996,9 +32135,91 @@ function youtubeMcpServer(config, accessToken) {
|
|
|
31996
32135
|
};
|
|
31997
32136
|
}
|
|
31998
32137
|
|
|
32138
|
+
// apps/body/dist/codegraph.js
|
|
32139
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
32140
|
+
import { access, appendFile, mkdir as mkdir9, readFile as readFile7 } from "node:fs/promises";
|
|
32141
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, resolve as resolve19 } from "node:path";
|
|
32142
|
+
import { promisify as promisify3 } from "node:util";
|
|
32143
|
+
var execFileAsync3 = promisify3(execFile5);
|
|
32144
|
+
var CODEGRAPH_MCP_SERVER_NAME = "codegraph";
|
|
32145
|
+
var CODEGRAPH_INDEX_PATH = ".codegraph/codegraph.db";
|
|
32146
|
+
var CODEGRAPH_PREPARE_TIMEOUT_MS = 12e4;
|
|
32147
|
+
function codegraphIndexDirectory(cwd) {
|
|
32148
|
+
return resolve19(cwd, dirname11(CODEGRAPH_INDEX_PATH));
|
|
32149
|
+
}
|
|
32150
|
+
function codegraphMcpServer(config, cwd, options) {
|
|
32151
|
+
if (!config.codegraphCommand)
|
|
32152
|
+
return void 0;
|
|
32153
|
+
return {
|
|
32154
|
+
name: CODEGRAPH_MCP_SERVER_NAME,
|
|
32155
|
+
command: config.codegraphCommand,
|
|
32156
|
+
args: ["serve", "--mcp", "--path", resolve19(cwd), ...options.readonly ? ["--no-watch"] : []],
|
|
32157
|
+
env: [
|
|
32158
|
+
{ name: "CODEGRAPH_TELEMETRY", value: "0" },
|
|
32159
|
+
// One MCP child per harness session is easier to contain than a daemon
|
|
32160
|
+
// that can outlive the Room/corner whose index and permissions it used.
|
|
32161
|
+
{ name: "CODEGRAPH_NO_DAEMON", value: "1" }
|
|
32162
|
+
]
|
|
32163
|
+
};
|
|
32164
|
+
}
|
|
32165
|
+
function codegraphFingerprintServers(config, servers, mounted = Boolean(config.codegraphCommand)) {
|
|
32166
|
+
return config.codegraphCommand && mounted ? [...servers, CODEGRAPH_MCP_SERVER_NAME] : [...servers];
|
|
32167
|
+
}
|
|
32168
|
+
async function isGitWorktree(cwd) {
|
|
32169
|
+
try {
|
|
32170
|
+
const { stdout: stdout6 } = await execFileAsync3("git", ["rev-parse", "--show-toplevel"], {
|
|
32171
|
+
cwd,
|
|
32172
|
+
timeout: 1e4
|
|
32173
|
+
});
|
|
32174
|
+
return resolve19(stdout6.trim()) === resolve19(cwd);
|
|
32175
|
+
} catch {
|
|
32176
|
+
return false;
|
|
32177
|
+
}
|
|
32178
|
+
}
|
|
32179
|
+
async function excludeIndexFromGitStatus(cwd) {
|
|
32180
|
+
try {
|
|
32181
|
+
const { stdout: stdout6 } = await execFileAsync3("git", ["rev-parse", "--git-path", "info/exclude"], {
|
|
32182
|
+
cwd,
|
|
32183
|
+
timeout: 1e4
|
|
32184
|
+
});
|
|
32185
|
+
const gitPath = stdout6.trim();
|
|
32186
|
+
if (!gitPath)
|
|
32187
|
+
return;
|
|
32188
|
+
const path = isAbsolute3(gitPath) ? gitPath : resolve19(cwd, gitPath);
|
|
32189
|
+
await mkdir9(dirname11(path), { recursive: true });
|
|
32190
|
+
const existing = await readFile7(path, "utf8").catch(() => "");
|
|
32191
|
+
if (existing.split("\n").includes(".codegraph/"))
|
|
32192
|
+
return;
|
|
32193
|
+
await appendFile(path, `${existing && !existing.endsWith("\n") ? "\n" : ""}.codegraph/
|
|
32194
|
+
`);
|
|
32195
|
+
} catch {
|
|
32196
|
+
}
|
|
32197
|
+
}
|
|
32198
|
+
async function prepareCodegraphIndex(config, cwd) {
|
|
32199
|
+
const command = config.codegraphCommand;
|
|
32200
|
+
if (!command || !await isGitWorktree(cwd))
|
|
32201
|
+
return false;
|
|
32202
|
+
await excludeIndexFromGitStatus(cwd);
|
|
32203
|
+
const indexPath = resolve19(cwd, CODEGRAPH_INDEX_PATH);
|
|
32204
|
+
const indexed = await access(indexPath).then(() => true, () => false);
|
|
32205
|
+
const args = indexed ? ["sync", "--quiet", cwd] : ["init", "--yes", cwd];
|
|
32206
|
+
try {
|
|
32207
|
+
await execFileAsync3(command, args, {
|
|
32208
|
+
cwd,
|
|
32209
|
+
env: { ...process.env, CODEGRAPH_TELEMETRY: "0", CODEGRAPH_NO_DAEMON: "1" },
|
|
32210
|
+
timeout: CODEGRAPH_PREPARE_TIMEOUT_MS,
|
|
32211
|
+
maxBuffer: 4 * 1024 * 1024
|
|
32212
|
+
});
|
|
32213
|
+
return true;
|
|
32214
|
+
} catch (error) {
|
|
32215
|
+
console.warn(`[body] CodeGraph ${args[0]} failed for ${cwd}; continuing with Beeline read tools:`, error);
|
|
32216
|
+
return false;
|
|
32217
|
+
}
|
|
32218
|
+
}
|
|
32219
|
+
|
|
31999
32220
|
// apps/body/dist/pi-turn-record.js
|
|
32000
|
-
import { readdir as readdir3, readFile as
|
|
32001
|
-
import { resolve as
|
|
32221
|
+
import { readdir as readdir3, readFile as readFile8 } from "node:fs/promises";
|
|
32222
|
+
import { resolve as resolve20 } from "node:path";
|
|
32002
32223
|
function summarizeProviderError(errorMessage2) {
|
|
32003
32224
|
const trimmed = errorMessage2.trim();
|
|
32004
32225
|
const statusMatch = /^(\d{3}):\s*([\s\S]*)$/.exec(trimmed);
|
|
@@ -32019,7 +32240,7 @@ function summarizeProviderError(errorMessage2) {
|
|
|
32019
32240
|
}
|
|
32020
32241
|
async function sessionFileFromMap(home, sessionId) {
|
|
32021
32242
|
try {
|
|
32022
|
-
const raw = await
|
|
32243
|
+
const raw = await readFile8(resolve20(home, ".pi", "pi-acp", "session-map.json"), "utf8");
|
|
32023
32244
|
const map = JSON.parse(raw);
|
|
32024
32245
|
const file = map.sessions?.[sessionId]?.sessionFile;
|
|
32025
32246
|
return typeof file === "string" && file ? file : void 0;
|
|
@@ -32028,7 +32249,7 @@ async function sessionFileFromMap(home, sessionId) {
|
|
|
32028
32249
|
}
|
|
32029
32250
|
}
|
|
32030
32251
|
async function sessionFileFromLayout(piDir, sessionId) {
|
|
32031
|
-
const sessionsRoot =
|
|
32252
|
+
const sessionsRoot = resolve20(piDir, "sessions");
|
|
32032
32253
|
const suffix = `_${sessionId}.jsonl`;
|
|
32033
32254
|
let projects;
|
|
32034
32255
|
try {
|
|
@@ -32037,7 +32258,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
32037
32258
|
return void 0;
|
|
32038
32259
|
}
|
|
32039
32260
|
for (const project of projects) {
|
|
32040
|
-
const dir =
|
|
32261
|
+
const dir = resolve20(sessionsRoot, project);
|
|
32041
32262
|
let files;
|
|
32042
32263
|
try {
|
|
32043
32264
|
files = await readdir3(dir);
|
|
@@ -32046,7 +32267,7 @@ async function sessionFileFromLayout(piDir, sessionId) {
|
|
|
32046
32267
|
}
|
|
32047
32268
|
const match = files.find((file) => file.endsWith(suffix));
|
|
32048
32269
|
if (match)
|
|
32049
|
-
return
|
|
32270
|
+
return resolve20(dir, match);
|
|
32050
32271
|
}
|
|
32051
32272
|
return void 0;
|
|
32052
32273
|
}
|
|
@@ -32064,7 +32285,7 @@ async function readPiTurnRecord(input) {
|
|
|
32064
32285
|
return void 0;
|
|
32065
32286
|
let raw;
|
|
32066
32287
|
try {
|
|
32067
|
-
raw = await
|
|
32288
|
+
raw = await readFile8(file, "utf8");
|
|
32068
32289
|
} catch {
|
|
32069
32290
|
return void 0;
|
|
32070
32291
|
}
|
|
@@ -32220,8 +32441,8 @@ async function withTurnReceiptHeartbeat(api, receipt, task, onHeartbeatError) {
|
|
|
32220
32441
|
}
|
|
32221
32442
|
|
|
32222
32443
|
// apps/body/dist/turn-trace.js
|
|
32223
|
-
import { appendFile, mkdir as
|
|
32224
|
-
import { resolve as
|
|
32444
|
+
import { appendFile as appendFile2, mkdir as mkdir10, readdir as readdir4, rm as rm3 } from "node:fs/promises";
|
|
32445
|
+
import { resolve as resolve21 } from "node:path";
|
|
32225
32446
|
import { performance as performance2 } from "node:perf_hooks";
|
|
32226
32447
|
var TURN_PHASES = [
|
|
32227
32448
|
/** Enqueued on `SessionScheduler` until this turn holds a slot. A capacity wait lives here. */
|
|
@@ -32451,7 +32672,7 @@ function formatTurnTraceLine(record3) {
|
|
|
32451
32672
|
return [head, ...record3.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
|
|
32452
32673
|
}
|
|
32453
32674
|
function turnTraceDirectory(runtimeDir) {
|
|
32454
|
-
return
|
|
32675
|
+
return resolve21(runtimeDir, "turn-traces");
|
|
32455
32676
|
}
|
|
32456
32677
|
var TURN_TRACE_RETENTION_DAYS = 7;
|
|
32457
32678
|
function traceFileName(date) {
|
|
@@ -32468,14 +32689,14 @@ var TurnTraceFile = class {
|
|
|
32468
32689
|
this.options = options;
|
|
32469
32690
|
}
|
|
32470
32691
|
path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
|
|
32471
|
-
return
|
|
32692
|
+
return resolve21(this.directory, traceFileName(now2));
|
|
32472
32693
|
}
|
|
32473
32694
|
write(record3) {
|
|
32474
32695
|
this.tail = this.tail.catch(() => void 0).then(async () => {
|
|
32475
32696
|
const now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))();
|
|
32476
32697
|
const path = this.path(now2);
|
|
32477
|
-
await
|
|
32478
|
-
await
|
|
32698
|
+
await mkdir10(this.directory, { recursive: true, mode: 448 });
|
|
32699
|
+
await appendFile2(path, `${JSON.stringify(record3)}
|
|
32479
32700
|
`, { mode: 384 });
|
|
32480
32701
|
await this.prune(now2);
|
|
32481
32702
|
const log2 = this.options.log ?? ((line) => console.log(line));
|
|
@@ -32492,16 +32713,16 @@ var TurnTraceFile = class {
|
|
|
32492
32713
|
this.prunedDay = day;
|
|
32493
32714
|
const cutoff = traceFileName(new Date(now2.getTime() - TURN_TRACE_RETENTION_DAYS * 864e5));
|
|
32494
32715
|
const names = await readdir4(this.directory).catch(() => []);
|
|
32495
|
-
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(
|
|
32716
|
+
await Promise.all(names.filter((name) => /^turns-\d{4}-\d{2}-\d{2}\.jsonl$/.test(name) && name < cutoff).map((name) => rm3(resolve21(this.directory, name), { force: true })));
|
|
32496
32717
|
}
|
|
32497
32718
|
};
|
|
32498
32719
|
|
|
32499
32720
|
// apps/body/dist/corner-github-auth.js
|
|
32500
|
-
import { chmod as chmod4, mkdir as
|
|
32501
|
-
import { delimiter as delimiter2, resolve as
|
|
32721
|
+
import { chmod as chmod4, mkdir as mkdir11, writeFile as writeFile9 } from "node:fs/promises";
|
|
32722
|
+
import { delimiter as delimiter2, resolve as resolve22 } from "node:path";
|
|
32502
32723
|
async function installCornerGitHubWrappers(input) {
|
|
32503
|
-
const bin =
|
|
32504
|
-
await
|
|
32724
|
+
const bin = resolve22(input.root, "beeline-github-bin");
|
|
32725
|
+
await mkdir11(bin, { recursive: true, mode: 448 });
|
|
32505
32726
|
const common = {
|
|
32506
32727
|
node: process.execPath,
|
|
32507
32728
|
cli: input.cliEntrypoint,
|
|
@@ -32510,13 +32731,13 @@ async function installCornerGitHubWrappers(input) {
|
|
|
32510
32731
|
featureBranch: input.featureBranch,
|
|
32511
32732
|
targetBranch: input.targetBranch
|
|
32512
32733
|
};
|
|
32513
|
-
await writeLauncher(
|
|
32734
|
+
await writeLauncher(resolve22(bin, "git"), {
|
|
32514
32735
|
...common,
|
|
32515
32736
|
command: input.gitBinary,
|
|
32516
32737
|
launcher: "git"
|
|
32517
32738
|
});
|
|
32518
32739
|
if (input.ghBinary)
|
|
32519
|
-
await writeLauncher(
|
|
32740
|
+
await writeLauncher(resolve22(bin, "gh"), {
|
|
32520
32741
|
...common,
|
|
32521
32742
|
command: input.ghBinary,
|
|
32522
32743
|
launcher: "gh"
|
|
@@ -32653,23 +32874,23 @@ process.exit(result.status ?? 1);
|
|
|
32653
32874
|
// apps/body/dist/warm-node-modules.js
|
|
32654
32875
|
import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
|
|
32655
32876
|
import { constants as fsConstants } from "node:fs";
|
|
32656
|
-
import { chmod as chmod5, copyFile as copyFile2, link, lstat as lstat3, mkdir as
|
|
32657
|
-
import { dirname as
|
|
32877
|
+
import { chmod as chmod5, copyFile as copyFile2, link, lstat as lstat3, mkdir as mkdir12, readdir as readdir5, readFile as readFile9, readlink, rename as rename4, rm as rm4, stat as stat2, symlink as symlink2, utimes } from "node:fs/promises";
|
|
32878
|
+
import { dirname as dirname12, join as join10, resolve as resolve23 } from "node:path";
|
|
32658
32879
|
var STORE_FORMAT = "v1";
|
|
32659
32880
|
var STAGING_PREFIX = ".beeline-warm-";
|
|
32660
32881
|
var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
32661
32882
|
var WARM_STORE_MAX_ENTRIES = 3;
|
|
32662
32883
|
function sharedNpmCacheDir(supervisorRoot) {
|
|
32663
|
-
return
|
|
32884
|
+
return resolve23(supervisorRoot, "beeline", "npm-cache");
|
|
32664
32885
|
}
|
|
32665
32886
|
function warmNodeModulesStoreDir(supervisorRoot) {
|
|
32666
|
-
return
|
|
32887
|
+
return resolve23(supervisorRoot, "beeline", "node-modules");
|
|
32667
32888
|
}
|
|
32668
32889
|
function isPlanRefusal(value) {
|
|
32669
32890
|
return "failure" in value;
|
|
32670
32891
|
}
|
|
32671
32892
|
async function readWarmPlan(worktreePath) {
|
|
32672
|
-
const lockfile = await
|
|
32893
|
+
const lockfile = await readFile9(resolve23(worktreePath, "package-lock.json")).catch(() => void 0);
|
|
32673
32894
|
if (!lockfile)
|
|
32674
32895
|
return { failure: "no-lockfile" };
|
|
32675
32896
|
const packages = parseLockfilePackages(lockfile);
|
|
@@ -32708,11 +32929,11 @@ async function seedWarmNodeModules(input) {
|
|
|
32708
32929
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
32709
32930
|
}
|
|
32710
32931
|
for (const tree of plan.trees) {
|
|
32711
|
-
if (await pathExists(
|
|
32932
|
+
if (await pathExists(resolve23(input.worktreePath, tree))) {
|
|
32712
32933
|
return { reason: "present", key: plan.key };
|
|
32713
32934
|
}
|
|
32714
32935
|
}
|
|
32715
|
-
const entry =
|
|
32936
|
+
const entry = resolve23(input.storeRoot, plan.key);
|
|
32716
32937
|
if (!await isDirectory(entry))
|
|
32717
32938
|
return { reason: "cold", key: plan.key };
|
|
32718
32939
|
const [storeDevice, checkoutDevice] = await Promise.all([
|
|
@@ -32722,19 +32943,19 @@ async function seedWarmNodeModules(input) {
|
|
|
32722
32943
|
if (storeDevice === void 0 || storeDevice !== checkoutDevice) {
|
|
32723
32944
|
return { reason: "cross-device", key: plan.key };
|
|
32724
32945
|
}
|
|
32725
|
-
const staging =
|
|
32946
|
+
const staging = resolve23(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
32726
32947
|
const placed = [];
|
|
32727
32948
|
const now2 = (input.now ?? Date.now)();
|
|
32728
32949
|
try {
|
|
32729
32950
|
await sweepStaleStaging(input.storeRoot, now2);
|
|
32730
32951
|
await utimes(entry, now2 / 1e3, now2 / 1e3).catch(() => void 0);
|
|
32731
32952
|
for (const tree of plan.trees) {
|
|
32732
|
-
await cloneTree(
|
|
32953
|
+
await cloneTree(resolve23(entry, tree), resolve23(staging, tree), seedFile);
|
|
32733
32954
|
}
|
|
32734
32955
|
for (const tree of plan.trees) {
|
|
32735
|
-
const target =
|
|
32736
|
-
await
|
|
32737
|
-
await rename4(
|
|
32956
|
+
const target = resolve23(input.worktreePath, tree);
|
|
32957
|
+
await mkdir12(dirname12(target), { recursive: true });
|
|
32958
|
+
await rename4(resolve23(staging, tree), target);
|
|
32738
32959
|
placed.push(target);
|
|
32739
32960
|
}
|
|
32740
32961
|
return { reason: "seeded", key: plan.key };
|
|
@@ -32752,11 +32973,11 @@ async function harvestWarmNodeModules(input) {
|
|
|
32752
32973
|
if (isPlanRefusal(plan)) {
|
|
32753
32974
|
return { reason: plan.failure, ...plan.detail ? { detail: plan.detail } : {} };
|
|
32754
32975
|
}
|
|
32755
|
-
const entry =
|
|
32976
|
+
const entry = resolve23(input.storeRoot, plan.key);
|
|
32756
32977
|
if (await pathExists(entry))
|
|
32757
32978
|
return { reason: "already-warm", key: plan.key };
|
|
32758
32979
|
for (const tree of plan.trees) {
|
|
32759
|
-
if (!await isDirectory(
|
|
32980
|
+
if (!await isDirectory(resolve23(input.worktreePath, tree))) {
|
|
32760
32981
|
return { reason: "no-node-modules", key: plan.key, detail: tree };
|
|
32761
32982
|
}
|
|
32762
32983
|
}
|
|
@@ -32768,12 +32989,12 @@ async function harvestWarmNodeModules(input) {
|
|
|
32768
32989
|
detail: `${missing.length} absent, e.g. ${missing.slice(0, 3).join(", ")}`
|
|
32769
32990
|
};
|
|
32770
32991
|
}
|
|
32771
|
-
const staging =
|
|
32992
|
+
const staging = resolve23(input.storeRoot, `${STAGING_PREFIX}${process.pid}.${randomUUID5()}`);
|
|
32772
32993
|
try {
|
|
32773
|
-
await
|
|
32994
|
+
await mkdir12(input.storeRoot, { recursive: true, mode: 493 });
|
|
32774
32995
|
await sweepStaleStaging(input.storeRoot, (input.now ?? Date.now)());
|
|
32775
32996
|
for (const tree of plan.trees) {
|
|
32776
|
-
await cloneTree(
|
|
32997
|
+
await cloneTree(resolve23(input.worktreePath, tree), resolve23(staging, tree), harvestFile);
|
|
32777
32998
|
}
|
|
32778
32999
|
await input.onCopied?.();
|
|
32779
33000
|
if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
|
|
@@ -32796,16 +33017,16 @@ async function stagedTreeIsPublishable(worktreePath, staging, key) {
|
|
|
32796
33017
|
return false;
|
|
32797
33018
|
const hidden = join10("node_modules", ".package-lock.json");
|
|
32798
33019
|
const [copied, current] = await Promise.all([
|
|
32799
|
-
|
|
32800
|
-
|
|
33020
|
+
readFile9(resolve23(staging, hidden)).catch(() => void 0),
|
|
33021
|
+
readFile9(resolve23(worktreePath, hidden)).catch(() => void 0)
|
|
32801
33022
|
]);
|
|
32802
33023
|
if (!copied || !current || !copied.equals(current))
|
|
32803
33024
|
return false;
|
|
32804
33025
|
return (await missingInstalledPackages(worktreePath, staging)).length === 0;
|
|
32805
33026
|
}
|
|
32806
33027
|
async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
32807
|
-
const wanted = parseLockfilePackages(await
|
|
32808
|
-
const installed = parseLockfilePackages(await
|
|
33028
|
+
const wanted = parseLockfilePackages(await readFile9(resolve23(worktreePath, "package-lock.json")).catch(() => void 0));
|
|
33029
|
+
const installed = parseLockfilePackages(await readFile9(resolve23(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
|
|
32809
33030
|
if (!wanted)
|
|
32810
33031
|
return ["package-lock.json is unreadable"];
|
|
32811
33032
|
if (!installed)
|
|
@@ -32825,7 +33046,7 @@ async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
|
32825
33046
|
required.push(path);
|
|
32826
33047
|
}
|
|
32827
33048
|
await mapWithLimit(required, INTEGRITY_READ_CONCURRENCY, async (path) => {
|
|
32828
|
-
if (!(path in installed) || !await isInstalledPackage(
|
|
33049
|
+
if (!(path in installed) || !await isInstalledPackage(resolve23(treeRoot, path))) {
|
|
32829
33050
|
missing.push(path);
|
|
32830
33051
|
}
|
|
32831
33052
|
});
|
|
@@ -32864,7 +33085,7 @@ function isContainedTreePath(value) {
|
|
|
32864
33085
|
return isContainedPath(value) && value.split("/").pop() === "node_modules";
|
|
32865
33086
|
}
|
|
32866
33087
|
async function cloneTree(source, target, file, topLevel = true) {
|
|
32867
|
-
await
|
|
33088
|
+
await mkdir12(target, { recursive: true, mode: 493 });
|
|
32868
33089
|
for (const entry of await readdir5(source, { withFileTypes: true })) {
|
|
32869
33090
|
const from = join10(source, entry.name);
|
|
32870
33091
|
const to = join10(target, entry.name);
|
|
@@ -32933,7 +33154,7 @@ function describe3(error) {
|
|
|
32933
33154
|
}
|
|
32934
33155
|
|
|
32935
33156
|
// apps/body/dist/monolith-room-turn.js
|
|
32936
|
-
import { mkdir as
|
|
33157
|
+
import { mkdir as mkdir13 } from "node:fs/promises";
|
|
32937
33158
|
import { homedir as homedir9 } from "node:os";
|
|
32938
33159
|
import { join as join11 } from "node:path";
|
|
32939
33160
|
|
|
@@ -33020,6 +33241,8 @@ var MonolithRoomTurnLoop = class {
|
|
|
33020
33241
|
sessionId;
|
|
33021
33242
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
33022
33243
|
sessionFingerprint;
|
|
33244
|
+
/** Whether CodeGraph preparation succeeded for the live session. */
|
|
33245
|
+
sessionCodegraphReady = false;
|
|
33023
33246
|
/** The live session's environment, read back for pi's own turn record. */
|
|
33024
33247
|
agentEnv = {};
|
|
33025
33248
|
/** OpenRouter providers this activation pinned, in order (C92). */
|
|
@@ -33168,6 +33391,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33168
33391
|
this.client = void 0;
|
|
33169
33392
|
this.sessionId = void 0;
|
|
33170
33393
|
this.sessionFingerprint = void 0;
|
|
33394
|
+
this.sessionCodegraphReady = false;
|
|
33171
33395
|
this.pinnedProviderOverride = void 0;
|
|
33172
33396
|
if (client?.isAlive)
|
|
33173
33397
|
await client.stop();
|
|
@@ -33199,11 +33423,11 @@ var MonolithRoomTurnLoop = class {
|
|
|
33199
33423
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33200
33424
|
soul: configuration.soul ?? self?.soul,
|
|
33201
33425
|
agentName: self?.name ?? this.agent.name,
|
|
33202
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
33426
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
33203
33427
|
operatorHome: this.options.config.operatorHome,
|
|
33204
33428
|
agentKind: this.options.config.agentKind,
|
|
33205
33429
|
grantedHostRoutes
|
|
33206
|
-
})
|
|
33430
|
+
}), this.sessionCodegraphReady)
|
|
33207
33431
|
});
|
|
33208
33432
|
}
|
|
33209
33433
|
async grantedHostRoutes() {
|
|
@@ -33237,7 +33461,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33237
33461
|
]);
|
|
33238
33462
|
const self = roster.members.find((member) => member.identityId === this.agent.publicKey);
|
|
33239
33463
|
const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
|
|
33240
|
-
await
|
|
33464
|
+
await mkdir13(this.options.cwd, { recursive: true });
|
|
33241
33465
|
const selectionModel = configuration.model ?? this.options.config.modelSelection?.model;
|
|
33242
33466
|
const selectionEffort = configuration.effort ?? this.options.config.modelSelection?.effort;
|
|
33243
33467
|
const selection = selectionModel || selectionEffort ? { model: selectionModel, effort: selectionEffort } : void 0;
|
|
@@ -33264,17 +33488,6 @@ var MonolithRoomTurnLoop = class {
|
|
|
33264
33488
|
command
|
|
33265
33489
|
});
|
|
33266
33490
|
const agentEnv = { ...this.options.config.agentEnv, ...homeOverlay };
|
|
33267
|
-
const fingerprint = sessionConfigFingerprint({
|
|
33268
|
-
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
33269
|
-
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33270
|
-
soul: configuration.soul ?? self?.soul,
|
|
33271
|
-
agentName: self?.name ?? this.agent.name,
|
|
33272
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
33273
|
-
operatorHome: this.options.config.operatorHome,
|
|
33274
|
-
agentKind: this.options.config.agentKind,
|
|
33275
|
-
grantedHostRoutes
|
|
33276
|
-
})
|
|
33277
|
-
});
|
|
33278
33491
|
this.agentEnv = agentEnv;
|
|
33279
33492
|
const agentArgs = agentArgsWithModelSelection({
|
|
33280
33493
|
kind: this.options.config.agentKind,
|
|
@@ -33286,10 +33499,22 @@ var MonolithRoomTurnLoop = class {
|
|
|
33286
33499
|
this.sessionScratchDir = tmpDir;
|
|
33287
33500
|
this.sessionStateDirs = stateDirs;
|
|
33288
33501
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
33289
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
33502
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir13(dir, { recursive: true })));
|
|
33290
33503
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
33291
33504
|
if (attachScratchRoot)
|
|
33292
|
-
await
|
|
33505
|
+
await mkdir13(attachScratchRoot, { recursive: true });
|
|
33506
|
+
const codegraphReady = await prepareCodegraphIndex(this.options.config, this.options.cwd);
|
|
33507
|
+
const fingerprint = sessionConfigFingerprint({
|
|
33508
|
+
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
33509
|
+
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
33510
|
+
soul: configuration.soul ?? self?.soul,
|
|
33511
|
+
agentName: self?.name ?? this.agent.name,
|
|
33512
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
33513
|
+
operatorHome: this.options.config.operatorHome,
|
|
33514
|
+
agentKind: this.options.config.agentKind,
|
|
33515
|
+
grantedHostRoutes
|
|
33516
|
+
}), codegraphReady)
|
|
33517
|
+
});
|
|
33293
33518
|
const spawnCommand = wrapAgentCommand({
|
|
33294
33519
|
bwrapPath: this.options.config.bwrapPath,
|
|
33295
33520
|
spec: {
|
|
@@ -33300,6 +33525,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33300
33525
|
...tmpDir ? { tmpDir } : {},
|
|
33301
33526
|
additionalWritablePaths: [
|
|
33302
33527
|
...attachScratchRoot ? [attachScratchRoot] : [],
|
|
33528
|
+
...codegraphReady ? [codegraphIndexDirectory(this.options.cwd)] : [],
|
|
33303
33529
|
...grantedSquireHostBindPaths({
|
|
33304
33530
|
operatorHome,
|
|
33305
33531
|
agentKind: this.options.config.agentKind,
|
|
@@ -33324,9 +33550,16 @@ var MonolithRoomTurnLoop = class {
|
|
|
33324
33550
|
attachScratchRoot,
|
|
33325
33551
|
turnContextPath: this.commandContext.path,
|
|
33326
33552
|
directMessage,
|
|
33327
|
-
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
33553
|
+
...this.options.grantRunnerEndpoint && this.options.config.bwrapPath ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
33328
33554
|
})
|
|
33329
33555
|
];
|
|
33556
|
+
if (codegraphReady) {
|
|
33557
|
+
const codegraph = codegraphMcpServer(this.options.config, this.options.cwd, {
|
|
33558
|
+
readonly: true
|
|
33559
|
+
});
|
|
33560
|
+
if (codegraph)
|
|
33561
|
+
servers.push(codegraph);
|
|
33562
|
+
}
|
|
33330
33563
|
const youtube = youtubeMcpServer(this.options.config, this.options.youtubeAccessToken);
|
|
33331
33564
|
if (youtube)
|
|
33332
33565
|
servers.push(youtube);
|
|
@@ -33358,7 +33591,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
33358
33591
|
// (`config.ts`), which is exactly when `wrapAgentCommand` above wraps.
|
|
33359
33592
|
osSandbox: Boolean(this.options.config.bwrapPath),
|
|
33360
33593
|
autoApprovePermissions: false,
|
|
33361
|
-
permissionAllowlist: (request) => isRoomMcpPermissionRequest(request, mountedServers, hostServers)
|
|
33594
|
+
permissionAllowlist: (request) => isRoomMcpPermissionRequest(request, mountedServers, hostServers),
|
|
33595
|
+
onCommands: agentCommandCatalogPublisher({
|
|
33596
|
+
api: this.options.api,
|
|
33597
|
+
agentId: this.agent.publicKey,
|
|
33598
|
+
workspaceId: this.options.workspaceId
|
|
33599
|
+
})
|
|
33362
33600
|
};
|
|
33363
33601
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
33364
33602
|
await this.client.start();
|
|
@@ -33390,6 +33628,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33390
33628
|
});
|
|
33391
33629
|
this.sessionId = opened.sessionId;
|
|
33392
33630
|
this.sessionFingerprint = fingerprint;
|
|
33631
|
+
this.sessionCodegraphReady = codegraphReady;
|
|
33393
33632
|
if (selection) {
|
|
33394
33633
|
const options = filterAllowedModelConfigOptions(parseAdvertisedConfigOptions(opened.raw, selection.model));
|
|
33395
33634
|
await applyAgentModelSelection(this.client, opened.sessionId, options, selection);
|
|
@@ -33453,6 +33692,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33453
33692
|
this.client = void 0;
|
|
33454
33693
|
this.sessionId = void 0;
|
|
33455
33694
|
this.sessionFingerprint = void 0;
|
|
33695
|
+
this.sessionCodegraphReady = false;
|
|
33456
33696
|
if (client?.isAlive)
|
|
33457
33697
|
await client.stop();
|
|
33458
33698
|
this.pinnedProviderOverride = next;
|
|
@@ -33545,7 +33785,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
33545
33785
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
33546
33786
|
const transcriptRows = conversation.items.filter((message) => message.type === "message" && message.id !== item.id && !active.steers.some((steerItem) => steerItem.id === message.id)).slice(-80).map((message) => ({
|
|
33547
33787
|
id: message.id,
|
|
33548
|
-
line: roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id), this.acceptsImages())
|
|
33788
|
+
line: roomMessagePrompt(names.get(message.authorId) ?? message.authorId.slice(0, 12), message.body, message.attachments, this.deliveredAttachments.get(message.id), this.acceptsImages(), message.id)
|
|
33549
33789
|
}));
|
|
33550
33790
|
const grantDecision = this.commandContext.current?.action === "resume";
|
|
33551
33791
|
const resumedRequestId = grantDecision ? this.pausedOnGrantRequestId : void 0;
|
|
@@ -33565,7 +33805,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
33565
33805
|
MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE
|
|
33566
33806
|
].join(" "),
|
|
33567
33807
|
`Current task selected by the server from ${inboxItemAuthorName(item, names)}:`,
|
|
33568
|
-
roomMessagePrompt("", inboxItemPromptBody(item), item.attachments, delivered, this.acceptsImages())
|
|
33808
|
+
roomMessagePrompt("", inboxItemPromptBody(item), item.attachments, delivered, this.acceptsImages(), item.type === "message" ? item.id : void 0)
|
|
33569
33809
|
].filter(Boolean).join("\n\n");
|
|
33570
33810
|
const stream = new AgentTurnStream({
|
|
33571
33811
|
api,
|
|
@@ -33608,7 +33848,7 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
33608
33848
|
"The previous run was cancelled because its harness could not accept every live steer.",
|
|
33609
33849
|
"Resume the same turn. Keep the original request and everything that happened before it was cancelled.",
|
|
33610
33850
|
"Human messages that arrived after the original request, in transcript order:",
|
|
33611
|
-
...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments, this.deliveredAttachments.get(steerItem.id), this.acceptsImages())),
|
|
33851
|
+
...active.steers.map((steerItem) => roomMessagePrompt(steerItem.authorId.slice(0, 12), steerItem.body, steerItem.attachments, this.deliveredAttachments.get(steerItem.id), this.acceptsImages(), steerItem.type === "message" ? steerItem.id : void 0)),
|
|
33612
33852
|
"Continue now and answer the updated request without erasing the earlier context."
|
|
33613
33853
|
].join("\n\n");
|
|
33614
33854
|
}
|
|
@@ -33766,14 +34006,18 @@ function openCornerToolCall(calls) {
|
|
|
33766
34006
|
function openedACorner(call) {
|
|
33767
34007
|
return !!call && isCompletedToolCall(call);
|
|
33768
34008
|
}
|
|
33769
|
-
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true) {
|
|
34009
|
+
function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsImages = true, messageId) {
|
|
33770
34010
|
const message = body.trim() || "(shared attachments)";
|
|
33771
34011
|
const rendered = author ? `${author}: ${message}` : message;
|
|
33772
|
-
return [
|
|
34012
|
+
return [
|
|
34013
|
+
...messageId ? [`[message id: ${messageId}]`] : [],
|
|
34014
|
+
rendered,
|
|
34015
|
+
...attachmentPromptLines(attachments, delivered, harnessAcceptsImages)
|
|
34016
|
+
].join("\n");
|
|
33773
34017
|
}
|
|
33774
34018
|
|
|
33775
34019
|
// apps/body/dist/monolith-corner-turn.js
|
|
33776
|
-
var
|
|
34020
|
+
var execFileAsync4 = promisify4(execFile6);
|
|
33777
34021
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
33778
34022
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
33779
34023
|
var TOOL_PATH_LIMIT = 12;
|
|
@@ -33790,6 +34034,8 @@ function cornerReviewerInstruction(input) {
|
|
|
33790
34034
|
const headSha = input.headSha ?? "<head sha>";
|
|
33791
34035
|
return `Checks are green on PR #${number} at ${headSha}. Review it now with the beeline-review skill against that exact head. FAIL: reply \`@${author}\` with the confirmed findings to fix. PASS: call the approve_merge tool for ${headSha}, then reply \`@${author} approved ${headSha}, merge\`. Never merge yourself. Never say you are holding or waiting for checks.`;
|
|
33792
34036
|
}
|
|
34037
|
+
var CORNER_REVIEWER_SESSION_INSTRUCTION = "You are this Room's configured reviewer. The active turn prompt names the latest stable green PR head. Review and approve only that exact head; if no stable green head is named, end the turn without a verdict. Never merge yourself.";
|
|
34038
|
+
var CORNER_REVIEWER_UNSTABLE_HEAD_INSTRUCTION = "There is no stable green PR head for this active reviewer turn. Do not review or call approve_merge. End this turn without a verdict; the next green transition will wake you.";
|
|
33793
34039
|
function cornerSelfReviewerInstruction(input) {
|
|
33794
34040
|
if (!input.reviewerHandle || !input.agentHandle || !input.openedByAgent || input.agentHandle.replace(/^@/, "") !== input.reviewerHandle.replace(/^@/, ""))
|
|
33795
34041
|
return void 0;
|
|
@@ -33816,8 +34062,8 @@ function isCornerChecksTurn(trigger, restates) {
|
|
|
33816
34062
|
async function cornerUndeliveredRepositoryState(worktreePath, featureBranch, targetBranch) {
|
|
33817
34063
|
try {
|
|
33818
34064
|
const [{ stdout: status }, ahead] = await Promise.all([
|
|
33819
|
-
|
|
33820
|
-
featureBranch ? firstResolvedRemoteRef(worktreePath, [featureBranch, targetBranch]).then((remoteRef) => remoteRef ?
|
|
34065
|
+
execFileAsync4("git", ["-C", worktreePath, "status", "--porcelain=v1"]),
|
|
34066
|
+
featureBranch ? firstResolvedRemoteRef(worktreePath, [featureBranch, targetBranch]).then((remoteRef) => remoteRef ? execFileAsync4("git", [
|
|
33821
34067
|
"-C",
|
|
33822
34068
|
worktreePath,
|
|
33823
34069
|
"rev-list",
|
|
@@ -33837,7 +34083,7 @@ async function firstResolvedRemoteRef(worktreePath, branches) {
|
|
|
33837
34083
|
if (!branch)
|
|
33838
34084
|
continue;
|
|
33839
34085
|
const ref = `refs/remotes/origin/${branch}`;
|
|
33840
|
-
const resolved = await
|
|
34086
|
+
const resolved = await execFileAsync4("git", ["-C", worktreePath, "rev-parse", "--verify", ref]).then(() => true).catch(() => false);
|
|
33841
34087
|
if (resolved)
|
|
33842
34088
|
return ref;
|
|
33843
34089
|
}
|
|
@@ -33945,7 +34191,7 @@ async function cornerToolActivity(call, worktreePath, requestedBy) {
|
|
|
33945
34191
|
let title = oneLine(redactToolDetail(call.title ?? "")) || `${operation} tool`;
|
|
33946
34192
|
if (isSuccessfulCommit(call)) {
|
|
33947
34193
|
try {
|
|
33948
|
-
const shown = await
|
|
34194
|
+
const shown = await execFileAsync4("git", ["-C", worktreePath, "show", "--format=%s", "--name-only", "--no-renames", "HEAD"], { maxBuffer: 1024 * 1024 });
|
|
33949
34195
|
const lines = shown.stdout.split(/\r?\n/);
|
|
33950
34196
|
const subject = oneLine(lines.shift() ?? "commit");
|
|
33951
34197
|
const files = new Set(lines.map(oneLine).filter(Boolean));
|
|
@@ -33967,20 +34213,36 @@ async function cornerToolActivity(call, worktreePath, requestedBy) {
|
|
|
33967
34213
|
...paths.length ? { files: paths.map((path) => ({ path })) } : {}
|
|
33968
34214
|
};
|
|
33969
34215
|
}
|
|
34216
|
+
var CORNER_CLOSE_POLL_BASE_MS = 10 * 6e4;
|
|
34217
|
+
function cornerClosePollMs(random = Math.random) {
|
|
34218
|
+
return CORNER_CLOSE_POLL_BASE_MS + Math.floor(random() * 3e3);
|
|
34219
|
+
}
|
|
33970
34220
|
var MonolithCornerTurnLoop = class {
|
|
33971
34221
|
options;
|
|
33972
34222
|
commandContext;
|
|
33973
34223
|
agent;
|
|
33974
34224
|
wakeIntake;
|
|
33975
|
-
/**
|
|
34225
|
+
/**
|
|
34226
|
+
* Called by the daemon's one slow workspace reconciliation sweep, and by the
|
|
34227
|
+
* fast reconcile a socket reconnect arms. A `corner-complete` published while
|
|
34228
|
+
* that socket was down is never replayed, so the sweep clears the close
|
|
34229
|
+
* throttle too: the durable read is what recovers the frame nobody heard.
|
|
34230
|
+
*
|
|
34231
|
+
* The wake is the intake loop's own stable notify and is handed back exactly
|
|
34232
|
+
* once, so it is kept: clearing it here left `requestClose` waking nothing
|
|
34233
|
+
* after the first sweep, and a pushed `corner-complete` then waited out the
|
|
34234
|
+
* idle timer. Intake clears it itself when it exits.
|
|
34235
|
+
*/
|
|
33976
34236
|
requestReconciliation() {
|
|
34237
|
+
this.lastCloseCheck = 0;
|
|
33977
34238
|
this.wakeIntake?.();
|
|
33978
|
-
this.wakeIntake = void 0;
|
|
33979
34239
|
}
|
|
33980
34240
|
client;
|
|
33981
34241
|
sessionId;
|
|
33982
34242
|
/** The configuration the live session baked in; a change invalidates it. */
|
|
33983
34243
|
sessionFingerprint;
|
|
34244
|
+
/** Whether CodeGraph preparation succeeded for the live session. */
|
|
34245
|
+
sessionCodegraphReady = false;
|
|
33984
34246
|
/** What this exact ACP session has already been prompted with (`warm-transcript.ts`). */
|
|
33985
34247
|
warmTranscript = new WarmTranscript();
|
|
33986
34248
|
/** The live session's environment, read back for pi's own turn record. */
|
|
@@ -33995,6 +34257,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
33995
34257
|
yoloMode = false;
|
|
33996
34258
|
/** The live parent-Room reviewer baked into the current session. */
|
|
33997
34259
|
reviewerHandle;
|
|
34260
|
+
/** Identity-only reviewer context; the exact PR head is refreshed inside each active turn. */
|
|
34261
|
+
reviewerInstructionInput;
|
|
33998
34262
|
/** The role-specific second-chance instruction for this session. */
|
|
33999
34263
|
cornerTurnEndNudge = CORNER_DELIVERY_NUDGE;
|
|
34000
34264
|
/** Repository state already given a delivery reminder, until that state changes. */
|
|
@@ -34023,10 +34287,17 @@ var MonolithCornerTurnLoop = class {
|
|
|
34023
34287
|
* by the same replay window the inbox already de-duplicates over.
|
|
34024
34288
|
*/
|
|
34025
34289
|
stoppedTurns = /* @__PURE__ */ new Set();
|
|
34290
|
+
/** Live corner-complete: close now, do not wait for the recovery GET. */
|
|
34291
|
+
closePushed = false;
|
|
34292
|
+
/** Last durable close read; 0 means the first check still runs. */
|
|
34293
|
+
lastCloseCheck = 0;
|
|
34294
|
+
/** This corner's own jittered recovery interval, drawn once. */
|
|
34295
|
+
closePollMs;
|
|
34026
34296
|
/** In-flight warm-store harvest, awaited at shutdown and never by a turn. */
|
|
34027
34297
|
harvest;
|
|
34028
34298
|
constructor(options) {
|
|
34029
34299
|
this.options = options;
|
|
34300
|
+
this.closePollMs = options.closePollMs ?? cornerClosePollMs();
|
|
34030
34301
|
this.agent = runtimeIdentity(options.runtime.agent);
|
|
34031
34302
|
this.commandContext = new CommandExecutionContext(options.config.agentHomeRoot);
|
|
34032
34303
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
@@ -34046,6 +34317,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34046
34317
|
isBusy() {
|
|
34047
34318
|
return this.busy;
|
|
34048
34319
|
}
|
|
34320
|
+
/** corner-complete on the wire: wake intake so `closed()` reaps now. */
|
|
34321
|
+
requestClose() {
|
|
34322
|
+
this.closePushed = true;
|
|
34323
|
+
this.wakeIntake?.();
|
|
34324
|
+
}
|
|
34049
34325
|
refreshPersonaForSoulUpdate() {
|
|
34050
34326
|
return this.options.scheduler.suspend(this.options.cornerId);
|
|
34051
34327
|
}
|
|
@@ -34091,6 +34367,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34091
34367
|
this.client = void 0;
|
|
34092
34368
|
this.sessionId = void 0;
|
|
34093
34369
|
this.sessionFingerprint = void 0;
|
|
34370
|
+
this.sessionCodegraphReady = false;
|
|
34094
34371
|
this.pinnedProviderOverride = void 0;
|
|
34095
34372
|
if (client?.isAlive)
|
|
34096
34373
|
await client.stop();
|
|
@@ -34117,11 +34394,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34117
34394
|
soul: configuration.soul ?? self?.soul,
|
|
34118
34395
|
agentName: self?.name ?? this.agent.name,
|
|
34119
34396
|
yoloMode: configuration.yoloMode,
|
|
34120
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
34397
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
34121
34398
|
operatorHome: this.options.config.operatorHome,
|
|
34122
34399
|
agentKind: this.options.config.agentKind,
|
|
34123
34400
|
grantedHostRoutes
|
|
34124
|
-
}),
|
|
34401
|
+
}), this.sessionCodegraphReady),
|
|
34125
34402
|
reviewerHandle: configuration.reviewerHandle
|
|
34126
34403
|
});
|
|
34127
34404
|
}
|
|
@@ -34156,20 +34433,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34156
34433
|
authorHandle: opener?.handle,
|
|
34157
34434
|
openedByAgent: !this.options.openedBy || this.options.openedBy === this.agent.publicKey
|
|
34158
34435
|
};
|
|
34159
|
-
|
|
34160
|
-
|
|
34161
|
-
const restore = await this.options.api.execute("getCornerRestoreState", {
|
|
34162
|
-
cornerId: this.options.cornerId
|
|
34163
|
-
});
|
|
34164
|
-
reviewerInstruction = cornerReviewerInstruction({
|
|
34165
|
-
...reviewerInput,
|
|
34166
|
-
pullRequestNumber: restore.lifecycle?.pr?.number,
|
|
34167
|
-
headSha: restore.lifecycle?.pr?.headSha
|
|
34168
|
-
});
|
|
34169
|
-
}
|
|
34436
|
+
const reviewerInstruction = cornerReviewerInstruction(reviewerInput) ? CORNER_REVIEWER_SESSION_INSTRUCTION : void 0;
|
|
34437
|
+
this.reviewerInstructionInput = reviewerInstruction ? reviewerInput : void 0;
|
|
34170
34438
|
const selfReviewerInstruction = cornerSelfReviewerInstruction(reviewerInput);
|
|
34171
34439
|
this.cornerTurnEndNudge = reviewerInstruction ?? selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
|
|
34172
|
-
await
|
|
34440
|
+
await mkdir14(this.options.worktreePath, { recursive: true });
|
|
34173
34441
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
34174
34442
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
34175
34443
|
root: this.options.config.agentHomeRoot,
|
|
@@ -34195,8 +34463,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
34195
34463
|
const repository = this.options.repository;
|
|
34196
34464
|
let githubEnv = repository ? { GH_TOKEN: repository.githubToken, GITHUB_TOKEN: repository.githubToken } : {};
|
|
34197
34465
|
if (repository && this.options.config.runtimeConfigPath && this.options.config.agentHomeRoot) {
|
|
34198
|
-
const gitBinary = (await
|
|
34199
|
-
const ghBinary = await
|
|
34466
|
+
const gitBinary = (await execFileAsync4("which", ["git"])).stdout.trim();
|
|
34467
|
+
const ghBinary = await execFileAsync4("which", ["gh"]).then((result) => result.stdout.trim()).catch(() => void 0);
|
|
34200
34468
|
githubEnv = await installCornerGitHubWrappers({
|
|
34201
34469
|
root: this.options.config.agentHomeRoot,
|
|
34202
34470
|
runtimeConfigPath: this.options.config.runtimeConfigPath,
|
|
@@ -34210,7 +34478,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34210
34478
|
});
|
|
34211
34479
|
}
|
|
34212
34480
|
const npmCacheDir = sharedNpmCacheDir(this.options.runtime.supervisorRoot);
|
|
34213
|
-
await
|
|
34481
|
+
await mkdir14(npmCacheDir, { recursive: true, mode: 448 });
|
|
34214
34482
|
const agentEnv = {
|
|
34215
34483
|
...this.options.config.agentEnv,
|
|
34216
34484
|
...homeOverlay,
|
|
@@ -34218,19 +34486,6 @@ var MonolithCornerTurnLoop = class {
|
|
|
34218
34486
|
npm_config_cache: npmCacheDir
|
|
34219
34487
|
};
|
|
34220
34488
|
const operatorHome = this.options.config.operatorHome ?? homedir10();
|
|
34221
|
-
const fingerprint = sessionConfigFingerprint({
|
|
34222
|
-
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
34223
|
-
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
34224
|
-
soul: configuration.soul ?? self?.soul,
|
|
34225
|
-
agentName: self?.name ?? this.agent.name,
|
|
34226
|
-
yoloMode: configuration.yoloMode,
|
|
34227
|
-
mcpServers: expectedMountedImportedMcpServerNames({
|
|
34228
|
-
operatorHome: this.options.config.operatorHome,
|
|
34229
|
-
agentKind: this.options.config.agentKind,
|
|
34230
|
-
grantedHostRoutes
|
|
34231
|
-
}),
|
|
34232
|
-
reviewerHandle: configuration.reviewerHandle
|
|
34233
|
-
});
|
|
34234
34489
|
this.agentEnv = agentEnv;
|
|
34235
34490
|
const agentArgs = agentArgsWithModelSelection({
|
|
34236
34491
|
kind: this.options.config.agentKind,
|
|
@@ -34241,10 +34496,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
34241
34496
|
this.attachmentDir = tmpDir ? join12(tmpDir, "beeline-attachments") : void 0;
|
|
34242
34497
|
this.sessionScratchDir = tmpDir;
|
|
34243
34498
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
34244
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
34499
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir14(dir, { recursive: true })));
|
|
34245
34500
|
const attachScratchRoot = this.options.config.agentHomeRoot ?? tmpDir;
|
|
34246
34501
|
if (attachScratchRoot)
|
|
34247
|
-
await
|
|
34502
|
+
await mkdir14(attachScratchRoot, { recursive: true });
|
|
34248
34503
|
const spawnCommand = wrapAgentCommand({
|
|
34249
34504
|
bwrapPath: this.options.config.bwrapPath,
|
|
34250
34505
|
spec: {
|
|
@@ -34280,10 +34535,29 @@ var MonolithCornerTurnLoop = class {
|
|
|
34280
34535
|
agentCwd: this.options.worktreePath,
|
|
34281
34536
|
agentLabel: harnessLabel,
|
|
34282
34537
|
autoApprovePermissions: true,
|
|
34283
|
-
permissionHandler: () => Promise.resolve("allow")
|
|
34538
|
+
permissionHandler: () => Promise.resolve("allow"),
|
|
34539
|
+
onCommands: agentCommandCatalogPublisher({
|
|
34540
|
+
api: this.options.api,
|
|
34541
|
+
agentId: this.agent.publicKey,
|
|
34542
|
+
workspaceId: this.options.workspaceId
|
|
34543
|
+
})
|
|
34284
34544
|
};
|
|
34285
34545
|
this.client = (this.options.createAcpClient ?? ((value) => new AcpClient(value)))(clientOptions);
|
|
34286
34546
|
await this.client.start();
|
|
34547
|
+
const codegraphReady = await prepareCodegraphIndex(this.options.config, this.options.worktreePath);
|
|
34548
|
+
const fingerprint = sessionConfigFingerprint({
|
|
34549
|
+
model: configuration.model ?? this.options.config.modelSelection?.model,
|
|
34550
|
+
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
34551
|
+
soul: configuration.soul ?? self?.soul,
|
|
34552
|
+
agentName: self?.name ?? this.agent.name,
|
|
34553
|
+
yoloMode: configuration.yoloMode,
|
|
34554
|
+
mcpServers: codegraphFingerprintServers(this.options.config, expectedMountedImportedMcpServerNames({
|
|
34555
|
+
operatorHome: this.options.config.operatorHome,
|
|
34556
|
+
agentKind: this.options.config.agentKind,
|
|
34557
|
+
grantedHostRoutes
|
|
34558
|
+
}), codegraphReady),
|
|
34559
|
+
reviewerHandle: configuration.reviewerHandle
|
|
34560
|
+
});
|
|
34287
34561
|
const servers = [
|
|
34288
34562
|
...repository ? [
|
|
34289
34563
|
{
|
|
@@ -34308,6 +34582,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34308
34582
|
roomId: this.options.parentRoomId,
|
|
34309
34583
|
workspaceId: this.options.workspaceId,
|
|
34310
34584
|
cornerId: this.options.cornerId,
|
|
34585
|
+
agentMayCloseCorner: Boolean(repository),
|
|
34311
34586
|
reviewer: Boolean(reviewerInstruction),
|
|
34312
34587
|
attachRoot: this.options.worktreePath,
|
|
34313
34588
|
// The whole per-session overlay, not an enumerated subset: see
|
|
@@ -34317,6 +34592,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
34317
34592
|
...this.options.grantRunnerEndpoint ? { grantRunner: this.options.grantRunnerEndpoint } : {}
|
|
34318
34593
|
})
|
|
34319
34594
|
];
|
|
34595
|
+
if (codegraphReady) {
|
|
34596
|
+
const codegraph = codegraphMcpServer(this.options.config, this.options.worktreePath, {
|
|
34597
|
+
readonly: false
|
|
34598
|
+
});
|
|
34599
|
+
if (codegraph)
|
|
34600
|
+
servers.push(codegraph);
|
|
34601
|
+
}
|
|
34320
34602
|
const youtube = youtubeMcpServer(this.options.config, this.options.youtubeAccessToken);
|
|
34321
34603
|
if (youtube)
|
|
34322
34604
|
servers.push(youtube);
|
|
@@ -34357,21 +34639,57 @@ var MonolithCornerTurnLoop = class {
|
|
|
34357
34639
|
"This is a no-code corner with no repository checkout and no GitHub workflow.",
|
|
34358
34640
|
"Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then post_artifact with the path to send them back to the corner.",
|
|
34359
34641
|
"Do not initialize a repository, create a branch, commit, push, open a pull request, or wait for GitHub checks.",
|
|
34360
|
-
// This lane has no pull request URL and no merge card, so
|
|
34361
|
-
//
|
|
34362
|
-
//
|
|
34363
|
-
this.options.requesterHandle ? `Deliver the result as artifacts: post_artifact everything the objective asked for
|
|
34642
|
+
// This lane has no pull request URL and no merge card, so its
|
|
34643
|
+
// attached final reply reports delivery to the requester. The
|
|
34644
|
+
// corner remains open until a human explicitly closes it.
|
|
34645
|
+
this.options.requesterHandle ? `Deliver the result as artifacts: post_artifact everything the objective asked for. Finish the turn by replying with @${this.options.requesterHandle} and one line on what you posted. The corner stays open until a human explicitly closes it.` : `Deliver the result as artifacts: post_artifact everything the objective asked for. Finish the turn by replying with one line on what you posted. The corner stays open until a human explicitly closes it.`
|
|
34364
34646
|
]
|
|
34365
34647
|
].filter(Boolean).join("\n\n")
|
|
34366
34648
|
});
|
|
34367
34649
|
this.sessionId = opened.sessionId;
|
|
34368
34650
|
this.sessionFingerprint = fingerprint;
|
|
34651
|
+
this.sessionCodegraphReady = codegraphReady;
|
|
34369
34652
|
if (selection) {
|
|
34370
34653
|
const options = filterAllowedModelConfigOptions(parseAdvertisedConfigOptions(opened.raw, selection.model));
|
|
34371
34654
|
await applyAgentModelSelection(this.client, opened.sessionId, options, selection);
|
|
34372
34655
|
}
|
|
34373
34656
|
return opened.sessionId;
|
|
34374
34657
|
}
|
|
34658
|
+
/**
|
|
34659
|
+
* Resolve the review target at prompt time, not session activation time.
|
|
34660
|
+
*
|
|
34661
|
+
* A push can land after a reviewer session starts but before its first
|
|
34662
|
+
* prompt, or while that prompt is running. The active prompt and its bounded
|
|
34663
|
+
* second pass therefore each read the current green lifecycle head. The
|
|
34664
|
+
* server still compares approve_merge's SHA with the current head, so a push
|
|
34665
|
+
* after this read fails closed too.
|
|
34666
|
+
*/
|
|
34667
|
+
async activeReviewerInstruction() {
|
|
34668
|
+
const input = this.reviewerInstructionInput;
|
|
34669
|
+
if (!input)
|
|
34670
|
+
return void 0;
|
|
34671
|
+
try {
|
|
34672
|
+
const [restore, localHead] = await Promise.all([
|
|
34673
|
+
this.options.api.execute("getCornerRestoreState", {
|
|
34674
|
+
cornerId: this.options.cornerId
|
|
34675
|
+
}),
|
|
34676
|
+
execFileAsync4("git", ["-C", this.options.worktreePath, "rev-parse", "HEAD"]).then(({ stdout: stdout6 }) => stdout6.trim())
|
|
34677
|
+
]);
|
|
34678
|
+
const pr = restore.lifecycle?.pr;
|
|
34679
|
+
if (restore.lifecycle?.checks !== "passing" || !pr?.number || !pr.headSha || pr.headSha !== localHead)
|
|
34680
|
+
return CORNER_REVIEWER_UNSTABLE_HEAD_INSTRUCTION;
|
|
34681
|
+
return [
|
|
34682
|
+
"Current stable reviewer target for this active turn; it supersedes any older head in the trigger or transcript:",
|
|
34683
|
+
cornerReviewerInstruction({
|
|
34684
|
+
...input,
|
|
34685
|
+
pullRequestNumber: pr.number,
|
|
34686
|
+
headSha: pr.headSha
|
|
34687
|
+
})
|
|
34688
|
+
].join("\n");
|
|
34689
|
+
} catch {
|
|
34690
|
+
return CORNER_REVIEWER_UNSTABLE_HEAD_INSTRUCTION;
|
|
34691
|
+
}
|
|
34692
|
+
}
|
|
34375
34693
|
/** The scheduler seam: `queue-wait` closes when a slot buys a session. */
|
|
34376
34694
|
lifecycle(trace) {
|
|
34377
34695
|
return {
|
|
@@ -34429,6 +34747,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34429
34747
|
this.client = void 0;
|
|
34430
34748
|
this.sessionId = void 0;
|
|
34431
34749
|
this.sessionFingerprint = void 0;
|
|
34750
|
+
this.sessionCodegraphReady = false;
|
|
34432
34751
|
if (client?.isAlive)
|
|
34433
34752
|
await client.stop();
|
|
34434
34753
|
this.pinnedProviderOverride = next;
|
|
@@ -34448,7 +34767,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
34448
34767
|
...this.turnTraceSink ? { sink: this.turnTraceSink } : {}
|
|
34449
34768
|
});
|
|
34450
34769
|
}
|
|
34451
|
-
async prompt(requestId, trigger, attachments = [], requestedById, restates) {
|
|
34770
|
+
async prompt(requestId, trigger, attachments = [], requestedById, restates, sourceMessageId) {
|
|
34452
34771
|
const { api, cornerId } = this.options;
|
|
34453
34772
|
if (this.stoppedTurns.has(requestId))
|
|
34454
34773
|
return;
|
|
@@ -34476,10 +34795,11 @@ var MonolithCornerTurnLoop = class {
|
|
|
34476
34795
|
throw new Error("corner turn stopped for daemon handoff");
|
|
34477
34796
|
this.busy = true;
|
|
34478
34797
|
await this.syncBranch();
|
|
34479
|
-
const [conversation, roster, delivered] = await trace.measure("context-fetch", () => Promise.all([
|
|
34798
|
+
const [conversation, roster, delivered, activeReviewerInstruction] = await trace.measure("context-fetch", () => Promise.all([
|
|
34480
34799
|
api.execute("getRoomConversation", { roomId: cornerId, limit: 200 }),
|
|
34481
34800
|
this.roster(),
|
|
34482
|
-
this.attachmentDir && attachments.length ? deliverAttachments(attachments, join12(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([])
|
|
34801
|
+
this.attachmentDir && attachments.length ? deliverAttachments(attachments, join12(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : Promise.resolve([]),
|
|
34802
|
+
this.activeReviewerInstruction()
|
|
34483
34803
|
]));
|
|
34484
34804
|
const names = new Map(roster.members.map((member) => [member.identityId, member.name]));
|
|
34485
34805
|
const requestedBy = requester && !requester.name && names.get(requester.pubkey) ? { ...requester, name: names.get(requester.pubkey) } : requester;
|
|
@@ -34487,7 +34807,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
34487
34807
|
this.currentTurn = { requestId, requester: requestedBy };
|
|
34488
34808
|
const transcriptRows = conversation.items.slice(-120).map((message) => ({
|
|
34489
34809
|
id: message.id,
|
|
34490
|
-
line:
|
|
34810
|
+
line: [
|
|
34811
|
+
...message.type === "message" ? [`[message id: ${message.id}]`] : [],
|
|
34812
|
+
`${names.get(message.authorId) ?? "Beeline"} [${message.type}]: ${message.body}`
|
|
34813
|
+
].join("\n")
|
|
34491
34814
|
}));
|
|
34492
34815
|
const buildPrompt = () => [
|
|
34493
34816
|
this.turnIdentityInstructions,
|
|
@@ -34495,12 +34818,14 @@ var MonolithCornerTurnLoop = class {
|
|
|
34495
34818
|
${this.options.objective}`,
|
|
34496
34819
|
WarmTranscript.render(this.warmTranscript.select(this.sessionId, transcriptRows), "Corner transcript:", "New in the corner since your last turn (the earlier transcript is already in this session):"),
|
|
34497
34820
|
roomMentionDirectory(roster, this.agent.publicKey),
|
|
34821
|
+
activeReviewerInstruction,
|
|
34498
34822
|
[
|
|
34823
|
+
...sourceMessageId ? [`Reaction target message id: ${sourceMessageId}`] : [],
|
|
34499
34824
|
`Newest trigger:
|
|
34500
34825
|
${trigger}`,
|
|
34501
34826
|
...attachmentPromptLines(attachments, delivered, this.acceptsImages())
|
|
34502
34827
|
].join("\n"),
|
|
34503
|
-
this.options.repository ? "Continue the objective. Obey the PR checks and human hold rules in your session instructions." : "Continue the objective. Attach completed files
|
|
34828
|
+
this.options.repository ? "Continue the objective. Obey the PR checks and human hold rules in your session instructions." : "Continue the objective. Attach completed files; only a human can close this corner.",
|
|
34504
34829
|
MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE
|
|
34505
34830
|
].filter(Boolean).join("\n\n");
|
|
34506
34831
|
const stream = new AgentTurnStream({
|
|
@@ -34641,7 +34966,9 @@ ${trigger}`,
|
|
|
34641
34966
|
this.lastDeliveryNudgeState = deliveryState;
|
|
34642
34967
|
replyBeforeNudge = durableReplyText(result.agentText);
|
|
34643
34968
|
await flushToolCalls(result.toolCalls, "");
|
|
34644
|
-
|
|
34969
|
+
if (this.reviewerInstructionInput)
|
|
34970
|
+
await this.syncBranch();
|
|
34971
|
+
result = await runPrompt(this.reviewerHandle ? await this.activeReviewerInstruction() ?? this.cornerTurnEndNudge : checksTurn ? CORNER_YOLO_MERGE_NUDGE : CORNER_DELIVERY_NUDGE);
|
|
34645
34972
|
trace.promptSettled();
|
|
34646
34973
|
explained = await this.explainEmpty(result);
|
|
34647
34974
|
}
|
|
@@ -34779,6 +35106,16 @@ ${trigger}`,
|
|
|
34779
35106
|
onError: (error) => console.error("[thin-core] corner command failed", error),
|
|
34780
35107
|
stop: (requestId) => this.stopTurn(requestId),
|
|
34781
35108
|
closed: async () => {
|
|
35109
|
+
if (this.closePushed) {
|
|
35110
|
+
this.closePushed = false;
|
|
35111
|
+
await this.harvest;
|
|
35112
|
+
await this.options.onCloseRequested();
|
|
35113
|
+
return true;
|
|
35114
|
+
}
|
|
35115
|
+
const now2 = Date.now();
|
|
35116
|
+
if (this.lastCloseCheck !== 0 && now2 - this.lastCloseCheck < this.closePollMs)
|
|
35117
|
+
return false;
|
|
35118
|
+
this.lastCloseCheck = now2;
|
|
34782
35119
|
const state = await api.execute("getCornerRestoreState", { cornerId });
|
|
34783
35120
|
if (!state.closeRequested)
|
|
34784
35121
|
return false;
|
|
@@ -34786,7 +35123,9 @@ ${trigger}`,
|
|
|
34786
35123
|
await this.options.onCloseRequested();
|
|
34787
35124
|
return true;
|
|
34788
35125
|
},
|
|
34789
|
-
run: (command) => this.prompt(command.turnRequestId, command.source.body, command.source.attachments, command.source.authorId, command.reason === "corner_check" ? [command.source.body] : void 0)
|
|
35126
|
+
run: (command) => this.prompt(command.turnRequestId, command.source.body, command.source.attachments, command.source.authorId, command.reason === "corner_check" ? [command.source.body] : void 0, command.source.type === "message" ? command.sourceMessageId : void 0).finally(() => {
|
|
35127
|
+
this.lastCloseCheck = 0;
|
|
35128
|
+
})
|
|
34790
35129
|
});
|
|
34791
35130
|
} finally {
|
|
34792
35131
|
this.options.grantRunner?.unregister(cornerId);
|
|
@@ -35221,7 +35560,7 @@ var SessionScheduler = class {
|
|
|
35221
35560
|
var REMOVAL_CONFIRMATION_READS = 2;
|
|
35222
35561
|
var ROOM_JOIN_CONCURRENCY = 4;
|
|
35223
35562
|
var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
|
|
35224
|
-
var DEFAULT_RECONCILE_HEARTBEAT_MS = 6e4;
|
|
35563
|
+
var DEFAULT_RECONCILE_HEARTBEAT_MS = 10 * 6e4;
|
|
35225
35564
|
var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
|
|
35226
35565
|
var CORNER_BRANCH_DELETE_ATTEMPTS = 3;
|
|
35227
35566
|
var DiscoveryWakes = class {
|
|
@@ -35261,33 +35600,33 @@ function isStandingCornerStartFault(error) {
|
|
|
35261
35600
|
async function materializeCornerWorktree(input) {
|
|
35262
35601
|
const remote = roomCheckoutRemote(input.remote);
|
|
35263
35602
|
const repositoryHash = createHash7("sha256").update(remote).digest("hex").slice(0, 24);
|
|
35264
|
-
const gitCommonDir =
|
|
35265
|
-
const path =
|
|
35266
|
-
await
|
|
35267
|
-
await
|
|
35603
|
+
const gitCommonDir = resolve24(input.supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
35604
|
+
const path = resolve24(input.supervisorRoot, "beeline", "corners", input.cornerId);
|
|
35605
|
+
await mkdir15(dirname13(gitCommonDir), { recursive: true, mode: 448 });
|
|
35606
|
+
await mkdir15(dirname13(path), { recursive: true, mode: 448 });
|
|
35268
35607
|
const authEnv = githubGitEnv(input.token);
|
|
35269
|
-
if (!existsSync6(
|
|
35270
|
-
await
|
|
35608
|
+
if (!existsSync6(resolve24(gitCommonDir, "HEAD"))) {
|
|
35609
|
+
await execFileAsync5("git", ["clone", "--bare", remote, gitCommonDir], {
|
|
35271
35610
|
env: authEnv,
|
|
35272
35611
|
maxBuffer: 4 * 1024 * 1024
|
|
35273
35612
|
});
|
|
35274
35613
|
}
|
|
35275
|
-
await
|
|
35614
|
+
await execFileAsync5("git", [
|
|
35276
35615
|
`--git-dir=${gitCommonDir}`,
|
|
35277
35616
|
"fetch",
|
|
35278
35617
|
"--prune",
|
|
35279
35618
|
"origin",
|
|
35280
35619
|
`+refs/heads/${input.targetBranch}:refs/remotes/origin/${input.targetBranch}`
|
|
35281
35620
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 });
|
|
35282
|
-
const restored = await
|
|
35621
|
+
const restored = await execFileAsync5("git", [
|
|
35283
35622
|
`--git-dir=${gitCommonDir}`,
|
|
35284
35623
|
"fetch",
|
|
35285
35624
|
"origin",
|
|
35286
35625
|
`+refs/heads/${input.featureBranch}:refs/remotes/origin/${input.featureBranch}`
|
|
35287
35626
|
], { env: authEnv, maxBuffer: 4 * 1024 * 1024 }).then(() => true, () => false);
|
|
35288
|
-
if (!existsSync6(
|
|
35627
|
+
if (!existsSync6(resolve24(path, ".git"))) {
|
|
35289
35628
|
await rm5(path, { recursive: true, force: true });
|
|
35290
|
-
await
|
|
35629
|
+
await execFileAsync5("git", [
|
|
35291
35630
|
`--git-dir=${gitCommonDir}`,
|
|
35292
35631
|
"worktree",
|
|
35293
35632
|
"add",
|
|
@@ -35304,14 +35643,14 @@ async function materializeCornerWorktree(input) {
|
|
|
35304
35643
|
console.log(`[thin-core] corner ${input.cornerId} warm node_modules: ${seed.reason}${seed.detail ? ` (${seed.detail})` : ""}`);
|
|
35305
35644
|
}
|
|
35306
35645
|
}
|
|
35307
|
-
await
|
|
35646
|
+
await execFileAsync5("git", [
|
|
35308
35647
|
`--git-dir=${gitCommonDir}`,
|
|
35309
35648
|
"config",
|
|
35310
35649
|
"extensions.worktreeConfig",
|
|
35311
35650
|
"true"
|
|
35312
35651
|
]);
|
|
35313
|
-
await
|
|
35314
|
-
await
|
|
35652
|
+
await execFileAsync5("git", ["-C", path, "config", "--worktree", "core.bare", "false"]);
|
|
35653
|
+
await execFileAsync5("git", [
|
|
35315
35654
|
"-C",
|
|
35316
35655
|
path,
|
|
35317
35656
|
"config",
|
|
@@ -35319,7 +35658,7 @@ async function materializeCornerWorktree(input) {
|
|
|
35319
35658
|
"credential.https://github.com.helper",
|
|
35320
35659
|
"!f() { echo username=x-access-token; echo password=$GH_TOKEN; }; f"
|
|
35321
35660
|
]);
|
|
35322
|
-
await
|
|
35661
|
+
await execFileAsync5("git", [
|
|
35323
35662
|
"-C",
|
|
35324
35663
|
path,
|
|
35325
35664
|
"config",
|
|
@@ -35327,7 +35666,7 @@ async function materializeCornerWorktree(input) {
|
|
|
35327
35666
|
"user.name",
|
|
35328
35667
|
input.committer.name
|
|
35329
35668
|
]);
|
|
35330
|
-
await
|
|
35669
|
+
await execFileAsync5("git", [
|
|
35331
35670
|
"-C",
|
|
35332
35671
|
path,
|
|
35333
35672
|
"config",
|
|
@@ -35335,8 +35674,8 @@ async function materializeCornerWorktree(input) {
|
|
|
35335
35674
|
"user.email",
|
|
35336
35675
|
`${input.committer.publicKey.slice(0, 16)}@users.noreply.github.com`
|
|
35337
35676
|
]);
|
|
35338
|
-
const top = await
|
|
35339
|
-
if (
|
|
35677
|
+
const top = await execFileAsync5("git", ["-C", path, "rev-parse", "--show-toplevel"]);
|
|
35678
|
+
if (resolve24(top.stdout.trim()) !== resolve24(path)) {
|
|
35340
35679
|
throw new Error(`corner worktree escaped its isolated root: ${top.stdout.trim()}`);
|
|
35341
35680
|
}
|
|
35342
35681
|
return { path, gitCommonDir };
|
|
@@ -35360,7 +35699,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35360
35699
|
const worktreeExists = existsSync6(worktree.path);
|
|
35361
35700
|
const localExists = await gitRefExists(worktree.gitCommonDir, localRef);
|
|
35362
35701
|
if (worktreeExists) {
|
|
35363
|
-
const checkedOut = await
|
|
35702
|
+
const checkedOut = await execFileAsync5("git", [
|
|
35364
35703
|
"-C",
|
|
35365
35704
|
worktree.path,
|
|
35366
35705
|
"symbolic-ref",
|
|
@@ -35370,7 +35709,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35370
35709
|
if (checkedOut.stdout.trim() !== localRef) {
|
|
35371
35710
|
throw new Error(`corner worktree branch mismatch: expected ${localRef}`);
|
|
35372
35711
|
}
|
|
35373
|
-
await
|
|
35712
|
+
await execFileAsync5("git", [
|
|
35374
35713
|
`--git-dir=${worktree.gitCommonDir}`,
|
|
35375
35714
|
"worktree",
|
|
35376
35715
|
"remove",
|
|
@@ -35390,7 +35729,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35390
35729
|
}
|
|
35391
35730
|
await deleteExactRemoteBranch(worktree.gitCommonDir, remoteRef, worktree.token);
|
|
35392
35731
|
if (await gitRefExists(worktree.gitCommonDir, localRef)) {
|
|
35393
|
-
await
|
|
35732
|
+
await execFileAsync5("git", [
|
|
35394
35733
|
`--git-dir=${worktree.gitCommonDir}`,
|
|
35395
35734
|
"branch",
|
|
35396
35735
|
"--delete",
|
|
@@ -35400,10 +35739,10 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
35400
35739
|
]);
|
|
35401
35740
|
}
|
|
35402
35741
|
}
|
|
35403
|
-
var
|
|
35742
|
+
var execFileAsync5 = promisify5(execFile7);
|
|
35404
35743
|
async function removeCornerScratchWorkspace(input) {
|
|
35405
|
-
const expected =
|
|
35406
|
-
if (
|
|
35744
|
+
const expected = resolve24(input.roomRoot, "scratch");
|
|
35745
|
+
if (resolve24(input.scratchPath) !== expected) {
|
|
35407
35746
|
throw new Error(`refusing to remove scratch outside corner ${input.cornerId}`);
|
|
35408
35747
|
}
|
|
35409
35748
|
await rm5(expected, { recursive: true, force: true });
|
|
@@ -35417,6 +35756,19 @@ var RoomRuntimeCoordinator = class {
|
|
|
35417
35756
|
/** Close/reconcile leftovers retried until local and remote refs are gone. */
|
|
35418
35757
|
pendingCornerReaps = /* @__PURE__ */ new Map();
|
|
35419
35758
|
startingCorners = /* @__PURE__ */ new Set();
|
|
35759
|
+
/**
|
|
35760
|
+
* Rooms whose start is in flight. `running` is not set until the checkout
|
|
35761
|
+
* clone finishes, and three callers race for it — the membership push, the
|
|
35762
|
+
* reconcile sweep, and the watchdog restart — so without this two checkouts
|
|
35763
|
+
* run in the same shared directory and the second start orphans the first
|
|
35764
|
+
* loop's AbortController.
|
|
35765
|
+
*/
|
|
35766
|
+
startingRooms = /* @__PURE__ */ new Set();
|
|
35767
|
+
/** Pushed membership changes awaiting a bounded apply, latest per Room. */
|
|
35768
|
+
pendingMembershipEvents = /* @__PURE__ */ new Map();
|
|
35769
|
+
membershipDrain;
|
|
35770
|
+
/** Set by `shutdown`: a pushed event may no longer start anything. */
|
|
35771
|
+
stopped = false;
|
|
35420
35772
|
/** Corners whose start failure has already been said out loud, once each. */
|
|
35421
35773
|
reportedCornerStartFailures = /* @__PURE__ */ new Set();
|
|
35422
35774
|
/** Standing workspace-configuration faults, keyed by the config that failed. */
|
|
@@ -35433,10 +35785,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35433
35785
|
workspaceRemovalConfirmations = 0;
|
|
35434
35786
|
roomRemovalConfirmations = /* @__PURE__ */ new Map();
|
|
35435
35787
|
confirmationPending = false;
|
|
35436
|
-
/**
|
|
35437
|
-
* wake that arrives while a reconcile is already running must
|
|
35438
|
-
* start-pass clearing, or a
|
|
35439
|
-
* for a wake the daemon already received.
|
|
35788
|
+
/** Unscoped agent-directed discovery wakes (#1369), counted instead of
|
|
35789
|
+
* flagged: a wake that arrives while a reconcile is already running must
|
|
35790
|
+
* survive its start-pass clearing, or a reconnect mid-reconcile waits a
|
|
35791
|
+
* heartbeat for a wake the daemon already received. A scoped membership
|
|
35792
|
+
* event applies incrementally and never touches this latch. */
|
|
35440
35793
|
discoveryWakes = new DiscoveryWakes();
|
|
35441
35794
|
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
35442
35795
|
grantRunner;
|
|
@@ -35460,11 +35813,20 @@ var RoomRuntimeCoordinator = class {
|
|
|
35460
35813
|
});
|
|
35461
35814
|
this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
|
|
35462
35815
|
this.connectorUsage = new ConnectorUsageRecorder();
|
|
35463
|
-
this.options.daemonApi.setRoomsChangedListener?.(() => {
|
|
35464
|
-
|
|
35816
|
+
this.options.daemonApi.setRoomsChangedListener?.((event) => {
|
|
35817
|
+
const roomId = event?.roomId;
|
|
35818
|
+
if (!roomId) {
|
|
35819
|
+
this.discoveryWakes.wake();
|
|
35820
|
+
return;
|
|
35821
|
+
}
|
|
35822
|
+
this.queueMembershipEvent({ ...event, roomId });
|
|
35823
|
+
});
|
|
35824
|
+
this.options.daemonApi.setCornerCompleteListener?.((roomId) => {
|
|
35825
|
+
void this.applyCornerComplete(roomId).catch((error) => console.error("[thin-core] live corner-complete apply failed", error));
|
|
35465
35826
|
});
|
|
35466
35827
|
this.options.daemonApi.setConfigChangedListener?.(() => {
|
|
35467
35828
|
void this.scheduler.suspendIdle().catch((error) => console.error("[body] config-change session restart failed", error));
|
|
35829
|
+
void Promise.resolve(this.options.onConfigChanged?.()).catch((error) => console.error("[body] config-change catalog refresh failed", error));
|
|
35468
35830
|
});
|
|
35469
35831
|
this.options.daemonApi.setHiccupRestartListener?.((attempt) => {
|
|
35470
35832
|
this.options.onHiccupRestart?.(attempt);
|
|
@@ -35590,17 +35952,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
35590
35952
|
this.confirmationPending = true;
|
|
35591
35953
|
continue;
|
|
35592
35954
|
}
|
|
35593
|
-
|
|
35594
|
-
await running.promise.catch(() => void 0);
|
|
35595
|
-
try {
|
|
35596
|
-
if (running.worktree)
|
|
35597
|
-
await this.reapCornerWorktree(running.worktree);
|
|
35598
|
-
else if (running.scratch)
|
|
35599
|
-
await this.reapCornerScratch(running.scratch);
|
|
35600
|
-
} catch (error) {
|
|
35601
|
-
console.error(`[thin-core] corner ${channelId} cleanup failed; will retry:`, error);
|
|
35602
|
-
this.confirmationPending = true;
|
|
35603
|
-
}
|
|
35955
|
+
await this.stopRunning(channelId, running);
|
|
35604
35956
|
}
|
|
35605
35957
|
await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
|
|
35606
35958
|
if (this.running.has(roomId))
|
|
@@ -35620,17 +35972,97 @@ var RoomRuntimeCoordinator = class {
|
|
|
35620
35972
|
this.discoveryWakes.completeReconcile(coveredWakes);
|
|
35621
35973
|
return "member";
|
|
35622
35974
|
}
|
|
35975
|
+
/**
|
|
35976
|
+
* Pushed membership changes are applied at the same bound the reconcile pass
|
|
35977
|
+
* uses. One `rooms-changed` per row means a single human action (adding an
|
|
35978
|
+
* agent to a Room inherits a membership per corner under it) arrives as a
|
|
35979
|
+
* burst; unbounded, that burst is exactly the concurrent restore reads and
|
|
35980
|
+
* worktree checkouts this change exists to stop.
|
|
35981
|
+
*/
|
|
35982
|
+
queueMembershipEvent(event) {
|
|
35983
|
+
if (this.stopped)
|
|
35984
|
+
return;
|
|
35985
|
+
this.pendingMembershipEvents.set(event.roomId, event);
|
|
35986
|
+
this.membershipDrain ??= this.drainMembershipEvents().finally(() => {
|
|
35987
|
+
this.membershipDrain = void 0;
|
|
35988
|
+
});
|
|
35989
|
+
}
|
|
35990
|
+
async drainMembershipEvents() {
|
|
35991
|
+
while (this.pendingMembershipEvents.size) {
|
|
35992
|
+
const batch = [...this.pendingMembershipEvents.values()];
|
|
35993
|
+
this.pendingMembershipEvents.clear();
|
|
35994
|
+
await mapWithConcurrency(batch, ROOM_JOIN_CONCURRENCY, (event) => this.applyMembershipEvent(event).catch((error) => {
|
|
35995
|
+
console.error("[thin-core] live membership apply failed", error);
|
|
35996
|
+
this.discoveryWakes.wake();
|
|
35997
|
+
}));
|
|
35998
|
+
}
|
|
35999
|
+
}
|
|
36000
|
+
/**
|
|
36001
|
+
* Incremental apply of one scoped membership push.
|
|
36002
|
+
* An unscoped wake still uses the slow reconcile as recovery.
|
|
36003
|
+
*/
|
|
36004
|
+
async applyMembershipEvent(event) {
|
|
36005
|
+
const roomId = event.roomId;
|
|
36006
|
+
if (!roomId) {
|
|
36007
|
+
this.discoveryWakes.wake();
|
|
36008
|
+
return;
|
|
36009
|
+
}
|
|
36010
|
+
if (event.removed === true) {
|
|
36011
|
+
const running = this.running.get(roomId);
|
|
36012
|
+
if (running)
|
|
36013
|
+
await this.stopRunning(roomId, running);
|
|
36014
|
+
this.monolithCornerParents.delete(roomId);
|
|
36015
|
+
return;
|
|
36016
|
+
}
|
|
36017
|
+
if (event.archived === true)
|
|
36018
|
+
return;
|
|
36019
|
+
this.roomRemovalConfirmations.delete(roomId);
|
|
36020
|
+
if (this.running.has(roomId) || this.startingCorners.has(roomId))
|
|
36021
|
+
return;
|
|
36022
|
+
if (event.parentRoomId) {
|
|
36023
|
+
this.monolithCornerParents.set(roomId, event.parentRoomId);
|
|
36024
|
+
if (!event.openedBy) {
|
|
36025
|
+
this.discoveryWakes.wake();
|
|
36026
|
+
return;
|
|
36027
|
+
}
|
|
36028
|
+
await this.startCorner({
|
|
36029
|
+
cornerId: roomId,
|
|
36030
|
+
parentRoomId: event.parentRoomId,
|
|
36031
|
+
openedBy: event.openedBy
|
|
36032
|
+
});
|
|
36033
|
+
if (!this.running.has(roomId))
|
|
36034
|
+
this.discoveryWakes.wake();
|
|
36035
|
+
return;
|
|
36036
|
+
}
|
|
36037
|
+
await this.startRoom(roomId);
|
|
36038
|
+
}
|
|
36039
|
+
async applyCornerComplete(cornerId) {
|
|
36040
|
+
this.running.get(cornerId)?.body.requestClose?.();
|
|
36041
|
+
}
|
|
36042
|
+
async stopRunning(channelId, running) {
|
|
36043
|
+
running.controller.abort();
|
|
36044
|
+
await running.promise.catch(() => void 0);
|
|
36045
|
+
try {
|
|
36046
|
+
if (running.worktree)
|
|
36047
|
+
await this.reapCornerWorktree(running.worktree);
|
|
36048
|
+
else if (running.scratch)
|
|
36049
|
+
await this.reapCornerScratch(running.scratch);
|
|
36050
|
+
} catch (error) {
|
|
36051
|
+
console.error(`[thin-core] corner ${channelId} cleanup failed; will retry:`, error);
|
|
36052
|
+
this.confirmationPending = true;
|
|
36053
|
+
}
|
|
36054
|
+
}
|
|
35623
36055
|
roomRecord(roomId) {
|
|
35624
36056
|
return this.runtime.rooms.find((room) => room.channelId === roomId);
|
|
35625
36057
|
}
|
|
35626
36058
|
roomRoot(roomId) {
|
|
35627
|
-
return this.roomRecord(roomId)?.root ??
|
|
36059
|
+
return this.roomRecord(roomId)?.root ?? resolve24(dirname13(this.configPath), "rooms", roomId);
|
|
35628
36060
|
}
|
|
35629
36061
|
roomAgentHomeRoot(workspaceRoot, required = false) {
|
|
35630
36062
|
const flag = process.env.BUZZY_BODY_ROOM_HOME;
|
|
35631
36063
|
if (!required && flag === "0")
|
|
35632
36064
|
return void 0;
|
|
35633
|
-
const home =
|
|
36065
|
+
const home = resolve24(workspaceRoot, "agent-home");
|
|
35634
36066
|
if (!required && flag !== "1" && !existsSync6(home) && existsSync6(workspaceRoot))
|
|
35635
36067
|
return void 0;
|
|
35636
36068
|
try {
|
|
@@ -35646,10 +36078,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
35646
36078
|
return {
|
|
35647
36079
|
...this.baseConfig,
|
|
35648
36080
|
workspaceRoot,
|
|
35649
|
-
agentPrivateRoot:
|
|
35650
|
-
agentMemoryRoot:
|
|
35651
|
-
openRouterRoutingCacheDir: openRouterRoutingCacheDir(
|
|
35652
|
-
turnTraceDir: turnTraceDirectory(
|
|
36081
|
+
agentPrivateRoot: resolve24(workspaceRoot, "agent-private"),
|
|
36082
|
+
agentMemoryRoot: resolve24(dirname13(this.configPath), "memory"),
|
|
36083
|
+
openRouterRoutingCacheDir: openRouterRoutingCacheDir(dirname13(this.configPath)),
|
|
36084
|
+
turnTraceDir: turnTraceDirectory(dirname13(this.configPath)),
|
|
35653
36085
|
...agentHomeRoot ? { agentHomeRoot } : {}
|
|
35654
36086
|
};
|
|
35655
36087
|
}
|
|
@@ -35671,6 +36103,16 @@ var RoomRuntimeCoordinator = class {
|
|
|
35671
36103
|
});
|
|
35672
36104
|
}
|
|
35673
36105
|
async startRoom(roomId) {
|
|
36106
|
+
if (this.running.has(roomId) || this.startingRooms.has(roomId))
|
|
36107
|
+
return;
|
|
36108
|
+
this.startingRooms.add(roomId);
|
|
36109
|
+
try {
|
|
36110
|
+
await this.startRoomOnce(roomId);
|
|
36111
|
+
} finally {
|
|
36112
|
+
this.startingRooms.delete(roomId);
|
|
36113
|
+
}
|
|
36114
|
+
}
|
|
36115
|
+
async startRoomOnce(roomId) {
|
|
35674
36116
|
const controller = new AbortController();
|
|
35675
36117
|
const cwd = await this.materializeRoomCheckout(roomId);
|
|
35676
36118
|
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
@@ -35703,6 +36145,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35703
36145
|
if (this.running.get(roomId)?.body === loop)
|
|
35704
36146
|
this.running.delete(roomId);
|
|
35705
36147
|
});
|
|
36148
|
+
if (this.stopped) {
|
|
36149
|
+
controller.abort();
|
|
36150
|
+
await promise;
|
|
36151
|
+
return;
|
|
36152
|
+
}
|
|
35706
36153
|
this.running.set(roomId, {
|
|
35707
36154
|
body: loop,
|
|
35708
36155
|
controller,
|
|
@@ -35727,17 +36174,17 @@ var RoomRuntimeCoordinator = class {
|
|
|
35727
36174
|
const remote = roomCheckoutRemote(repository.remote);
|
|
35728
36175
|
const targetBranch = repository.targetBranch || "main";
|
|
35729
36176
|
const checkoutId = createHash7("sha256").update(`${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
35730
|
-
const path =
|
|
35731
|
-
await
|
|
36177
|
+
const path = resolve24(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
36178
|
+
await mkdir15(dirname13(path), { recursive: true, mode: 448 });
|
|
35732
36179
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
35733
36180
|
const env = token ? githubGitEnv(token.token) : process.env;
|
|
35734
|
-
if (!existsSync6(
|
|
35735
|
-
await
|
|
36181
|
+
if (!existsSync6(resolve24(path, ".git"))) {
|
|
36182
|
+
await execFileAsync5("git", ["clone", "--no-checkout", remote, path], {
|
|
35736
36183
|
env,
|
|
35737
36184
|
maxBuffer: 4 * 1024 * 1024
|
|
35738
36185
|
});
|
|
35739
36186
|
}
|
|
35740
|
-
await
|
|
36187
|
+
await execFileAsync5("git", [
|
|
35741
36188
|
"-C",
|
|
35742
36189
|
path,
|
|
35743
36190
|
"fetch",
|
|
@@ -35745,7 +36192,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
35745
36192
|
"origin",
|
|
35746
36193
|
`+refs/heads/${targetBranch}:refs/remotes/origin/${targetBranch}`
|
|
35747
36194
|
], { env, maxBuffer: 4 * 1024 * 1024 });
|
|
35748
|
-
await
|
|
36195
|
+
await execFileAsync5("git", ["-C", path, "checkout", "--detach", "--force", `origin/${targetBranch}`], {
|
|
35749
36196
|
env,
|
|
35750
36197
|
maxBuffer: 4 * 1024 * 1024
|
|
35751
36198
|
});
|
|
@@ -35763,7 +36210,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
35763
36210
|
roomId: corner.parentRoomId
|
|
35764
36211
|
})
|
|
35765
36212
|
]);
|
|
35766
|
-
|
|
36213
|
+
const objective = (restore.objective ?? "").trim() || (restore.kind === "human" ? (restore.title ?? "").trim() : "");
|
|
36214
|
+
configKey = cornerStartConfigKey(repository, objective);
|
|
35767
36215
|
const previousStanding = this.standingCornerStartFaults.get(corner.cornerId);
|
|
35768
36216
|
if (previousStanding === configKey)
|
|
35769
36217
|
return;
|
|
@@ -35777,7 +36225,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
35777
36225
|
if (repository.resolution === "repository" && (!repository.remote || !repository.key)) {
|
|
35778
36226
|
throw new Error("corner parent Room has an incomplete repository binding");
|
|
35779
36227
|
}
|
|
35780
|
-
const objective = (restore.objective ?? "").trim();
|
|
35781
36228
|
if (!objective)
|
|
35782
36229
|
throw new Error("corner has no authoritative objective fact");
|
|
35783
36230
|
const repositoryBacked = repository.resolution === "repository" && restore.lane !== "no_code";
|
|
@@ -35793,10 +36240,10 @@ var RoomRuntimeCoordinator = class {
|
|
|
35793
36240
|
featureBranch,
|
|
35794
36241
|
token: granted.token
|
|
35795
36242
|
}) : void 0;
|
|
35796
|
-
const workspacePath = worktree?.path ??
|
|
36243
|
+
const workspacePath = worktree?.path ?? resolve24(this.roomRoot(corner.cornerId), "scratch");
|
|
35797
36244
|
if (!worktree)
|
|
35798
|
-
await
|
|
35799
|
-
const isOpener =
|
|
36245
|
+
await mkdir15(workspacePath, { recursive: true, mode: 448 });
|
|
36246
|
+
const isOpener = corner.openedBy === this.agent.publicKey;
|
|
35800
36247
|
if (worktree && shouldPostInitialCornerWorkingState(restore, isOpener)) {
|
|
35801
36248
|
await this.options.daemonApi.execute("postCornerRemoteState", {
|
|
35802
36249
|
cornerId: corner.cornerId,
|
|
@@ -35852,6 +36299,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
35852
36299
|
this.running.delete(corner.cornerId);
|
|
35853
36300
|
}
|
|
35854
36301
|
});
|
|
36302
|
+
if (this.stopped) {
|
|
36303
|
+
controller.abort();
|
|
36304
|
+
await promise;
|
|
36305
|
+
return;
|
|
36306
|
+
}
|
|
35855
36307
|
this.running.set(corner.cornerId, {
|
|
35856
36308
|
body: loop,
|
|
35857
36309
|
controller,
|
|
@@ -35997,18 +36449,28 @@ var RoomRuntimeCoordinator = class {
|
|
|
35997
36449
|
}
|
|
35998
36450
|
}
|
|
35999
36451
|
async shutdown() {
|
|
36452
|
+
this.stopped = true;
|
|
36453
|
+
this.pendingMembershipEvents.clear();
|
|
36454
|
+
const deadlineAt = Math.min(this.now() + this.drainDeadlineMs, this.drainDeadlineAt ?? Number.POSITIVE_INFINITY);
|
|
36455
|
+
const untilDeadline = async (work) => {
|
|
36456
|
+
let timer;
|
|
36457
|
+
const deadline = new Promise((resolveDeadline) => {
|
|
36458
|
+
timer = setTimeout(() => resolveDeadline("deadline"), Math.max(0, deadlineAt - this.now()));
|
|
36459
|
+
});
|
|
36460
|
+
try {
|
|
36461
|
+
return await Promise.race([work, deadline]);
|
|
36462
|
+
} finally {
|
|
36463
|
+
if (timer)
|
|
36464
|
+
clearTimeout(timer);
|
|
36465
|
+
}
|
|
36466
|
+
};
|
|
36467
|
+
if (this.membershipDrain)
|
|
36468
|
+
await untilDeadline(this.membershipDrain);
|
|
36000
36469
|
const rooms = [...this.running.values()];
|
|
36001
36470
|
for (const room of rooms)
|
|
36002
36471
|
room.controller.abort();
|
|
36003
36472
|
const drained = Promise.all(rooms.map((room) => room.promise.catch(() => void 0)));
|
|
36004
|
-
const
|
|
36005
|
-
let timer;
|
|
36006
|
-
const deadline = new Promise((resolveDeadline) => {
|
|
36007
|
-
timer = setTimeout(() => resolveDeadline("deadline"), Math.max(0, deadlineAt - this.now()));
|
|
36008
|
-
});
|
|
36009
|
-
const result = await Promise.race([drained.then(() => "drained"), deadline]);
|
|
36010
|
-
if (timer)
|
|
36011
|
-
clearTimeout(timer);
|
|
36473
|
+
const result = await untilDeadline(drained.then(() => "drained"));
|
|
36012
36474
|
if (result === "deadline") {
|
|
36013
36475
|
await Promise.allSettled(rooms.map((room) => room.body.forceRecoverRoom()));
|
|
36014
36476
|
await drained;
|
|
@@ -36044,7 +36506,7 @@ function githubGitEnv(token) {
|
|
|
36044
36506
|
}
|
|
36045
36507
|
async function gitRefExists(gitCommonDir, ref) {
|
|
36046
36508
|
try {
|
|
36047
|
-
await
|
|
36509
|
+
await execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "show-ref", "--verify", "--quiet", ref]);
|
|
36048
36510
|
return true;
|
|
36049
36511
|
} catch (error) {
|
|
36050
36512
|
if (error.code === 1)
|
|
@@ -36054,7 +36516,7 @@ async function gitRefExists(gitCommonDir, ref) {
|
|
|
36054
36516
|
}
|
|
36055
36517
|
async function repositoryHeadRef(gitCommonDir) {
|
|
36056
36518
|
try {
|
|
36057
|
-
const head = await
|
|
36519
|
+
const head = await execFileAsync5("git", [
|
|
36058
36520
|
`--git-dir=${gitCommonDir}`,
|
|
36059
36521
|
"symbolic-ref",
|
|
36060
36522
|
"--quiet",
|
|
@@ -36074,7 +36536,7 @@ ${gitStderr(error)}`);
|
|
|
36074
36536
|
}
|
|
36075
36537
|
async function deleteExactRemoteBranch(gitCommonDir, remoteRef, token, options = {}) {
|
|
36076
36538
|
const authEnv = githubGitEnv(token);
|
|
36077
|
-
const listRemote = () =>
|
|
36539
|
+
const listRemote = () => execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "ls-remote", "--heads", "origin", remoteRef], {
|
|
36078
36540
|
env: authEnv,
|
|
36079
36541
|
maxBuffer: 4 * 1024 * 1024
|
|
36080
36542
|
});
|
|
@@ -36090,7 +36552,7 @@ async function deleteExactRemoteBranch(gitCommonDir, remoteRef, token, options =
|
|
|
36090
36552
|
try {
|
|
36091
36553
|
const remote = await listRemote();
|
|
36092
36554
|
if (remote.stdout.trim()) {
|
|
36093
|
-
await
|
|
36555
|
+
await execFileAsync5("git", [`--git-dir=${gitCommonDir}`, "push", "origin", `:${remoteRef}`], {
|
|
36094
36556
|
env: authEnv,
|
|
36095
36557
|
maxBuffer: 4 * 1024 * 1024
|
|
36096
36558
|
});
|
|
@@ -36185,17 +36647,17 @@ var ThinDaemonCore = class {
|
|
|
36185
36647
|
};
|
|
36186
36648
|
|
|
36187
36649
|
// apps/body/dist/agent-retirement.js
|
|
36188
|
-
import { mkdir as
|
|
36189
|
-
import { dirname as
|
|
36650
|
+
import { mkdir as mkdir17, rename as rename5 } from "node:fs/promises";
|
|
36651
|
+
import { dirname as dirname15, resolve as resolve26 } from "node:path";
|
|
36190
36652
|
|
|
36191
36653
|
// apps/body/dist/systemd.js
|
|
36192
|
-
import { execFile as
|
|
36193
|
-
import { mkdir as
|
|
36654
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
36655
|
+
import { mkdir as mkdir16, readFile as readFile10, stat as stat3, writeFile as writeFile10 } from "node:fs/promises";
|
|
36194
36656
|
import { homedir as homedir11 } from "node:os";
|
|
36195
|
-
import { dirname as
|
|
36657
|
+
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
36196
36658
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
36197
|
-
import { promisify as
|
|
36198
|
-
var
|
|
36659
|
+
import { promisify as promisify6 } from "node:util";
|
|
36660
|
+
var execFileAsync6 = promisify6(execFile8);
|
|
36199
36661
|
var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
|
|
36200
36662
|
var DAEMON_DISTRESS_EXIT_STATUS = 77;
|
|
36201
36663
|
var UNKNOWN_AGENT_EXIT_STATUS = 79;
|
|
@@ -36227,8 +36689,13 @@ TimeoutStartSec=90s
|
|
|
36227
36689
|
TimeoutStopSec=10min
|
|
36228
36690
|
KillMode=control-group
|
|
36229
36691
|
UMask=0077
|
|
36230
|
-
|
|
36231
|
-
|
|
36692
|
+
# A desktop-launched user manager may inherit Ubuntu's unprivileged_userns
|
|
36693
|
+
# AppArmor profile. Without an explicit transition every agent inherits it too,
|
|
36694
|
+
# so /usr/bin/bwrap cannot enter its package-provided bwrap profile.
|
|
36695
|
+
# Do not set NoNewPrivileges or PrivateTmp on this outer service: systemd applies
|
|
36696
|
+
# either before AppArmorProfile, which blocks this transition. Bubblewrap sets
|
|
36697
|
+
# no-new-privs and a private /tmp inside each agent sandbox it creates.
|
|
36698
|
+
AppArmorProfile=-unconfined
|
|
36232
36699
|
|
|
36233
36700
|
[Install]
|
|
36234
36701
|
WantedBy=default.target
|
|
@@ -36236,9 +36703,9 @@ WantedBy=default.target
|
|
|
36236
36703
|
}
|
|
36237
36704
|
function isCanonicalInstalledLauncher(env = process.env, invocationPath = process.argv[1]) {
|
|
36238
36705
|
const home = env.HOME?.trim() || homedir11();
|
|
36239
|
-
const expectedLibDir =
|
|
36706
|
+
const expectedLibDir = resolve25(home, ".local", "lib", "beeline");
|
|
36240
36707
|
const expectedPrefix = `${expectedLibDir}/`;
|
|
36241
|
-
return
|
|
36708
|
+
return resolve25(env.BEELINE_LIB_DIR?.trim() || "/") === expectedLibDir && Boolean(invocationPath) && resolve25(invocationPath).startsWith(expectedPrefix);
|
|
36242
36709
|
}
|
|
36243
36710
|
function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
36244
36711
|
if (isCanonicalInstalledLauncher(env, invocationPath))
|
|
@@ -36246,12 +36713,12 @@ function assertCanonicalInstalledLauncher(env, invocationPath) {
|
|
|
36246
36713
|
throw new Error("refusing to modify the shared Beeline systemd unit outside the canonical ~/.local/bin/beeline launcher");
|
|
36247
36714
|
}
|
|
36248
36715
|
function systemdUserUnitPath(env = process.env) {
|
|
36249
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
36250
|
-
return
|
|
36716
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve25(homedir11(), ".config");
|
|
36717
|
+
return resolve25(configRoot, "systemd", "user", SYSTEMD_UNIT_NAME);
|
|
36251
36718
|
}
|
|
36252
36719
|
function systemdBrokerUnitPath(env = process.env) {
|
|
36253
|
-
const configRoot = env.XDG_CONFIG_HOME?.trim() ||
|
|
36254
|
-
return
|
|
36720
|
+
const configRoot = env.XDG_CONFIG_HOME?.trim() || resolve25(homedir11(), ".config");
|
|
36721
|
+
return resolve25(configRoot, "systemd", "user", TRUSTY_SQUIRE_BROKER_UNIT_NAME);
|
|
36255
36722
|
}
|
|
36256
36723
|
async function installTrustySquireBrokerService(options = {}) {
|
|
36257
36724
|
const env = options.env ?? process.env;
|
|
@@ -36260,9 +36727,9 @@ async function installTrustySquireBrokerService(options = {}) {
|
|
|
36260
36727
|
ensureSquireHostDir(home);
|
|
36261
36728
|
const path = systemdBrokerUnitPath(env);
|
|
36262
36729
|
const content = trustySquireBrokerUnit();
|
|
36263
|
-
const existing = await
|
|
36730
|
+
const existing = await readFile10(path, "utf8").catch(() => "");
|
|
36264
36731
|
if (existing !== content) {
|
|
36265
|
-
await
|
|
36732
|
+
await mkdir16(dirname14(path), { recursive: true, mode: 448 });
|
|
36266
36733
|
await writeFile10(path, content, { mode: 384 });
|
|
36267
36734
|
}
|
|
36268
36735
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -36271,7 +36738,7 @@ async function installTrustySquireBrokerService(options = {}) {
|
|
|
36271
36738
|
}
|
|
36272
36739
|
var AGENT_SERVICE = /^beeline-agent@([0-9a-f]{64})\.service$/i;
|
|
36273
36740
|
var runSystemctl = async (args) => {
|
|
36274
|
-
const result = await
|
|
36741
|
+
const result = await execFileAsync6("systemctl", ["--user", ...args], {
|
|
36275
36742
|
timeout: SYSTEMD_COMMAND_TIMEOUT_MS,
|
|
36276
36743
|
encoding: "utf8"
|
|
36277
36744
|
});
|
|
@@ -36284,9 +36751,9 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
36284
36751
|
assertCanonicalInstalledLauncher(env, options.invocationPath);
|
|
36285
36752
|
const path = systemdUserUnitPath(env);
|
|
36286
36753
|
const content = agentServiceUnit();
|
|
36287
|
-
const existing = await
|
|
36754
|
+
const existing = await readFile10(path, "utf8").catch(() => "");
|
|
36288
36755
|
if (existing !== content) {
|
|
36289
|
-
await
|
|
36756
|
+
await mkdir16(dirname14(path), { recursive: true, mode: 448 });
|
|
36290
36757
|
await writeFile10(path, content, { mode: 384 });
|
|
36291
36758
|
}
|
|
36292
36759
|
const run2 = options.run ?? runSystemctl;
|
|
@@ -36407,7 +36874,7 @@ async function reconcileAgentServices(options = {}) {
|
|
|
36407
36874
|
async function notify(fields) {
|
|
36408
36875
|
if (process.env.BEELINE_MANAGED_BY_SYSTEMD !== "1")
|
|
36409
36876
|
return;
|
|
36410
|
-
await
|
|
36877
|
+
await execFileAsync6("systemd-notify", fields, { timeout: SYSTEMD_COMMAND_TIMEOUT_MS });
|
|
36411
36878
|
}
|
|
36412
36879
|
async function extendSystemdStartTimeout(ms) {
|
|
36413
36880
|
await notify([`EXTEND_TIMEOUT_USEC=${Math.max(0, Math.round(ms)) * 1e3}`]).catch(() => void 0);
|
|
@@ -36426,15 +36893,15 @@ var SystemdNotifier = class {
|
|
|
36426
36893
|
|
|
36427
36894
|
// apps/body/dist/agent-retirement.js
|
|
36428
36895
|
async function retireRemovedAgent(runtime, options = {}) {
|
|
36429
|
-
const deletedRoot =
|
|
36430
|
-
const target =
|
|
36896
|
+
const deletedRoot = resolve26(runtime.supervisorRoot, "beeline", "deleted-runtimes");
|
|
36897
|
+
const target = resolve26(deletedRoot, `${runtime.agent.publicKey}-${Date.now()}`);
|
|
36431
36898
|
return relocateAgentRuntime(runtime, target, {
|
|
36432
36899
|
...options.run ? { run: options.run } : {}
|
|
36433
36900
|
});
|
|
36434
36901
|
}
|
|
36435
36902
|
async function relocateAgentRuntime(runtime, target, options = {}) {
|
|
36436
36903
|
const source = runtimeDirectory(runtime.supervisorRoot, runtime.agent.publicKey);
|
|
36437
|
-
const destination =
|
|
36904
|
+
const destination = resolve26(target);
|
|
36438
36905
|
if (destination === source || destination.startsWith(`${source}/`)) {
|
|
36439
36906
|
throw new Error("agent runtime destination must be outside the live runtime");
|
|
36440
36907
|
}
|
|
@@ -36443,13 +36910,13 @@ async function relocateAgentRuntime(runtime, target, options = {}) {
|
|
|
36443
36910
|
...options.run ? { run: options.run } : {}
|
|
36444
36911
|
});
|
|
36445
36912
|
}
|
|
36446
|
-
await
|
|
36913
|
+
await mkdir17(dirname15(destination), { recursive: true, mode: 448 });
|
|
36447
36914
|
await rename5(source, destination);
|
|
36448
36915
|
return destination;
|
|
36449
36916
|
}
|
|
36450
36917
|
|
|
36451
36918
|
// apps/body/dist/start-command.js
|
|
36452
|
-
import { basename as basename7, dirname as
|
|
36919
|
+
import { basename as basename7, dirname as dirname19 } from "node:path";
|
|
36453
36920
|
var import_picocolors2 = __toESM(require_picocolors(), 1);
|
|
36454
36921
|
|
|
36455
36922
|
// apps/body/dist/self-update-cli.js
|
|
@@ -36460,21 +36927,21 @@ init_self_update_manifest();
|
|
|
36460
36927
|
// apps/body/dist/managed-update.js
|
|
36461
36928
|
init_self_update();
|
|
36462
36929
|
import { spawn as spawn9 } from "node:child_process";
|
|
36463
|
-
import { mkdir as
|
|
36464
|
-
import { dirname as
|
|
36930
|
+
import { mkdir as mkdir20, rm as rm7, stat as stat4, writeFile as writeFile13 } from "node:fs/promises";
|
|
36931
|
+
import { dirname as dirname18, resolve as resolve29 } from "node:path";
|
|
36465
36932
|
|
|
36466
36933
|
// apps/body/dist/update-rollback-alert.js
|
|
36467
|
-
import { mkdir as
|
|
36468
|
-
import { dirname as
|
|
36934
|
+
import { mkdir as mkdir19, readFile as readFile12, rename as rename7, unlink as unlink4, writeFile as writeFile12 } from "node:fs/promises";
|
|
36935
|
+
import { dirname as dirname17, resolve as resolve28 } from "node:path";
|
|
36469
36936
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
36470
36937
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
36471
36938
|
function updateRollbackAlertPath(runtimeDir) {
|
|
36472
|
-
return
|
|
36939
|
+
return resolve28(runtimeDir, "update-rollback-alert.json");
|
|
36473
36940
|
}
|
|
36474
36941
|
async function writeAlert(runtimeDir, alert) {
|
|
36475
36942
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
36476
36943
|
const staged = `${path}.${process.pid}.tmp`;
|
|
36477
|
-
await
|
|
36944
|
+
await mkdir19(dirname17(path), { recursive: true });
|
|
36478
36945
|
await writeFile12(staged, `${JSON.stringify(alert, null, 2)}
|
|
36479
36946
|
`, { mode: 384 });
|
|
36480
36947
|
await rename7(staged, path);
|
|
@@ -36487,7 +36954,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
|
|
|
36487
36954
|
}
|
|
36488
36955
|
async function readUpdateRollbackAlert(runtimeDir) {
|
|
36489
36956
|
try {
|
|
36490
|
-
const value = JSON.parse(await
|
|
36957
|
+
const value = JSON.parse(await readFile12(updateRollbackAlertPath(runtimeDir), "utf8"));
|
|
36491
36958
|
if (value.version !== 1 || typeof value.releaseId !== "string")
|
|
36492
36959
|
return void 0;
|
|
36493
36960
|
return value;
|
|
@@ -36538,13 +37005,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
36538
37005
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
36539
37006
|
async function withInstallLock(layout, work, options = {}) {
|
|
36540
37007
|
const now2 = options.now ?? Date.now;
|
|
36541
|
-
const lock =
|
|
37008
|
+
const lock = resolve29(layout.releasesRoot, ".state", "install.lock");
|
|
36542
37009
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
36543
|
-
await
|
|
37010
|
+
await mkdir20(dirname18(lock), { recursive: true });
|
|
36544
37011
|
for (; ; ) {
|
|
36545
37012
|
try {
|
|
36546
|
-
await
|
|
36547
|
-
await writeFile13(
|
|
37013
|
+
await mkdir20(lock);
|
|
37014
|
+
await writeFile13(resolve29(lock, "owner"), `${process.pid}
|
|
36548
37015
|
${now2()}
|
|
36549
37016
|
`, "utf8");
|
|
36550
37017
|
break;
|
|
@@ -36668,7 +37135,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
36668
37135
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
36669
37136
|
const from = await readInstalledBundleIdentity({
|
|
36670
37137
|
...this.#layout,
|
|
36671
|
-
libDir:
|
|
37138
|
+
libDir: resolve29(this.#layout.releasesRoot, this.#loadedRelease)
|
|
36672
37139
|
}).catch(() => void 0) ?? {};
|
|
36673
37140
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
36674
37141
|
const record3 = {
|
|
@@ -36955,7 +37422,7 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
36955
37422
|
});
|
|
36956
37423
|
if (!accepted)
|
|
36957
37424
|
return false;
|
|
36958
|
-
await writeFile13(
|
|
37425
|
+
await writeFile13(resolve29(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
36959
37426
|
readyAt: (options.now ?? Date.now)(),
|
|
36960
37427
|
loadedRelease,
|
|
36961
37428
|
functionalProof: options.functionalProof
|
|
@@ -37201,7 +37668,7 @@ async function startStoredRuntime(configPath, opts = {}, dependencyOverrides = {
|
|
|
37201
37668
|
return { status: "started", pid };
|
|
37202
37669
|
}
|
|
37203
37670
|
function agentStartId(configPath) {
|
|
37204
|
-
return basename7(
|
|
37671
|
+
return basename7(dirname19(configPath));
|
|
37205
37672
|
}
|
|
37206
37673
|
async function startRuntime(configPath, spinnerHandle) {
|
|
37207
37674
|
const report = (text2) => spinnerHandle ? spinnerHandle.message(text2) : console.log(text2);
|
|
@@ -37267,7 +37734,7 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
37267
37734
|
for (const path of unique) {
|
|
37268
37735
|
const id = agentStartId(path);
|
|
37269
37736
|
const spinnerHandle = interactiveUi ? spinner() : void 0;
|
|
37270
|
-
spinnerHandle?.start(`Starting ${
|
|
37737
|
+
spinnerHandle?.start(`Starting ${dirname19(path)}\u2026`);
|
|
37271
37738
|
try {
|
|
37272
37739
|
const outcome = await deps.startOne(path, spinnerHandle);
|
|
37273
37740
|
const report = { id, path, ...outcome };
|
|
@@ -37301,9 +37768,9 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
37301
37768
|
// apps/body/dist/connect-command.js
|
|
37302
37769
|
import { spawn as spawn11 } from "node:child_process";
|
|
37303
37770
|
import { createHash as createHash9, randomUUID as randomUUID6 } from "node:crypto";
|
|
37304
|
-
import { chmod as chmod7, mkdir as
|
|
37771
|
+
import { chmod as chmod7, mkdir as mkdir21, readFile as readFile13, unlink as unlink5, writeFile as writeFile14 } from "node:fs/promises";
|
|
37305
37772
|
import { homedir as homedir13, hostname as hostname2 } from "node:os";
|
|
37306
|
-
import { dirname as
|
|
37773
|
+
import { dirname as dirname20, resolve as resolve30 } from "node:path";
|
|
37307
37774
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
37308
37775
|
|
|
37309
37776
|
// apps/body/dist/clack-support.js
|
|
@@ -37621,6 +38088,9 @@ init_self_update();
|
|
|
37621
38088
|
init_self_update_manifest();
|
|
37622
38089
|
var CONNECT_HARNESSES = AUTO_DETECT_AGENT_KINDS;
|
|
37623
38090
|
var AGENT_NAME_MAX_LENGTH = 32;
|
|
38091
|
+
function isInteractiveConnectTerminal(input = stdin3) {
|
|
38092
|
+
return input.isTTY === true;
|
|
38093
|
+
}
|
|
37624
38094
|
function isReasonableAgentName(value) {
|
|
37625
38095
|
const normalized = value.trim().replace(/\s+/g, " ");
|
|
37626
38096
|
return normalized.length > 0 && normalized.length <= AGENT_NAME_MAX_LENGTH && new RegExp("^\\p{L}[\\p{L}\\p{M}'\u2019 -]*$", "u").test(normalized);
|
|
@@ -37784,8 +38254,6 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
|
|
|
37784
38254
|
return (picked ?? "").trim();
|
|
37785
38255
|
};
|
|
37786
38256
|
const askEffort = async (catalog2, forHarness, model2, forProvider, forKey) => {
|
|
37787
|
-
if (!catalog2.effort)
|
|
37788
|
-
return void 0;
|
|
37789
38257
|
let axis = catalog2.effort;
|
|
37790
38258
|
if (model2 !== catalog2.currentValue) {
|
|
37791
38259
|
const reread = await loadModels({
|
|
@@ -37797,7 +38265,7 @@ async function collectConnectWizard(prompts = clackPrompts, loadModels = loadCon
|
|
|
37797
38265
|
}).catch(() => void 0);
|
|
37798
38266
|
axis = reread?.effort ?? axis;
|
|
37799
38267
|
}
|
|
37800
|
-
if (!axis
|
|
38268
|
+
if (!axis?.options.length)
|
|
37801
38269
|
return void 0;
|
|
37802
38270
|
const picked = await prompts.select({
|
|
37803
38271
|
message: brass("Choose reasoning effort"),
|
|
@@ -37931,19 +38399,19 @@ function parseConnectSubscriptions(value) {
|
|
|
37931
38399
|
];
|
|
37932
38400
|
}
|
|
37933
38401
|
async function readMachineId(env = process.env) {
|
|
37934
|
-
const configDir =
|
|
37935
|
-
const machineIdPath =
|
|
38402
|
+
const configDir = resolve30(env.XDG_CONFIG_HOME ?? resolve30(homedir13(), ".config"), "beeline");
|
|
38403
|
+
const machineIdPath = resolve30(configDir, "machine-id");
|
|
37936
38404
|
let machineId;
|
|
37937
38405
|
let machineName = hostname2();
|
|
37938
38406
|
try {
|
|
37939
|
-
const existing = await
|
|
38407
|
+
const existing = await readFile13(machineIdPath, "utf8");
|
|
37940
38408
|
machineId = existing.trim();
|
|
37941
38409
|
if (!/^[0-9a-f-]{32,}$/.test(machineId))
|
|
37942
38410
|
throw new Error("invalid persisted machine id");
|
|
37943
38411
|
} catch {
|
|
37944
38412
|
machineId = randomUUID6();
|
|
37945
38413
|
try {
|
|
37946
|
-
await
|
|
38414
|
+
await mkdir21(configDir, { recursive: true, mode: 448 });
|
|
37947
38415
|
await writeFile14(machineIdPath, `${machineId}
|
|
37948
38416
|
`, { mode: 384 });
|
|
37949
38417
|
} catch {
|
|
@@ -38005,7 +38473,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
38005
38473
|
});
|
|
38006
38474
|
await activateRelease(layout, releaseId);
|
|
38007
38475
|
return {
|
|
38008
|
-
binary:
|
|
38476
|
+
binary: resolve30(layout.binDir, "beeline"),
|
|
38009
38477
|
version: published.version ?? releaseId
|
|
38010
38478
|
};
|
|
38011
38479
|
}
|
|
@@ -38028,7 +38496,7 @@ function providerEnvironment(selection) {
|
|
|
38028
38496
|
};
|
|
38029
38497
|
}
|
|
38030
38498
|
async function writePrivateJson(path, value) {
|
|
38031
|
-
await
|
|
38499
|
+
await mkdir21(dirname20(path), { recursive: true, mode: 448 });
|
|
38032
38500
|
await writeFile14(path, `${JSON.stringify(value, null, 2)}
|
|
38033
38501
|
`, { mode: 384 });
|
|
38034
38502
|
await chmod7(path, 384);
|
|
@@ -38037,8 +38505,8 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
38037
38505
|
const values = providerEnvironment(selection);
|
|
38038
38506
|
if (Object.keys(values).length === 0)
|
|
38039
38507
|
return void 0;
|
|
38040
|
-
const path =
|
|
38041
|
-
await
|
|
38508
|
+
const path = resolve30(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
38509
|
+
await mkdir21(dirname20(path), { recursive: true, mode: 448 });
|
|
38042
38510
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
38043
38511
|
await writeFile14(path, `${contents}
|
|
38044
38512
|
`, { mode: 384 });
|
|
@@ -38080,7 +38548,7 @@ async function brassSpinner(message, action, completion) {
|
|
|
38080
38548
|
}
|
|
38081
38549
|
}
|
|
38082
38550
|
async function runConnectCommand(code, options = {}) {
|
|
38083
|
-
if (!
|
|
38551
|
+
if (!isInteractiveConnectTerminal()) {
|
|
38084
38552
|
throw new Error("`usebeeline connect` needs an interactive terminal");
|
|
38085
38553
|
}
|
|
38086
38554
|
const restoreRails = paintWizardBrass();
|
|
@@ -38107,7 +38575,7 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
|
|
|
38107
38575
|
await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
|
|
38108
38576
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
38109
38577
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
38110
|
-
const grantPath =
|
|
38578
|
+
const grantPath = resolve30(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
38111
38579
|
await writePrivateJson(grantPath, {
|
|
38112
38580
|
agentSecretKey: grant.agent_secret_key,
|
|
38113
38581
|
bodySecretKey: grant.body_secret_key,
|
|
@@ -38192,18 +38660,18 @@ async function runConnectFinishCommand(path) {
|
|
|
38192
38660
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
38193
38661
|
}
|
|
38194
38662
|
try {
|
|
38195
|
-
const grant = JSON.parse(await
|
|
38663
|
+
const grant = JSON.parse(await readFile13(resolve30(path), "utf8"));
|
|
38196
38664
|
if (!isDevicePairingGrant(grant))
|
|
38197
38665
|
throw new Error("device connection grant is invalid");
|
|
38198
38666
|
const connected = await completeDevicePairing(grant);
|
|
38199
|
-
await unlink5(
|
|
38200
|
-
const providerEnv = grant.llmEnvFile ? await
|
|
38667
|
+
await unlink5(resolve30(path));
|
|
38668
|
+
const providerEnv = grant.llmEnvFile ? await readFile13(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
38201
38669
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
38202
38670
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
38203
38671
|
if (model) {
|
|
38204
38672
|
const decision2 = await resolveOpenRouterRouting({
|
|
38205
38673
|
model,
|
|
38206
|
-
cacheDir: openRouterRoutingCacheDir(
|
|
38674
|
+
cacheDir: openRouterRoutingCacheDir(dirname20(connected.configPath)),
|
|
38207
38675
|
...apiKey ? { apiKey } : {},
|
|
38208
38676
|
probeTimeoutMs: 1e4
|
|
38209
38677
|
});
|
|
@@ -38221,16 +38689,16 @@ async function runConnectFinishCommand(path) {
|
|
|
38221
38689
|
init_self_update();
|
|
38222
38690
|
|
|
38223
38691
|
// apps/body/dist/daemon-failure.js
|
|
38224
|
-
import { mkdir as
|
|
38225
|
-
import { dirname as
|
|
38692
|
+
import { mkdir as mkdir22, readFile as readFile14, rename as rename8, rm as rm8, writeFile as writeFile15 } from "node:fs/promises";
|
|
38693
|
+
import { dirname as dirname21, resolve as resolve31 } from "node:path";
|
|
38226
38694
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
38227
38695
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
38228
38696
|
function daemonFailurePath(runtimeDir) {
|
|
38229
|
-
return
|
|
38697
|
+
return resolve31(runtimeDir, "daemon-distress.json");
|
|
38230
38698
|
}
|
|
38231
38699
|
async function readFailureRecord(runtimeDir) {
|
|
38232
38700
|
try {
|
|
38233
|
-
const value = JSON.parse(await
|
|
38701
|
+
const value = JSON.parse(await readFile14(daemonFailurePath(runtimeDir), "utf8"));
|
|
38234
38702
|
if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure2) => typeof failure2 !== "number") || typeof value.lastError !== "string") {
|
|
38235
38703
|
return void 0;
|
|
38236
38704
|
}
|
|
@@ -38242,7 +38710,7 @@ async function readFailureRecord(runtimeDir) {
|
|
|
38242
38710
|
async function writeFailureRecord(runtimeDir, record3) {
|
|
38243
38711
|
const path = daemonFailurePath(runtimeDir);
|
|
38244
38712
|
const staged = `${path}.${process.pid}.tmp`;
|
|
38245
|
-
await
|
|
38713
|
+
await mkdir22(dirname21(path), { recursive: true, mode: 448 });
|
|
38246
38714
|
await writeFile15(staged, `${JSON.stringify(record3, null, 2)}
|
|
38247
38715
|
`, { mode: 384 });
|
|
38248
38716
|
await rename8(staged, path);
|
|
@@ -38265,9 +38733,9 @@ async function clearDaemonStartFailures(runtimeDir) {
|
|
|
38265
38733
|
}
|
|
38266
38734
|
|
|
38267
38735
|
// apps/body/dist/update-functional-probe.js
|
|
38268
|
-
import { mkdir as
|
|
38736
|
+
import { mkdir as mkdir23, rm as rm9 } from "node:fs/promises";
|
|
38269
38737
|
import { homedir as homedir14 } from "node:os";
|
|
38270
|
-
import { resolve as
|
|
38738
|
+
import { resolve as resolve32 } from "node:path";
|
|
38271
38739
|
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
38272
38740
|
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
38273
38741
|
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
@@ -38387,11 +38855,11 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38387
38855
|
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
38388
38856
|
};
|
|
38389
38857
|
}
|
|
38390
|
-
const root = input.probeRoot ??
|
|
38391
|
-
const cwd =
|
|
38392
|
-
const homeRoot =
|
|
38858
|
+
const root = input.probeRoot ?? resolve32(input.runtimeDir, "update-functional-probe");
|
|
38859
|
+
const cwd = resolve32(root, "checkout");
|
|
38860
|
+
const homeRoot = resolve32(root, "agent-home");
|
|
38393
38861
|
await rm9(root, { recursive: true, force: true });
|
|
38394
|
-
await
|
|
38862
|
+
await mkdir23(cwd, { recursive: true, mode: 448 });
|
|
38395
38863
|
let client;
|
|
38396
38864
|
try {
|
|
38397
38865
|
const agentEnv = {
|
|
@@ -38421,7 +38889,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38421
38889
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
38422
38890
|
const operatorHome = input.config.operatorHome ?? homedir14();
|
|
38423
38891
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
38424
|
-
await Promise.all(homeStateDirs.map((dir) =>
|
|
38892
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir23(dir, { recursive: true })));
|
|
38425
38893
|
spawnCommand = wrapAgentCommand({
|
|
38426
38894
|
bwrapPath: input.config.bwrapPath,
|
|
38427
38895
|
spec: {
|
|
@@ -38571,7 +39039,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
38571
39039
|
|
|
38572
39040
|
// apps/body/dist/current-release-probe.js
|
|
38573
39041
|
import { spawn as spawn12 } from "node:child_process";
|
|
38574
|
-
import { dirname as
|
|
39042
|
+
import { dirname as dirname22, join as join14 } from "node:path";
|
|
38575
39043
|
init_self_update();
|
|
38576
39044
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
38577
39045
|
var UPDATE_PROBE_COMMAND = "update-probe";
|
|
@@ -38620,7 +39088,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
38620
39088
|
return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
|
|
38621
39089
|
}
|
|
38622
39090
|
const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
|
|
38623
|
-
return new Promise((
|
|
39091
|
+
return new Promise((resolve36) => {
|
|
38624
39092
|
const child = spawn12(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
38625
39093
|
let stdout6 = "";
|
|
38626
39094
|
let stderr = "";
|
|
@@ -38630,7 +39098,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
38630
39098
|
return;
|
|
38631
39099
|
settled = true;
|
|
38632
39100
|
clearTimeout(timer);
|
|
38633
|
-
|
|
39101
|
+
resolve36(outcome);
|
|
38634
39102
|
};
|
|
38635
39103
|
const timer = setTimeout(() => {
|
|
38636
39104
|
child.kill("SIGKILL");
|
|
@@ -38677,7 +39145,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38677
39145
|
const runtime = await readRuntimeRecord(configPath);
|
|
38678
39146
|
const agent = runtimeAgentCommand(runtime);
|
|
38679
39147
|
const config = loadBodyConfig({
|
|
38680
|
-
workspaceRoot: join14(
|
|
39148
|
+
workspaceRoot: join14(dirname22(configPath), "workspace"),
|
|
38681
39149
|
llmEnvFile: runtime.llmEnvFile,
|
|
38682
39150
|
env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
|
|
38683
39151
|
agent
|
|
@@ -38695,7 +39163,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38695
39163
|
}
|
|
38696
39164
|
const layout = beelineInstallLayout(env);
|
|
38697
39165
|
const releaseId = (layout && await activeReleaseId(layout).catch(() => void 0)) ?? "unknown";
|
|
38698
|
-
const runtimeDir =
|
|
39166
|
+
const runtimeDir = dirname22(configPath);
|
|
38699
39167
|
const outcome = await probeOutcome(() => (options.probe ?? runUpdateFunctionalProbe)({
|
|
38700
39168
|
config,
|
|
38701
39169
|
runtimeDir,
|
|
@@ -38710,8 +39178,8 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
38710
39178
|
}
|
|
38711
39179
|
|
|
38712
39180
|
// apps/body/dist/release-status.js
|
|
38713
|
-
import { readFile as
|
|
38714
|
-
import { resolve as
|
|
39181
|
+
import { readFile as readFile15, readdir as readdir6, rename as rename9, writeFile as writeFile16 } from "node:fs/promises";
|
|
39182
|
+
import { resolve as resolve33 } from "node:path";
|
|
38715
39183
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
38716
39184
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
38717
39185
|
var SOURCE_SHA = /^[0-9a-f]{7,64}$/;
|
|
@@ -38728,7 +39196,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
38728
39196
|
pid: options.pid ?? process.pid,
|
|
38729
39197
|
readyAt: (options.now?.() ?? /* @__PURE__ */ new Date()).toISOString()
|
|
38730
39198
|
};
|
|
38731
|
-
const target =
|
|
39199
|
+
const target = resolve33(runtimeDir, DAEMON_RELEASE_STATUS_FILE);
|
|
38732
39200
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
38733
39201
|
await writeFile16(temporary, `${JSON.stringify(status, null, 2)}
|
|
38734
39202
|
`, { mode: 384 });
|
|
@@ -38738,7 +39206,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
38738
39206
|
|
|
38739
39207
|
// apps/body/dist/scratch-sweep.js
|
|
38740
39208
|
import { lstat as lstat5, readdir as readdir7, rmdir, unlink as unlink6 } from "node:fs/promises";
|
|
38741
|
-
import { resolve as
|
|
39209
|
+
import { resolve as resolve34 } from "node:path";
|
|
38742
39210
|
var DEFAULT_SCRATCH_TTL_HOURS = 72;
|
|
38743
39211
|
var NEVER_SWEEP_SUBDIR_NAMES = new Set(HOME_SUBDIRS.filter((name) => name !== "tmp"));
|
|
38744
39212
|
function scratchTtlMs(env = process.env) {
|
|
@@ -38747,7 +39215,7 @@ function scratchTtlMs(env = process.env) {
|
|
|
38747
39215
|
return (Number.isFinite(hours) && hours > 0 ? hours : DEFAULT_SCRATCH_TTL_HOURS) * 60 * 60 * 1e3;
|
|
38748
39216
|
}
|
|
38749
39217
|
async function discoverAttachScratchRoots(runtimeDir) {
|
|
38750
|
-
const roomsDir =
|
|
39218
|
+
const roomsDir = resolve34(runtimeDir, "rooms");
|
|
38751
39219
|
let entries;
|
|
38752
39220
|
try {
|
|
38753
39221
|
entries = await readdir7(roomsDir, { withFileTypes: true });
|
|
@@ -38758,7 +39226,7 @@ async function discoverAttachScratchRoots(runtimeDir) {
|
|
|
38758
39226
|
for (const entry of entries) {
|
|
38759
39227
|
if (!entry.isDirectory())
|
|
38760
39228
|
continue;
|
|
38761
|
-
const home =
|
|
39229
|
+
const home = resolve34(roomsDir, entry.name, "agent-home");
|
|
38762
39230
|
const stats = await lstat5(home).catch(() => void 0);
|
|
38763
39231
|
if (stats?.isDirectory())
|
|
38764
39232
|
roots.push(home);
|
|
@@ -38777,7 +39245,7 @@ async function removeStaleFiles(dir, cutoffMs, protectNamesHere) {
|
|
|
38777
39245
|
for (const entry of entries) {
|
|
38778
39246
|
if (protectNamesHere && NEVER_SWEEP_SUBDIR_NAMES.has(entry.name))
|
|
38779
39247
|
continue;
|
|
38780
|
-
const path =
|
|
39248
|
+
const path = resolve34(dir, entry.name);
|
|
38781
39249
|
const stats = await lstat5(path).catch(() => void 0);
|
|
38782
39250
|
if (!stats || stats.isSymbolicLink())
|
|
38783
39251
|
continue;
|
|
@@ -38865,7 +39333,7 @@ var DaemonExitError = class extends Error {
|
|
|
38865
39333
|
};
|
|
38866
39334
|
async function runStoredDaemon(pathOrPointer) {
|
|
38867
39335
|
const configPath = await resolveRuntimeConfigPath(pathOrPointer);
|
|
38868
|
-
daemonFailureRuntimeDir =
|
|
39336
|
+
daemonFailureRuntimeDir = dirname23(configPath);
|
|
38869
39337
|
const accessMigration = await migrateRuntimeRecordAccessPolicy(configPath);
|
|
38870
39338
|
let runtime = accessMigration.runtime;
|
|
38871
39339
|
if (!runtime.transport) {
|
|
@@ -38884,7 +39352,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
38884
39352
|
BUZZ_DEV_MCP_BIN: runtime.mcpBinary
|
|
38885
39353
|
};
|
|
38886
39354
|
const config = loadBodyConfig({
|
|
38887
|
-
workspaceRoot:
|
|
39355
|
+
workspaceRoot: resolve35(dirname23(configPath), "workspace"),
|
|
38888
39356
|
llmEnvFile: runtime.llmEnvFile,
|
|
38889
39357
|
env,
|
|
38890
39358
|
agent
|
|
@@ -38919,7 +39387,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
38919
39387
|
const stop = () => controller.abort();
|
|
38920
39388
|
process.once("SIGINT", stop);
|
|
38921
39389
|
process.once("SIGTERM", stop);
|
|
38922
|
-
const runtimeDir =
|
|
39390
|
+
const runtimeDir = dirname23(configPath);
|
|
38923
39391
|
const layout = beelineInstallLayout(process.env);
|
|
38924
39392
|
const notifier = new SystemdNotifier();
|
|
38925
39393
|
let rollbackAlertDrain;
|
|
@@ -38967,10 +39435,27 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
38967
39435
|
scratchSweepTimer.unref();
|
|
38968
39436
|
let ready = false;
|
|
38969
39437
|
let connectorLoop;
|
|
39438
|
+
let catalogRefresh;
|
|
39439
|
+
const refreshCatalog = () => {
|
|
39440
|
+
catalogRefresh ??= syncAgentModelCatalog({
|
|
39441
|
+
api: daemonApi,
|
|
39442
|
+
agent,
|
|
39443
|
+
agentEnv: config.agentEnv,
|
|
39444
|
+
agentId: runtime.agent.publicKey,
|
|
39445
|
+
workspaceId: runtime.communityId,
|
|
39446
|
+
runtimeDir,
|
|
39447
|
+
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {},
|
|
39448
|
+
force: true
|
|
39449
|
+
}).then(() => void 0).finally(() => {
|
|
39450
|
+
catalogRefresh = void 0;
|
|
39451
|
+
});
|
|
39452
|
+
return catalogRefresh;
|
|
39453
|
+
};
|
|
38970
39454
|
let stoppingStatus = "daemon stopped";
|
|
38971
39455
|
try {
|
|
38972
39456
|
const core = new ThinDaemonCore(runtime, configPath, config, {
|
|
38973
39457
|
daemonApi,
|
|
39458
|
+
onConfigChanged: refreshCatalog,
|
|
38974
39459
|
onHiccupRestart: (attempt) => {
|
|
38975
39460
|
const delay = hiccupBackoffMs(attempt);
|
|
38976
39461
|
console.warn(`[thin-core] hiccup restart attempt ${attempt}; exiting so systemd can start a fresh helper`);
|
|
@@ -39057,6 +39542,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
39057
39542
|
agentId: runtime.agent.publicKey,
|
|
39058
39543
|
log: (message) => console.log(`[body] connector: ${message}`)
|
|
39059
39544
|
});
|
|
39545
|
+
daemonApi.setConnectorAssignmentListener(() => connectorLoop?.wake());
|
|
39060
39546
|
connectorLoop.start();
|
|
39061
39547
|
},
|
|
39062
39548
|
onProgress: async (status) => {
|
|
@@ -39123,7 +39609,7 @@ async function main() {
|
|
|
39123
39609
|
const roomId = roomFlag >= 0 ? args[roomFlag + 1] : void 0;
|
|
39124
39610
|
if (!configPath || !roomId)
|
|
39125
39611
|
throw new Error("corner-read-token requires --config and --room");
|
|
39126
|
-
const activated = await activateDaemonTransport(
|
|
39612
|
+
const activated = await activateDaemonTransport(resolve35(configPath));
|
|
39127
39613
|
if (!activated)
|
|
39128
39614
|
throw new Error("corner-read-token requires monolith transport");
|
|
39129
39615
|
const credential = await activated.client.execute("getRoomGitHubToken", { roomId });
|
|
@@ -39177,14 +39663,14 @@ async function main() {
|
|
|
39177
39663
|
}
|
|
39178
39664
|
if (!configPath && agentPubkey) {
|
|
39179
39665
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
39180
|
-
configPath = configs.find((candidate) =>
|
|
39666
|
+
configPath = configs.find((candidate) => dirname23(candidate).endsWith(agentPubkey));
|
|
39181
39667
|
}
|
|
39182
39668
|
if (!configPath && agentPubkey) {
|
|
39183
39669
|
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing systemd restart loop`, UNKNOWN_AGENT_EXIT_STATUS);
|
|
39184
39670
|
}
|
|
39185
39671
|
if (!configPath)
|
|
39186
39672
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
39187
|
-
await runStoredDaemon(
|
|
39673
|
+
await runStoredDaemon(resolve35(configPath));
|
|
39188
39674
|
return;
|
|
39189
39675
|
}
|
|
39190
39676
|
if (command === "update") {
|
|
@@ -39205,7 +39691,7 @@ async function main() {
|
|
|
39205
39691
|
if (!agentPubkey)
|
|
39206
39692
|
throw new Error("stop requires --agent <pubkey>");
|
|
39207
39693
|
const configs = await findAgentRuntimeConfigPaths(process.env, process.cwd());
|
|
39208
|
-
const configPath = configs.find((candidate) =>
|
|
39694
|
+
const configPath = configs.find((candidate) => dirname23(candidate).endsWith(agentPubkey));
|
|
39209
39695
|
if (!configPath)
|
|
39210
39696
|
throw new Error(`no stored runtime found for agent ${agentPubkey}`);
|
|
39211
39697
|
const runtime = await readRuntimeRecord(configPath);
|