usebeeline 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/usebeeline.mjs +1378 -632
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -9711,7 +9711,7 @@ var init_squire_host = __esm({
|
|
|
9711
9711
|
|
|
9712
9712
|
// apps/body/dist/systemd.js
|
|
9713
9713
|
import { execFile as execFile2 } from "node:child_process";
|
|
9714
|
-
import { mkdir as mkdir2, readFile as readFile3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
9714
|
+
import { mkdir as mkdir2, readFile as readFile3, rename as rename2, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
9715
9715
|
import { homedir as homedir3 } from "node:os";
|
|
9716
9716
|
import { dirname as dirname3, resolve as resolve3 } from "node:path";
|
|
9717
9717
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
@@ -9740,6 +9740,7 @@ CPUWeight=1000
|
|
|
9740
9740
|
IOWeight=1000
|
|
9741
9741
|
Nice=-5
|
|
9742
9742
|
OOMScoreAdjust=-1000
|
|
9743
|
+
OOMPolicy=continue
|
|
9743
9744
|
TimeoutStartSec=90s
|
|
9744
9745
|
TimeoutStopSec=90s
|
|
9745
9746
|
KillMode=control-group
|
|
@@ -9812,6 +9813,26 @@ async function convergeTrustySquireBrokerService(options) {
|
|
|
9812
9813
|
return false;
|
|
9813
9814
|
}
|
|
9814
9815
|
}
|
|
9816
|
+
async function convergeAgentServiceUnit(options) {
|
|
9817
|
+
const env = options.env ?? process.env;
|
|
9818
|
+
try {
|
|
9819
|
+
assertCanonicalInstalledLauncher(env, resolve3(options.libDir, "lib", "beeline", "beeline-cli.mjs"));
|
|
9820
|
+
const path = systemdUserUnitPath(env);
|
|
9821
|
+
const content = agentServiceUnit();
|
|
9822
|
+
const existing = await readFile3(path, "utf8").catch(() => "");
|
|
9823
|
+
if (existing === content)
|
|
9824
|
+
return false;
|
|
9825
|
+
await mkdir2(dirname3(path), { recursive: true, mode: 448 });
|
|
9826
|
+
await writeFile2(path, content, { mode: 384 });
|
|
9827
|
+
const run2 = options.run ?? runSystemctl;
|
|
9828
|
+
await run2(["daemon-reload"]);
|
|
9829
|
+
options.log?.("[beeline] agent systemd unit updated; it applies on the next daemon restart");
|
|
9830
|
+
return true;
|
|
9831
|
+
} catch (error) {
|
|
9832
|
+
options.log?.(`[beeline] agent systemd unit not converged: ${error instanceof Error ? error.message : String(error)}`);
|
|
9833
|
+
return false;
|
|
9834
|
+
}
|
|
9835
|
+
}
|
|
9815
9836
|
async function installAgentService(publicKey, options = {}) {
|
|
9816
9837
|
if (!/^[0-9a-f]{64}$/i.test(publicKey))
|
|
9817
9838
|
throw new Error("agent public key must be 64 hex");
|
|
@@ -9844,6 +9865,23 @@ async function installAgentService(publicKey, options = {}) {
|
|
|
9844
9865
|
} while (Date.now() < deadline);
|
|
9845
9866
|
throw new Error(`systemd did not replace ${service}'s MainPID before the restart deadline`);
|
|
9846
9867
|
}
|
|
9868
|
+
async function rewriteAgentServiceUnit(options) {
|
|
9869
|
+
const env = options.env ?? process.env;
|
|
9870
|
+
assertCanonicalInstalledLauncher(env, options.invocationPath);
|
|
9871
|
+
const path = systemdUserUnitPath(env);
|
|
9872
|
+
const content = agentServiceUnit();
|
|
9873
|
+
const run2 = options.run ?? runSystemctl;
|
|
9874
|
+
return options.lock(async () => {
|
|
9875
|
+
if (await readFile3(path, "utf8").catch(() => "") === content)
|
|
9876
|
+
return false;
|
|
9877
|
+
await mkdir2(dirname3(path), { recursive: true, mode: 448 });
|
|
9878
|
+
const staged = `${path}.${process.pid}.tmp`;
|
|
9879
|
+
await writeFile2(staged, content, { mode: 384 });
|
|
9880
|
+
await rename2(staged, path);
|
|
9881
|
+
await run2(["daemon-reload"]);
|
|
9882
|
+
return true;
|
|
9883
|
+
});
|
|
9884
|
+
}
|
|
9847
9885
|
async function serviceStatus(run2, service) {
|
|
9848
9886
|
const status = await run2([
|
|
9849
9887
|
"show",
|
|
@@ -9947,8 +9985,10 @@ async function notify(fields) {
|
|
|
9947
9985
|
async function extendSystemdStartTimeout(ms) {
|
|
9948
9986
|
await notify([`EXTEND_TIMEOUT_USEC=${Math.max(0, Math.round(ms)) * 1e3}`]).catch(() => void 0);
|
|
9949
9987
|
}
|
|
9950
|
-
function startLocalWatchdog(notifier, status, intervalMs = 6e4) {
|
|
9951
|
-
|
|
9988
|
+
function startLocalWatchdog(notifier, status, intervalMs = 6e4, env = process.env) {
|
|
9989
|
+
if (!env.WATCHDOG_USEC)
|
|
9990
|
+
return () => void 0;
|
|
9991
|
+
const timer = setInterval(() => void notifier.progress(status()).catch((error) => console.error("[thin-core] watchdog notification failed:", error)), intervalMs);
|
|
9952
9992
|
timer.unref?.();
|
|
9953
9993
|
return () => clearInterval(timer);
|
|
9954
9994
|
}
|
|
@@ -9990,7 +10030,7 @@ var init_systemd = __esm({
|
|
|
9990
10030
|
// apps/body/dist/launchd.js
|
|
9991
10031
|
import { execFile as execFile3 } from "node:child_process";
|
|
9992
10032
|
import { realpathSync } from "node:fs";
|
|
9993
|
-
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile4, readdir as readdir2, rename as
|
|
10033
|
+
import { chmod as chmod2, mkdir as mkdir3, readFile as readFile4, readdir as readdir2, rename as rename3, rm, stat as stat3, writeFile as writeFile3 } from "node:fs/promises";
|
|
9994
10034
|
import { homedir as homedir4 } from "node:os";
|
|
9995
10035
|
import { dirname as dirname4, resolve as resolve4 } from "node:path";
|
|
9996
10036
|
import { setTimeout as sleep2 } from "node:timers/promises";
|
|
@@ -10158,7 +10198,7 @@ async function writeManagedFile(path, content, mode) {
|
|
|
10158
10198
|
const pending = `${path}.${process.pid}.pending`;
|
|
10159
10199
|
await writeFile3(pending, content, { mode });
|
|
10160
10200
|
await chmod2(pending, mode);
|
|
10161
|
-
await
|
|
10201
|
+
await rename3(pending, path);
|
|
10162
10202
|
return true;
|
|
10163
10203
|
}
|
|
10164
10204
|
function launchctlFailureText(error) {
|
|
@@ -10409,7 +10449,7 @@ __export(self_update_exports, {
|
|
|
10409
10449
|
});
|
|
10410
10450
|
import { createHash } from "node:crypto";
|
|
10411
10451
|
import { constants as fsConstants2 } from "node:fs";
|
|
10412
|
-
import { access, chmod as chmod3, lstat, mkdir as mkdir4, open, readFile as readFile5, rename as
|
|
10452
|
+
import { access, chmod as chmod3, lstat, mkdir as mkdir4, open, readFile as readFile5, rename as rename4, rm as rm2, symlink, writeFile as writeFile4 } from "node:fs/promises";
|
|
10413
10453
|
import { spawn as spawn4 } from "node:child_process";
|
|
10414
10454
|
import { homedir as homedir5 } from "node:os";
|
|
10415
10455
|
import { dirname as dirname5, join as join3, resolve as resolve5 } from "node:path";
|
|
@@ -10712,7 +10752,7 @@ async function replaceFile(path, contents, mode) {
|
|
|
10712
10752
|
const temp = `${path}.new-${process.pid}`;
|
|
10713
10753
|
await writeFile4(temp, contents, { mode });
|
|
10714
10754
|
await chmod3(temp, mode);
|
|
10715
|
-
await
|
|
10755
|
+
await rename4(temp, path);
|
|
10716
10756
|
}
|
|
10717
10757
|
async function activateRelease(layout, releaseId) {
|
|
10718
10758
|
const releaseDir = join3(layout.releasesRoot, releaseId);
|
|
@@ -10728,10 +10768,10 @@ async function activateRelease(layout, releaseId) {
|
|
|
10728
10768
|
try {
|
|
10729
10769
|
await access(legacyDir, fsConstants2.F_OK);
|
|
10730
10770
|
previousReleaseId = `${legacyId}-${Date.now()}`;
|
|
10731
|
-
await
|
|
10771
|
+
await rename4(layout.libDir, join3(layout.releasesRoot, previousReleaseId));
|
|
10732
10772
|
await normalizeLegacyBundleShape(join3(layout.releasesRoot, previousReleaseId));
|
|
10733
10773
|
} catch {
|
|
10734
|
-
await
|
|
10774
|
+
await rename4(layout.libDir, legacyDir);
|
|
10735
10775
|
await normalizeLegacyBundleShape(legacyDir);
|
|
10736
10776
|
previousReleaseId = legacyId;
|
|
10737
10777
|
}
|
|
@@ -10739,7 +10779,7 @@ async function activateRelease(layout, releaseId) {
|
|
|
10739
10779
|
const tempLink = `${layout.libDir}.new-${process.pid}`;
|
|
10740
10780
|
await rm2(tempLink, { force: true });
|
|
10741
10781
|
await symlink(join3("beeline-releases", releaseId), tempLink);
|
|
10742
|
-
await
|
|
10782
|
+
await rename4(tempLink, layout.libDir);
|
|
10743
10783
|
await fsyncDir(dirname5(layout.libDir));
|
|
10744
10784
|
await writeBinForwarders(layout, releaseDir);
|
|
10745
10785
|
return { previousReleaseId };
|
|
@@ -10764,7 +10804,7 @@ async function normalizeLegacyBundleShape(bundleDir) {
|
|
|
10764
10804
|
continue;
|
|
10765
10805
|
} catch {
|
|
10766
10806
|
}
|
|
10767
|
-
await
|
|
10807
|
+
await rename4(join3(bundleDir, name), join3(innerLib, name)).catch(() => void 0);
|
|
10768
10808
|
}
|
|
10769
10809
|
}
|
|
10770
10810
|
async function writeBinForwarders(layout, activeBundleRoot) {
|
|
@@ -10815,7 +10855,7 @@ async function rollbackToPreviousRelease(layout, previousReleaseId) {
|
|
|
10815
10855
|
const tempLink = `${layout.libDir}.rollback-${process.pid}`;
|
|
10816
10856
|
await rm2(tempLink, { force: true });
|
|
10817
10857
|
await symlink(join3("beeline-releases", previousReleaseId), tempLink);
|
|
10818
|
-
await
|
|
10858
|
+
await rename4(tempLink, layout.libDir);
|
|
10819
10859
|
await fsyncDir(dirname5(layout.libDir));
|
|
10820
10860
|
}
|
|
10821
10861
|
function updateAttemptPath(layout) {
|
|
@@ -10838,7 +10878,7 @@ async function writeUpdateAttempt(layout, record6) {
|
|
|
10838
10878
|
const staged = `${path}.${process.pid}.tmp`;
|
|
10839
10879
|
await writeFile4(staged, `${JSON.stringify(record6, null, 2)}
|
|
10840
10880
|
`, { mode: 384 });
|
|
10841
|
-
await
|
|
10881
|
+
await rename4(staged, path);
|
|
10842
10882
|
}
|
|
10843
10883
|
async function replaceUpdateAttempt(layout, record6) {
|
|
10844
10884
|
await writeUpdateAttempt(layout, record6);
|
|
@@ -10925,7 +10965,6 @@ var init_self_update = __esm({
|
|
|
10925
10965
|
fetchImpl;
|
|
10926
10966
|
log;
|
|
10927
10967
|
stageOnly;
|
|
10928
|
-
timer;
|
|
10929
10968
|
nextCheckAllowedAt = 0;
|
|
10930
10969
|
disposed = false;
|
|
10931
10970
|
applying = false;
|
|
@@ -10942,9 +10981,7 @@ var init_self_update = __esm({
|
|
|
10942
10981
|
};
|
|
10943
10982
|
this.options = {
|
|
10944
10983
|
layout: options.layout,
|
|
10945
|
-
tickMs: options.tickMs ?? numberEnv2("BEELINE_UPDATE_TICK_MS", 6e4),
|
|
10946
10984
|
checkIntervalMs: options.checkIntervalMs ?? numberEnv2("BEELINE_UPDATE_INTERVAL_MS", 6 * 60 * 6e4),
|
|
10947
|
-
initialDelayMs: options.initialDelayMs ?? numberEnv2("BEELINE_UPDATE_INITIAL_DELAY_MS", 2 * 6e4),
|
|
10948
10985
|
idleTimeoutMs: options.idleTimeoutMs ?? numberEnv2("BEELINE_UPDATE_IDLE_TIMEOUT_MS", 30 * 6e4),
|
|
10949
10986
|
idlePollMs: options.idlePollMs ?? 5e3,
|
|
10950
10987
|
env,
|
|
@@ -10956,20 +10993,8 @@ var init_self_update = __esm({
|
|
|
10956
10993
|
this.log = options.logger ?? ((line) => console.log(line));
|
|
10957
10994
|
this.stageOnly = options.stageOnly ?? false;
|
|
10958
10995
|
}
|
|
10959
|
-
start() {
|
|
10960
|
-
if (this.timer || this.disposed)
|
|
10961
|
-
return;
|
|
10962
|
-
this.nextCheckAllowedAt = this.options.now() + this.options.initialDelayMs;
|
|
10963
|
-
this.timer = setInterval(() => {
|
|
10964
|
-
void this.tick();
|
|
10965
|
-
}, this.options.tickMs);
|
|
10966
|
-
this.timer.unref?.();
|
|
10967
|
-
}
|
|
10968
10996
|
dispose() {
|
|
10969
10997
|
this.disposed = true;
|
|
10970
|
-
if (this.timer)
|
|
10971
|
-
clearInterval(this.timer);
|
|
10972
|
-
this.timer = void 0;
|
|
10973
10998
|
}
|
|
10974
10999
|
busy() {
|
|
10975
11000
|
return !this.isIdle();
|
|
@@ -11087,6 +11112,11 @@ var init_self_update = __esm({
|
|
|
11087
11112
|
env: this.options.env,
|
|
11088
11113
|
log: this.log
|
|
11089
11114
|
});
|
|
11115
|
+
await convergeAgentServiceUnit({
|
|
11116
|
+
libDir: this.options.layout.libDir,
|
|
11117
|
+
env: this.options.env,
|
|
11118
|
+
log: this.log
|
|
11119
|
+
});
|
|
11090
11120
|
} else if (process.platform === "darwin" && this.options.env.BEELINE_LAUNCHD_USER !== "0") {
|
|
11091
11121
|
await convergeLaunchdTrustySquireBrokerService({
|
|
11092
11122
|
libDir: this.options.layout.libDir,
|
|
@@ -11251,7 +11281,7 @@ var init_cursor_models = __esm({
|
|
|
11251
11281
|
// apps/body/dist/cursor-acp-bridge.js
|
|
11252
11282
|
import { spawn as spawn6 } from "node:child_process";
|
|
11253
11283
|
import { existsSync as existsSync3 } from "node:fs";
|
|
11254
|
-
import { chmod as chmod4, lstat as lstat2, mkdir as mkdir6, rename as
|
|
11284
|
+
import { chmod as chmod4, lstat as lstat2, mkdir as mkdir6, rename as rename5, unlink as unlink3, writeFile as writeFile5 } from "node:fs/promises";
|
|
11255
11285
|
import { createRequire as createRequire3 } from "node:module";
|
|
11256
11286
|
import { createInterface as createInterface3 } from "node:readline";
|
|
11257
11287
|
import { basename, dirname as dirname7, resolve as resolvePath } from "node:path";
|
|
@@ -11324,7 +11354,7 @@ async function writeIsolatedCursorFile(path, content) {
|
|
|
11324
11354
|
try {
|
|
11325
11355
|
await writeFile5(temporary, content, { mode: 384, flag: "wx" });
|
|
11326
11356
|
await chmod4(temporary, 384);
|
|
11327
|
-
await
|
|
11357
|
+
await rename5(temporary, path);
|
|
11328
11358
|
} finally {
|
|
11329
11359
|
await unlink3(temporary).catch(() => void 0);
|
|
11330
11360
|
}
|
|
@@ -26668,6 +26698,11 @@ var ARTIFACT_EXTENSIONS_BY_MIME = {
|
|
|
26668
26698
|
"application/json": [".json"],
|
|
26669
26699
|
"text/csv": [".csv"],
|
|
26670
26700
|
"application/zip": [".zip"],
|
|
26701
|
+
"video/mp4": [".mp4", ".m4v"],
|
|
26702
|
+
"video/quicktime": [".mov"],
|
|
26703
|
+
"video/webm": [".webm"],
|
|
26704
|
+
"audio/mpeg": [".mp3"],
|
|
26705
|
+
"audio/wav": [".wav"],
|
|
26671
26706
|
"application/octet-stream": []
|
|
26672
26707
|
};
|
|
26673
26708
|
var ARTIFACT_MIME_BY_EXTENSION = Object.fromEntries(Object.entries(ARTIFACT_EXTENSIONS_BY_MIME).flatMap(([mime, extensions]) => extensions.map((extension2) => [extension2, mime])));
|
|
@@ -27787,6 +27822,72 @@ function matchProfile(agentCommand) {
|
|
|
27787
27822
|
return void 0;
|
|
27788
27823
|
}
|
|
27789
27824
|
|
|
27825
|
+
// apps/body/dist/oom-kill.js
|
|
27826
|
+
import { readFile as readFile9 } from "node:fs/promises";
|
|
27827
|
+
import { join as join5 } from "node:path";
|
|
27828
|
+
var CGROUP_V2_ROOT = "/sys/fs/cgroup";
|
|
27829
|
+
function parseOomKillCount(contents) {
|
|
27830
|
+
for (const line of contents.split("\n")) {
|
|
27831
|
+
const [key, raw] = line.trim().split(/\s+/, 2);
|
|
27832
|
+
if (key !== "oom_kill")
|
|
27833
|
+
continue;
|
|
27834
|
+
const value = Number(raw);
|
|
27835
|
+
return Number.isSafeInteger(value) && value >= 0 ? value : null;
|
|
27836
|
+
}
|
|
27837
|
+
return null;
|
|
27838
|
+
}
|
|
27839
|
+
function unifiedCgroupPath(procSelfCgroup) {
|
|
27840
|
+
const line = procSelfCgroup.split("\n").map((value) => value.trim()).find((value) => value.startsWith("0::"));
|
|
27841
|
+
const path = line?.slice(3).trim();
|
|
27842
|
+
return path && path.startsWith("/") ? path : null;
|
|
27843
|
+
}
|
|
27844
|
+
function cgroupOomKillProbe(options = {}) {
|
|
27845
|
+
const readProc = options.procSelfCgroup ?? (() => readFile9("/proc/self/cgroup", "utf8"));
|
|
27846
|
+
const root = options.cgroupRoot ?? CGROUP_V2_ROOT;
|
|
27847
|
+
let directory;
|
|
27848
|
+
return {
|
|
27849
|
+
async read() {
|
|
27850
|
+
try {
|
|
27851
|
+
if (directory === void 0) {
|
|
27852
|
+
const path = unifiedCgroupPath(await readProc());
|
|
27853
|
+
directory = path ? join5(root, path) : null;
|
|
27854
|
+
}
|
|
27855
|
+
if (!directory)
|
|
27856
|
+
return null;
|
|
27857
|
+
return parseOomKillCount(await readFile9(join5(directory, "memory.events"), "utf8"));
|
|
27858
|
+
} catch {
|
|
27859
|
+
return null;
|
|
27860
|
+
}
|
|
27861
|
+
}
|
|
27862
|
+
};
|
|
27863
|
+
}
|
|
27864
|
+
var OomKillTracker = class {
|
|
27865
|
+
probe;
|
|
27866
|
+
#last = null;
|
|
27867
|
+
#banked = 0;
|
|
27868
|
+
constructor(probe) {
|
|
27869
|
+
this.probe = probe;
|
|
27870
|
+
}
|
|
27871
|
+
/** Record the current count so the next kill is measured against it. */
|
|
27872
|
+
async prime() {
|
|
27873
|
+
const current = await this.probe.read();
|
|
27874
|
+
if (current !== null)
|
|
27875
|
+
this.#last = current;
|
|
27876
|
+
}
|
|
27877
|
+
async consume() {
|
|
27878
|
+
const current = await this.probe.read();
|
|
27879
|
+
if (current === null)
|
|
27880
|
+
return false;
|
|
27881
|
+
if (this.#last !== null && current > this.#last)
|
|
27882
|
+
this.#banked += current - this.#last;
|
|
27883
|
+
this.#last = current;
|
|
27884
|
+
if (this.#banked === 0)
|
|
27885
|
+
return false;
|
|
27886
|
+
this.#banked -= 1;
|
|
27887
|
+
return true;
|
|
27888
|
+
}
|
|
27889
|
+
};
|
|
27890
|
+
|
|
27790
27891
|
// apps/body/dist/acp.js
|
|
27791
27892
|
var STDERR_TAIL_MAX_CHARS2 = 2e3;
|
|
27792
27893
|
function braceDelta(line) {
|
|
@@ -28122,6 +28223,9 @@ var AcpClient = class extends EventEmitter {
|
|
|
28122
28223
|
permissionHandler;
|
|
28123
28224
|
permissionAllowlist;
|
|
28124
28225
|
commandsHandler;
|
|
28226
|
+
oomKills;
|
|
28227
|
+
/** A deliberate stop owns its SIGKILL; that exit is never labelled OOM. */
|
|
28228
|
+
stopping = false;
|
|
28125
28229
|
constructor(opts) {
|
|
28126
28230
|
super();
|
|
28127
28231
|
const command = opts.agentCommand ?? opts.agentBinary;
|
|
@@ -28139,10 +28243,13 @@ var AcpClient = class extends EventEmitter {
|
|
|
28139
28243
|
this.permissionHandler = opts.permissionHandler;
|
|
28140
28244
|
this.permissionAllowlist = opts.permissionAllowlist;
|
|
28141
28245
|
this.commandsHandler = opts.onCommands;
|
|
28246
|
+
this.oomKills = opts.oomKills ?? new OomKillTracker(cgroupOomKillProbe());
|
|
28142
28247
|
}
|
|
28143
28248
|
async start(timeoutMs = 6e4) {
|
|
28144
28249
|
if (this.alive)
|
|
28145
28250
|
return;
|
|
28251
|
+
await this.oomKills.prime().catch(() => void 0);
|
|
28252
|
+
this.stopping = false;
|
|
28146
28253
|
this.child = await withAdapterInstallLock(() => spawn10(this.agentCommand, this.agentArgs, {
|
|
28147
28254
|
// agentEnv is the child's whole environment: buildAgentEnv's allowlist is
|
|
28148
28255
|
// a real boundary, not a decorative one layered over a full inherit.
|
|
@@ -28164,19 +28271,7 @@ var AcpClient = class extends EventEmitter {
|
|
|
28164
28271
|
this.child.stdout.setEncoding("utf8");
|
|
28165
28272
|
this.child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
28166
28273
|
this.child.on("exit", (code, signal) => {
|
|
28167
|
-
this.
|
|
28168
|
-
const meaningful = flattenHarnessCause(this.stderrTail);
|
|
28169
|
-
const stderrSuffix = meaningful ? `: ${meaningful}` : "";
|
|
28170
|
-
for (const [, p] of this.pending) {
|
|
28171
|
-
this.clearTimer(p);
|
|
28172
|
-
p.reject(new Error(`ACP agent ${this.agentLabel} exited code=${code} signal=${signal}${stderrSuffix}`));
|
|
28173
|
-
}
|
|
28174
|
-
this.pending.clear();
|
|
28175
|
-
this.activeRunIds.clear();
|
|
28176
|
-
this.activePromptSessions.clear();
|
|
28177
|
-
this.toolCallMetadata.clear();
|
|
28178
|
-
this.sessionCommands.clear();
|
|
28179
|
-
this.emit("exit", { code, signal });
|
|
28274
|
+
void this.handleChildExit(code, signal);
|
|
28180
28275
|
});
|
|
28181
28276
|
this.child.on("error", (err) => {
|
|
28182
28277
|
this.alive = false;
|
|
@@ -28202,12 +28297,36 @@ var AcpClient = class extends EventEmitter {
|
|
|
28202
28297
|
if (!isPiAcpHarness(this.agentLabel))
|
|
28203
28298
|
this.notify("notifications/initialized", {});
|
|
28204
28299
|
}
|
|
28300
|
+
/**
|
|
28301
|
+
* One child exit: reject every in-flight request so the turn fails and is
|
|
28302
|
+
* reported instead of hanging, then publish the exit. A SIGKILL is checked
|
|
28303
|
+
* against the daemon cgroup's OOM counter so the rejection can name the real
|
|
28304
|
+
* cause instead of a bare signal.
|
|
28305
|
+
*/
|
|
28306
|
+
async handleChildExit(code, signal) {
|
|
28307
|
+
this.alive = false;
|
|
28308
|
+
const oomKilled = signal === "SIGKILL" && !this.stopping && await this.oomKills.consume().catch(() => false);
|
|
28309
|
+
const exitText = oomKilled ? `ACP agent ${this.agentLabel} was killed by the kernel OOM killer (signal=SIGKILL)` : `ACP agent ${this.agentLabel} exited code=${code} signal=${signal}`;
|
|
28310
|
+
const meaningful = flattenHarnessCause(this.stderrTail);
|
|
28311
|
+
const stderrSuffix = meaningful ? `: ${meaningful}` : "";
|
|
28312
|
+
for (const [, p] of this.pending) {
|
|
28313
|
+
this.clearTimer(p);
|
|
28314
|
+
p.reject(new Error(`${exitText}${stderrSuffix}`));
|
|
28315
|
+
}
|
|
28316
|
+
this.pending.clear();
|
|
28317
|
+
this.activeRunIds.clear();
|
|
28318
|
+
this.activePromptSessions.clear();
|
|
28319
|
+
this.toolCallMetadata.clear();
|
|
28320
|
+
this.sessionCommands.clear();
|
|
28321
|
+
this.emit("exit", { code, signal });
|
|
28322
|
+
}
|
|
28205
28323
|
async stop() {
|
|
28206
28324
|
if (!this.alive)
|
|
28207
28325
|
return;
|
|
28208
28326
|
const child = this.child;
|
|
28209
28327
|
if (!child)
|
|
28210
28328
|
return;
|
|
28329
|
+
this.stopping = true;
|
|
28211
28330
|
try {
|
|
28212
28331
|
if (this.child?.stdin.writable) {
|
|
28213
28332
|
this.notify("shutdown", {});
|
|
@@ -28852,7 +28971,7 @@ async function applyRuntimeModelPreflight(config, agent, selection, validate = v
|
|
|
28852
28971
|
|
|
28853
28972
|
// apps/body/dist/model-catalog-sync.js
|
|
28854
28973
|
import { createHash as createHash3 } from "node:crypto";
|
|
28855
|
-
import { readFile as
|
|
28974
|
+
import { readFile as readFile10, writeFile as writeFile6 } from "node:fs/promises";
|
|
28856
28975
|
import { resolve as resolve11 } from "node:path";
|
|
28857
28976
|
var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
|
|
28858
28977
|
var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
|
|
@@ -28890,7 +29009,7 @@ async function syncAgentModelCatalog(input) {
|
|
|
28890
29009
|
const { catalog } = await withTimeout(fetchCatalog(input.agent, input.agentEnv, input.startupUnavailable ? void 0 : selection), input.timeoutMs ?? MODEL_CATALOG_PROBE_TIMEOUT_MS, "model catalog probe");
|
|
28891
29010
|
const effectiveCatalog = input.startupUnavailable ? catalog : withEffectiveCurrentValues(catalog, selection);
|
|
28892
29011
|
const hash = modelCatalogHash(effectiveCatalog, selection, input.startupUnavailable);
|
|
28893
|
-
const previous = await
|
|
29012
|
+
const previous = await readFile10(hashPath, "utf8").catch(() => "");
|
|
28894
29013
|
if (!input.force && previous.trim() === hash)
|
|
28895
29014
|
return "unchanged";
|
|
28896
29015
|
await input.api.execute("postAgentModelCatalog", {
|
|
@@ -28917,7 +29036,7 @@ import { execFile as execFile6 } from "node:child_process";
|
|
|
28917
29036
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
28918
29037
|
import { rm as rm4 } from "node:fs/promises";
|
|
28919
29038
|
import { tmpdir as tmpdir2, userInfo } from "node:os";
|
|
28920
|
-
import { join as
|
|
29039
|
+
import { join as join6 } from "node:path";
|
|
28921
29040
|
var COMMAND_TIMEOUT_MS = 2 * 6e4;
|
|
28922
29041
|
var OUTPUT_LIMIT = 32 * 1024;
|
|
28923
29042
|
var LOGIN_URL = /https:\/\/login\.tailscale\.com\/[A-Za-z0-9_/-]+/u;
|
|
@@ -29004,7 +29123,7 @@ async function ensureInstalled(run2, onProgress, platform) {
|
|
|
29004
29123
|
}
|
|
29005
29124
|
};
|
|
29006
29125
|
}
|
|
29007
|
-
const script =
|
|
29126
|
+
const script = join6(tmpdir2(), `beeline-tailscale-install-${process.pid}-${randomUUID4()}.sh`);
|
|
29008
29127
|
const steps2 = [
|
|
29009
29128
|
step2("Tailscale installed", "running", {
|
|
29010
29129
|
command: "curl -fsSL https://tailscale.com/install.sh"
|
|
@@ -29127,7 +29246,7 @@ import { createRequire as createRequire4 } from "node:module";
|
|
|
29127
29246
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync4, rmSync, writeFileSync } from "node:fs";
|
|
29128
29247
|
import { createConnection as createConnection3, createServer as createServer2, isIPv4, isIPv6 } from "node:net";
|
|
29129
29248
|
import { homedir as homedir10 } from "node:os";
|
|
29130
|
-
import { dirname as dirname9, join as
|
|
29249
|
+
import { dirname as dirname9, join as join7 } from "node:path";
|
|
29131
29250
|
import { createInterface as createInterface5 } from "node:readline";
|
|
29132
29251
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
29133
29252
|
|
|
@@ -29262,12 +29381,12 @@ async function assertNotOwnMachine(url, remoteIsOwnMachine, resolveHost) {
|
|
|
29262
29381
|
return url;
|
|
29263
29382
|
}
|
|
29264
29383
|
function registryMcpStateRoot(home = homedir10()) {
|
|
29265
|
-
return
|
|
29384
|
+
return join7(home, ".beeline", "registry-mcp");
|
|
29266
29385
|
}
|
|
29267
29386
|
function registryMcpStatePath(connectorId, home = homedir10()) {
|
|
29268
29387
|
if (!/^[0-9a-f-]{20,}$/i.test(connectorId))
|
|
29269
29388
|
throw new Error("Registry connector id is invalid");
|
|
29270
|
-
return
|
|
29389
|
+
return join7(registryMcpStateRoot(home), `${connectorId}.json`);
|
|
29271
29390
|
}
|
|
29272
29391
|
function readState(path) {
|
|
29273
29392
|
try {
|
|
@@ -29653,7 +29772,7 @@ function jsonRpcToolName(line) {
|
|
|
29653
29772
|
}
|
|
29654
29773
|
}
|
|
29655
29774
|
function brokerSocketPath(home = homedir10()) {
|
|
29656
|
-
return
|
|
29775
|
+
return join7(home, ".beeline", "registry-mcp-broker", `${process.pid}.sock`);
|
|
29657
29776
|
}
|
|
29658
29777
|
var RegistryMcpHostBroker = class {
|
|
29659
29778
|
home;
|
|
@@ -30218,13 +30337,13 @@ function describe(error) {
|
|
|
30218
30337
|
}
|
|
30219
30338
|
|
|
30220
30339
|
// apps/body/dist/institutional-memory-shadow-worker.js
|
|
30221
|
-
import { mkdtemp as mkdtemp2, readFile as
|
|
30340
|
+
import { mkdtemp as mkdtemp2, readFile as readFile11, rm as rm5 } from "node:fs/promises";
|
|
30222
30341
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
30223
|
-
import { join as
|
|
30342
|
+
import { join as join9, resolve as resolve12 } from "node:path";
|
|
30224
30343
|
|
|
30225
30344
|
// apps/body/dist/attachment-delivery.js
|
|
30226
30345
|
import { mkdir as mkdir7, writeFile as writeFile7 } from "node:fs/promises";
|
|
30227
|
-
import { basename as basename4, extname, join as
|
|
30346
|
+
import { basename as basename4, extname, join as join8 } from "node:path";
|
|
30228
30347
|
var MAX_ATTACHMENT_BYTES = 25 * 1024 * 1024;
|
|
30229
30348
|
var EXPIRED_REASON = "expired: these attachment bytes are past their retention window";
|
|
30230
30349
|
var FETCH_TIMEOUT_MS = 3e4;
|
|
@@ -30283,7 +30402,7 @@ async function deliverAttachments(attachments, dir, fetchImpl = fetch) {
|
|
|
30283
30402
|
return { attachment, reason: EXPIRED_REASON };
|
|
30284
30403
|
if (!fetched.ok)
|
|
30285
30404
|
throw new Error(`HTTP ${fetched.status}`);
|
|
30286
|
-
const path =
|
|
30405
|
+
const path = join8(dir, safeFileName(attachment, index, taken));
|
|
30287
30406
|
await writeFile7(path, fetched.bytes);
|
|
30288
30407
|
const bytes = fetched.bytes;
|
|
30289
30408
|
const mimeType = attachment.mimeType ?? fetched.mimeType;
|
|
@@ -30422,7 +30541,7 @@ async function readInstitutionalMemoryAttachments(job, dir, acceptsImages, fetch
|
|
|
30422
30541
|
for (const message of job.messages) {
|
|
30423
30542
|
if (!message.attachments?.length)
|
|
30424
30543
|
continue;
|
|
30425
|
-
const delivered = await deliverAttachments(message.attachments,
|
|
30544
|
+
const delivered = await deliverAttachments(message.attachments, join9(dir, message.id), fetchImpl);
|
|
30426
30545
|
for (const entry of delivered) {
|
|
30427
30546
|
const { attachment } = entry;
|
|
30428
30547
|
const mimeType = attachment.mimeType ?? entry.image?.mimeType;
|
|
@@ -30447,7 +30566,7 @@ async function readInstitutionalMemoryAttachments(job, dir, acceptsImages, fetch
|
|
|
30447
30566
|
evidence.push({ ...base, notRead: "the attachment text budget is used up" });
|
|
30448
30567
|
continue;
|
|
30449
30568
|
}
|
|
30450
|
-
const text2 = clipText(await
|
|
30569
|
+
const text2 = clipText(await readFile11(entry.path, "utf8"), textBudget);
|
|
30451
30570
|
textBudget -= Buffer.byteLength(text2, "utf8");
|
|
30452
30571
|
evidence.push({ ...base, text: text2 });
|
|
30453
30572
|
} else {
|
|
@@ -30561,7 +30680,7 @@ async function extractInstitutionalMemoryShadowJob(job, options, signal) {
|
|
|
30561
30680
|
if (options.modelSelection) {
|
|
30562
30681
|
await applyAgentModelSelection(client, opened.sessionId, parseAdvertisedConfigOptions(opened.raw, options.modelSelection.model), options.modelSelection);
|
|
30563
30682
|
}
|
|
30564
|
-
const attachments = await readInstitutionalMemoryAttachments(job,
|
|
30683
|
+
const attachments = await readInstitutionalMemoryAttachments(job, join9(scratch, "attachments"), client.canPromptWithImages(), options.fetch);
|
|
30565
30684
|
prompt = institutionalMemoryExtractionPrompt(job, attachments.evidence);
|
|
30566
30685
|
const result = await client.sessionPrompt(opened.sessionId, promptWithImages(prompt, attachments.images));
|
|
30567
30686
|
const proposal = parseExtractionText(result.agentText, job.triggerKind);
|
|
@@ -30704,9 +30823,9 @@ function helperVersionHeader(value) {
|
|
|
30704
30823
|
|
|
30705
30824
|
// apps/body/dist/host-read-budget.js
|
|
30706
30825
|
import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
|
|
30707
|
-
import { mkdir as mkdir8, open as open3, readFile as
|
|
30826
|
+
import { mkdir as mkdir8, open as open3, readFile as readFile12, readdir as readdir3, stat as stat5, unlink as unlink4, writeFile as writeFile8 } from "node:fs/promises";
|
|
30708
30827
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
30709
|
-
import { join as
|
|
30828
|
+
import { join as join10 } from "node:path";
|
|
30710
30829
|
var READ_SLOTS = 6;
|
|
30711
30830
|
var BACKGROUND_SLOTS = 4;
|
|
30712
30831
|
var MAX_WAITERS = 256;
|
|
@@ -30737,7 +30856,7 @@ var HostReadBudget = class {
|
|
|
30737
30856
|
const uid = process.getuid?.() ?? "unknown";
|
|
30738
30857
|
const origin = new URL(baseUrl).origin;
|
|
30739
30858
|
const key = createHash5("sha256").update(origin).digest("hex").slice(0, 16);
|
|
30740
|
-
this.directory =
|
|
30859
|
+
this.directory = join10(root, `beeline-read-budget-${uid}-${key}`);
|
|
30741
30860
|
}
|
|
30742
30861
|
metrics() {
|
|
30743
30862
|
return { waiting: this.waiting, active: this.active, totalWaitMs: this.totalWaitMs };
|
|
@@ -30745,7 +30864,7 @@ var HostReadBudget = class {
|
|
|
30745
30864
|
async acquire(priority, deadlineAt) {
|
|
30746
30865
|
await mkdir8(this.directory, { recursive: true, mode: 448 });
|
|
30747
30866
|
const ticket = `wait-${priority === "urgent" ? "0" : "1"}-${String(Date.now()).padStart(13, "0")}-${randomUUID5()}`;
|
|
30748
|
-
const ticketPath =
|
|
30867
|
+
const ticketPath = join10(this.directory, ticket);
|
|
30749
30868
|
const tickets = (await readdir3(this.directory)).filter((name) => name.startsWith("wait-"));
|
|
30750
30869
|
if (tickets.length >= MAX_WAITERS)
|
|
30751
30870
|
throw new ReadBudgetFullError();
|
|
@@ -30757,14 +30876,14 @@ var HostReadBudget = class {
|
|
|
30757
30876
|
const queue = (await readdir3(this.directory)).filter((name) => name.startsWith("wait-")).sort();
|
|
30758
30877
|
for (const name of queue.slice(0, READ_SLOTS)) {
|
|
30759
30878
|
if (name !== ticket)
|
|
30760
|
-
await this.reapStaleTicket(
|
|
30879
|
+
await this.reapStaleTicket(join10(this.directory, name));
|
|
30761
30880
|
}
|
|
30762
30881
|
const liveQueue = (await readdir3(this.directory)).filter((name) => name.startsWith("wait-")).sort();
|
|
30763
30882
|
const rank = liveQueue.indexOf(ticket);
|
|
30764
30883
|
const available = priority === "urgent" ? READ_SLOTS : BACKGROUND_SLOTS;
|
|
30765
30884
|
if (rank >= 0 && rank < available) {
|
|
30766
30885
|
for (let slot = 0; slot < available; slot += 1) {
|
|
30767
|
-
const slotPath =
|
|
30886
|
+
const slotPath = join10(this.directory, `slot-${slot}`);
|
|
30768
30887
|
try {
|
|
30769
30888
|
const handle2 = await open3(slotPath, "wx", 384);
|
|
30770
30889
|
try {
|
|
@@ -30814,7 +30933,7 @@ var HostReadBudget = class {
|
|
|
30814
30933
|
const details = await stat5(path).catch(() => void 0);
|
|
30815
30934
|
if (!details || Date.now() - details.mtimeMs < 2e3)
|
|
30816
30935
|
return;
|
|
30817
|
-
const owner = await
|
|
30936
|
+
const owner = await readFile12(path, "utf8").then((value) => {
|
|
30818
30937
|
try {
|
|
30819
30938
|
return JSON.parse(value);
|
|
30820
30939
|
} catch {
|
|
@@ -30829,13 +30948,13 @@ var HostReadBudget = class {
|
|
|
30829
30948
|
const details = await stat5(path).catch(() => void 0);
|
|
30830
30949
|
if (!details || Date.now() - details.mtimeMs < 2e3)
|
|
30831
30950
|
return;
|
|
30832
|
-
const pid = Number(await
|
|
30951
|
+
const pid = Number(await readFile12(path, "utf8").catch(() => "0"));
|
|
30833
30952
|
if (pid > 0 && ownerAlive(pid) && Date.now() - details.mtimeMs < STALE_SLOT_MS)
|
|
30834
30953
|
return;
|
|
30835
30954
|
await unlink4(path).catch(() => void 0);
|
|
30836
30955
|
}
|
|
30837
30956
|
async takeRateToken(deadlineAt) {
|
|
30838
|
-
const lock =
|
|
30957
|
+
const lock = join10(this.directory, "rate.lock");
|
|
30839
30958
|
let handle2;
|
|
30840
30959
|
try {
|
|
30841
30960
|
handle2 = await open3(lock, "wx", 384);
|
|
@@ -30846,8 +30965,8 @@ var HostReadBudget = class {
|
|
|
30846
30965
|
return false;
|
|
30847
30966
|
}
|
|
30848
30967
|
try {
|
|
30849
|
-
const path =
|
|
30850
|
-
const next = Number(await
|
|
30968
|
+
const path = join10(this.directory, "next-admission");
|
|
30969
|
+
const next = Number(await readFile12(path, "utf8").catch(() => "0")) || 0;
|
|
30851
30970
|
const now2 = Date.now();
|
|
30852
30971
|
if (next > now2 || now2 >= deadlineAt)
|
|
30853
30972
|
return false;
|
|
@@ -30860,6 +30979,251 @@ var HostReadBudget = class {
|
|
|
30860
30979
|
}
|
|
30861
30980
|
};
|
|
30862
30981
|
|
|
30982
|
+
// apps/body/dist/live-link.js
|
|
30983
|
+
var LIVE_LINK_TIMING = Object.freeze({
|
|
30984
|
+
handshakeTimeoutMs: 3e4,
|
|
30985
|
+
pongTimeoutMs: 15e3,
|
|
30986
|
+
backoffBaseMs: 1e3,
|
|
30987
|
+
backoffMaxMs: 3e4,
|
|
30988
|
+
stableSocketMs: 3e4,
|
|
30989
|
+
stableCloseJitterMs: 1e4,
|
|
30990
|
+
keepAliveDelayMs: 5 * 6e4
|
|
30991
|
+
});
|
|
30992
|
+
var NETWORK_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
30993
|
+
"ECONNRESET",
|
|
30994
|
+
"ECONNREFUSED",
|
|
30995
|
+
"ENOTFOUND",
|
|
30996
|
+
"EAI_AGAIN",
|
|
30997
|
+
"ETIMEDOUT",
|
|
30998
|
+
"EHOSTUNREACH",
|
|
30999
|
+
"ENETUNREACH",
|
|
31000
|
+
"EPIPE",
|
|
31001
|
+
"ECONNABORTED"
|
|
31002
|
+
]);
|
|
31003
|
+
function isNetworkFailure(error) {
|
|
31004
|
+
const seen = /* @__PURE__ */ new Set();
|
|
31005
|
+
const visit = (value) => {
|
|
31006
|
+
if (!value || typeof value !== "object" || seen.has(value))
|
|
31007
|
+
return false;
|
|
31008
|
+
seen.add(value);
|
|
31009
|
+
const code = value.code;
|
|
31010
|
+
if (typeof code === "string" && (NETWORK_ERROR_CODES.has(code) || code.startsWith("UND_ERR_")))
|
|
31011
|
+
return true;
|
|
31012
|
+
if (value instanceof AggregateError && value.errors.length > 0 && value.errors.every(visit))
|
|
31013
|
+
return true;
|
|
31014
|
+
return visit(value.cause);
|
|
31015
|
+
};
|
|
31016
|
+
return visit(error);
|
|
31017
|
+
}
|
|
31018
|
+
function retryAfterMs(value) {
|
|
31019
|
+
if (!value)
|
|
31020
|
+
return void 0;
|
|
31021
|
+
const seconds = Number(value);
|
|
31022
|
+
const delay = Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : Date.parse(value) - Date.now();
|
|
31023
|
+
return Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 6e4) : void 0;
|
|
31024
|
+
}
|
|
31025
|
+
var LiveLink = class {
|
|
31026
|
+
options;
|
|
31027
|
+
#state = "disconnected";
|
|
31028
|
+
#socket;
|
|
31029
|
+
#openedAt;
|
|
31030
|
+
#failedConnects = 0;
|
|
31031
|
+
#retryAfterMs = 0;
|
|
31032
|
+
#reconnect;
|
|
31033
|
+
#pongDeadline;
|
|
31034
|
+
#wanted = false;
|
|
31035
|
+
#updateAnnounced = false;
|
|
31036
|
+
#reconnects = 0;
|
|
31037
|
+
#openListeners = /* @__PURE__ */ new Set();
|
|
31038
|
+
#timing;
|
|
31039
|
+
#random;
|
|
31040
|
+
#now;
|
|
31041
|
+
constructor(options) {
|
|
31042
|
+
this.options = options;
|
|
31043
|
+
this.#timing = { ...LIVE_LINK_TIMING, ...options.timing };
|
|
31044
|
+
this.#random = options.random ?? Math.random;
|
|
31045
|
+
this.#now = options.now ?? Date.now;
|
|
31046
|
+
}
|
|
31047
|
+
get state() {
|
|
31048
|
+
return this.#state;
|
|
31049
|
+
}
|
|
31050
|
+
get reconnects() {
|
|
31051
|
+
return this.#reconnects;
|
|
31052
|
+
}
|
|
31053
|
+
/** A suspect socket is still open for writes until its pong deadline. */
|
|
31054
|
+
isOpen() {
|
|
31055
|
+
return this.#state === "open" || this.#state === "suspect";
|
|
31056
|
+
}
|
|
31057
|
+
/** Called on every open, after the link's own open handling. */
|
|
31058
|
+
onOpen(listener) {
|
|
31059
|
+
this.#openListeners.add(listener);
|
|
31060
|
+
return () => this.#openListeners.delete(listener);
|
|
31061
|
+
}
|
|
31062
|
+
start() {
|
|
31063
|
+
if (this.#state === "update-required")
|
|
31064
|
+
return;
|
|
31065
|
+
this.#wanted = true;
|
|
31066
|
+
if (!this.#socket && !this.#reconnect)
|
|
31067
|
+
this.#connect();
|
|
31068
|
+
}
|
|
31069
|
+
stop() {
|
|
31070
|
+
this.#wanted = false;
|
|
31071
|
+
this.#clearTimers();
|
|
31072
|
+
const socket = this.#socket;
|
|
31073
|
+
this.#socket = void 0;
|
|
31074
|
+
if (this.#state !== "update-required")
|
|
31075
|
+
this.#state = "disconnected";
|
|
31076
|
+
socket?.close();
|
|
31077
|
+
}
|
|
31078
|
+
send(data) {
|
|
31079
|
+
if (!this.isOpen() || this.#socket?.readyState !== wrapper_default.OPEN)
|
|
31080
|
+
return false;
|
|
31081
|
+
this.#socket.send(data);
|
|
31082
|
+
return true;
|
|
31083
|
+
}
|
|
31084
|
+
/**
|
|
31085
|
+
* Something suggests the socket may be dead (a network-class HTTP failure,
|
|
31086
|
+
* an admitted fetch past its deadline, a failed reconcile). Ask the socket
|
|
31087
|
+
* itself: one ping, and no pong within the deadline drops it. A socket that
|
|
31088
|
+
* is not open is already on its way to a new one.
|
|
31089
|
+
*/
|
|
31090
|
+
suspect() {
|
|
31091
|
+
const socket = this.#socket;
|
|
31092
|
+
if (this.#state !== "open" || !socket)
|
|
31093
|
+
return;
|
|
31094
|
+
this.#state = "suspect";
|
|
31095
|
+
try {
|
|
31096
|
+
socket.ping?.();
|
|
31097
|
+
} catch {
|
|
31098
|
+
}
|
|
31099
|
+
this.#pongDeadline = setTimeout(() => {
|
|
31100
|
+
this.#pongDeadline = void 0;
|
|
31101
|
+
if (this.#socket === socket && this.#state === "suspect")
|
|
31102
|
+
socket.terminate();
|
|
31103
|
+
}, this.#timing.pongTimeoutMs);
|
|
31104
|
+
this.#pongDeadline.unref?.();
|
|
31105
|
+
}
|
|
31106
|
+
/** The server requires a newer helper: stop for good and say so once. */
|
|
31107
|
+
requireUpdate(minVersion) {
|
|
31108
|
+
this.#markUpdateRequired();
|
|
31109
|
+
if (this.#updateAnnounced)
|
|
31110
|
+
return;
|
|
31111
|
+
this.#updateAnnounced = true;
|
|
31112
|
+
this.options.onUpdateRequired(minVersion);
|
|
31113
|
+
}
|
|
31114
|
+
#markUpdateRequired() {
|
|
31115
|
+
this.#state = "update-required";
|
|
31116
|
+
this.#clearTimers();
|
|
31117
|
+
const socket = this.#socket;
|
|
31118
|
+
this.#socket = void 0;
|
|
31119
|
+
socket?.close();
|
|
31120
|
+
}
|
|
31121
|
+
#clearTimers() {
|
|
31122
|
+
clearTimeout(this.#reconnect);
|
|
31123
|
+
this.#reconnect = void 0;
|
|
31124
|
+
clearTimeout(this.#pongDeadline);
|
|
31125
|
+
this.#pongDeadline = void 0;
|
|
31126
|
+
}
|
|
31127
|
+
#connect() {
|
|
31128
|
+
const socket = this.options.factory(this.options.url(), this.options.protocols(), {
|
|
31129
|
+
handshakeTimeout: this.#timing.handshakeTimeoutMs
|
|
31130
|
+
});
|
|
31131
|
+
this.#socket = socket;
|
|
31132
|
+
this.#state = "connecting";
|
|
31133
|
+
this.#openedAt = void 0;
|
|
31134
|
+
socket.on?.("upgrade", (response) => {
|
|
31135
|
+
response.socket?.setKeepAlive?.(true, this.#timing.keepAliveDelayMs);
|
|
31136
|
+
});
|
|
31137
|
+
socket.on?.("unexpected-response", (_request, response) => {
|
|
31138
|
+
if (response.statusCode !== 426) {
|
|
31139
|
+
this.#retryAfterMs = retryAfterMs(headerValue(response.headers?.["retry-after"])) ?? 0;
|
|
31140
|
+
socket.terminate();
|
|
31141
|
+
return;
|
|
31142
|
+
}
|
|
31143
|
+
this.#state = "update-required";
|
|
31144
|
+
this.#clearTimers();
|
|
31145
|
+
let body = "";
|
|
31146
|
+
response.on("data", (chunk) => {
|
|
31147
|
+
if (body.length < 1024)
|
|
31148
|
+
body += chunk.toString("utf8").slice(0, 1024 - body.length);
|
|
31149
|
+
});
|
|
31150
|
+
response.on("end", () => {
|
|
31151
|
+
try {
|
|
31152
|
+
const refusal = JSON.parse(body);
|
|
31153
|
+
if (refusal.error === "update_required" && typeof refusal.minVersion === "string")
|
|
31154
|
+
this.requireUpdate(refusal.minVersion);
|
|
31155
|
+
} catch {
|
|
31156
|
+
}
|
|
31157
|
+
});
|
|
31158
|
+
response.on("close", () => socket.terminate());
|
|
31159
|
+
});
|
|
31160
|
+
socket.on?.("pong", () => {
|
|
31161
|
+
if (this.#socket !== socket || this.#state !== "suspect")
|
|
31162
|
+
return;
|
|
31163
|
+
clearTimeout(this.#pongDeadline);
|
|
31164
|
+
this.#pongDeadline = void 0;
|
|
31165
|
+
this.#state = "open";
|
|
31166
|
+
});
|
|
31167
|
+
socket.onopen = () => {
|
|
31168
|
+
if (this.#socket !== socket)
|
|
31169
|
+
return;
|
|
31170
|
+
this.#state = "open";
|
|
31171
|
+
this.#openedAt = this.#now();
|
|
31172
|
+
this.options.onOpen();
|
|
31173
|
+
for (const listener of [...this.#openListeners]) {
|
|
31174
|
+
try {
|
|
31175
|
+
listener();
|
|
31176
|
+
} catch (error) {
|
|
31177
|
+
console.error("[live-link] open listener failed", error);
|
|
31178
|
+
}
|
|
31179
|
+
}
|
|
31180
|
+
};
|
|
31181
|
+
socket.onmessage = (message) => {
|
|
31182
|
+
if (this.#socket === socket)
|
|
31183
|
+
this.options.onMessage(String(message.data));
|
|
31184
|
+
};
|
|
31185
|
+
socket.onerror = () => void 0;
|
|
31186
|
+
socket.onclose = () => {
|
|
31187
|
+
if (this.#socket !== socket)
|
|
31188
|
+
return;
|
|
31189
|
+
this.#socket = void 0;
|
|
31190
|
+
clearTimeout(this.#pongDeadline);
|
|
31191
|
+
this.#pongDeadline = void 0;
|
|
31192
|
+
const openedAt = this.#openedAt;
|
|
31193
|
+
this.#openedAt = void 0;
|
|
31194
|
+
if (this.#state !== "update-required")
|
|
31195
|
+
this.#state = "disconnected";
|
|
31196
|
+
this.options.onClose();
|
|
31197
|
+
if (this.#state === "update-required" || !this.#wanted)
|
|
31198
|
+
return;
|
|
31199
|
+
this.#scheduleReconnect(openedAt);
|
|
31200
|
+
};
|
|
31201
|
+
}
|
|
31202
|
+
#scheduleReconnect(openedAt) {
|
|
31203
|
+
let delay;
|
|
31204
|
+
if (openedAt !== void 0 && this.#now() - openedAt >= this.#timing.stableSocketMs) {
|
|
31205
|
+
this.#failedConnects = 0;
|
|
31206
|
+
delay = this.#random() * this.#timing.stableCloseJitterMs;
|
|
31207
|
+
} else {
|
|
31208
|
+
const ceiling = Math.min(this.#timing.backoffMaxMs, this.#timing.backoffBaseMs * 2 ** this.#failedConnects);
|
|
31209
|
+
this.#failedConnects += 1;
|
|
31210
|
+
delay = this.#random() * ceiling;
|
|
31211
|
+
}
|
|
31212
|
+
delay = Math.floor(Math.max(delay, this.#retryAfterMs));
|
|
31213
|
+
this.#retryAfterMs = 0;
|
|
31214
|
+
this.#reconnects += 1;
|
|
31215
|
+
this.#reconnect = setTimeout(() => {
|
|
31216
|
+
this.#reconnect = void 0;
|
|
31217
|
+
if (this.#wanted && !this.#socket && this.#state !== "update-required")
|
|
31218
|
+
this.#connect();
|
|
31219
|
+
}, delay);
|
|
31220
|
+
this.#reconnect.unref?.();
|
|
31221
|
+
}
|
|
31222
|
+
};
|
|
31223
|
+
function headerValue(value) {
|
|
31224
|
+
return Array.isArray(value) ? value[0] : value;
|
|
31225
|
+
}
|
|
31226
|
+
|
|
30863
31227
|
// apps/body/dist/daemon-api-client.js
|
|
30864
31228
|
init_runtime();
|
|
30865
31229
|
var DaemonApiError = class extends Error {
|
|
@@ -30908,32 +31272,19 @@ async function responseError(response) {
|
|
|
30908
31272
|
}
|
|
30909
31273
|
return new DaemonApiError(`monolith daemon request failed (${response.status}: ${code})`, response.status, response.status === 408 || response.status === 429 || response.status >= 500, code, retryAfterMs(response.headers.get("retry-after")), minVersion);
|
|
30910
31274
|
}
|
|
30911
|
-
function retryAfterMs(value) {
|
|
30912
|
-
if (!value)
|
|
30913
|
-
return void 0;
|
|
30914
|
-
const seconds = Number(value);
|
|
30915
|
-
const delay = Number.isFinite(seconds) && seconds >= 0 ? seconds * 1e3 : Date.parse(value) - Date.now();
|
|
30916
|
-
return Number.isFinite(delay) && delay >= 0 ? Math.min(delay, 6e4) : void 0;
|
|
30917
|
-
}
|
|
30918
31275
|
var REQUEST_DEADLINE_MS = 3e4;
|
|
30919
|
-
var STABLE_SOCKET_MS = 3e4;
|
|
30920
31276
|
var DaemonApiClient = class {
|
|
30921
31277
|
baseUrl;
|
|
30922
31278
|
daemonToken;
|
|
30923
31279
|
agentId;
|
|
30924
31280
|
fetchImpl;
|
|
30925
|
-
webSocketFactory;
|
|
30926
31281
|
presenceLifecycleId = randomUUID6();
|
|
30927
|
-
|
|
30928
|
-
|
|
30929
|
-
liveReconnectDelayMs = 1e3;
|
|
30930
|
-
liveOpenedAt = 0;
|
|
31282
|
+
/** The one live socket; it reconnects by itself for as long as it is wanted. */
|
|
31283
|
+
link;
|
|
30931
31284
|
inFlight = 0;
|
|
30932
31285
|
requestCount = 0;
|
|
30933
31286
|
timeoutCount = 0;
|
|
30934
|
-
reconnectCount = 0;
|
|
30935
31287
|
subscriptionCount = 0;
|
|
30936
|
-
discoveryWakeSupported = false;
|
|
30937
31288
|
readBudget;
|
|
30938
31289
|
liveRooms = /* @__PURE__ */ new Map();
|
|
30939
31290
|
roomsChangedListener;
|
|
@@ -30944,17 +31295,28 @@ var DaemonApiClient = class {
|
|
|
30944
31295
|
cornerRestartListener;
|
|
30945
31296
|
helperReleaseListener;
|
|
30946
31297
|
forceUpdateListener;
|
|
30947
|
-
forceUpdatePending = false;
|
|
30948
31298
|
helperIdentity = {
|
|
30949
31299
|
releaseVersion: "v0.0.0"
|
|
30950
31300
|
};
|
|
30951
|
-
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols), readBudget) {
|
|
31301
|
+
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols, options) => new wrapper_default(url, protocols, options), readBudget, linkTiming) {
|
|
30952
31302
|
this.baseUrl = baseUrl;
|
|
30953
31303
|
this.daemonToken = daemonToken;
|
|
30954
31304
|
this.agentId = agentId;
|
|
30955
31305
|
this.fetchImpl = fetchImpl;
|
|
30956
|
-
this.webSocketFactory = webSocketFactory;
|
|
30957
31306
|
this.readBudget = readBudget ?? (fetchImpl === fetch ? new HostReadBudget(baseUrl) : void 0);
|
|
31307
|
+
this.link = new LiveLink({
|
|
31308
|
+
url: () => this.liveUrl(),
|
|
31309
|
+
protocols: () => [`bearer.${this.daemonToken}`],
|
|
31310
|
+
factory: webSocketFactory,
|
|
31311
|
+
onOpen: () => this.liveOpened(),
|
|
31312
|
+
onMessage: (data) => this.liveMessage(data),
|
|
31313
|
+
onClose: () => {
|
|
31314
|
+
for (const room of this.liveRooms.values())
|
|
31315
|
+
room.onState?.(false);
|
|
31316
|
+
},
|
|
31317
|
+
onUpdateRequired: (minVersion) => this.forceUpdateListener?.(minVersion),
|
|
31318
|
+
...linkTiming ? { timing: linkTiming } : {}
|
|
31319
|
+
});
|
|
30958
31320
|
}
|
|
30959
31321
|
/** Connection material for the daemon-owned MCP proxy and corner credentials. */
|
|
30960
31322
|
connection() {
|
|
@@ -30980,24 +31342,15 @@ var DaemonApiClient = class {
|
|
|
30980
31342
|
inFlight: this.inFlight,
|
|
30981
31343
|
requests: this.requestCount,
|
|
30982
31344
|
timeouts: this.timeoutCount,
|
|
30983
|
-
reconnects: this.
|
|
31345
|
+
reconnects: this.link.reconnects,
|
|
30984
31346
|
subscriptionsSent: this.subscriptionCount,
|
|
30985
31347
|
readQueueDepth: budget?.waiting ?? 0,
|
|
30986
31348
|
readQueueWaitMs: budget?.totalWaitMs ?? 0
|
|
30987
31349
|
};
|
|
30988
31350
|
}
|
|
30989
|
-
/**
|
|
30990
|
-
|
|
30991
|
-
return this.
|
|
30992
|
-
}
|
|
30993
|
-
requestForceUpdate(minVersion) {
|
|
30994
|
-
if (this.forceUpdatePending)
|
|
30995
|
-
return;
|
|
30996
|
-
this.forceUpdatePending = true;
|
|
30997
|
-
clearTimeout(this.liveReconnect);
|
|
30998
|
-
this.liveReconnect = void 0;
|
|
30999
|
-
this.forceUpdateListener?.(minVersion);
|
|
31000
|
-
this.liveSocket?.close();
|
|
31351
|
+
/** Called on every live socket open, after resubscription and the wakes. */
|
|
31352
|
+
onLiveOpen(listener) {
|
|
31353
|
+
return this.link.onOpen(listener);
|
|
31001
31354
|
}
|
|
31002
31355
|
/** Add one Room to this agent's shared live socket. */
|
|
31003
31356
|
liveSubscribe(roomId, cursor3, onItems, onState, presence, onCommands) {
|
|
@@ -31020,18 +31373,12 @@ var DaemonApiClient = class {
|
|
|
31020
31373
|
});
|
|
31021
31374
|
}
|
|
31022
31375
|
this.ensureLiveSocket();
|
|
31023
|
-
|
|
31024
|
-
this.sendLiveSubscription(roomId);
|
|
31376
|
+
this.sendLiveSubscription(roomId);
|
|
31025
31377
|
return () => {
|
|
31026
31378
|
this.liveRooms.delete(roomId);
|
|
31027
|
-
|
|
31028
|
-
|
|
31029
|
-
|
|
31030
|
-
if (!this.liveRooms.size && !this.roomsChangedListener) {
|
|
31031
|
-
clearTimeout(this.liveReconnect);
|
|
31032
|
-
this.liveSocket?.close();
|
|
31033
|
-
this.liveSocket = void 0;
|
|
31034
|
-
}
|
|
31379
|
+
this.link.send(JSON.stringify({ type: "unsubscribe", roomId }));
|
|
31380
|
+
if (!this.liveRooms.size && !this.roomsChangedListener)
|
|
31381
|
+
this.link.stop();
|
|
31035
31382
|
};
|
|
31036
31383
|
}
|
|
31037
31384
|
/** Register the one listener invoked when the server reports this agent's
|
|
@@ -31072,17 +31419,9 @@ var DaemonApiClient = class {
|
|
|
31072
31419
|
if (room && cursor3)
|
|
31073
31420
|
room.cursor = cursor3;
|
|
31074
31421
|
}
|
|
31075
|
-
/** Retry an uncertain discovery read through socket reconnect backoff. */
|
|
31076
|
-
reconnectLive() {
|
|
31077
|
-
this.liveSocket?.close();
|
|
31078
|
-
}
|
|
31079
31422
|
/** Release the long-lived socket when the helper itself is shutting down. */
|
|
31080
31423
|
closeLive() {
|
|
31081
|
-
|
|
31082
|
-
this.liveReconnect = void 0;
|
|
31083
|
-
const socket = this.liveSocket;
|
|
31084
|
-
this.liveSocket = void 0;
|
|
31085
|
-
socket?.close();
|
|
31424
|
+
this.link.stop();
|
|
31086
31425
|
}
|
|
31087
31426
|
async execute(name, input) {
|
|
31088
31427
|
const candidate = input;
|
|
@@ -31116,7 +31455,7 @@ var DaemonApiClient = class {
|
|
|
31116
31455
|
if (!response.ok) {
|
|
31117
31456
|
const error = await responseError(response);
|
|
31118
31457
|
if (error.status === 426 && error.code === "update_required" && error.minVersion)
|
|
31119
|
-
this.
|
|
31458
|
+
this.link.requireUpdate(error.minVersion);
|
|
31120
31459
|
throw error;
|
|
31121
31460
|
}
|
|
31122
31461
|
output = await response.json();
|
|
@@ -31126,8 +31465,12 @@ var DaemonApiClient = class {
|
|
|
31126
31465
|
}
|
|
31127
31466
|
if (controller.signal.aborted) {
|
|
31128
31467
|
this.timeoutCount += 1;
|
|
31468
|
+
if (admitted)
|
|
31469
|
+
this.link.suspect();
|
|
31129
31470
|
throw new DaemonApiError(`monolith daemon ${String(name)} timed out`, 408, true, "deadline_exceeded");
|
|
31130
31471
|
}
|
|
31472
|
+
if (isNetworkFailure(error))
|
|
31473
|
+
this.link.suspect();
|
|
31131
31474
|
throw error;
|
|
31132
31475
|
} finally {
|
|
31133
31476
|
clearTimeout(deadline);
|
|
@@ -31138,171 +31481,121 @@ var DaemonApiClient = class {
|
|
|
31138
31481
|
return output;
|
|
31139
31482
|
}
|
|
31140
31483
|
ensureLiveSocket() {
|
|
31141
|
-
if (this.
|
|
31142
|
-
|
|
31484
|
+
if (this.liveRooms.size || this.roomsChangedListener)
|
|
31485
|
+
this.link.start();
|
|
31486
|
+
}
|
|
31487
|
+
liveUrl() {
|
|
31143
31488
|
const liveUrl = new URL("/v1/phone/live", this.baseUrl);
|
|
31144
31489
|
liveUrl.protocol = liveUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
31145
31490
|
liveUrl.searchParams.set("helperVersion", this.helperIdentity.releaseVersion);
|
|
31146
31491
|
if (this.helperIdentity.sourceSha)
|
|
31147
31492
|
liveUrl.searchParams.set("sourceSha", this.helperIdentity.sourceSha);
|
|
31148
|
-
|
|
31149
|
-
|
|
31150
|
-
|
|
31151
|
-
this.
|
|
31152
|
-
|
|
31153
|
-
|
|
31154
|
-
|
|
31155
|
-
|
|
31156
|
-
|
|
31157
|
-
|
|
31158
|
-
|
|
31159
|
-
|
|
31160
|
-
|
|
31161
|
-
|
|
31162
|
-
|
|
31163
|
-
|
|
31164
|
-
|
|
31165
|
-
|
|
31166
|
-
|
|
31167
|
-
|
|
31168
|
-
|
|
31169
|
-
|
|
31170
|
-
|
|
31171
|
-
|
|
31172
|
-
|
|
31173
|
-
|
|
31174
|
-
|
|
31175
|
-
|
|
31176
|
-
|
|
31493
|
+
return liveUrl.toString();
|
|
31494
|
+
}
|
|
31495
|
+
liveOpened() {
|
|
31496
|
+
for (const roomId of this.liveRooms.keys())
|
|
31497
|
+
this.sendLiveSubscription(roomId);
|
|
31498
|
+
this.roomsChangedListener?.();
|
|
31499
|
+
this.connectorAssignmentListener?.();
|
|
31500
|
+
this.memoryJobListener?.();
|
|
31501
|
+
}
|
|
31502
|
+
liveMessage(data) {
|
|
31503
|
+
let value;
|
|
31504
|
+
try {
|
|
31505
|
+
value = JSON.parse(data);
|
|
31506
|
+
} catch {
|
|
31507
|
+
return;
|
|
31508
|
+
}
|
|
31509
|
+
if (!value || typeof value !== "object")
|
|
31510
|
+
return;
|
|
31511
|
+
const event = value;
|
|
31512
|
+
if (event.type === "force-update" && typeof event.minVersion === "string") {
|
|
31513
|
+
this.link.requireUpdate(event.minVersion);
|
|
31514
|
+
return;
|
|
31515
|
+
}
|
|
31516
|
+
if (event.type === "helper-release" && typeof event.version === "string" && typeof event.sha === "string") {
|
|
31517
|
+
this.helperReleaseListener?.({ version: event.version, sha: event.sha });
|
|
31518
|
+
return;
|
|
31519
|
+
}
|
|
31520
|
+
if (event.type === "rooms-changed") {
|
|
31521
|
+
this.roomsChangedListener?.(membershipChange(event));
|
|
31522
|
+
return;
|
|
31523
|
+
}
|
|
31524
|
+
if (event.type === "discovery-wake" && (event.reason === "listener-resync" || event.reason === "database-recovered")) {
|
|
31177
31525
|
this.roomsChangedListener?.();
|
|
31526
|
+
return;
|
|
31527
|
+
}
|
|
31528
|
+
if (event.type === "corner-complete" && typeof event.roomId === "string") {
|
|
31529
|
+
this.cornerCompleteListener?.(event.roomId);
|
|
31530
|
+
return;
|
|
31531
|
+
}
|
|
31532
|
+
if (event.type === "corner-restart" && typeof event.roomId === "string") {
|
|
31533
|
+
this.cornerRestartListener?.(event.roomId);
|
|
31534
|
+
return;
|
|
31535
|
+
}
|
|
31536
|
+
if (event.type === "connector-assignment") {
|
|
31178
31537
|
this.connectorAssignmentListener?.();
|
|
31538
|
+
return;
|
|
31539
|
+
}
|
|
31540
|
+
if (event.type === "memory-job") {
|
|
31179
31541
|
this.memoryJobListener?.();
|
|
31180
|
-
|
|
31181
|
-
|
|
31182
|
-
|
|
31183
|
-
|
|
31184
|
-
|
|
31185
|
-
|
|
31186
|
-
|
|
31187
|
-
|
|
31188
|
-
|
|
31189
|
-
|
|
31190
|
-
|
|
31191
|
-
|
|
31192
|
-
|
|
31193
|
-
|
|
31194
|
-
|
|
31195
|
-
|
|
31196
|
-
|
|
31197
|
-
if (capabilities?.discoveryWake === true)
|
|
31198
|
-
this.discoveryWakeSupported = true;
|
|
31199
|
-
return;
|
|
31200
|
-
}
|
|
31201
|
-
if (event.type === "helper-release" && typeof event.version === "string" && typeof event.sha === "string") {
|
|
31202
|
-
this.helperReleaseListener?.({ version: event.version, sha: event.sha });
|
|
31203
|
-
return;
|
|
31204
|
-
}
|
|
31205
|
-
if (event.type === "rooms-changed") {
|
|
31206
|
-
this.roomsChangedListener?.(membershipChange(event));
|
|
31207
|
-
return;
|
|
31208
|
-
}
|
|
31209
|
-
if (event.type === "discovery-wake" && (event.reason === "listener-resync" || event.reason === "database-recovered")) {
|
|
31210
|
-
this.roomsChangedListener?.();
|
|
31211
|
-
return;
|
|
31212
|
-
}
|
|
31213
|
-
if (event.type === "corner-complete" && typeof event.roomId === "string") {
|
|
31214
|
-
this.cornerCompleteListener?.(event.roomId);
|
|
31215
|
-
return;
|
|
31216
|
-
}
|
|
31217
|
-
if (event.type === "corner-restart" && typeof event.roomId === "string") {
|
|
31218
|
-
this.cornerRestartListener?.(event.roomId);
|
|
31219
|
-
return;
|
|
31220
|
-
}
|
|
31221
|
-
if (event.type === "connector-assignment") {
|
|
31222
|
-
this.connectorAssignmentListener?.();
|
|
31223
|
-
return;
|
|
31224
|
-
}
|
|
31225
|
-
if (event.type === "memory-job") {
|
|
31226
|
-
this.memoryJobListener?.();
|
|
31227
|
-
return;
|
|
31228
|
-
}
|
|
31229
|
-
if (event.type === "config-changed") {
|
|
31230
|
-
this.configChangedListener?.();
|
|
31231
|
-
return;
|
|
31232
|
-
}
|
|
31233
|
-
if (event.type === "subscribed" && typeof event.roomId === "string") {
|
|
31234
|
-
const capabilities = event.capabilities;
|
|
31235
|
-
if (capabilities?.discoveryWake === true)
|
|
31236
|
-
this.discoveryWakeSupported = true;
|
|
31237
|
-
this.liveRooms.get(event.roomId)?.onState?.(true, {
|
|
31238
|
-
pushIntake: capabilities?.pushIntake === true,
|
|
31239
|
-
connectionPresence: capabilities?.connectionPresence === true
|
|
31240
|
-
});
|
|
31241
|
-
return;
|
|
31242
|
-
}
|
|
31243
|
-
if (event.type === "commands" && typeof event.roomId === "string" && event.commandProtocol === 1 && Array.isArray(event.commands)) {
|
|
31244
|
-
const room2 = this.liveRooms.get(event.roomId);
|
|
31245
|
-
if (!room2)
|
|
31246
|
-
return;
|
|
31247
|
-
const commands = event.commands.filter((command) => {
|
|
31248
|
-
if (!isAgentCommand(command) || command.roomId !== event.roomId || command.agentId !== this.agentId || room2.pushedCommandIds.has(command.id))
|
|
31249
|
-
return false;
|
|
31250
|
-
room2.pushedCommandIds.add(command.id);
|
|
31251
|
-
return true;
|
|
31252
|
-
});
|
|
31253
|
-
while (room2.pushedCommandIds.size > 1e4)
|
|
31254
|
-
room2.pushedCommandIds.delete(room2.pushedCommandIds.values().next().value);
|
|
31255
|
-
if (commands.length)
|
|
31256
|
-
room2.onCommands?.(commands);
|
|
31257
|
-
return;
|
|
31258
|
-
}
|
|
31259
|
-
if (event.type !== "inbox" || typeof event.roomId !== "string" || !Array.isArray(event.items))
|
|
31260
|
-
return;
|
|
31261
|
-
const room = this.liveRooms.get(event.roomId);
|
|
31262
|
-
if (!room)
|
|
31542
|
+
return;
|
|
31543
|
+
}
|
|
31544
|
+
if (event.type === "config-changed") {
|
|
31545
|
+
this.configChangedListener?.();
|
|
31546
|
+
return;
|
|
31547
|
+
}
|
|
31548
|
+
if (event.type === "subscribed" && typeof event.roomId === "string") {
|
|
31549
|
+
const capabilities = event.capabilities;
|
|
31550
|
+
this.liveRooms.get(event.roomId)?.onState?.(true, {
|
|
31551
|
+
pushIntake: capabilities?.pushIntake === true,
|
|
31552
|
+
connectionPresence: capabilities?.connectionPresence === true
|
|
31553
|
+
});
|
|
31554
|
+
return;
|
|
31555
|
+
}
|
|
31556
|
+
if (event.type === "commands" && typeof event.roomId === "string" && event.commandProtocol === 1 && Array.isArray(event.commands)) {
|
|
31557
|
+
const room2 = this.liveRooms.get(event.roomId);
|
|
31558
|
+
if (!room2)
|
|
31263
31559
|
return;
|
|
31264
|
-
const
|
|
31265
|
-
|
|
31266
|
-
|
|
31267
|
-
|
|
31268
|
-
|
|
31269
|
-
|
|
31270
|
-
|
|
31271
|
-
|
|
31272
|
-
|
|
31273
|
-
|
|
31560
|
+
const commands = event.commands.filter((command) => {
|
|
31561
|
+
if (!isAgentCommand(command) || command.roomId !== event.roomId || command.agentId !== this.agentId || room2.pushedCommandIds.has(command.id))
|
|
31562
|
+
return false;
|
|
31563
|
+
room2.pushedCommandIds.add(command.id);
|
|
31564
|
+
return true;
|
|
31565
|
+
});
|
|
31566
|
+
while (room2.pushedCommandIds.size > 1e4)
|
|
31567
|
+
room2.pushedCommandIds.delete(room2.pushedCommandIds.values().next().value);
|
|
31568
|
+
if (commands.length)
|
|
31569
|
+
room2.onCommands?.(commands);
|
|
31570
|
+
return;
|
|
31571
|
+
}
|
|
31572
|
+
if (event.type !== "inbox" || typeof event.roomId !== "string" || !Array.isArray(event.items))
|
|
31573
|
+
return;
|
|
31574
|
+
const room = this.liveRooms.get(event.roomId);
|
|
31575
|
+
if (!room)
|
|
31576
|
+
return;
|
|
31577
|
+
const items = [];
|
|
31578
|
+
for (const candidate of event.items) {
|
|
31579
|
+
if (!candidate || typeof candidate !== "object")
|
|
31580
|
+
continue;
|
|
31581
|
+
const id = candidate.id;
|
|
31582
|
+
if (typeof id === "string") {
|
|
31583
|
+
if (!room.pushedIds.has(id))
|
|
31584
|
+
items.push(candidate);
|
|
31585
|
+
room.pushedIds.add(id);
|
|
31274
31586
|
}
|
|
31275
|
-
|
|
31276
|
-
|
|
31277
|
-
|
|
31278
|
-
|
|
31279
|
-
|
|
31280
|
-
const reconnect = () => {
|
|
31281
|
-
if (this.liveSocket !== socket)
|
|
31282
|
-
return;
|
|
31283
|
-
this.liveSocket = void 0;
|
|
31284
|
-
this.discoveryWakeSupported = false;
|
|
31285
|
-
for (const room of this.liveRooms.values())
|
|
31286
|
-
room.onState?.(false);
|
|
31287
|
-
if (this.forceUpdatePending)
|
|
31288
|
-
return;
|
|
31289
|
-
if (!this.liveRooms.size && !this.roomsChangedListener)
|
|
31290
|
-
return;
|
|
31291
|
-
const delay = this.liveReconnectDelayMs;
|
|
31292
|
-
this.liveReconnectDelayMs = Date.now() - this.liveOpenedAt >= STABLE_SOCKET_MS ? 1e3 : Math.min(delay * 2, 3e4);
|
|
31293
|
-
this.reconnectCount += 1;
|
|
31294
|
-
this.liveReconnect = setTimeout(() => this.ensureLiveSocket(), Math.floor(Math.random() * delay));
|
|
31295
|
-
this.liveReconnect.unref?.();
|
|
31296
|
-
};
|
|
31297
|
-
socket.onerror = () => void 0;
|
|
31298
|
-
socket.onclose = reconnect;
|
|
31587
|
+
}
|
|
31588
|
+
while (room.pushedIds.size > 1e4)
|
|
31589
|
+
room.pushedIds.delete(room.pushedIds.values().next().value);
|
|
31590
|
+
if (items.length)
|
|
31591
|
+
room.onItems?.(items, typeof event.cursor === "string" ? event.cursor : void 0);
|
|
31299
31592
|
}
|
|
31300
31593
|
sendLiveSubscription(roomId) {
|
|
31301
31594
|
const room = this.liveRooms.get(roomId);
|
|
31302
|
-
if (!room || this.
|
|
31595
|
+
if (!room || !this.link.isOpen())
|
|
31303
31596
|
return;
|
|
31304
31597
|
this.subscriptionCount += 1;
|
|
31305
|
-
this.
|
|
31598
|
+
this.link.send(JSON.stringify({
|
|
31306
31599
|
type: "subscribe",
|
|
31307
31600
|
roomId,
|
|
31308
31601
|
lifecycleId: this.presenceLifecycleId,
|
|
@@ -31363,7 +31656,7 @@ import { promisify as promisify8 } from "node:util";
|
|
|
31363
31656
|
// apps/body/dist/grant-runner.js
|
|
31364
31657
|
import { execFile as execFile8 } from "node:child_process";
|
|
31365
31658
|
import { createHash as createHash6, randomBytes as randomBytes7, randomUUID as randomUUID7 } from "node:crypto";
|
|
31366
|
-
import { chmod as chmod6, mkdir as mkdir10, readFile as
|
|
31659
|
+
import { chmod as chmod6, mkdir as mkdir10, readFile as readFile14, readdir as readdir4, rename as rename6, rm as rm6, rmdir, stat as stat6, writeFile as writeFile10 } from "node:fs/promises";
|
|
31367
31660
|
import { createServer as createServer3 } from "node:http";
|
|
31368
31661
|
import { dirname as dirname12, resolve as resolve16 } from "node:path";
|
|
31369
31662
|
|
|
@@ -32058,7 +32351,7 @@ async function ensureBwrapSandbox(options = {}) {
|
|
|
32058
32351
|
}
|
|
32059
32352
|
|
|
32060
32353
|
// apps/body/dist/provider-key-store.js
|
|
32061
|
-
import { chmod as chmod5, mkdir as mkdir9, readFile as
|
|
32354
|
+
import { chmod as chmod5, mkdir as mkdir9, readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
|
|
32062
32355
|
import { homedir as homedir12 } from "node:os";
|
|
32063
32356
|
import { dirname as dirname11, resolve as resolve15 } from "node:path";
|
|
32064
32357
|
var PROVIDER_KEY_ENV_VARS = {
|
|
@@ -32075,7 +32368,7 @@ function providerKeyStorePath(env = process.env) {
|
|
|
32075
32368
|
}
|
|
32076
32369
|
async function readProviderKeyStore(env = process.env) {
|
|
32077
32370
|
const path = providerKeyStorePath(env);
|
|
32078
|
-
const raw = await
|
|
32371
|
+
const raw = await readFile13(path, "utf8").catch(() => void 0);
|
|
32079
32372
|
if (!raw)
|
|
32080
32373
|
return {};
|
|
32081
32374
|
try {
|
|
@@ -32127,7 +32420,7 @@ function operatorSecretsPath(env = process.env) {
|
|
|
32127
32420
|
return resolve16(dirname12(providerKeyStorePath(env)), "secrets.json");
|
|
32128
32421
|
}
|
|
32129
32422
|
async function readOperatorSecrets(env = process.env) {
|
|
32130
|
-
const raw = await
|
|
32423
|
+
const raw = await readFile14(operatorSecretsPath(env), "utf8").catch(() => void 0);
|
|
32131
32424
|
if (!raw)
|
|
32132
32425
|
return {};
|
|
32133
32426
|
try {
|
|
@@ -32339,7 +32632,7 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
32339
32632
|
return;
|
|
32340
32633
|
let bytes;
|
|
32341
32634
|
for (const candidate of candidates) {
|
|
32342
|
-
bytes = await
|
|
32635
|
+
bytes = await readFile14(candidate).catch(() => void 0);
|
|
32343
32636
|
if (bytes)
|
|
32344
32637
|
break;
|
|
32345
32638
|
}
|
|
@@ -32541,10 +32834,10 @@ function captureConnectionUsage(recorder, turn, calls) {
|
|
|
32541
32834
|
}
|
|
32542
32835
|
|
|
32543
32836
|
// apps/body/dist/turn-usage.js
|
|
32544
|
-
import { readFile as
|
|
32837
|
+
import { readFile as readFile16 } from "node:fs/promises";
|
|
32545
32838
|
|
|
32546
32839
|
// apps/body/dist/pi-turn-record.js
|
|
32547
|
-
import { readdir as readdir5, readFile as
|
|
32840
|
+
import { readdir as readdir5, readFile as readFile15 } from "node:fs/promises";
|
|
32548
32841
|
import { resolve as resolve17 } from "node:path";
|
|
32549
32842
|
function summarizeProviderError(errorMessage2) {
|
|
32550
32843
|
const trimmed = errorMessage2.trim();
|
|
@@ -32572,7 +32865,7 @@ async function piSessionFilePath(agentEnv, sessionId) {
|
|
|
32572
32865
|
}
|
|
32573
32866
|
async function sessionFileFromMap(home, sessionId) {
|
|
32574
32867
|
try {
|
|
32575
|
-
const raw = await
|
|
32868
|
+
const raw = await readFile15(resolve17(home, ".pi", "pi-acp", "session-map.json"), "utf8");
|
|
32576
32869
|
const map = JSON.parse(raw);
|
|
32577
32870
|
const file = map.sessions?.[sessionId]?.sessionFile;
|
|
32578
32871
|
return typeof file === "string" && file ? file : void 0;
|
|
@@ -32614,7 +32907,7 @@ async function readPiTurnRecord(input) {
|
|
|
32614
32907
|
return void 0;
|
|
32615
32908
|
let raw;
|
|
32616
32909
|
try {
|
|
32617
|
-
raw = await
|
|
32910
|
+
raw = await readFile15(file, "utf8");
|
|
32618
32911
|
} catch {
|
|
32619
32912
|
return void 0;
|
|
32620
32913
|
}
|
|
@@ -32675,7 +32968,7 @@ async function readHarnessTurnUsage(input) {
|
|
|
32675
32968
|
return void 0;
|
|
32676
32969
|
let raw;
|
|
32677
32970
|
try {
|
|
32678
|
-
raw = await
|
|
32971
|
+
raw = await readFile16(file, "utf8");
|
|
32679
32972
|
} catch {
|
|
32680
32973
|
return void 0;
|
|
32681
32974
|
}
|
|
@@ -32717,13 +33010,13 @@ import { randomUUID as randomUUID8 } from "node:crypto";
|
|
|
32717
33010
|
import { mkdir as mkdir11, writeFile as writeFile11 } from "node:fs/promises";
|
|
32718
33011
|
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
32719
33012
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
32720
|
-
import { dirname as dirname13, join as
|
|
33013
|
+
import { dirname as dirname13, join as join11 } from "node:path";
|
|
32721
33014
|
var CommandExecutionContext = class {
|
|
32722
33015
|
generationId = randomUUID8();
|
|
32723
33016
|
path;
|
|
32724
33017
|
current;
|
|
32725
33018
|
constructor(root) {
|
|
32726
|
-
this.path =
|
|
33019
|
+
this.path = join11(root ?? tmpdir5(), `beeline-command-${this.generationId}.json`);
|
|
32727
33020
|
}
|
|
32728
33021
|
async enter(command) {
|
|
32729
33022
|
this.current = command;
|
|
@@ -32831,6 +33124,7 @@ async function runServerCommandIntake(options) {
|
|
|
32831
33124
|
requestReconciliation();
|
|
32832
33125
|
hasSubscribed = true;
|
|
32833
33126
|
}, options.presence, notify2);
|
|
33127
|
+
let failed = false;
|
|
32834
33128
|
try {
|
|
32835
33129
|
while (!signal?.aborted) {
|
|
32836
33130
|
if (reconciliationError)
|
|
@@ -32908,22 +33202,36 @@ async function runServerCommandIntake(options) {
|
|
|
32908
33202
|
if (reconcile)
|
|
32909
33203
|
requestReconciliation();
|
|
32910
33204
|
}
|
|
33205
|
+
} catch (error) {
|
|
33206
|
+
failed = true;
|
|
33207
|
+
throw error;
|
|
32911
33208
|
} finally {
|
|
32912
33209
|
stopped = true;
|
|
32913
33210
|
off?.();
|
|
32914
33211
|
options.onWake?.(void 0);
|
|
32915
|
-
|
|
32916
|
-
|
|
32917
|
-
|
|
33212
|
+
const current = context.current;
|
|
33213
|
+
const stopCurrent = () => {
|
|
33214
|
+
if (current)
|
|
33215
|
+
options.stop(current.turnRequestId);
|
|
33216
|
+
};
|
|
33217
|
+
if (!failed || signal?.aborted)
|
|
33218
|
+
stopCurrent();
|
|
33219
|
+
else
|
|
33220
|
+
signal?.addEventListener("abort", stopCurrent, { once: true });
|
|
33221
|
+
try {
|
|
33222
|
+
await busy;
|
|
33223
|
+
} finally {
|
|
33224
|
+
signal?.removeEventListener("abort", stopCurrent);
|
|
33225
|
+
}
|
|
32918
33226
|
}
|
|
32919
33227
|
}
|
|
32920
33228
|
|
|
32921
33229
|
// apps/body/dist/monolith-corner-turn.js
|
|
32922
33230
|
import { execFile as execFile11 } from "node:child_process";
|
|
32923
33231
|
import { createHash as createHash10 } from "node:crypto";
|
|
32924
|
-
import { mkdir as mkdir20, readFile as
|
|
33232
|
+
import { mkdir as mkdir20, readFile as readFile22, writeFile as writeFile16 } from "node:fs/promises";
|
|
32925
33233
|
import { homedir as homedir15, hostname as hostname2 } from "node:os";
|
|
32926
|
-
import { dirname as dirname18, join as
|
|
33234
|
+
import { dirname as dirname18, join as join17, resolve as resolve28 } from "node:path";
|
|
32927
33235
|
import { promisify as promisify7 } from "node:util";
|
|
32928
33236
|
|
|
32929
33237
|
// apps/body/dist/agent-home.js
|
|
@@ -33726,9 +34034,9 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
33726
34034
|
|
|
33727
34035
|
// apps/body/dist/agent-home.js
|
|
33728
34036
|
import { createHash as createHash7, randomUUID as randomUUID9 } from "node:crypto";
|
|
33729
|
-
import { chmod as chmod7, copyFile, lstat as lstat3, mkdir as mkdir13, readFile as
|
|
34037
|
+
import { chmod as chmod7, copyFile, lstat as lstat3, mkdir as mkdir13, readFile as readFile18, readlink, readdir as readdir6, realpath, rename as rename7, rm as rm7, symlink as symlink2, unlink as unlink5, writeFile as writeFile13 } from "node:fs/promises";
|
|
33730
34038
|
import { homedir as homedir13 } from "node:os";
|
|
33731
|
-
import { basename as basename6, dirname as dirname14, join as
|
|
34039
|
+
import { basename as basename6, dirname as dirname14, join as join13, relative as relative2, resolve as resolve21, sep } from "node:path";
|
|
33732
34040
|
|
|
33733
34041
|
// apps/body/dist/draw-avatar-skill.js
|
|
33734
34042
|
var DRAW_AVATAR_SKILL_NAME = "draw-avatar";
|
|
@@ -33852,14 +34160,235 @@ function isAgentPairingCode(value) {
|
|
|
33852
34160
|
// packages/api-contract/dist/phone.js
|
|
33853
34161
|
init_agent_access();
|
|
33854
34162
|
|
|
34163
|
+
// packages/api-contract/dist/message-search.js
|
|
34164
|
+
var MESSAGE_SEARCH_FILLER_WORDS = [
|
|
34165
|
+
// Articles and determiners
|
|
34166
|
+
"a",
|
|
34167
|
+
"an",
|
|
34168
|
+
"the",
|
|
34169
|
+
"this",
|
|
34170
|
+
"that",
|
|
34171
|
+
"these",
|
|
34172
|
+
"those",
|
|
34173
|
+
"some",
|
|
34174
|
+
"any",
|
|
34175
|
+
"each",
|
|
34176
|
+
"every",
|
|
34177
|
+
"all",
|
|
34178
|
+
"both",
|
|
34179
|
+
"either",
|
|
34180
|
+
"neither",
|
|
34181
|
+
"much",
|
|
34182
|
+
"many",
|
|
34183
|
+
"more",
|
|
34184
|
+
"most",
|
|
34185
|
+
"such",
|
|
34186
|
+
"other",
|
|
34187
|
+
"another",
|
|
34188
|
+
"own",
|
|
34189
|
+
"same",
|
|
34190
|
+
"no",
|
|
34191
|
+
"not",
|
|
34192
|
+
"only",
|
|
34193
|
+
"very",
|
|
34194
|
+
// Pronouns
|
|
34195
|
+
"i",
|
|
34196
|
+
"me",
|
|
34197
|
+
"my",
|
|
34198
|
+
"mine",
|
|
34199
|
+
"myself",
|
|
34200
|
+
"you",
|
|
34201
|
+
"your",
|
|
34202
|
+
"yours",
|
|
34203
|
+
"yourself",
|
|
34204
|
+
"he",
|
|
34205
|
+
"him",
|
|
34206
|
+
"his",
|
|
34207
|
+
"himself",
|
|
34208
|
+
"she",
|
|
34209
|
+
"her",
|
|
34210
|
+
"hers",
|
|
34211
|
+
"herself",
|
|
34212
|
+
"it",
|
|
34213
|
+
"its",
|
|
34214
|
+
"itself",
|
|
34215
|
+
"we",
|
|
34216
|
+
"us",
|
|
34217
|
+
"our",
|
|
34218
|
+
"ours",
|
|
34219
|
+
"ourselves",
|
|
34220
|
+
"they",
|
|
34221
|
+
"them",
|
|
34222
|
+
"their",
|
|
34223
|
+
"theirs",
|
|
34224
|
+
"themselves",
|
|
34225
|
+
"who",
|
|
34226
|
+
"whom",
|
|
34227
|
+
"whose",
|
|
34228
|
+
"which",
|
|
34229
|
+
"what",
|
|
34230
|
+
"someone",
|
|
34231
|
+
"something",
|
|
34232
|
+
"anyone",
|
|
34233
|
+
"anything",
|
|
34234
|
+
"everyone",
|
|
34235
|
+
"everything",
|
|
34236
|
+
// Auxiliaries
|
|
34237
|
+
"am",
|
|
34238
|
+
"is",
|
|
34239
|
+
"are",
|
|
34240
|
+
"was",
|
|
34241
|
+
"were",
|
|
34242
|
+
"be",
|
|
34243
|
+
"been",
|
|
34244
|
+
"being",
|
|
34245
|
+
"have",
|
|
34246
|
+
"has",
|
|
34247
|
+
"had",
|
|
34248
|
+
"having",
|
|
34249
|
+
"do",
|
|
34250
|
+
"does",
|
|
34251
|
+
"did",
|
|
34252
|
+
"doing",
|
|
34253
|
+
"done",
|
|
34254
|
+
"will",
|
|
34255
|
+
"would",
|
|
34256
|
+
"shall",
|
|
34257
|
+
"should",
|
|
34258
|
+
"can",
|
|
34259
|
+
"could",
|
|
34260
|
+
"might",
|
|
34261
|
+
"must",
|
|
34262
|
+
"get",
|
|
34263
|
+
"gets",
|
|
34264
|
+
"got",
|
|
34265
|
+
// Contraction pieces
|
|
34266
|
+
"s",
|
|
34267
|
+
"t",
|
|
34268
|
+
"d",
|
|
34269
|
+
"ll",
|
|
34270
|
+
"m",
|
|
34271
|
+
"re",
|
|
34272
|
+
"ve",
|
|
34273
|
+
"don",
|
|
34274
|
+
"doesn",
|
|
34275
|
+
"didn",
|
|
34276
|
+
"isn",
|
|
34277
|
+
"aren",
|
|
34278
|
+
"wasn",
|
|
34279
|
+
"weren",
|
|
34280
|
+
"haven",
|
|
34281
|
+
"hasn",
|
|
34282
|
+
"hadn",
|
|
34283
|
+
"won",
|
|
34284
|
+
"wouldn",
|
|
34285
|
+
"couldn",
|
|
34286
|
+
"shouldn",
|
|
34287
|
+
"cannot",
|
|
34288
|
+
// Conjunctions
|
|
34289
|
+
"and",
|
|
34290
|
+
"or",
|
|
34291
|
+
"but",
|
|
34292
|
+
"nor",
|
|
34293
|
+
"so",
|
|
34294
|
+
"yet",
|
|
34295
|
+
"if",
|
|
34296
|
+
"then",
|
|
34297
|
+
"than",
|
|
34298
|
+
"because",
|
|
34299
|
+
"as",
|
|
34300
|
+
"while",
|
|
34301
|
+
"though",
|
|
34302
|
+
"although",
|
|
34303
|
+
"unless",
|
|
34304
|
+
"until",
|
|
34305
|
+
"whether",
|
|
34306
|
+
"also",
|
|
34307
|
+
// Prepositions
|
|
34308
|
+
"of",
|
|
34309
|
+
"to",
|
|
34310
|
+
"in",
|
|
34311
|
+
"on",
|
|
34312
|
+
"at",
|
|
34313
|
+
"by",
|
|
34314
|
+
"for",
|
|
34315
|
+
"with",
|
|
34316
|
+
"from",
|
|
34317
|
+
"into",
|
|
34318
|
+
"onto",
|
|
34319
|
+
"about",
|
|
34320
|
+
"above",
|
|
34321
|
+
"below",
|
|
34322
|
+
"over",
|
|
34323
|
+
"under",
|
|
34324
|
+
"after",
|
|
34325
|
+
"before",
|
|
34326
|
+
"between",
|
|
34327
|
+
"through",
|
|
34328
|
+
"during",
|
|
34329
|
+
"without",
|
|
34330
|
+
"within",
|
|
34331
|
+
"again",
|
|
34332
|
+
"against",
|
|
34333
|
+
"upon",
|
|
34334
|
+
"via",
|
|
34335
|
+
"per",
|
|
34336
|
+
"around",
|
|
34337
|
+
// Adverbs
|
|
34338
|
+
"here",
|
|
34339
|
+
"there",
|
|
34340
|
+
"where",
|
|
34341
|
+
"when",
|
|
34342
|
+
"why",
|
|
34343
|
+
"how",
|
|
34344
|
+
"now",
|
|
34345
|
+
"just",
|
|
34346
|
+
"too",
|
|
34347
|
+
"still",
|
|
34348
|
+
"even",
|
|
34349
|
+
"ever",
|
|
34350
|
+
"never",
|
|
34351
|
+
"always",
|
|
34352
|
+
"really",
|
|
34353
|
+
"maybe",
|
|
34354
|
+
"already",
|
|
34355
|
+
"soon",
|
|
34356
|
+
// Chat filler
|
|
34357
|
+
"ok",
|
|
34358
|
+
"okay",
|
|
34359
|
+
"yes",
|
|
34360
|
+
"yeah",
|
|
34361
|
+
"yep",
|
|
34362
|
+
"yup",
|
|
34363
|
+
"nope",
|
|
34364
|
+
"hi",
|
|
34365
|
+
"hey",
|
|
34366
|
+
"hello",
|
|
34367
|
+
"thanks",
|
|
34368
|
+
"thank",
|
|
34369
|
+
"thx",
|
|
34370
|
+
"please",
|
|
34371
|
+
"pls",
|
|
34372
|
+
"like",
|
|
34373
|
+
"gonna",
|
|
34374
|
+
"wanna",
|
|
34375
|
+
"kinda",
|
|
34376
|
+
"lol",
|
|
34377
|
+
"oh",
|
|
34378
|
+
"ah",
|
|
34379
|
+
"um",
|
|
34380
|
+
"uh",
|
|
34381
|
+
"hmm",
|
|
34382
|
+
"oops",
|
|
34383
|
+
"anyway"
|
|
34384
|
+
];
|
|
34385
|
+
var FILLER_WORDS = new Set(MESSAGE_SEARCH_FILLER_WORDS);
|
|
34386
|
+
|
|
33855
34387
|
// apps/body/dist/beeline-skill.js
|
|
33856
34388
|
var USING_BEELINE_SKILL_NAME = "using-beeline";
|
|
33857
34389
|
var BEELINE_TRIAGE_SKILL_NAME = "beeline-triage";
|
|
33858
34390
|
var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
|
|
33859
34391
|
var BEELINE_SPEC_SKILL_NAME = "beeline-spec";
|
|
33860
|
-
function isConfiguredReviewer(agentHandle, reviewerHandle) {
|
|
33861
|
-
return Boolean(agentHandle && reviewerHandle && agentHandle.replace(/^@/, "") === reviewerHandle.replace(/^@/, ""));
|
|
33862
|
-
}
|
|
33863
34392
|
var BEELINE_ROOM_MECHANICS = [
|
|
33864
34393
|
"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.",
|
|
33865
34394
|
"To create a file you can send, 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.",
|
|
@@ -34024,7 +34553,7 @@ Only the author may report final_authorization as passed, after pr_checks_status
|
|
|
34024
34553
|
|
|
34025
34554
|
Then take exactly one action:
|
|
34026
34555
|
|
|
34027
|
-
- FAIL: reply \`@author\` with the confirmed findings to fix.
|
|
34556
|
+
- FAIL: call \`record_validation_stage\` with stage \`review\`, status \`failed\`, and the reviewed head SHA, then reply \`@author\` with the confirmed findings and that reviewed head SHA to fix. The recorded review stage is how an implementer who has already fixed the findings can tell whether the review is stale.
|
|
34028
34557
|
- PASS: call \`approve_merge\` with the reviewed head SHA and assigned briefRevision (omit the revision only for a legacy corner without a brief), then reply \`approved <reviewed sha>\` without tagging the author. Do not tell the author to merge.
|
|
34029
34558
|
- Approving is your last step as reviewer. The server squash-merges that exact head once checks are green, the worker's yolo is on, and no human hold stands. Neither you nor the author merges it.
|
|
34030
34559
|
`;
|
|
@@ -34194,7 +34723,7 @@ var SQUIRE_GOVERNED_TOOLS = [
|
|
|
34194
34723
|
var SQUIRE_GOVERNED_TOOL_SET = new Set(SQUIRE_GOVERNED_TOOLS);
|
|
34195
34724
|
|
|
34196
34725
|
// apps/body/dist/host-mcp-route.js
|
|
34197
|
-
import { join as
|
|
34726
|
+
import { join as join12, resolve as resolve19 } from "node:path";
|
|
34198
34727
|
var import_yaml = __toESM(require_dist(), 1);
|
|
34199
34728
|
init_squire_host();
|
|
34200
34729
|
function grantedMcpServerNames(grants) {
|
|
@@ -34249,7 +34778,7 @@ function grantedSquireHostRouteNames(granted, declarations = {}) {
|
|
|
34249
34778
|
});
|
|
34250
34779
|
}
|
|
34251
34780
|
function hostRouteEnv(hostHome) {
|
|
34252
|
-
return { XDG_CONFIG_HOME:
|
|
34781
|
+
return { XDG_CONFIG_HOME: join12(resolve19(hostHome), ".config") };
|
|
34253
34782
|
}
|
|
34254
34783
|
function rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile, squireScope) {
|
|
34255
34784
|
const next = { ...declaration };
|
|
@@ -34368,7 +34897,7 @@ function mergeGooseHostRoutes(existing, routes) {
|
|
|
34368
34897
|
init_squire_host();
|
|
34369
34898
|
|
|
34370
34899
|
// apps/body/dist/openrouter-routing.js
|
|
34371
|
-
import { mkdir as mkdir12, readFile as
|
|
34900
|
+
import { mkdir as mkdir12, readFile as readFile17, writeFile as writeFile12 } from "node:fs/promises";
|
|
34372
34901
|
import { resolve as resolve20 } from "node:path";
|
|
34373
34902
|
var OPENROUTER_ENDPOINTS_BASE_URL = "https://openrouter.ai/api/v1/models";
|
|
34374
34903
|
var OPENROUTER_COMPLETIONS_URL = "https://openrouter.ai/api/v1/chat/completions";
|
|
@@ -34496,7 +35025,7 @@ function cachePath(cacheDir, model) {
|
|
|
34496
35025
|
}
|
|
34497
35026
|
async function readCache(cacheDir, model) {
|
|
34498
35027
|
try {
|
|
34499
|
-
const parsed = JSON.parse(await
|
|
35028
|
+
const parsed = JSON.parse(await readFile17(cachePath(cacheDir, model), "utf8"));
|
|
34500
35029
|
if (!parsed || typeof parsed !== "object")
|
|
34501
35030
|
return void 0;
|
|
34502
35031
|
const cached = parsed;
|
|
@@ -34539,7 +35068,7 @@ function probeCachePath(cacheDir, model) {
|
|
|
34539
35068
|
}
|
|
34540
35069
|
async function readProbeCache(cacheDir, model) {
|
|
34541
35070
|
try {
|
|
34542
|
-
const parsed = JSON.parse(await
|
|
35071
|
+
const parsed = JSON.parse(await readFile17(probeCachePath(cacheDir, model), "utf8"));
|
|
34543
35072
|
if (!parsed || typeof parsed !== "object")
|
|
34544
35073
|
return void 0;
|
|
34545
35074
|
const cached = parsed;
|
|
@@ -35090,10 +35619,10 @@ function claudeLoginExpiresAt(text2) {
|
|
|
35090
35619
|
}
|
|
35091
35620
|
}
|
|
35092
35621
|
async function hasNewerDetachedClaudeLogin(detached, source) {
|
|
35093
|
-
const detachedExpiresAt = claudeLoginExpiresAt(await
|
|
35622
|
+
const detachedExpiresAt = claudeLoginExpiresAt(await readFile18(detached, "utf8"));
|
|
35094
35623
|
if (detachedExpiresAt === void 0)
|
|
35095
35624
|
return false;
|
|
35096
|
-
const sourceExpiresAt = claudeLoginExpiresAt(await
|
|
35625
|
+
const sourceExpiresAt = claudeLoginExpiresAt(await readFile18(source, "utf8"));
|
|
35097
35626
|
return sourceExpiresAt !== void 0 && detachedExpiresAt > sourceExpiresAt;
|
|
35098
35627
|
}
|
|
35099
35628
|
async function repairRoomAgentCredentialLinks(input) {
|
|
@@ -35120,7 +35649,7 @@ async function repairRoomAgentCredentialLinks(input) {
|
|
|
35120
35649
|
continue;
|
|
35121
35650
|
}
|
|
35122
35651
|
const quarantine = `${target}.beeline-quarantine-${Date.now()}-${randomUUID9()}`;
|
|
35123
|
-
await
|
|
35652
|
+
await rename7(target, quarantine);
|
|
35124
35653
|
}
|
|
35125
35654
|
await symlink2(source, target);
|
|
35126
35655
|
}
|
|
@@ -35600,7 +36129,7 @@ async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, op
|
|
|
35600
36129
|
const existing = await lstat3(target).catch(() => void 0);
|
|
35601
36130
|
if (existing)
|
|
35602
36131
|
await rm7(target, { recursive: existing.isDirectory(), force: true });
|
|
35603
|
-
await
|
|
36132
|
+
await rename7(staged, target);
|
|
35604
36133
|
} finally {
|
|
35605
36134
|
await rm7(staged, { recursive: true, force: true });
|
|
35606
36135
|
}
|
|
@@ -35621,8 +36150,8 @@ async function planManagedSkills(managedSkills, sharedSkills, optionalShares) {
|
|
|
35621
36150
|
try {
|
|
35622
36151
|
const tree = [];
|
|
35623
36152
|
await walkSafeSkillTree(shared.source, shared.source, {
|
|
35624
|
-
directory: async (rel) => void tree.push(`d ${
|
|
35625
|
-
file: async (rel, realPath) => void tree.push(`f ${
|
|
36153
|
+
directory: async (rel) => void tree.push(`d ${join13(shared.name, rel)}`),
|
|
36154
|
+
file: async (rel, realPath) => void tree.push(`f ${join13(shared.name, rel)} ${sha2563(await readFile18(realPath))}`)
|
|
35626
36155
|
});
|
|
35627
36156
|
entries.push({ kind: "shared", name: shared.name, source: shared.source });
|
|
35628
36157
|
lines.push(...tree);
|
|
@@ -35642,7 +36171,7 @@ async function materializedSkillManifest(target) {
|
|
|
35642
36171
|
const visit = async (directory, prefix) => {
|
|
35643
36172
|
for (const entry of await readdir6(directory)) {
|
|
35644
36173
|
const path = resolve21(directory, entry);
|
|
35645
|
-
const rel = prefix ?
|
|
36174
|
+
const rel = prefix ? join13(prefix, entry) : entry;
|
|
35646
36175
|
const entryStats = await lstat3(path);
|
|
35647
36176
|
if (entryStats.isSymbolicLink())
|
|
35648
36177
|
return false;
|
|
@@ -35654,7 +36183,7 @@ async function materializedSkillManifest(target) {
|
|
|
35654
36183
|
}
|
|
35655
36184
|
if (!entryStats.isFile() || entryStats.nlink !== 1)
|
|
35656
36185
|
return false;
|
|
35657
|
-
lines.push(`f ${rel} ${sha2563(await
|
|
36186
|
+
lines.push(`f ${rel} ${sha2563(await readFile18(path))}`);
|
|
35658
36187
|
}
|
|
35659
36188
|
return true;
|
|
35660
36189
|
};
|
|
@@ -35810,7 +36339,7 @@ async function walkSafeSkillTree(source, sourceRoot, visitor, rel = "") {
|
|
|
35810
36339
|
for (const entry of await readdir6(resolvedSource)) {
|
|
35811
36340
|
if (entry === "." || entry === "..")
|
|
35812
36341
|
throw new Error("invalid shared skill entry");
|
|
35813
|
-
await walkSafeSkillTree(resolve21(source, entry), sourceRoot, visitor, rel ?
|
|
36342
|
+
await walkSafeSkillTree(resolve21(source, entry), sourceRoot, visitor, rel ? join13(rel, entry) : entry);
|
|
35814
36343
|
}
|
|
35815
36344
|
return;
|
|
35816
36345
|
}
|
|
@@ -35841,7 +36370,7 @@ async function writeIsolatedHarnessFile(path, content) {
|
|
|
35841
36370
|
try {
|
|
35842
36371
|
await writeFile13(temporary, content, { mode: 384, flag: "wx" });
|
|
35843
36372
|
await chmod7(temporary, 384);
|
|
35844
|
-
await
|
|
36373
|
+
await rename7(temporary, path);
|
|
35845
36374
|
} finally {
|
|
35846
36375
|
await unlink5(temporary).catch(() => void 0);
|
|
35847
36376
|
}
|
|
@@ -36832,7 +37361,7 @@ function readOnlyMcpServer(config, cwd, agentMemoryDir) {
|
|
|
36832
37361
|
|
|
36833
37362
|
// apps/body/dist/codegraph.js
|
|
36834
37363
|
import { execFile as execFile10 } from "node:child_process";
|
|
36835
|
-
import { access as access2, appendFile, mkdir as mkdir15, readFile as
|
|
37364
|
+
import { access as access2, appendFile, mkdir as mkdir15, readFile as readFile19 } from "node:fs/promises";
|
|
36836
37365
|
import { dirname as dirname15, isAbsolute as isAbsolute3, resolve as resolve24 } from "node:path";
|
|
36837
37366
|
import { promisify as promisify6 } from "node:util";
|
|
36838
37367
|
var execFileAsync6 = promisify6(execFile10);
|
|
@@ -36882,7 +37411,7 @@ async function excludeIndexFromGitStatus(cwd) {
|
|
|
36882
37411
|
return;
|
|
36883
37412
|
const path = isAbsolute3(gitPath) ? gitPath : resolve24(cwd, gitPath);
|
|
36884
37413
|
await mkdir15(dirname15(path), { recursive: true });
|
|
36885
|
-
const existing = await
|
|
37414
|
+
const existing = await readFile19(path, "utf8").catch(() => "");
|
|
36886
37415
|
if (existing.split("\n").includes(".codegraph/"))
|
|
36887
37416
|
return;
|
|
36888
37417
|
await appendFile(path, `${existing && !existing.endsWith("\n") ? "\n" : ""}.codegraph/
|
|
@@ -36918,20 +37447,20 @@ init_cursor_acp_bridge();
|
|
|
36918
37447
|
// apps/body/dist/monolith-room-turn.js
|
|
36919
37448
|
import { mkdir as mkdir17, writeFile as writeFile14 } from "node:fs/promises";
|
|
36920
37449
|
import { homedir as homedir14, hostname } from "node:os";
|
|
36921
|
-
import { dirname as dirname16, join as
|
|
37450
|
+
import { dirname as dirname16, join as join15 } from "node:path";
|
|
36922
37451
|
|
|
36923
37452
|
// packages/api-contract/dist/scheduled-prompts.js
|
|
36924
37453
|
var SCHEDULE_SCHEDULER_NAME = "Beeline Scheduler";
|
|
36925
37454
|
var SCHEDULE_RAN_VERB = "ran a schedule for";
|
|
36926
37455
|
|
|
36927
37456
|
// apps/body/dist/model-context-window.js
|
|
36928
|
-
import { readFile as
|
|
36929
|
-
import { join as
|
|
37457
|
+
import { readFile as readFile20 } from "node:fs/promises";
|
|
37458
|
+
import { join as join14 } from "node:path";
|
|
36930
37459
|
async function modelContextWindowTokens(model, piHome) {
|
|
36931
37460
|
if (!model || !piHome)
|
|
36932
37461
|
return void 0;
|
|
36933
37462
|
try {
|
|
36934
|
-
const catalog = JSON.parse(await
|
|
37463
|
+
const catalog = JSON.parse(await readFile20(join14(piHome, "models.json"), "utf8"));
|
|
36935
37464
|
const parts = model.split("/");
|
|
36936
37465
|
const preferredProvider = parts.length > 2 ? parts.shift() : void 0;
|
|
36937
37466
|
const modelId = parts.join("/");
|
|
@@ -37082,7 +37611,7 @@ var CORNERS = [
|
|
|
37082
37611
|
];
|
|
37083
37612
|
var EVERYWHERE = PROMPT_SURFACES;
|
|
37084
37613
|
var CORNER_AUTHOR_CONTRACT = `The current brief's spec is the scope and its checklist says what done means. Its approval quote is the human's own words and wins any conflict with the spec. The short objective is navigation-only text and cannot add, remove, or narrow a requirement.
|
|
37085
|
-
Complete every checklist item in the current spec and use the brief's file manifest. Record
|
|
37614
|
+
Complete every checklist item in the current spec and use the brief's file manifest. Record your stages with record_validation_stage for this revision and head: intent, base, tests, docs, lint_types, publication, ci, final_authorization; the configured reviewer records review. Do not call a missing stage passed.
|
|
37086
37615
|
When a human correction changes the assignment, read the latest revision and use revise_corner_brief with the complete updated brief and a change description before doing dependent work. A chat reply does not revise the assignment.
|
|
37087
37616
|
Before any code, write its end-user story in one sentence: "a person who does X sees Y".
|
|
37088
37617
|
Follow the beeline-triage skill's bugfix execution contract when the spec or its approval quote reports a defect.
|
|
@@ -37113,15 +37642,15 @@ function cornerMergeInstruction(yoloMode, reviewerHandle) {
|
|
|
37113
37642
|
return yoloMode ? `Once the pull request exists, reply with its full URL and end the turn; do not tag ${reviewer}, check, or wait for CI. Green checks wake ${reviewer}; after its PASS the server merges the pull request itself. Never merge it yourself. You are woken only to fix failing checks, requested changes, or a merge GitHub refused; push the fix, and the new head needs green checks and a fresh PASS.` : `Once the pull request exists, reply with its full URL and end the turn; do not tag ${reviewer}, check, or wait for CI. Yolo is off, so the merge gate stays closed for you: never merge; a person merges it after ${reviewer} approves.`;
|
|
37114
37643
|
}
|
|
37115
37644
|
function cornerReviewerInstruction(input) {
|
|
37116
|
-
if (!input.
|
|
37645
|
+
if (!input.isReviewer || input.openedByAgent)
|
|
37117
37646
|
return void 0;
|
|
37118
37647
|
const author = input.authorHandle ? handle(input.authorHandle) : "author";
|
|
37119
37648
|
const number = input.pullRequestNumber ?? "N";
|
|
37120
37649
|
const headSha = input.headSha ?? "<head sha>";
|
|
37121
|
-
return `Checks are green on PR #${number} at ${headSha}${input.briefRevision ? ` with assigned brief revision ${input.briefRevision}` : ""}. Review it now with the beeline-review skill against that exact head and current assigned brief. FAIL: reply \`@${author}\` with the confirmed findings to fix. PASS: call the approve_merge tool for ${headSha}${input.briefRevision ? ` with briefRevision=${input.briefRevision}` : ""}, then reply \`approved ${headSha}\` without tagging ${author}: the server merges it, and a tag would wake the author for nothing. Never merge yourself or tell the author to merge. Never say you are holding or waiting for checks.`;
|
|
37650
|
+
return `Checks are green on PR #${number} at ${headSha}${input.briefRevision ? ` with assigned brief revision ${input.briefRevision}` : ""}. Review it now with the beeline-review skill against that exact head and current assigned brief. FAIL: call record_validation_stage with stage "review", status "failed", and headSha ${headSha}, then reply \`@${author}\` with the confirmed findings and the reviewed head ${headSha} to fix. PASS: call the approve_merge tool for ${headSha}${input.briefRevision ? ` with briefRevision=${input.briefRevision}` : ""}, then reply \`approved ${headSha}\` without tagging ${author}: the server merges it, and a tag would wake the author for nothing. Never merge yourself or tell the author to merge. Never say you are holding or waiting for checks.`;
|
|
37122
37651
|
}
|
|
37123
37652
|
function cornerSelfReviewerInstruction(input) {
|
|
37124
|
-
if (!input.
|
|
37653
|
+
if (!input.isReviewer || !input.openedByAgent)
|
|
37125
37654
|
return void 0;
|
|
37126
37655
|
return "Once the pull request exists, reply with its full URL and end the turn. You are this Room's reviewer, so your own pull request needs no review: do not request one or tag any agent for review. The server merges it once checks are green, yolo is on, and no human hold stands; never merge it yourself. You are woken only to fix failing checks or a merge GitHub refused.";
|
|
37127
37656
|
}
|
|
@@ -37324,11 +37853,7 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
37324
37853
|
budgetBytes: 520,
|
|
37325
37854
|
layer: "surface",
|
|
37326
37855
|
surfaces: ["code-corner"],
|
|
37327
|
-
render: ({ yoloMode, reviewerHandle, selfReviewer }) => (selfReviewer ? cornerSelfReviewerInstruction({
|
|
37328
|
-
reviewerHandle,
|
|
37329
|
-
agentHandle: reviewerHandle,
|
|
37330
|
-
openedByAgent: true
|
|
37331
|
-
}) : cornerMergeInstruction(Boolean(yoloMode), reviewerHandle)) + " Never merge any other pull request."
|
|
37856
|
+
render: ({ yoloMode, reviewerHandle, selfReviewer }) => (selfReviewer ? cornerSelfReviewerInstruction({ isReviewer: true, openedByAgent: true }) : cornerMergeInstruction(Boolean(yoloMode), reviewerHandle)) + " Never merge any other pull request."
|
|
37332
37857
|
},
|
|
37333
37858
|
{
|
|
37334
37859
|
id: "corner.checks",
|
|
@@ -38228,7 +38753,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
38228
38753
|
const cached = this.deliveredAttachments.get(item.id);
|
|
38229
38754
|
if (cached)
|
|
38230
38755
|
return cached;
|
|
38231
|
-
const delivered = await deliverAttachments(item.attachments,
|
|
38756
|
+
const delivered = await deliverAttachments(item.attachments, join15(this.attachmentDir, item.id.replace(/[^\w-]/g, "_")), this.options.fetchImpl);
|
|
38232
38757
|
this.deliveredAttachments.set(item.id, withoutImageData(delivered));
|
|
38233
38758
|
return delivered;
|
|
38234
38759
|
}
|
|
@@ -38415,7 +38940,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
38415
38940
|
root: this.options.config.agentHomeRoot,
|
|
38416
38941
|
squireScope,
|
|
38417
38942
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
38418
|
-
isReviewer:
|
|
38943
|
+
isReviewer: configuration.isReviewer === true,
|
|
38419
38944
|
grantedHostRoutes: mountedHostRoutes,
|
|
38420
38945
|
extraHostRoutes: registryHostDeclarations,
|
|
38421
38946
|
resourceAuthFile,
|
|
@@ -38444,7 +38969,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
38444
38969
|
args: this.options.config.agentArgs ?? []
|
|
38445
38970
|
}, selection);
|
|
38446
38971
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
38447
|
-
this.attachmentDir = tmpDir ?
|
|
38972
|
+
this.attachmentDir = tmpDir ? join15(tmpDir, "beeline-attachments") : void 0;
|
|
38448
38973
|
this.sessionScratchDir = tmpDir;
|
|
38449
38974
|
this.sessionStateDirs = stateDirs;
|
|
38450
38975
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
@@ -38693,7 +39218,6 @@ var MonolithRoomTurnLoop = class {
|
|
|
38693
39218
|
};
|
|
38694
39219
|
this.activeTurn = active;
|
|
38695
39220
|
active.promise = this.prompt(active).catch((error) => {
|
|
38696
|
-
this.options.health.failure(1e3);
|
|
38697
39221
|
console.error(`[thin-core] monolith Room ${this.options.roomId} turn failed:`, error);
|
|
38698
39222
|
}).finally(() => {
|
|
38699
39223
|
if (this.activeTurn === active) {
|
|
@@ -39076,56 +39600,60 @@ var MonolithRoomTurnLoop = class {
|
|
|
39076
39600
|
}
|
|
39077
39601
|
}
|
|
39078
39602
|
async run() {
|
|
39079
|
-
const { api, roomId, signal } = this.options;
|
|
39603
|
+
const { api, roomId, signal, supervisor } = this.options;
|
|
39604
|
+
const intake = (progress) => runServerCommandIntake({
|
|
39605
|
+
api,
|
|
39606
|
+
roomId,
|
|
39607
|
+
agentId: this.agent.publicKey,
|
|
39608
|
+
context: this.commandContext,
|
|
39609
|
+
signal,
|
|
39610
|
+
pollMs: this.options.pollMs,
|
|
39611
|
+
presence: {
|
|
39612
|
+
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
39613
|
+
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {},
|
|
39614
|
+
available: !this.options.config.modelUnavailable
|
|
39615
|
+
},
|
|
39616
|
+
onWake: (wake) => {
|
|
39617
|
+
this.wakeIntake = wake;
|
|
39618
|
+
},
|
|
39619
|
+
onPoll: () => {
|
|
39620
|
+
progress();
|
|
39621
|
+
this.options.health.poll();
|
|
39622
|
+
},
|
|
39623
|
+
onSubscriptionState: this.options.onSubscriptionState,
|
|
39624
|
+
onError: (error) => {
|
|
39625
|
+
this.options.onIntakeError?.(error);
|
|
39626
|
+
console.error("[thin-core] Room command failed", error);
|
|
39627
|
+
},
|
|
39628
|
+
// Marks this loop busy the instant it attempts to claim a real
|
|
39629
|
+
// turn, before the claim's own network round trip and before
|
|
39630
|
+
// context.enter()'s own file I/O - closing the window where
|
|
39631
|
+
// RoomRuntimeCoordinator.reconcile's repository-revision restart
|
|
39632
|
+
// could see a claiming-but-not-yet-`busy` Room as idle and stop it,
|
|
39633
|
+
// cancelling a turn that had already begun (or was about to) (C112).
|
|
39634
|
+
onClaiming: () => {
|
|
39635
|
+
this.busy = true;
|
|
39636
|
+
},
|
|
39637
|
+
onClaimFailed: () => {
|
|
39638
|
+
this.busy = false;
|
|
39639
|
+
},
|
|
39640
|
+
onEnter: (command) => this.squireRelay.activate(command, this.commandContext.generationId),
|
|
39641
|
+
onLeave: (command) => this.squireRelay.deactivate(command.turnRequestId, this.pausedOnGrantRequestId),
|
|
39642
|
+
stop: (requestId) => this.stopTurn(requestId),
|
|
39643
|
+
restart: () => this.options.onRestartRequested?.(),
|
|
39644
|
+
canStartTurn: this.options.canStartTurn,
|
|
39645
|
+
run: async (command) => {
|
|
39646
|
+
const item = {
|
|
39647
|
+
...command.source,
|
|
39648
|
+
id: command.turnRequestId,
|
|
39649
|
+
body: command.action === "resume" ? `Resume the paused turn. The server supplied this answer: ${command.source.body}` : command.source.body
|
|
39650
|
+
};
|
|
39651
|
+
this.startPrompt(item);
|
|
39652
|
+
await this.activeTurn?.promise;
|
|
39653
|
+
}
|
|
39654
|
+
});
|
|
39080
39655
|
try {
|
|
39081
|
-
await
|
|
39082
|
-
api,
|
|
39083
|
-
roomId,
|
|
39084
|
-
agentId: this.agent.publicKey,
|
|
39085
|
-
context: this.commandContext,
|
|
39086
|
-
signal,
|
|
39087
|
-
pollMs: this.options.pollMs,
|
|
39088
|
-
presence: {
|
|
39089
|
-
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
39090
|
-
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {},
|
|
39091
|
-
available: !this.options.config.modelUnavailable
|
|
39092
|
-
},
|
|
39093
|
-
onWake: (wake) => {
|
|
39094
|
-
this.wakeIntake = wake;
|
|
39095
|
-
},
|
|
39096
|
-
onPoll: () => this.options.health.poll(),
|
|
39097
|
-
onSubscriptionState: this.options.onSubscriptionState,
|
|
39098
|
-
onError: (error) => {
|
|
39099
|
-
this.options.onIntakeError?.(error);
|
|
39100
|
-
console.error("[thin-core] Room command failed", error);
|
|
39101
|
-
},
|
|
39102
|
-
// Marks this loop busy the instant it attempts to claim a real
|
|
39103
|
-
// turn, before the claim's own network round trip and before
|
|
39104
|
-
// context.enter()'s own file I/O - closing the window where
|
|
39105
|
-
// RoomRuntimeCoordinator.reconcile's repository-revision restart
|
|
39106
|
-
// could see a claiming-but-not-yet-`busy` Room as idle and stop it,
|
|
39107
|
-
// cancelling a turn that had already begun (or was about to) (C112).
|
|
39108
|
-
onClaiming: () => {
|
|
39109
|
-
this.busy = true;
|
|
39110
|
-
},
|
|
39111
|
-
onClaimFailed: () => {
|
|
39112
|
-
this.busy = false;
|
|
39113
|
-
},
|
|
39114
|
-
onEnter: (command) => this.squireRelay.activate(command, this.commandContext.generationId),
|
|
39115
|
-
onLeave: (command) => this.squireRelay.deactivate(command.turnRequestId, this.pausedOnGrantRequestId),
|
|
39116
|
-
stop: (requestId) => this.stopTurn(requestId),
|
|
39117
|
-
restart: () => this.options.onRestartRequested?.(),
|
|
39118
|
-
canStartTurn: this.options.canStartTurn,
|
|
39119
|
-
run: async (command) => {
|
|
39120
|
-
const item = {
|
|
39121
|
-
...command.source,
|
|
39122
|
-
id: command.turnRequestId,
|
|
39123
|
-
body: command.action === "resume" ? `Resume the paused turn. The server supplied this answer: ${command.source.body}` : command.source.body
|
|
39124
|
-
};
|
|
39125
|
-
this.startPrompt(item);
|
|
39126
|
-
await this.activeTurn?.promise;
|
|
39127
|
-
}
|
|
39128
|
-
});
|
|
39656
|
+
await (supervisor ? supervisor.supervise(`Room ${roomId}`, signal, intake) : intake(() => void 0));
|
|
39129
39657
|
} finally {
|
|
39130
39658
|
this.squireRelay.close();
|
|
39131
39659
|
this.options.grantRunner?.unregister(roomId);
|
|
@@ -39311,8 +39839,8 @@ process.exit(result.status ?? 1);
|
|
|
39311
39839
|
// apps/body/dist/warm-node-modules.js
|
|
39312
39840
|
import { createHash as createHash9, randomUUID as randomUUID10 } from "node:crypto";
|
|
39313
39841
|
import { constants as fsConstants3 } from "node:fs";
|
|
39314
|
-
import { chmod as chmod9, copyFile as copyFile2, link, lstat as lstat4, mkdir as mkdir19, readdir as readdir8, readFile as
|
|
39315
|
-
import { dirname as dirname17, join as
|
|
39842
|
+
import { chmod as chmod9, copyFile as copyFile2, link, lstat as lstat4, mkdir as mkdir19, readdir as readdir8, readFile as readFile21, readlink as readlink2, rename as rename8, rm as rm9, stat as stat7, symlink as symlink3, utimes } from "node:fs/promises";
|
|
39843
|
+
import { dirname as dirname17, join as join16, resolve as resolve27 } from "node:path";
|
|
39316
39844
|
var STORE_FORMAT = "v1";
|
|
39317
39845
|
var STAGING_PREFIX = ".beeline-warm-";
|
|
39318
39846
|
var STAGING_SWEEP_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -39333,7 +39861,7 @@ function isPlanRefusal(value) {
|
|
|
39333
39861
|
return "failure" in value;
|
|
39334
39862
|
}
|
|
39335
39863
|
async function readWarmPlan(worktreePath) {
|
|
39336
|
-
const lockfile = await
|
|
39864
|
+
const lockfile = await readFile21(resolve27(worktreePath, "package-lock.json")).catch(() => void 0);
|
|
39337
39865
|
if (!lockfile)
|
|
39338
39866
|
return { failure: "no-lockfile" };
|
|
39339
39867
|
const packages = parseLockfilePackages(lockfile);
|
|
@@ -39398,7 +39926,7 @@ async function seedWarmNodeModules(input) {
|
|
|
39398
39926
|
for (const tree of plan.trees) {
|
|
39399
39927
|
const target = resolve27(input.worktreePath, tree);
|
|
39400
39928
|
await mkdir19(dirname17(target), { recursive: true });
|
|
39401
|
-
await
|
|
39929
|
+
await rename8(resolve27(staging, tree), target);
|
|
39402
39930
|
placed.push(target);
|
|
39403
39931
|
}
|
|
39404
39932
|
return { reason: "seeded", key: plan.key };
|
|
@@ -39443,7 +39971,7 @@ async function harvestWarmNodeModules(input) {
|
|
|
39443
39971
|
if (!await stagedTreeIsPublishable(input.worktreePath, staging, plan.key)) {
|
|
39444
39972
|
return { reason: "changed", key: plan.key };
|
|
39445
39973
|
}
|
|
39446
|
-
await
|
|
39974
|
+
await rename8(staging, entry);
|
|
39447
39975
|
await pruneWarmStore(input.storeRoot, WARM_STORE_MAX_ENTRIES);
|
|
39448
39976
|
return { reason: "stored", key: plan.key };
|
|
39449
39977
|
} catch (error) {
|
|
@@ -39458,18 +39986,18 @@ async function stagedTreeIsPublishable(worktreePath, staging, key) {
|
|
|
39458
39986
|
const settled = await readWarmPlan(worktreePath);
|
|
39459
39987
|
if (isPlanRefusal(settled) || settled.key !== key)
|
|
39460
39988
|
return false;
|
|
39461
|
-
const hidden =
|
|
39989
|
+
const hidden = join16("node_modules", ".package-lock.json");
|
|
39462
39990
|
const [copied, current] = await Promise.all([
|
|
39463
|
-
|
|
39464
|
-
|
|
39991
|
+
readFile21(resolve27(staging, hidden)).catch(() => void 0),
|
|
39992
|
+
readFile21(resolve27(worktreePath, hidden)).catch(() => void 0)
|
|
39465
39993
|
]);
|
|
39466
39994
|
if (!copied || !current || !copied.equals(current))
|
|
39467
39995
|
return false;
|
|
39468
39996
|
return (await missingInstalledPackages(worktreePath, staging)).length === 0;
|
|
39469
39997
|
}
|
|
39470
39998
|
async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
39471
|
-
const wanted = parseLockfilePackages(await
|
|
39472
|
-
const installed = parseLockfilePackages(await
|
|
39999
|
+
const wanted = parseLockfilePackages(await readFile21(resolve27(worktreePath, "package-lock.json")).catch(() => void 0));
|
|
40000
|
+
const installed = parseLockfilePackages(await readFile21(resolve27(treeRoot, "node_modules", ".package-lock.json")).catch(() => void 0));
|
|
39473
40001
|
if (!wanted)
|
|
39474
40002
|
return ["package-lock.json is unreadable"];
|
|
39475
40003
|
if (!installed)
|
|
@@ -39496,7 +40024,7 @@ async function missingInstalledPackages(worktreePath, treeRoot = worktreePath) {
|
|
|
39496
40024
|
return missing.sort();
|
|
39497
40025
|
}
|
|
39498
40026
|
async function isInstalledPackage(path) {
|
|
39499
|
-
return stat7(
|
|
40027
|
+
return stat7(join16(path, "package.json")).then((info) => info.isFile(), () => false);
|
|
39500
40028
|
}
|
|
39501
40029
|
var INTEGRITY_READ_CONCURRENCY = 64;
|
|
39502
40030
|
async function mapWithLimit(values, limit, visit) {
|
|
@@ -39530,8 +40058,8 @@ function isContainedTreePath(value) {
|
|
|
39530
40058
|
async function cloneTree(source, target, file, topLevel = true) {
|
|
39531
40059
|
await mkdir19(target, { recursive: true, mode: 493 });
|
|
39532
40060
|
for (const entry of await readdir8(source, { withFileTypes: true })) {
|
|
39533
|
-
const from =
|
|
39534
|
-
const to =
|
|
40061
|
+
const from = join16(source, entry.name);
|
|
40062
|
+
const to = join16(target, entry.name);
|
|
39535
40063
|
if (entry.isSymbolicLink()) {
|
|
39536
40064
|
await symlink3(await readlink2(from), to);
|
|
39537
40065
|
continue;
|
|
@@ -39561,13 +40089,13 @@ async function pruneWarmStore(storeRoot, keep) {
|
|
|
39561
40089
|
const names = (await readdir8(storeRoot).catch(() => [])).filter((name) => !name.startsWith(STAGING_PREFIX));
|
|
39562
40090
|
const entries = [];
|
|
39563
40091
|
for (const name of names) {
|
|
39564
|
-
const info = await lstat4(
|
|
40092
|
+
const info = await lstat4(join16(storeRoot, name)).catch(() => void 0);
|
|
39565
40093
|
if (info?.isDirectory())
|
|
39566
40094
|
entries.push({ name, usedAt: info.mtimeMs });
|
|
39567
40095
|
}
|
|
39568
40096
|
const dropped = entries.sort((a2, b) => b.usedAt - a2.usedAt || a2.name.localeCompare(b.name)).slice(keep);
|
|
39569
40097
|
for (const entry of dropped) {
|
|
39570
|
-
await rm9(
|
|
40098
|
+
await rm9(join16(storeRoot, entry.name), { recursive: true, force: true }).catch(() => void 0);
|
|
39571
40099
|
}
|
|
39572
40100
|
return dropped.map((entry) => entry.name);
|
|
39573
40101
|
}
|
|
@@ -39576,7 +40104,7 @@ async function sweepStaleStaging(storeRoot, now2) {
|
|
|
39576
40104
|
for (const name of entries) {
|
|
39577
40105
|
if (!name.startsWith(STAGING_PREFIX))
|
|
39578
40106
|
continue;
|
|
39579
|
-
const path =
|
|
40107
|
+
const path = join16(storeRoot, name);
|
|
39580
40108
|
const info = await lstat4(path).catch(() => void 0);
|
|
39581
40109
|
if (!info || now2 - info.mtimeMs < STAGING_SWEEP_MS)
|
|
39582
40110
|
continue;
|
|
@@ -40092,8 +40620,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
40092
40620
|
this.yoloMode = configuration.yoloMode;
|
|
40093
40621
|
const opener = this.options.openedBy ? roster.members.find((member) => member.identityId === this.options.openedBy) : void 0;
|
|
40094
40622
|
const reviewerInput = {
|
|
40095
|
-
|
|
40096
|
-
agentHandle: self?.handle,
|
|
40623
|
+
isReviewer: configuration.isReviewer === true,
|
|
40097
40624
|
authorHandle: opener?.handle,
|
|
40098
40625
|
openedByAgent: !this.options.openedBy || this.options.openedBy === this.agent.publicKey
|
|
40099
40626
|
};
|
|
@@ -40135,7 +40662,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
40135
40662
|
root: this.options.config.agentHomeRoot,
|
|
40136
40663
|
squireScope,
|
|
40137
40664
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
40138
|
-
isReviewer:
|
|
40665
|
+
isReviewer: Boolean(reviewerInstruction),
|
|
40139
40666
|
grantedHostRoutes: mountedHostRoutes,
|
|
40140
40667
|
extraHostRoutes: registryHostDeclarations,
|
|
40141
40668
|
resourceAuthFile,
|
|
@@ -40193,7 +40720,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
40193
40720
|
args: this.options.config.agentArgs ?? []
|
|
40194
40721
|
}, selection);
|
|
40195
40722
|
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
40196
|
-
this.attachmentDir = tmpDir ?
|
|
40723
|
+
this.attachmentDir = tmpDir ? join17(tmpDir, "beeline-attachments") : void 0;
|
|
40197
40724
|
this.sessionScratchDir = tmpDir;
|
|
40198
40725
|
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
40199
40726
|
await Promise.all(homeStateDirs.map((dir) => mkdir20(dir, { recursive: true })));
|
|
@@ -40594,10 +41121,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
40594
41121
|
size: file.size
|
|
40595
41122
|
}));
|
|
40596
41123
|
const allAttachments = [...attachments, ...briefAttachments];
|
|
40597
|
-
const delivered = this.attachmentDir && allAttachments.length ? await deliverAttachments(allAttachments,
|
|
41124
|
+
const delivered = this.attachmentDir && allAttachments.length ? await deliverAttachments(allAttachments, join17(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : [];
|
|
40598
41125
|
const briefFileLines = await Promise.all((restored.brief?.attachments ?? []).map(async (file, index) => {
|
|
40599
41126
|
const entry = delivered.find((item) => item.attachment === briefAttachments[index]);
|
|
40600
|
-
const verified = entry?.path && createHash10("sha256").update(await
|
|
41127
|
+
const verified = entry?.path && createHash10("sha256").update(await readFile22(entry.path)).digest("hex") === file.sha256;
|
|
40601
41128
|
return `- ${file.title} (${file.purpose}; ${file.required ? "required" : "optional"}; sha256 ${file.sha256}): ${verified ? entry.path : "UNAVAILABLE OR CONTENT MISMATCH"}`;
|
|
40602
41129
|
}));
|
|
40603
41130
|
const missingRequiredBriefFile = (restored.brief?.attachments ?? []).some((file, index) => file.required && briefFileLines[index]?.includes("UNAVAILABLE OR CONTENT MISMATCH"));
|
|
@@ -41037,68 +41564,72 @@ ${deviceGrantResumePrompt(grantedDevice, result.agentText)}`);
|
|
|
41037
41564
|
});
|
|
41038
41565
|
}
|
|
41039
41566
|
async run() {
|
|
41040
|
-
const { api, cornerId, signal } = this.options;
|
|
41041
|
-
|
|
41042
|
-
|
|
41043
|
-
|
|
41044
|
-
|
|
41045
|
-
|
|
41046
|
-
|
|
41047
|
-
|
|
41048
|
-
|
|
41049
|
-
|
|
41050
|
-
|
|
41051
|
-
|
|
41052
|
-
|
|
41053
|
-
|
|
41054
|
-
|
|
41055
|
-
|
|
41056
|
-
|
|
41057
|
-
|
|
41058
|
-
|
|
41059
|
-
|
|
41060
|
-
|
|
41061
|
-
|
|
41062
|
-
|
|
41063
|
-
|
|
41064
|
-
|
|
41065
|
-
|
|
41066
|
-
|
|
41067
|
-
|
|
41068
|
-
|
|
41069
|
-
|
|
41070
|
-
|
|
41071
|
-
|
|
41072
|
-
|
|
41073
|
-
|
|
41074
|
-
|
|
41075
|
-
|
|
41076
|
-
|
|
41077
|
-
|
|
41078
|
-
|
|
41079
|
-
|
|
41080
|
-
|
|
41081
|
-
|
|
41082
|
-
|
|
41083
|
-
|
|
41084
|
-
return true;
|
|
41085
|
-
}
|
|
41086
|
-
if (!this.reconcileCloseRequested)
|
|
41087
|
-
return false;
|
|
41088
|
-
this.reconcileCloseRequested = false;
|
|
41089
|
-
const state = await api.execute("getCornerRestoreState", { cornerId });
|
|
41090
|
-
if (this.options.lane === "no_code" && state.lane !== "no_code") {
|
|
41091
|
-
this.options.onLaneChanged?.();
|
|
41092
|
-
return true;
|
|
41093
|
-
}
|
|
41094
|
-
if (!state.closeRequested)
|
|
41095
|
-
return false;
|
|
41567
|
+
const { api, cornerId, signal, supervisor } = this.options;
|
|
41568
|
+
const intake = (progress) => runServerCommandIntake({
|
|
41569
|
+
api,
|
|
41570
|
+
roomId: cornerId,
|
|
41571
|
+
agentId: this.agent.publicKey,
|
|
41572
|
+
context: this.commandContext,
|
|
41573
|
+
signal,
|
|
41574
|
+
pollMs: this.options.pollMs,
|
|
41575
|
+
presence: {
|
|
41576
|
+
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
41577
|
+
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {},
|
|
41578
|
+
available: !this.options.config.modelUnavailable
|
|
41579
|
+
},
|
|
41580
|
+
onWake: (wake) => {
|
|
41581
|
+
this.wakeIntake = wake;
|
|
41582
|
+
},
|
|
41583
|
+
onPoll: () => {
|
|
41584
|
+
progress();
|
|
41585
|
+
this.options.onPoll();
|
|
41586
|
+
},
|
|
41587
|
+
onSubscriptionState: this.options.onSubscriptionState,
|
|
41588
|
+
onError: (error) => {
|
|
41589
|
+
this.options.onIntakeError?.(error);
|
|
41590
|
+
console.error("[thin-core] corner command failed", error);
|
|
41591
|
+
},
|
|
41592
|
+
// See monolith-room-turn.ts (C112): mark busy the instant a claim
|
|
41593
|
+
// attempt starts, before its own network round trip and before
|
|
41594
|
+
// context.enter()'s own file I/O, so a reconcile busy-check landing
|
|
41595
|
+
// anywhere in that window can never see this corner as idle when a
|
|
41596
|
+
// turn was already claimed (or about to be).
|
|
41597
|
+
onClaiming: () => {
|
|
41598
|
+
this.busy = true;
|
|
41599
|
+
},
|
|
41600
|
+
onClaimFailed: () => {
|
|
41601
|
+
this.busy = false;
|
|
41602
|
+
},
|
|
41603
|
+
onEnter: (command) => this.squireRelay.activate(command, this.commandContext.generationId),
|
|
41604
|
+
onLeave: (command) => this.squireRelay.deactivate(command.turnRequestId),
|
|
41605
|
+
stop: (requestId) => this.stopTurn(requestId),
|
|
41606
|
+
restart: () => this.options.onRestartRequested?.(),
|
|
41607
|
+
canStartTurn: this.options.canStartTurn,
|
|
41608
|
+
closed: async () => {
|
|
41609
|
+
if (this.closePushed) {
|
|
41610
|
+
this.closePushed = false;
|
|
41096
41611
|
await this.harvest;
|
|
41097
41612
|
await this.options.onCloseRequested();
|
|
41098
41613
|
return true;
|
|
41099
|
-
}
|
|
41100
|
-
|
|
41101
|
-
|
|
41614
|
+
}
|
|
41615
|
+
if (!this.reconcileCloseRequested)
|
|
41616
|
+
return false;
|
|
41617
|
+
this.reconcileCloseRequested = false;
|
|
41618
|
+
const state = await api.execute("getCornerRestoreState", { cornerId });
|
|
41619
|
+
if (this.options.lane === "no_code" && state.lane !== "no_code") {
|
|
41620
|
+
this.options.onLaneChanged?.();
|
|
41621
|
+
return true;
|
|
41622
|
+
}
|
|
41623
|
+
if (!state.closeRequested)
|
|
41624
|
+
return false;
|
|
41625
|
+
await this.harvest;
|
|
41626
|
+
await this.options.onCloseRequested();
|
|
41627
|
+
return true;
|
|
41628
|
+
},
|
|
41629
|
+
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)
|
|
41630
|
+
});
|
|
41631
|
+
try {
|
|
41632
|
+
await (supervisor ? supervisor.supervise(`corner ${cornerId}`, signal, intake) : intake(() => void 0));
|
|
41102
41633
|
} finally {
|
|
41103
41634
|
this.squireRelay.close();
|
|
41104
41635
|
this.options.grantRunner?.unregister(cornerId);
|
|
@@ -41170,6 +41701,89 @@ var SurfaceHealth = class {
|
|
|
41170
41701
|
}
|
|
41171
41702
|
};
|
|
41172
41703
|
|
|
41704
|
+
// apps/body/dist/room-supervisor.js
|
|
41705
|
+
var ROOM_RETRY_DELAYS_MS = [1e3, 5e3, 3e4];
|
|
41706
|
+
var RoomSupervisor = class {
|
|
41707
|
+
delaysMs;
|
|
41708
|
+
#parked = /* @__PURE__ */ new Set();
|
|
41709
|
+
#wakes = 0;
|
|
41710
|
+
constructor(delaysMs = ROOM_RETRY_DELAYS_MS) {
|
|
41711
|
+
this.delaysMs = delaysMs;
|
|
41712
|
+
}
|
|
41713
|
+
/** The live socket opened or discovery was woken: every waiting loop retries now. */
|
|
41714
|
+
wake() {
|
|
41715
|
+
this.#wakes += 1;
|
|
41716
|
+
for (const resume of [...this.#parked])
|
|
41717
|
+
resume();
|
|
41718
|
+
}
|
|
41719
|
+
/** Wakes so far; a wait that started from an older count does not wait. */
|
|
41720
|
+
get wakes() {
|
|
41721
|
+
return this.#wakes;
|
|
41722
|
+
}
|
|
41723
|
+
/** How many timed retries follow one failure before a loop parks. */
|
|
41724
|
+
get retries() {
|
|
41725
|
+
return this.delaysMs.length;
|
|
41726
|
+
}
|
|
41727
|
+
/**
|
|
41728
|
+
* Wait before retrying failure number `failures` (1-based). Within the
|
|
41729
|
+
* bound the fixed delay applies, and a wake may cut it short; past the bound
|
|
41730
|
+
* only a wake (or the abort) ends the wait. A wake that already arrived
|
|
41731
|
+
* after `since` (a `wakes` count) ends it at once, so none is lost.
|
|
41732
|
+
*/
|
|
41733
|
+
retryDelay(failures, signal, since = this.#wakes) {
|
|
41734
|
+
const delay = this.delaysMs[failures - 1];
|
|
41735
|
+
return new Promise((resolve40) => {
|
|
41736
|
+
let timer;
|
|
41737
|
+
const done = () => {
|
|
41738
|
+
clearTimeout(timer);
|
|
41739
|
+
this.#parked.delete(done);
|
|
41740
|
+
signal?.removeEventListener("abort", done);
|
|
41741
|
+
resolve40();
|
|
41742
|
+
};
|
|
41743
|
+
this.#parked.add(done);
|
|
41744
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
41745
|
+
if (signal?.aborted || this.#wakes !== since) {
|
|
41746
|
+
done();
|
|
41747
|
+
return;
|
|
41748
|
+
}
|
|
41749
|
+
if (delay !== void 0) {
|
|
41750
|
+
timer = setTimeout(done, delay);
|
|
41751
|
+
timer.unref?.();
|
|
41752
|
+
}
|
|
41753
|
+
});
|
|
41754
|
+
}
|
|
41755
|
+
/**
|
|
41756
|
+
* Run one Room or corner intake under supervision: a failed read never ends
|
|
41757
|
+
* it. `run` resolves only when the intake itself is finished (aborted or
|
|
41758
|
+
* closed); it calls `progress` once its reads succeed again, which restores
|
|
41759
|
+
* the full retry budget. The intake rejects only after its active turn has
|
|
41760
|
+
* settled, so re-entering cannot claim the same command twice.
|
|
41761
|
+
*/
|
|
41762
|
+
async supervise(label, signal, run2) {
|
|
41763
|
+
let failures = 0;
|
|
41764
|
+
for (; ; ) {
|
|
41765
|
+
const since = this.#wakes;
|
|
41766
|
+
try {
|
|
41767
|
+
await run2(() => {
|
|
41768
|
+
failures = 0;
|
|
41769
|
+
});
|
|
41770
|
+
return;
|
|
41771
|
+
} catch (error) {
|
|
41772
|
+
if (signal?.aborted)
|
|
41773
|
+
return;
|
|
41774
|
+
failures += 1;
|
|
41775
|
+
const parked = failures > this.delaysMs.length;
|
|
41776
|
+
console.error(`[thin-core] ${label} intake failed; ${parked ? "waiting for the next live open or discovery wake" : `retry ${failures} of ${this.delaysMs.length} in ${this.delaysMs[failures - 1]}ms`}:`, error);
|
|
41777
|
+
await this.retryDelay(failures, signal, since);
|
|
41778
|
+
if (signal?.aborted)
|
|
41779
|
+
return;
|
|
41780
|
+
if (parked)
|
|
41781
|
+
failures = 0;
|
|
41782
|
+
}
|
|
41783
|
+
}
|
|
41784
|
+
}
|
|
41785
|
+
};
|
|
41786
|
+
|
|
41173
41787
|
// apps/body/dist/room-runtime.js
|
|
41174
41788
|
init_runtime();
|
|
41175
41789
|
|
|
@@ -41597,11 +42211,6 @@ var SessionScheduler = class {
|
|
|
41597
42211
|
// apps/body/dist/room-runtime.js
|
|
41598
42212
|
var REMOVAL_CONFIRMATION_READS = 2;
|
|
41599
42213
|
var ROOM_JOIN_CONCURRENCY = 4;
|
|
41600
|
-
var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
|
|
41601
|
-
var DEFAULT_RECONCILE_HEARTBEAT_MS = 10 * 6e4;
|
|
41602
|
-
function defaultReconcileHeartbeatMs(random = Math.random) {
|
|
41603
|
-
return DEFAULT_RECONCILE_HEARTBEAT_MS + Math.floor(random() * 5 * 6e4);
|
|
41604
|
-
}
|
|
41605
42214
|
var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
|
|
41606
42215
|
var CORNER_BRANCH_DELETE_ATTEMPTS = 3;
|
|
41607
42216
|
var CornerCredentialLookupError = class extends Error {
|
|
@@ -41609,25 +42218,6 @@ var CornerCredentialLookupError = class extends Error {
|
|
|
41609
42218
|
super("corner repository credential lookup failed", { cause });
|
|
41610
42219
|
}
|
|
41611
42220
|
};
|
|
41612
|
-
var DiscoveryWakes = class {
|
|
41613
|
-
arrived = 0;
|
|
41614
|
-
served = 0;
|
|
41615
|
-
/** Called for every agent-directed `rooms-changed` wake. */
|
|
41616
|
-
wake() {
|
|
41617
|
-
this.arrived += 1;
|
|
41618
|
-
}
|
|
41619
|
-
needsFastReconcile() {
|
|
41620
|
-
return this.arrived !== this.served;
|
|
41621
|
-
}
|
|
41622
|
-
/** Snapshot at reconcile entry: the wake count its reads will cover. */
|
|
41623
|
-
beginReconcile() {
|
|
41624
|
-
return this.arrived;
|
|
41625
|
-
}
|
|
41626
|
-
/** Clear only wakes the completed start pass actually served. */
|
|
41627
|
-
completeReconcile(covered) {
|
|
41628
|
-
this.served = Math.max(this.served, covered);
|
|
41629
|
-
}
|
|
41630
|
-
};
|
|
41631
42221
|
function shouldPostInitialCornerWorkingState(restore) {
|
|
41632
42222
|
return !restore.lifecycle?.branch && !restore.lifecycle?.pr;
|
|
41633
42223
|
}
|
|
@@ -41908,8 +42498,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
41908
42498
|
/** Parent ownership retained so a failed corner listing never authorizes removal. */
|
|
41909
42499
|
monolithCornerParents = /* @__PURE__ */ new Map();
|
|
41910
42500
|
now;
|
|
41911
|
-
watchdogStaleMs;
|
|
41912
|
-
reconcileHeartbeatMs;
|
|
41913
42501
|
drainDeadlineMs;
|
|
41914
42502
|
drainDeadlineAt;
|
|
41915
42503
|
workspaceRemovalConfirmations = 0;
|
|
@@ -41921,14 +42509,9 @@ var RoomRuntimeCoordinator = class {
|
|
|
41921
42509
|
repositoryStateFlights = /* @__PURE__ */ new Map();
|
|
41922
42510
|
tokenCache = /* @__PURE__ */ new Map();
|
|
41923
42511
|
tokenFlights = /* @__PURE__ */ new Map();
|
|
41924
|
-
confirmationPending = false;
|
|
41925
42512
|
restartRequested = false;
|
|
41926
|
-
/**
|
|
41927
|
-
|
|
41928
|
-
* survive its start-pass clearing, or a reconnect mid-reconcile waits a
|
|
41929
|
-
* heartbeat for a wake the daemon already received. A scoped membership
|
|
41930
|
-
* event applies incrementally and never touches this latch. */
|
|
41931
|
-
discoveryWakes = new DiscoveryWakes();
|
|
42513
|
+
/** Keeps Room and corner intakes alive across failed reads, without polling. */
|
|
42514
|
+
supervisor = new RoomSupervisor();
|
|
41932
42515
|
discoveryWakeListener;
|
|
41933
42516
|
interactiveIdleListener;
|
|
41934
42517
|
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
@@ -41975,8 +42558,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
41975
42558
|
void this.scheduler.suspendIdle().catch((error) => console.error("[body] config-change session restart failed", error));
|
|
41976
42559
|
void Promise.resolve(this.options.onConfigChanged?.()).catch((error) => console.error("[body] config-change catalog refresh failed", error));
|
|
41977
42560
|
});
|
|
41978
|
-
this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
|
|
41979
|
-
this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? defaultReconcileHeartbeatMs();
|
|
41980
42561
|
this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
|
|
41981
42562
|
const fixedWorkspaceCeiling = process.env.BUZZY_BODY_MAX_SESSIONS;
|
|
41982
42563
|
this.scheduler = new SessionScheduler({
|
|
@@ -41998,12 +42579,18 @@ var RoomRuntimeCoordinator = class {
|
|
|
41998
42579
|
setInteractiveIdleListener(listener) {
|
|
41999
42580
|
this.interactiveIdleListener = listener;
|
|
42000
42581
|
}
|
|
42001
|
-
|
|
42002
|
-
|
|
42003
|
-
|
|
42582
|
+
/**
|
|
42583
|
+
* A failed reconcile makes the live socket suspect: it is pinged, and a
|
|
42584
|
+
* silent one is replaced, which works with any server version. A response
|
|
42585
|
+
* the server actually sent (or a full local read budget) says nothing about
|
|
42586
|
+
* the socket.
|
|
42587
|
+
*/
|
|
42588
|
+
suspectLink(error) {
|
|
42589
|
+
if (!(error instanceof DaemonApiError))
|
|
42590
|
+
this.options.daemonApi.link?.suspect();
|
|
42004
42591
|
}
|
|
42005
42592
|
wakeDiscovery() {
|
|
42006
|
-
this.
|
|
42593
|
+
this.supervisor.wake();
|
|
42007
42594
|
this.discoveryWakeListener?.();
|
|
42008
42595
|
}
|
|
42009
42596
|
activeRoomCount() {
|
|
@@ -42027,12 +42614,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42027
42614
|
schedulerSnapshot() {
|
|
42028
42615
|
return this.scheduler.snapshot();
|
|
42029
42616
|
}
|
|
42030
|
-
needsFastReconcile() {
|
|
42031
|
-
return this.confirmationPending || this.discoveryWakes.needsFastReconcile();
|
|
42032
|
-
}
|
|
42033
|
-
reconcileHeartbeatIntervalMs() {
|
|
42034
|
-
return this.reconcileHeartbeatMs;
|
|
42035
|
-
}
|
|
42036
42617
|
isWorkspaceIdle() {
|
|
42037
42618
|
return this.activeTurnCount() === 0;
|
|
42038
42619
|
}
|
|
@@ -42058,6 +42639,16 @@ var RoomRuntimeCoordinator = class {
|
|
|
42058
42639
|
room.controller.abort();
|
|
42059
42640
|
return true;
|
|
42060
42641
|
}
|
|
42642
|
+
/**
|
|
42643
|
+
* Undo `quiesceForUpdateIfIdle` after a managed restart failed: clear the
|
|
42644
|
+
* latch and let reconciliation start the Room loops the quiesce stopped.
|
|
42645
|
+
*/
|
|
42646
|
+
resumeServing() {
|
|
42647
|
+
if (this.stopped)
|
|
42648
|
+
return;
|
|
42649
|
+
this.restartRequested = false;
|
|
42650
|
+
this.wakeDiscovery();
|
|
42651
|
+
}
|
|
42061
42652
|
setDrainDeadlineAt(deadlineAt) {
|
|
42062
42653
|
if (Number.isFinite(deadlineAt)) {
|
|
42063
42654
|
this.drainDeadlineAt = Math.min(this.drainDeadlineAt ?? Number.POSITIVE_INFINITY, deadlineAt);
|
|
@@ -42080,9 +42671,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
42080
42671
|
async reconcile() {
|
|
42081
42672
|
if (this.stopped)
|
|
42082
42673
|
return "member";
|
|
42083
|
-
this.confirmationPending = false;
|
|
42084
42674
|
const existingRooms = new Set(this.running.keys());
|
|
42085
|
-
const coveredWakes = this.discoveryWakes.beginReconcile();
|
|
42086
42675
|
const bootstrap = await this.options.daemonApi.execute("getDaemonBootstrap", {
|
|
42087
42676
|
agentId: this.agent.publicKey
|
|
42088
42677
|
});
|
|
@@ -42091,7 +42680,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42091
42680
|
if (!bootstrap.workspaceIds.includes(this.runtime.communityId)) {
|
|
42092
42681
|
this.workspaceRemovalConfirmations += 1;
|
|
42093
42682
|
if (this.workspaceRemovalConfirmations < REMOVAL_CONFIRMATION_READS) {
|
|
42094
|
-
this.confirmationPending = true;
|
|
42095
42683
|
this.wakeDiscovery();
|
|
42096
42684
|
return "unknown";
|
|
42097
42685
|
}
|
|
@@ -42179,10 +42767,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
42179
42767
|
continue;
|
|
42180
42768
|
const confirmations = (this.roomRemovalConfirmations.get(channelId) ?? 0) + 1;
|
|
42181
42769
|
this.roomRemovalConfirmations.set(channelId, confirmations);
|
|
42182
|
-
if (confirmations < REMOVAL_CONFIRMATION_READS)
|
|
42183
|
-
this.confirmationPending = true;
|
|
42770
|
+
if (confirmations < REMOVAL_CONFIRMATION_READS)
|
|
42184
42771
|
continue;
|
|
42185
|
-
}
|
|
42186
42772
|
await this.stopRunning(channelId, running);
|
|
42187
42773
|
this.roomRemovalConfirmations.delete(channelId);
|
|
42188
42774
|
}
|
|
@@ -42207,7 +42793,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42207
42793
|
for (const [roomId, running] of this.running)
|
|
42208
42794
|
if (existingRooms.has(roomId))
|
|
42209
42795
|
running.body.requestReconciliation();
|
|
42210
|
-
this.discoveryWakes.completeReconcile(coveredWakes);
|
|
42211
42796
|
if (this.roomRemovalConfirmations.size)
|
|
42212
42797
|
this.wakeDiscovery();
|
|
42213
42798
|
return "member";
|
|
@@ -42310,7 +42895,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42310
42895
|
await this.reapCornerScratch(running.scratch);
|
|
42311
42896
|
} catch (error) {
|
|
42312
42897
|
console.error(`[thin-core] corner ${channelId} cleanup failed; will retry:`, error);
|
|
42313
|
-
this.confirmationPending = true;
|
|
42314
42898
|
}
|
|
42315
42899
|
}
|
|
42316
42900
|
roomRecord(roomId) {
|
|
@@ -42373,7 +42957,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42373
42957
|
const controller = new AbortController();
|
|
42374
42958
|
const cwd = this.roomRoot(roomId);
|
|
42375
42959
|
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
42376
|
-
const startedAt = this.now();
|
|
42377
42960
|
const loop = new MonolithRoomTurnLoop({
|
|
42378
42961
|
roomId,
|
|
42379
42962
|
workspaceId: this.runtime.communityId,
|
|
@@ -42386,18 +42969,12 @@ var RoomRuntimeCoordinator = class {
|
|
|
42386
42969
|
api: this.options.daemonApi,
|
|
42387
42970
|
scheduler: this.scheduler,
|
|
42388
42971
|
signal: controller.signal,
|
|
42389
|
-
health: {
|
|
42390
|
-
poll: () => this.notePoll(roomId),
|
|
42391
|
-
failure: (retryInMs) => this.noteFailure(roomId, retryInMs),
|
|
42392
|
-
presence: () => void 0
|
|
42393
|
-
},
|
|
42972
|
+
health: { poll: () => this.notePoll(roomId) },
|
|
42394
42973
|
onSubscriptionState: (connected) => this.surfaceHealth.subscribed(roomId, connected),
|
|
42395
42974
|
onIntakeError: () => this.surfaceHealth.degraded(roomId, "Room command intake failed"),
|
|
42396
|
-
onCornerOpened: () => {
|
|
42397
|
-
this.confirmationPending = true;
|
|
42398
|
-
},
|
|
42399
42975
|
onRestartRequested: () => this.requestLifecycleRestart(),
|
|
42400
|
-
canStartTurn: () => !this.restartRequested
|
|
42976
|
+
canStartTurn: () => !this.restartRequested,
|
|
42977
|
+
supervisor: this.supervisor
|
|
42401
42978
|
});
|
|
42402
42979
|
const promise = loop.run().catch((error) => {
|
|
42403
42980
|
if (!controller.signal.aborted) {
|
|
@@ -42413,14 +42990,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
42413
42990
|
await promise;
|
|
42414
42991
|
return;
|
|
42415
42992
|
}
|
|
42416
|
-
this.running.set(roomId, {
|
|
42417
|
-
body: loop,
|
|
42418
|
-
controller,
|
|
42419
|
-
promise,
|
|
42420
|
-
lastPollAt: startedAt,
|
|
42421
|
-
backoffUntil: 0,
|
|
42422
|
-
recovering: false
|
|
42423
|
-
});
|
|
42993
|
+
this.running.set(roomId, { body: loop, controller, promise });
|
|
42424
42994
|
console.log(`[thin-core] serving monolith Room ${roomId}`);
|
|
42425
42995
|
}
|
|
42426
42996
|
/**
|
|
@@ -42546,7 +43116,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42546
43116
|
}
|
|
42547
43117
|
const controller = new AbortController();
|
|
42548
43118
|
const grantRunnerEndpoint = await this.grantRunnerEndpoint();
|
|
42549
|
-
const startedAt = this.now();
|
|
42550
43119
|
const loop = new MonolithCornerTurnLoop({
|
|
42551
43120
|
cornerId: corner.cornerId,
|
|
42552
43121
|
grantRunner: this.grantRunner,
|
|
@@ -42574,7 +43143,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42574
43143
|
scheduler: this.scheduler,
|
|
42575
43144
|
signal: controller.signal,
|
|
42576
43145
|
onPoll: () => this.notePoll(corner.cornerId),
|
|
42577
|
-
onFailure: (retryInMs) => this.noteFailure(corner.cornerId, retryInMs),
|
|
42578
43146
|
onSubscriptionState: (connected) => this.surfaceHealth.subscribed(corner.cornerId, connected),
|
|
42579
43147
|
onIntakeError: () => this.surfaceHealth.degraded(corner.cornerId, "corner command intake failed"),
|
|
42580
43148
|
onCloseRequested: () => worktree ? this.reapCornerWorktree({
|
|
@@ -42586,7 +43154,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
42586
43154
|
}) : this.reapCornerScratch({ path: workspacePath, cornerId: corner.cornerId }),
|
|
42587
43155
|
onLaneChanged: () => void this.applyCornerRestart(corner.cornerId).catch((error) => console.error("[thin-core] corner lane-change restart failed", error)),
|
|
42588
43156
|
onRestartRequested: () => this.requestLifecycleRestart(),
|
|
42589
|
-
canStartTurn: () => !this.restartRequested
|
|
43157
|
+
canStartTurn: () => !this.restartRequested,
|
|
43158
|
+
supervisor: this.supervisor
|
|
42590
43159
|
});
|
|
42591
43160
|
const promise = loop.run().catch((error) => {
|
|
42592
43161
|
if (!controller.signal.aborted) {
|
|
@@ -42607,9 +43176,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42607
43176
|
body: loop,
|
|
42608
43177
|
controller,
|
|
42609
43178
|
promise,
|
|
42610
|
-
lastPollAt: startedAt,
|
|
42611
|
-
backoffUntil: 0,
|
|
42612
|
-
recovering: false,
|
|
42613
43179
|
...worktree ? {
|
|
42614
43180
|
worktree: {
|
|
42615
43181
|
...worktree,
|
|
@@ -42937,8 +43503,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42937
43503
|
const room = this.running.get(roomId);
|
|
42938
43504
|
if (!room)
|
|
42939
43505
|
return;
|
|
42940
|
-
room.lastPollAt = this.now();
|
|
42941
|
-
room.backoffUntil = 0;
|
|
42942
43506
|
if (this.deferredRepositoryRestarts.has(roomId) && !room.body.isBusy()) {
|
|
42943
43507
|
this.deferredRepositoryRestarts.delete(roomId);
|
|
42944
43508
|
void this.stopRunning(roomId, room).then(() => this.stopped ? void 0 : this.startRoom(roomId)).catch((error) => {
|
|
@@ -42949,29 +43513,6 @@ var RoomRuntimeCoordinator = class {
|
|
|
42949
43513
|
if (this.isWorkspaceIdle())
|
|
42950
43514
|
this.interactiveIdleListener?.();
|
|
42951
43515
|
}
|
|
42952
|
-
noteFailure(roomId, retryInMs) {
|
|
42953
|
-
const room = this.running.get(roomId);
|
|
42954
|
-
if (room)
|
|
42955
|
-
room.backoffUntil = Math.max(room.backoffUntil, this.now() + retryInMs);
|
|
42956
|
-
}
|
|
42957
|
-
async watchdogTick() {
|
|
42958
|
-
if (this.stopped)
|
|
42959
|
-
return;
|
|
42960
|
-
for (const [roomId, room] of [...this.running]) {
|
|
42961
|
-
if (room.recovering || room.body.isBusy())
|
|
42962
|
-
continue;
|
|
42963
|
-
if (this.now() <= Math.max(room.lastPollAt + this.watchdogStaleMs, room.backoffUntil))
|
|
42964
|
-
continue;
|
|
42965
|
-
room.recovering = true;
|
|
42966
|
-
room.controller.abort();
|
|
42967
|
-
await room.promise.catch(() => void 0);
|
|
42968
|
-
if (room.worktree) {
|
|
42969
|
-
this.confirmationPending = true;
|
|
42970
|
-
} else if (!this.stopped && !this.running.has(roomId)) {
|
|
42971
|
-
this.startRoom(roomId);
|
|
42972
|
-
}
|
|
42973
|
-
}
|
|
42974
|
-
}
|
|
42975
43516
|
beginShutdown() {
|
|
42976
43517
|
if (this.stopped)
|
|
42977
43518
|
return;
|
|
@@ -43160,6 +43701,10 @@ var ThinDaemonCore = class {
|
|
|
43160
43701
|
quiesceForUpdateIfIdle() {
|
|
43161
43702
|
return this.roomRuntime.quiesceForUpdateIfIdle();
|
|
43162
43703
|
}
|
|
43704
|
+
/** A managed restart failed after intake closed: take turns again. */
|
|
43705
|
+
resumeServing() {
|
|
43706
|
+
this.roomRuntime.resumeServing();
|
|
43707
|
+
}
|
|
43163
43708
|
async prepareForForcedUpdateRestart() {
|
|
43164
43709
|
await this.roomRuntime.prepareForForcedUpdateRestart();
|
|
43165
43710
|
}
|
|
@@ -43192,25 +43737,35 @@ var ThinDaemonCore = class {
|
|
|
43192
43737
|
try {
|
|
43193
43738
|
if (!opts.signal?.aborted)
|
|
43194
43739
|
await opts.onEstablished?.();
|
|
43740
|
+
const supervisor = this.roomRuntime.supervisor;
|
|
43741
|
+
let failures = 0;
|
|
43195
43742
|
while (!opts.signal?.aborted) {
|
|
43196
43743
|
this.pendingDiscovery = false;
|
|
43744
|
+
const wakesBefore = supervisor.wakes;
|
|
43197
43745
|
try {
|
|
43198
43746
|
const membership = await this.roomRuntime.reconcile();
|
|
43199
43747
|
if (membership === "not-member")
|
|
43200
43748
|
return "agent-removed";
|
|
43201
43749
|
this.discoveryStatus = membership === "unknown" ? "monolith membership degraded" : "";
|
|
43750
|
+
failures = 0;
|
|
43202
43751
|
} catch (error) {
|
|
43203
43752
|
if (isAgentRemovedError(error))
|
|
43204
43753
|
return "agent-removed";
|
|
43205
|
-
|
|
43754
|
+
failures += 1;
|
|
43755
|
+
console.error(failures > supervisor.retries ? "[thin-core] discovery failed; waiting for the next live open or discovery wake:" : `[thin-core] discovery failed; retry ${failures} of ${supervisor.retries}:`, error);
|
|
43206
43756
|
this.discoveryStatus = `monolith discovery degraded: ${error instanceof Error ? error.message : String(error)}`;
|
|
43207
|
-
this.roomRuntime.
|
|
43757
|
+
this.roomRuntime.suspectLink(error);
|
|
43208
43758
|
}
|
|
43209
43759
|
if (opts.signal?.aborted)
|
|
43210
43760
|
break;
|
|
43211
43761
|
await opts.onProgress?.(this.healthStatus());
|
|
43212
|
-
if (
|
|
43762
|
+
if (failures > 0) {
|
|
43763
|
+
await supervisor.retryDelay(failures, opts.signal, wakesBefore);
|
|
43764
|
+
if (failures > supervisor.retries)
|
|
43765
|
+
failures = 0;
|
|
43766
|
+
} else if (!this.pendingDiscovery) {
|
|
43213
43767
|
await this.waitForDiscovery(opts.signal);
|
|
43768
|
+
}
|
|
43214
43769
|
}
|
|
43215
43770
|
return "aborted";
|
|
43216
43771
|
} finally {
|
|
@@ -43220,28 +43775,169 @@ var ThinDaemonCore = class {
|
|
|
43220
43775
|
}
|
|
43221
43776
|
};
|
|
43222
43777
|
|
|
43223
|
-
// apps/body/dist/
|
|
43224
|
-
|
|
43225
|
-
|
|
43226
|
-
|
|
43227
|
-
|
|
43228
|
-
|
|
43229
|
-
|
|
43778
|
+
// apps/body/dist/helper-lifecycle.js
|
|
43779
|
+
init_systemd();
|
|
43780
|
+
var HELPER_HARD_STOP_MS = 8e4;
|
|
43781
|
+
var HELPER_EXIT_CODES = {
|
|
43782
|
+
stopped: 0,
|
|
43783
|
+
"restart-requested": 0,
|
|
43784
|
+
update: 0,
|
|
43785
|
+
failed: 75,
|
|
43786
|
+
"rolled-back": 75,
|
|
43787
|
+
"force-update-failed": 75,
|
|
43788
|
+
distress: DAEMON_DISTRESS_EXIT_STATUS,
|
|
43789
|
+
"agent-removed": DELIBERATE_REMOVAL_EXIT_STATUS,
|
|
43790
|
+
"unknown-agent": UNKNOWN_AGENT_EXIT_STATUS
|
|
43791
|
+
};
|
|
43792
|
+
var HelperLifecycle = class {
|
|
43793
|
+
options;
|
|
43794
|
+
#state = { kind: "serving" };
|
|
43795
|
+
#hardStop;
|
|
43796
|
+
#exitProcess;
|
|
43797
|
+
constructor(options) {
|
|
43798
|
+
this.options = options;
|
|
43799
|
+
this.#exitProcess = options.exitProcess ?? ((code) => process.exit(code));
|
|
43800
|
+
}
|
|
43801
|
+
get state() {
|
|
43802
|
+
return this.#state;
|
|
43803
|
+
}
|
|
43804
|
+
get serving() {
|
|
43805
|
+
return this.#state.kind === "serving";
|
|
43806
|
+
}
|
|
43807
|
+
/** The reason the final stop was asked for, once one was. */
|
|
43808
|
+
get stopReason() {
|
|
43809
|
+
return this.#state.kind === "stopping" ? this.#state.reason : void 0;
|
|
43810
|
+
}
|
|
43811
|
+
/**
|
|
43812
|
+
* Close intake. An update may only start from serving; `/restart` and a
|
|
43813
|
+
* forced update also take over a waiting update. Returns whether this call
|
|
43814
|
+
* moved the helper to quiescing.
|
|
43815
|
+
*/
|
|
43816
|
+
quiesce(reason) {
|
|
43817
|
+
const state = this.#state;
|
|
43818
|
+
if (state.kind === "serving" || state.kind === "quiescing" && state.reason === "update" && reason !== "update") {
|
|
43819
|
+
this.#state = { kind: "quiescing", reason };
|
|
43820
|
+
return true;
|
|
43821
|
+
}
|
|
43822
|
+
return false;
|
|
43823
|
+
}
|
|
43824
|
+
/** A managed restart failed after intake closed: serve again. */
|
|
43825
|
+
resume() {
|
|
43826
|
+
if (this.#state.kind !== "quiescing")
|
|
43827
|
+
return false;
|
|
43828
|
+
this.#state = { kind: "serving" };
|
|
43829
|
+
return true;
|
|
43830
|
+
}
|
|
43831
|
+
/**
|
|
43832
|
+
* The managed update's idle gate: quiesce only while serving and only if
|
|
43833
|
+
* `closeIntakeIfIdle` proves no turn is running (and closes intake in the
|
|
43834
|
+
* same step). A busy helper stays serving, so the update waits.
|
|
43835
|
+
*/
|
|
43836
|
+
quiesceUpdateIfIdle(closeIntakeIfIdle) {
|
|
43837
|
+
return this.serving && closeIntakeIfIdle() && this.quiesce("update");
|
|
43838
|
+
}
|
|
43839
|
+
/** A managed restart failed after its quiesce: serve again and reopen intake. */
|
|
43840
|
+
resumeAfterFailedUpdate(reopenIntake) {
|
|
43841
|
+
const state = this.#state;
|
|
43842
|
+
if (state.kind !== "quiescing" || state.reason !== "update" || !this.resume())
|
|
43843
|
+
return false;
|
|
43844
|
+
reopenIntake();
|
|
43845
|
+
return true;
|
|
43846
|
+
}
|
|
43847
|
+
/** The final stop: abort the core, and exit no later than the hard limit. */
|
|
43848
|
+
stop(reason) {
|
|
43849
|
+
if (this.#state.kind === "stopping" || this.#state.kind === "exited")
|
|
43230
43850
|
return;
|
|
43231
|
-
|
|
43232
|
-
|
|
43233
|
-
(
|
|
43234
|
-
|
|
43235
|
-
|
|
43236
|
-
|
|
43237
|
-
|
|
43238
|
-
|
|
43239
|
-
|
|
43240
|
-
|
|
43241
|
-
|
|
43242
|
-
|
|
43243
|
-
|
|
43244
|
-
|
|
43851
|
+
this.#state = { kind: "stopping", reason };
|
|
43852
|
+
this.#hardStop = setTimeout(() => {
|
|
43853
|
+
console.error("[thin-core] shutdown deadline reached; exiting helper");
|
|
43854
|
+
this.exit(reason);
|
|
43855
|
+
}, this.options.hardStopMs ?? HELPER_HARD_STOP_MS);
|
|
43856
|
+
this.#hardStop.unref?.();
|
|
43857
|
+
this.options.controller.abort();
|
|
43858
|
+
}
|
|
43859
|
+
/** The one exit funnel: each reason maps to exactly one code. */
|
|
43860
|
+
exit(reason) {
|
|
43861
|
+
if (this.#state.kind === "exited")
|
|
43862
|
+
return;
|
|
43863
|
+
clearTimeout(this.#hardStop);
|
|
43864
|
+
const code = HELPER_EXIT_CODES[reason];
|
|
43865
|
+
this.#state = { kind: "exited", code };
|
|
43866
|
+
this.#exitProcess(code);
|
|
43867
|
+
}
|
|
43868
|
+
/** SIGTERM and SIGINT are a final stop. */
|
|
43869
|
+
installSignals(emitter = process) {
|
|
43870
|
+
const stop = () => this.stop("stopped");
|
|
43871
|
+
emitter.once("SIGINT", stop);
|
|
43872
|
+
emitter.once("SIGTERM", stop);
|
|
43873
|
+
return () => {
|
|
43874
|
+
emitter.removeListener("SIGINT", stop);
|
|
43875
|
+
emitter.removeListener("SIGTERM", stop);
|
|
43876
|
+
};
|
|
43877
|
+
}
|
|
43878
|
+
};
|
|
43879
|
+
function installUnhandledRejectionGuard(emitter = process) {
|
|
43880
|
+
const log2 = (reason) => console.error("[thin-core] unhandled rejection (kept running):", reason);
|
|
43881
|
+
emitter.on("unhandledRejection", log2);
|
|
43882
|
+
return () => emitter.removeListener("unhandledRejection", log2);
|
|
43883
|
+
}
|
|
43884
|
+
function isTransientServerFailure(error) {
|
|
43885
|
+
return isNetworkFailure(error) || error instanceof DaemonApiError && error.retryable;
|
|
43886
|
+
}
|
|
43887
|
+
function successorRollbackAllowed(error, deadlineAt, now2 = Date.now()) {
|
|
43888
|
+
return !isTransientServerFailure(error) || deadlineAt !== void 0 && now2 >= deadlineAt;
|
|
43889
|
+
}
|
|
43890
|
+
async function retryBeforeReady(work, options) {
|
|
43891
|
+
const now2 = options.now ?? Date.now;
|
|
43892
|
+
const slice = options.waitSliceMs ?? 6e4;
|
|
43893
|
+
let failures = 0;
|
|
43894
|
+
for (; ; ) {
|
|
43895
|
+
try {
|
|
43896
|
+
return await work();
|
|
43897
|
+
} catch (error) {
|
|
43898
|
+
if (!isTransientServerFailure(error) || options.signal?.aborted)
|
|
43899
|
+
throw error;
|
|
43900
|
+
if (options.deadlineAt !== void 0 && now2() >= options.deadlineAt)
|
|
43901
|
+
throw error;
|
|
43902
|
+
failures += 1;
|
|
43903
|
+
const timedRetry = ROOM_RETRY_DELAYS_MS[failures - 1];
|
|
43904
|
+
console.warn(`[thin-core] server unreachable before READY; ${timedRetry === void 0 ? "waiting for the live socket to open" : `retrying in ${timedRetry}ms or on open`}:`, error);
|
|
43905
|
+
let opened = false;
|
|
43906
|
+
let wake = () => void 0;
|
|
43907
|
+
const off = options.onLinkOpen(() => {
|
|
43908
|
+
opened = true;
|
|
43909
|
+
wake();
|
|
43910
|
+
});
|
|
43911
|
+
try {
|
|
43912
|
+
const waitStarted = now2();
|
|
43913
|
+
while (!opened && !options.signal?.aborted) {
|
|
43914
|
+
const remaining = [
|
|
43915
|
+
slice,
|
|
43916
|
+
options.deadlineAt === void 0 ? Infinity : options.deadlineAt - now2(),
|
|
43917
|
+
timedRetry === void 0 ? Infinity : timedRetry - (now2() - waitStarted)
|
|
43918
|
+
];
|
|
43919
|
+
const wait2 = Math.min(...remaining);
|
|
43920
|
+
if (wait2 <= 0)
|
|
43921
|
+
break;
|
|
43922
|
+
await options.extendStartTimeout(wait2 + 15e3);
|
|
43923
|
+
await new Promise((resolve40) => {
|
|
43924
|
+
const timer = setTimeout(done, wait2);
|
|
43925
|
+
function done() {
|
|
43926
|
+
clearTimeout(timer);
|
|
43927
|
+
options.signal?.removeEventListener("abort", done);
|
|
43928
|
+
resolve40();
|
|
43929
|
+
}
|
|
43930
|
+
wake = done;
|
|
43931
|
+
options.signal?.addEventListener("abort", done, { once: true });
|
|
43932
|
+
if (opened || options.signal?.aborted)
|
|
43933
|
+
done();
|
|
43934
|
+
});
|
|
43935
|
+
}
|
|
43936
|
+
} finally {
|
|
43937
|
+
off();
|
|
43938
|
+
}
|
|
43939
|
+
}
|
|
43940
|
+
}
|
|
43245
43941
|
}
|
|
43246
43942
|
|
|
43247
43943
|
// apps/body/dist/force-update-journal.js
|
|
@@ -43667,7 +44363,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
43667
44363
|
}
|
|
43668
44364
|
|
|
43669
44365
|
// apps/body/dist/update-rollback-alert.js
|
|
43670
|
-
import { mkdir as mkdir23, readFile as
|
|
44366
|
+
import { mkdir as mkdir23, readFile as readFile23, rename as rename9, unlink as unlink6, writeFile as writeFile17 } from "node:fs/promises";
|
|
43671
44367
|
import { dirname as dirname20, resolve as resolve32 } from "node:path";
|
|
43672
44368
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
43673
44369
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
@@ -43680,7 +44376,7 @@ async function writeAlert(runtimeDir, alert) {
|
|
|
43680
44376
|
await mkdir23(dirname20(path), { recursive: true });
|
|
43681
44377
|
await writeFile17(staged, `${JSON.stringify(alert, null, 2)}
|
|
43682
44378
|
`, { mode: 384 });
|
|
43683
|
-
await
|
|
44379
|
+
await rename9(staged, path);
|
|
43684
44380
|
}
|
|
43685
44381
|
async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()) {
|
|
43686
44382
|
const existing = await readUpdateRollbackAlert(runtimeDir);
|
|
@@ -43690,7 +44386,7 @@ async function queueUpdateRollbackAlert(runtimeDir, releaseId, now2 = Date.now()
|
|
|
43690
44386
|
}
|
|
43691
44387
|
async function readUpdateRollbackAlert(runtimeDir) {
|
|
43692
44388
|
try {
|
|
43693
|
-
const value = JSON.parse(await
|
|
44389
|
+
const value = JSON.parse(await readFile23(updateRollbackAlertPath(runtimeDir), "utf8"));
|
|
43694
44390
|
if (value.version !== 1 || typeof value.releaseId !== "string")
|
|
43695
44391
|
return void 0;
|
|
43696
44392
|
return value;
|
|
@@ -44024,6 +44720,11 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
44024
44720
|
env: this.#env,
|
|
44025
44721
|
log: (line) => console.log(line)
|
|
44026
44722
|
});
|
|
44723
|
+
await convergeAgentServiceUnit({
|
|
44724
|
+
libDir: this.#layout.libDir,
|
|
44725
|
+
env: this.#env,
|
|
44726
|
+
log: (line) => console.log(line)
|
|
44727
|
+
});
|
|
44027
44728
|
} else if (process.platform === "darwin" && this.#env.BEELINE_LAUNCHD_USER !== "0") {
|
|
44028
44729
|
await convergeLaunchdTrustySquireBrokerService({
|
|
44029
44730
|
libDir: this.#layout.libDir,
|
|
@@ -44375,7 +45076,7 @@ init_runtime();
|
|
|
44375
45076
|
init_launchd();
|
|
44376
45077
|
init_systemd();
|
|
44377
45078
|
init_runtime();
|
|
44378
|
-
import { mkdir as mkdir25, rename as
|
|
45079
|
+
import { mkdir as mkdir25, rename as rename10 } from "node:fs/promises";
|
|
44379
45080
|
import { dirname as dirname22, resolve as resolve34 } from "node:path";
|
|
44380
45081
|
async function retireRemovedAgent(runtime, options = {}) {
|
|
44381
45082
|
const deletedRoot = resolve34(runtime.supervisorRoot, "beeline", "deleted-runtimes");
|
|
@@ -44400,7 +45101,7 @@ async function relocateAgentRuntime(runtime, target, options = {}) {
|
|
|
44400
45101
|
});
|
|
44401
45102
|
}
|
|
44402
45103
|
await mkdir25(dirname22(destination), { recursive: true, mode: 448 });
|
|
44403
|
-
await
|
|
45104
|
+
await rename10(source, destination);
|
|
44404
45105
|
return destination;
|
|
44405
45106
|
}
|
|
44406
45107
|
|
|
@@ -44735,7 +45436,7 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
44735
45436
|
// apps/body/dist/connect-command.js
|
|
44736
45437
|
import { spawn as spawn12 } from "node:child_process";
|
|
44737
45438
|
import { createHash as createHash13, randomUUID as randomUUID11 } from "node:crypto";
|
|
44738
|
-
import { chmod as chmod10, mkdir as mkdir26, readFile as
|
|
45439
|
+
import { chmod as chmod10, mkdir as mkdir26, readFile as readFile24, unlink as unlink7, writeFile as writeFile19 } from "node:fs/promises";
|
|
44739
45440
|
import { homedir as homedir17, hostname as hostname3 } from "node:os";
|
|
44740
45441
|
import { dirname as dirname24, resolve as resolve35 } from "node:path";
|
|
44741
45442
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
@@ -45413,7 +46114,7 @@ async function readMachineId(env = process.env) {
|
|
|
45413
46114
|
let machineId;
|
|
45414
46115
|
let machineName = hostname3();
|
|
45415
46116
|
try {
|
|
45416
|
-
const existing = await
|
|
46117
|
+
const existing = await readFile24(machineIdPath, "utf8");
|
|
45417
46118
|
machineId = existing.trim();
|
|
45418
46119
|
if (!/^[0-9a-f-]{32,}$/.test(machineId))
|
|
45419
46120
|
throw new Error("invalid persisted machine id");
|
|
@@ -45669,12 +46370,12 @@ async function runConnectFinishCommand(path) {
|
|
|
45669
46370
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
45670
46371
|
}
|
|
45671
46372
|
try {
|
|
45672
|
-
const grant = JSON.parse(await
|
|
46373
|
+
const grant = JSON.parse(await readFile24(resolve35(path), "utf8"));
|
|
45673
46374
|
if (!isDevicePairingGrant(grant))
|
|
45674
46375
|
throw new Error("device connection grant is invalid");
|
|
45675
46376
|
const connected = await completeDevicePairing(grant);
|
|
45676
46377
|
await unlink7(resolve35(path));
|
|
45677
|
-
const providerEnv = grant.llmEnvFile ? await
|
|
46378
|
+
const providerEnv = grant.llmEnvFile ? await readFile24(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
45678
46379
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
45679
46380
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
45680
46381
|
if (model) {
|
|
@@ -45698,7 +46399,7 @@ async function runConnectFinishCommand(path) {
|
|
|
45698
46399
|
init_self_update();
|
|
45699
46400
|
|
|
45700
46401
|
// apps/body/dist/daemon-failure.js
|
|
45701
|
-
import { mkdir as mkdir27, readFile as
|
|
46402
|
+
import { mkdir as mkdir27, readFile as readFile25, rename as rename11, rm as rm13, writeFile as writeFile20 } from "node:fs/promises";
|
|
45702
46403
|
import { dirname as dirname25, resolve as resolve36 } from "node:path";
|
|
45703
46404
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
45704
46405
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
@@ -45707,7 +46408,7 @@ function daemonFailurePath(runtimeDir) {
|
|
|
45707
46408
|
}
|
|
45708
46409
|
async function readFailureRecord(runtimeDir) {
|
|
45709
46410
|
try {
|
|
45710
|
-
const value = JSON.parse(await
|
|
46411
|
+
const value = JSON.parse(await readFile25(daemonFailurePath(runtimeDir), "utf8"));
|
|
45711
46412
|
if (value.version !== 1 || !Array.isArray(value.failures) || value.failures.some((failure) => typeof failure !== "number") || typeof value.lastError !== "string") {
|
|
45712
46413
|
return void 0;
|
|
45713
46414
|
}
|
|
@@ -45722,7 +46423,14 @@ async function writeFailureRecord(runtimeDir, record6) {
|
|
|
45722
46423
|
await mkdir27(dirname25(path), { recursive: true, mode: 448 });
|
|
45723
46424
|
await writeFile20(staged, `${JSON.stringify(record6, null, 2)}
|
|
45724
46425
|
`, { mode: 384 });
|
|
45725
|
-
await
|
|
46426
|
+
await rename11(staged, path);
|
|
46427
|
+
}
|
|
46428
|
+
function countsTowardDistress(error) {
|
|
46429
|
+
if (isNetworkFailure(error))
|
|
46430
|
+
return false;
|
|
46431
|
+
if (error instanceof DaemonApiError && (error.retryable || error.status === 401))
|
|
46432
|
+
return false;
|
|
46433
|
+
return true;
|
|
45726
46434
|
}
|
|
45727
46435
|
async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
45728
46436
|
const prior = await readFailureRecord(runtimeDir);
|
|
@@ -45737,6 +46445,11 @@ async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
|
45737
46445
|
await writeFailureRecord(runtimeDir, record6);
|
|
45738
46446
|
return { distressed, count: failures.length, path: daemonFailurePath(runtimeDir) };
|
|
45739
46447
|
}
|
|
46448
|
+
async function settleDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
46449
|
+
if (!countsTowardDistress(error))
|
|
46450
|
+
return { distressed: false };
|
|
46451
|
+
return recordDaemonStartFailure(runtimeDir, error, now2);
|
|
46452
|
+
}
|
|
45740
46453
|
async function clearDaemonStartFailures(runtimeDir) {
|
|
45741
46454
|
await rm13(daemonFailurePath(runtimeDir), { force: true });
|
|
45742
46455
|
}
|
|
@@ -45746,7 +46459,7 @@ init_systemd();
|
|
|
45746
46459
|
|
|
45747
46460
|
// apps/body/dist/current-release-probe.js
|
|
45748
46461
|
import { spawn as spawn13 } from "node:child_process";
|
|
45749
|
-
import { dirname as dirname26, join as
|
|
46462
|
+
import { dirname as dirname26, join as join18 } from "node:path";
|
|
45750
46463
|
init_runtime();
|
|
45751
46464
|
init_self_update();
|
|
45752
46465
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
@@ -45790,7 +46503,7 @@ function outcomeFromReport(report) {
|
|
|
45790
46503
|
}
|
|
45791
46504
|
}
|
|
45792
46505
|
async function probeReleaseInSubprocess(input) {
|
|
45793
|
-
const bundleDir =
|
|
46506
|
+
const bundleDir = join18(input.layout.releasesRoot, input.releaseId);
|
|
45794
46507
|
const entrypoint = await resolveBundleEntrypoint(bundleDir);
|
|
45795
46508
|
if (!entrypoint) {
|
|
45796
46509
|
return { kind: "unavailable", reason: `release ${input.releaseId} has no runnable CLI entrypoint` };
|
|
@@ -45853,7 +46566,7 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
45853
46566
|
const runtime = await readRuntimeRecord(configPath);
|
|
45854
46567
|
const agent = runtimeAgentCommand(runtime);
|
|
45855
46568
|
const config = loadBodyConfig({
|
|
45856
|
-
workspaceRoot:
|
|
46569
|
+
workspaceRoot: join18(dirname26(configPath), "workspace"),
|
|
45857
46570
|
llmEnvFile: runtime.llmEnvFile,
|
|
45858
46571
|
env: { ...env, BUZZ_AGENT_BIN: agent.command, BUZZ_DEV_MCP_BIN: runtime.mcpBinary },
|
|
45859
46572
|
agent
|
|
@@ -45879,14 +46592,14 @@ async function runUpdateProbeCommand(args, options = {}) {
|
|
|
45879
46592
|
sandboxRequired: runtime.sandbox !== "off",
|
|
45880
46593
|
sandboxUnavailableDetail: sandbox.advisory,
|
|
45881
46594
|
// The successor's probe still holds `<runtimeDir>/update-functional-probe`.
|
|
45882
|
-
probeRoot:
|
|
46595
|
+
probeRoot: join18(runtimeDir, "current-release-probe")
|
|
45883
46596
|
}));
|
|
45884
46597
|
const report = outcome.kind === "served" ? { probe: "served" } : outcome.kind === "refused" ? { probe: "refused", status: outcome.status, reason: outcome.reason } : outcome.kind === "sandbox-unavailable" ? { probe: "sandbox-unavailable", reason: outcome.reason } : { probe: "failed", reason: outcome.reason };
|
|
45885
46598
|
write2(JSON.stringify(report));
|
|
45886
46599
|
}
|
|
45887
46600
|
|
|
45888
46601
|
// apps/body/dist/release-status.js
|
|
45889
|
-
import { readFile as
|
|
46602
|
+
import { readFile as readFile26, readdir as readdir10, rename as rename12, writeFile as writeFile21 } from "node:fs/promises";
|
|
45890
46603
|
import { resolve as resolve37 } from "node:path";
|
|
45891
46604
|
var DAEMON_RELEASE_STATUS_FILE = "release-status.json";
|
|
45892
46605
|
var RELEASE_VERSION = /^v\d+\.\d+\.\d+$/;
|
|
@@ -45908,7 +46621,7 @@ async function writeDaemonReleaseStatus(runtimeDir, agentPubkey, identity, optio
|
|
|
45908
46621
|
const temporary = `${target}.${status.pid}.tmp`;
|
|
45909
46622
|
await writeFile21(temporary, `${JSON.stringify(status, null, 2)}
|
|
45910
46623
|
`, { mode: 384 });
|
|
45911
|
-
await
|
|
46624
|
+
await rename12(temporary, target);
|
|
45912
46625
|
return status;
|
|
45913
46626
|
}
|
|
45914
46627
|
|
|
@@ -46028,15 +46741,16 @@ All other config via env vars (see config.ts).
|
|
|
46028
46741
|
process.exit(exitCode);
|
|
46029
46742
|
}
|
|
46030
46743
|
var daemonFailureRuntimeDir;
|
|
46744
|
+
var daemonLifecycle;
|
|
46031
46745
|
var SCRATCH_SWEEP_INTERVAL_MS = 6 * 60 * 6e4;
|
|
46032
46746
|
function runScratchSweepLogged(runtimeDir) {
|
|
46033
46747
|
void runScratchSweep(runtimeDir).catch((error) => console.error("[body] scratch sweep failed:", error));
|
|
46034
46748
|
}
|
|
46035
46749
|
var DaemonExitError = class extends Error {
|
|
46036
|
-
|
|
46037
|
-
constructor(message,
|
|
46750
|
+
reason;
|
|
46751
|
+
constructor(message, reason) {
|
|
46038
46752
|
super(message);
|
|
46039
|
-
this.
|
|
46753
|
+
this.reason = reason;
|
|
46040
46754
|
this.name = "DaemonExitError";
|
|
46041
46755
|
}
|
|
46042
46756
|
};
|
|
@@ -46117,7 +46831,9 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46117
46831
|
config.sandboxMaskPaths = [...config.sandboxMaskPaths ?? [], ...runtime.sandboxMaskPaths];
|
|
46118
46832
|
}
|
|
46119
46833
|
const controller = new AbortController();
|
|
46120
|
-
const
|
|
46834
|
+
const lifecycle = new HelperLifecycle({ controller });
|
|
46835
|
+
daemonLifecycle = lifecycle;
|
|
46836
|
+
const disposeStopSignals = lifecycle.installSignals();
|
|
46121
46837
|
const runtimeDir = dirname27(configPath);
|
|
46122
46838
|
const layout = beelineInstallLayout(process.env);
|
|
46123
46839
|
const notifier = new SystemdNotifier();
|
|
@@ -46139,15 +46855,17 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46139
46855
|
let update;
|
|
46140
46856
|
let pendingSuccessor = false;
|
|
46141
46857
|
let successorRolledBack = false;
|
|
46858
|
+
let attemptDeadlineAt;
|
|
46142
46859
|
if (layout) {
|
|
46143
46860
|
const settle = await settleUpdateAttemptOnStart(layout);
|
|
46144
46861
|
if (settle.kind === "rolled-back") {
|
|
46145
46862
|
await queueUpdateRollbackAlert(runtimeDir, settle.record.releaseId);
|
|
46146
46863
|
await drainRollbackAlert(runtime.rooms[0]?.channelId);
|
|
46147
46864
|
console.error(`[body] self-update ROLLED BACK: bundle ${describeIdentity(settle.record.to)} never confirmed healthy; restored ${settle.record.previousReleaseId ?? "previous release"}`);
|
|
46148
|
-
throw new DaemonExitError("stale unconfirmed release rolled back; supervisor must restart",
|
|
46865
|
+
throw new DaemonExitError("stale unconfirmed release rolled back; supervisor must restart", "rolled-back");
|
|
46149
46866
|
} else if (settle.kind === "pending") {
|
|
46150
46867
|
pendingSuccessor = true;
|
|
46868
|
+
attemptDeadlineAt = (await readUpdateAttempt(layout))?.confirmBy;
|
|
46151
46869
|
}
|
|
46152
46870
|
loadedRelease = await activeReleaseId(layout);
|
|
46153
46871
|
await clearUpdateRollbackAlertIfConfirmed(runtimeDir, loadedRelease);
|
|
@@ -46162,6 +46880,9 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46162
46880
|
requiredProbeIds: [...await runningRuntimeProbeIds(process.env), runtime.agent.publicKey]
|
|
46163
46881
|
});
|
|
46164
46882
|
if (pendingSuccessor && process.platform === "linux" && process.env.BEELINE_SYSTEMD_USER !== "0") {
|
|
46883
|
+
await rewriteAgentServiceUnit({ lock: (work) => withInstallLock(layout, work) }).catch((error) => {
|
|
46884
|
+
console.error(`[beeline] agent unit not converged: ${error instanceof Error ? error.message : String(error)}`);
|
|
46885
|
+
});
|
|
46165
46886
|
await installTrustySquireBrokerService().catch((error) => {
|
|
46166
46887
|
console.error(`[beeline] host Squire broker unit not converged: ${error instanceof Error ? error.message : String(error)}`);
|
|
46167
46888
|
});
|
|
@@ -46201,6 +46922,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46201
46922
|
return catalogRefresh;
|
|
46202
46923
|
};
|
|
46203
46924
|
let stoppingStatus = "daemon stopped";
|
|
46925
|
+
let exitReason;
|
|
46204
46926
|
try {
|
|
46205
46927
|
registryMcpBroker = new RegistryMcpHostBroker(
|
|
46206
46928
|
config.operatorHome,
|
|
@@ -46224,16 +46946,16 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46224
46946
|
daemonApi,
|
|
46225
46947
|
onConfigChanged: refreshCatalog,
|
|
46226
46948
|
onRestartRequested: () => {
|
|
46227
|
-
if (lifecycleRestart)
|
|
46949
|
+
if (lifecycleRestart || !lifecycle.quiesce("restart"))
|
|
46228
46950
|
return;
|
|
46229
46951
|
lifecycleRestart = (async () => {
|
|
46230
46952
|
const cancelled = await core.cancelActiveWorkForRestart();
|
|
46231
46953
|
stoppingStatus = cancelled ? "restart requested; active work cancelled" : "restart requested; no active work";
|
|
46232
|
-
await notifier.stopping(stoppingStatus);
|
|
46233
|
-
|
|
46954
|
+
await notifier.stopping(stoppingStatus).catch((error) => console.error("[thin-core] stopping notification failed:", error));
|
|
46955
|
+
lifecycle.stop("restart-requested");
|
|
46234
46956
|
})().catch((error) => {
|
|
46235
46957
|
console.error("[thin-core] requested restart failed:", error);
|
|
46236
|
-
|
|
46958
|
+
lifecycle.stop("restart-requested");
|
|
46237
46959
|
});
|
|
46238
46960
|
}
|
|
46239
46961
|
});
|
|
@@ -46242,6 +46964,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46242
46964
|
loadedVersion: loadedReleaseIdentity?.version,
|
|
46243
46965
|
runtimeDir,
|
|
46244
46966
|
interrupt: () => {
|
|
46967
|
+
lifecycle.quiesce("force-update");
|
|
46245
46968
|
core.setDrainDeadlineAt(Date.now() + 6e4);
|
|
46246
46969
|
return core.interruptForServerMinimum();
|
|
46247
46970
|
},
|
|
@@ -46260,25 +46983,25 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46260
46983
|
if (staggerMs > 0)
|
|
46261
46984
|
await new Promise((resolve40) => setTimeout(resolve40, staggerMs));
|
|
46262
46985
|
stoppingStatus = `server minimum installed; restarting onto ${desiredRelease}`;
|
|
46263
|
-
await notifier.stopping(stoppingStatus);
|
|
46264
|
-
|
|
46986
|
+
await notifier.stopping(stoppingStatus).catch((error) => console.error("[thin-core] stopping notification failed:", error));
|
|
46987
|
+
lifecycle.stop("update");
|
|
46265
46988
|
},
|
|
46266
46989
|
failed: (error) => {
|
|
46267
46990
|
console.error("[thin-core] forced helper update failed:", error);
|
|
46268
|
-
|
|
46991
|
+
lifecycle.stop("force-update-failed");
|
|
46269
46992
|
}
|
|
46270
46993
|
}) : void 0;
|
|
46271
46994
|
daemonApi.setForceUpdateListener((minVersion) => {
|
|
46272
46995
|
if (!forceUpdate) {
|
|
46273
46996
|
console.error("[thin-core] server requires a published helper, but this process has no install layout");
|
|
46274
|
-
|
|
46997
|
+
lifecycle.stop("stopped");
|
|
46275
46998
|
return;
|
|
46276
46999
|
}
|
|
46277
47000
|
forceUpdate.request(minVersion);
|
|
46278
47001
|
});
|
|
46279
47002
|
const updateDrain = update ? new ManagedUpdateDrain({
|
|
46280
47003
|
update,
|
|
46281
|
-
quiesceIfIdle: () => !forceUpdate?.active && core.quiesceForUpdateIfIdle(),
|
|
47004
|
+
quiesceIfIdle: () => !forceUpdate?.active && lifecycle.quiesceUpdateIfIdle(() => core.quiesceForUpdateIfIdle()),
|
|
46282
47005
|
activeTurnCount: () => core.activeTurnCount(),
|
|
46283
47006
|
restart: async ({ desiredRelease, drainDeadlineAt }) => {
|
|
46284
47007
|
core.setDrainDeadlineAt(drainDeadlineAt);
|
|
@@ -46286,20 +47009,29 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46286
47009
|
if (staggerMs > 0)
|
|
46287
47010
|
await new Promise((resolve40) => setTimeout(resolve40, staggerMs));
|
|
46288
47011
|
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; active work drained; intake quiesced`;
|
|
46289
|
-
await notifier.stopping(stoppingStatus);
|
|
46290
|
-
|
|
47012
|
+
await notifier.stopping(stoppingStatus).catch((error) => console.error("[thin-core] stopping notification failed:", error));
|
|
47013
|
+
lifecycle.stop("update");
|
|
46291
47014
|
},
|
|
46292
47015
|
waiting: async ({ desiredRelease }) => {
|
|
46293
47016
|
await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}`);
|
|
46294
47017
|
}
|
|
46295
47018
|
}) : void 0;
|
|
47019
|
+
const tickUpdate = async () => {
|
|
47020
|
+
try {
|
|
47021
|
+
await updateDrain?.tick();
|
|
47022
|
+
} catch (error) {
|
|
47023
|
+
console.error("[thin-core] managed restart failed; serving again:", error);
|
|
47024
|
+
lifecycle.resumeAfterFailedUpdate(() => core.resumeServing());
|
|
47025
|
+
}
|
|
47026
|
+
};
|
|
46296
47027
|
daemonApi.setHelperReleaseListener(({ version, sha }) => {
|
|
46297
47028
|
update?.notifyReleaseAvailable(`${version}:${sha}`);
|
|
46298
|
-
void
|
|
47029
|
+
void tickUpdate();
|
|
46299
47030
|
});
|
|
47031
|
+
daemonApi.onLiveOpen(() => void tickUpdate());
|
|
46300
47032
|
core.setInteractiveIdleListener(() => {
|
|
46301
47033
|
institutionalMemoryWorker?.wake();
|
|
46302
|
-
void
|
|
47034
|
+
void tickUpdate();
|
|
46303
47035
|
});
|
|
46304
47036
|
const result = await core.run({
|
|
46305
47037
|
signal: controller.signal,
|
|
@@ -46359,7 +47091,12 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46359
47091
|
console.log(`[thin-core] successor functional probe passed on exact release ${loadedRelease}: ${functionalProof?.harness ?? "unknown"} session/new + turn` + (functionalProof?.modelAnswer === "unavailable" ? ` (model answer unavailable: ${functionalProof.modelAnswerReason})` : ""));
|
|
46360
47092
|
}
|
|
46361
47093
|
try {
|
|
46362
|
-
await reportInterruptedTurns(runtimeDir, daemonApi, runtime.agent.publicKey)
|
|
47094
|
+
await retryBeforeReady(() => reportInterruptedTurns(runtimeDir, daemonApi, runtime.agent.publicKey), {
|
|
47095
|
+
onLinkOpen: (listener) => daemonApi.onLiveOpen(listener),
|
|
47096
|
+
extendStartTimeout: extendSystemdStartTimeout,
|
|
47097
|
+
...attemptDeadlineAt !== void 0 ? { deadlineAt: attemptDeadlineAt } : {},
|
|
47098
|
+
signal: controller.signal
|
|
47099
|
+
});
|
|
46363
47100
|
} catch (error) {
|
|
46364
47101
|
if (error instanceof DaemonApiError && error.status === 426 && error.code === "update_required" && forceUpdate?.pending) {
|
|
46365
47102
|
await forceUpdate.pending;
|
|
@@ -46407,18 +47144,18 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46407
47144
|
onProgress: async (status) => {
|
|
46408
47145
|
lastCoreStatus = status;
|
|
46409
47146
|
void drainRollbackAlert(core.activeRoomIds()[0] ?? runtime.rooms[0]?.channelId);
|
|
46410
|
-
await notifier.progress(`loaded_release=${loadedRelease ?? "development"}; ${status}`);
|
|
46411
|
-
await
|
|
47147
|
+
await notifier.progress(`loaded_release=${loadedRelease ?? "development"}; ${status}`).catch((error) => console.error("[thin-core] progress notification failed:", error));
|
|
47148
|
+
await tickUpdate();
|
|
46412
47149
|
}
|
|
46413
47150
|
});
|
|
46414
47151
|
if (result === "agent-removed") {
|
|
46415
47152
|
controller.abort();
|
|
46416
47153
|
const archivedRuntime = await retireRemovedAgent(runtime);
|
|
46417
|
-
|
|
47154
|
+
exitReason = "agent-removed";
|
|
46418
47155
|
console.log(`[beeline] agent ${runtime.agent.publicKey} removed; runtime archived at ${archivedRuntime}`);
|
|
46419
47156
|
}
|
|
46420
47157
|
} catch (error) {
|
|
46421
|
-
const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && // Named, so a sibling daemon's journal can say whose failure reverted
|
|
47158
|
+
const rolledBack = successorRolledBack || layout && pendingSuccessor && !ready && successorRollbackAllowed(error, attemptDeadlineAt) && // Named, so a sibling daemon's journal can say whose failure reverted
|
|
46422
47159
|
// the attempt it shares with this one.
|
|
46423
47160
|
await rollbackFailedSuccessor(layout, runtimeDir, {
|
|
46424
47161
|
probeId: runtime.agent.publicKey,
|
|
@@ -46441,6 +47178,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
46441
47178
|
await clearDaemonPidRecordIfPid(configPath, process.pid);
|
|
46442
47179
|
disposeStopSignals();
|
|
46443
47180
|
}
|
|
47181
|
+
return exitReason ?? lifecycle.stopReason ?? "stopped";
|
|
46444
47182
|
}
|
|
46445
47183
|
async function main() {
|
|
46446
47184
|
const args = process.argv.slice(2);
|
|
@@ -46529,6 +47267,7 @@ async function main() {
|
|
|
46529
47267
|
return;
|
|
46530
47268
|
}
|
|
46531
47269
|
if (command === "daemon") {
|
|
47270
|
+
installUnhandledRejectionGuard();
|
|
46532
47271
|
const configFlag = args.indexOf("--config");
|
|
46533
47272
|
const agentFlag = args.indexOf("--agent");
|
|
46534
47273
|
let configPath = configFlag >= 0 ? args[configFlag + 1] : void 0;
|
|
@@ -46547,11 +47286,12 @@ async function main() {
|
|
|
46547
47286
|
configPath = configs.find((candidate) => dirname27(candidate).endsWith(agentPubkey));
|
|
46548
47287
|
}
|
|
46549
47288
|
if (!configPath && agentPubkey) {
|
|
46550
|
-
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing service restart loop`,
|
|
47289
|
+
throw new DaemonExitError(`unknown agent ${agentPubkey}: no durable runtime exists; refusing service restart loop`, "unknown-agent");
|
|
46551
47290
|
}
|
|
46552
47291
|
if (!configPath)
|
|
46553
47292
|
throw new Error("daemon requires --config <runtime.json> or --agent <pubkey>");
|
|
46554
|
-
await runStoredDaemon(resolve39(configPath));
|
|
47293
|
+
const reason = await runStoredDaemon(resolve39(configPath));
|
|
47294
|
+
daemonLifecycle?.exit(reason);
|
|
46555
47295
|
return;
|
|
46556
47296
|
}
|
|
46557
47297
|
if (command === "update") {
|
|
@@ -46591,7 +47331,8 @@ async function main() {
|
|
|
46591
47331
|
main().catch(async (err) => {
|
|
46592
47332
|
if (process.argv[2] === "daemon") {
|
|
46593
47333
|
const layout = beelineInstallLayout(process.env);
|
|
46594
|
-
|
|
47334
|
+
const attempt = layout ? await readUpdateAttempt(layout).catch(() => void 0) : void 0;
|
|
47335
|
+
if (layout && successorRollbackAllowed(err, attempt?.confirmBy) && await rollbackFailedSuccessor(layout).catch(() => false)) {
|
|
46595
47336
|
console.error("[thin-core] successor failed during startup; previous release restored once");
|
|
46596
47337
|
}
|
|
46597
47338
|
}
|
|
@@ -46603,19 +47344,24 @@ main().catch(async (err) => {
|
|
|
46603
47344
|
} else {
|
|
46604
47345
|
console.error(import_picocolors3.default.red("[body] fatal:"), err);
|
|
46605
47346
|
}
|
|
46606
|
-
|
|
46607
|
-
|
|
47347
|
+
if (process.argv[2] !== "daemon")
|
|
47348
|
+
process.exit(1);
|
|
47349
|
+
let reason = err instanceof DaemonExitError ? err.reason : "failed";
|
|
47350
|
+
if (daemonFailureRuntimeDir && reason === "failed") {
|
|
46608
47351
|
try {
|
|
46609
|
-
const failure = await
|
|
47352
|
+
const failure = await settleDaemonStartFailure(daemonFailureRuntimeDir, err);
|
|
46610
47353
|
if (failure.distressed) {
|
|
46611
|
-
|
|
47354
|
+
reason = "distress";
|
|
46612
47355
|
console.error(`[thin-core] daemon start failed ${failure.count} times; service restart stopped. operator record: ${failure.path}`);
|
|
46613
47356
|
}
|
|
46614
47357
|
} catch (recordError) {
|
|
46615
47358
|
console.error("[thin-core] could not persist daemon distress record:", recordError);
|
|
46616
47359
|
}
|
|
46617
47360
|
}
|
|
46618
|
-
|
|
47361
|
+
if (daemonLifecycle)
|
|
47362
|
+
daemonLifecycle.exit(reason);
|
|
47363
|
+
else
|
|
47364
|
+
process.exit(HELPER_EXIT_CODES[reason]);
|
|
46619
47365
|
});
|
|
46620
47366
|
/*! Bundled license information:
|
|
46621
47367
|
|