usebeeline 0.0.128 → 0.0.130
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/usebeeline.mjs +512 -435
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -1080,6 +1080,7 @@ var init_agent_command = __esm({
|
|
|
1080
1080
|
|
|
1081
1081
|
// apps/body/dist/squire-host.js
|
|
1082
1082
|
import { spawn as spawn5 } from "node:child_process";
|
|
1083
|
+
import { createHash } from "node:crypto";
|
|
1083
1084
|
import { existsSync as existsSync5, lstatSync, mkdirSync } from "node:fs";
|
|
1084
1085
|
import { createRequire as createRequire3 } from "node:module";
|
|
1085
1086
|
import { homedir as homedir2 } from "node:os";
|
|
@@ -1120,8 +1121,16 @@ function squireBrokerSocketReady(socketPath) {
|
|
|
1120
1121
|
return false;
|
|
1121
1122
|
}
|
|
1122
1123
|
}
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1124
|
+
function squireAgentIdentity({ agentId, roomId }) {
|
|
1125
|
+
if (!agentId || !roomId)
|
|
1126
|
+
throw new Error("Squire agent and Room IDs are required");
|
|
1127
|
+
return `beeline:${createHash("sha256").update(JSON.stringify([agentId, roomId])).digest("hex")}`;
|
|
1128
|
+
}
|
|
1129
|
+
function squireFacadeLaunch(home, scope) {
|
|
1130
|
+
const env = {
|
|
1131
|
+
...squireHostRewriteEnv(home),
|
|
1132
|
+
TRUSTY_SQUIRE_AGENT_IDENTITY: squireAgentIdentity(scope)
|
|
1133
|
+
};
|
|
1125
1134
|
const meta = "usebeeline:npm";
|
|
1126
1135
|
if (meta.startsWith("beeline:")) {
|
|
1127
1136
|
const entry = process.argv[1];
|
|
@@ -3478,7 +3487,7 @@ var require_websocket = __commonJS({
|
|
|
3478
3487
|
var http = __require("http");
|
|
3479
3488
|
var net2 = __require("net");
|
|
3480
3489
|
var tls = __require("tls");
|
|
3481
|
-
var { randomBytes: randomBytes8, createHash:
|
|
3490
|
+
var { randomBytes: randomBytes8, createHash: createHash11 } = __require("crypto");
|
|
3482
3491
|
var { Duplex, Readable } = __require("stream");
|
|
3483
3492
|
var { URL: URL2 } = __require("url");
|
|
3484
3493
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
@@ -4146,7 +4155,7 @@ var require_websocket = __commonJS({
|
|
|
4146
4155
|
abortHandshake(websocket, socket, "Invalid Upgrade header");
|
|
4147
4156
|
return;
|
|
4148
4157
|
}
|
|
4149
|
-
const digest =
|
|
4158
|
+
const digest = createHash11("sha1").update(key + GUID).digest("base64");
|
|
4150
4159
|
if (res.headers["sec-websocket-accept"] !== digest) {
|
|
4151
4160
|
abortHandshake(websocket, socket, "Invalid Sec-WebSocket-Accept header");
|
|
4152
4161
|
return;
|
|
@@ -4515,7 +4524,7 @@ var require_websocket_server = __commonJS({
|
|
|
4515
4524
|
var EventEmitter2 = __require("events");
|
|
4516
4525
|
var http = __require("http");
|
|
4517
4526
|
var { Duplex } = __require("stream");
|
|
4518
|
-
var { createHash:
|
|
4527
|
+
var { createHash: createHash11 } = __require("crypto");
|
|
4519
4528
|
var extension2 = require_extension();
|
|
4520
4529
|
var PerMessageDeflate2 = require_permessage_deflate();
|
|
4521
4530
|
var subprotocol2 = require_subprotocol();
|
|
@@ -4822,7 +4831,7 @@ var require_websocket_server = __commonJS({
|
|
|
4822
4831
|
);
|
|
4823
4832
|
}
|
|
4824
4833
|
if (this._state > RUNNING) return abortHandshake(socket, 503);
|
|
4825
|
-
const digest =
|
|
4834
|
+
const digest = createHash11("sha1").update(key + GUID).digest("base64");
|
|
4826
4835
|
const headers = [
|
|
4827
4836
|
"HTTP/1.1 101 Switching Protocols",
|
|
4828
4837
|
"Upgrade: websocket",
|
|
@@ -22072,7 +22081,7 @@ __export(self_update_exports, {
|
|
|
22072
22081
|
writeUpdateAttemptFixture: () => writeUpdateAttemptFixture,
|
|
22073
22082
|
writeUpdateState: () => writeUpdateState
|
|
22074
22083
|
});
|
|
22075
|
-
import { createHash as
|
|
22084
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
22076
22085
|
import { constants as fsConstants2 } from "node:fs";
|
|
22077
22086
|
import { access as access2, chmod as chmod7, lstat as lstat4, mkdir as mkdir19, open, readFile as readFile15, rename as rename7, rm as rm9, symlink as symlink3, writeFile as writeFile14 } from "node:fs/promises";
|
|
22078
22087
|
import { spawn as spawn10 } from "node:child_process";
|
|
@@ -22305,7 +22314,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
22305
22314
|
if (!response.ok || !response.body) {
|
|
22306
22315
|
throw new Error(`downloading ${published.file} failed: HTTP ${response.status}`);
|
|
22307
22316
|
}
|
|
22308
|
-
const hash =
|
|
22317
|
+
const hash = createHash9("sha256");
|
|
22309
22318
|
const chunks = [];
|
|
22310
22319
|
for await (const chunk of response.body) {
|
|
22311
22320
|
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
@@ -22667,7 +22676,7 @@ var init_self_update = __esm({
|
|
|
22667
22676
|
if (previousAttempt?.status === "reverted" && !opts.force) {
|
|
22668
22677
|
const sameCommit = Boolean(previousAttempt.to.commit && previousAttempt.to.commit === bundle.commit);
|
|
22669
22678
|
const sameVersion = Boolean(previousAttempt.to.version && previousAttempt.to.version === bundle.version);
|
|
22670
|
-
if (sameCommit
|
|
22679
|
+
if (sameCommit && sameVersion) {
|
|
22671
22680
|
const line = `release ${describeIdentity(previousAttempt.to)} reverted after a failed served-turn proof; waiting for a newer publish or \`beeline update --force\``;
|
|
22672
22681
|
if (line !== this.lastVerdictLog) {
|
|
22673
22682
|
this.log(`[body] self-update: ${line}`);
|
|
@@ -26737,13 +26746,13 @@ async function applyRuntimeModelPreflight(config, agent, selection, validate = v
|
|
|
26737
26746
|
}
|
|
26738
26747
|
|
|
26739
26748
|
// apps/body/dist/model-catalog-sync.js
|
|
26740
|
-
import { createHash } from "node:crypto";
|
|
26749
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
26741
26750
|
import { readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
26742
26751
|
import { resolve as resolve6 } from "node:path";
|
|
26743
26752
|
var MODEL_CATALOG_PROBE_TIMEOUT_MS = 3e4;
|
|
26744
26753
|
var MODEL_CATALOG_HASH_FILE = "model-catalog.sha256";
|
|
26745
26754
|
function modelCatalogHash(options, selection, startupUnavailable) {
|
|
26746
|
-
return
|
|
26755
|
+
return createHash2("sha256").update(JSON.stringify({
|
|
26747
26756
|
options,
|
|
26748
26757
|
selection: selection ?? null,
|
|
26749
26758
|
startupUnavailable: startupUnavailable ?? null
|
|
@@ -27963,7 +27972,7 @@ var StdioSquireMcpClient = class {
|
|
|
27963
27972
|
initialized;
|
|
27964
27973
|
closed = false;
|
|
27965
27974
|
log;
|
|
27966
|
-
constructor(options
|
|
27975
|
+
constructor(options) {
|
|
27967
27976
|
this.options = options;
|
|
27968
27977
|
this.log = options.log ?? (() => {
|
|
27969
27978
|
});
|
|
@@ -28007,8 +28016,13 @@ var StdioSquireMcpClient = class {
|
|
|
28007
28016
|
return this.initialized;
|
|
28008
28017
|
}
|
|
28009
28018
|
initialize() {
|
|
28010
|
-
const launch = squireFacadeLaunch(homedir4());
|
|
28011
|
-
const child = (this.options.spawn ?? spawn8)(this.options.command ?? launch.command, [...this.options.args ?? launch.args], {
|
|
28019
|
+
const launch = squireFacadeLaunch(homedir4(), this.options.scope);
|
|
28020
|
+
const child = (this.options.spawn ?? spawn8)(this.options.command ?? launch.command, [...this.options.args ?? launch.args], {
|
|
28021
|
+
env: {
|
|
28022
|
+
...this.options.env ?? { ...squireConnectProcessEnv(), ...launch.env },
|
|
28023
|
+
TRUSTY_SQUIRE_AGENT_IDENTITY: launch.env.TRUSTY_SQUIRE_AGENT_IDENTITY
|
|
28024
|
+
}
|
|
28025
|
+
});
|
|
28012
28026
|
this.child = child;
|
|
28013
28027
|
this.buffer = "";
|
|
28014
28028
|
child.stdout.setEncoding("utf8");
|
|
@@ -28085,12 +28099,12 @@ var StdioSquireMcpClient = class {
|
|
|
28085
28099
|
}
|
|
28086
28100
|
}
|
|
28087
28101
|
};
|
|
28088
|
-
function defaultSquireMcpClient() {
|
|
28089
|
-
return new StdioSquireMcpClient();
|
|
28102
|
+
function defaultSquireMcpClient(scope) {
|
|
28103
|
+
return new StdioSquireMcpClient({ scope });
|
|
28090
28104
|
}
|
|
28091
28105
|
|
|
28092
28106
|
// apps/body/dist/registry-mcp.js
|
|
28093
|
-
import { createHash as
|
|
28107
|
+
import { createHash as createHash3, randomBytes } from "node:crypto";
|
|
28094
28108
|
import { lookup } from "node:dns/promises";
|
|
28095
28109
|
import { createRequire as createRequire4 } from "node:module";
|
|
28096
28110
|
import { chmodSync, existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -28101,13 +28115,28 @@ import { createInterface as createInterface4 } from "node:readline";
|
|
|
28101
28115
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
28102
28116
|
|
|
28103
28117
|
// apps/body/dist/mcp-route-class.js
|
|
28118
|
+
import { isIP } from "node:net";
|
|
28104
28119
|
var CODE_OWNED_HOST_MCP_NAMES = ["squire"];
|
|
28105
28120
|
var MCP_ROUTE_CLASS_KEY = "beeline_route";
|
|
28106
28121
|
var MCP_ROUTE_HOST = "host";
|
|
28107
28122
|
function isCodeOwnedHostMcpName(name) {
|
|
28108
28123
|
return CODE_OWNED_HOST_MCP_NAMES.includes(name.trim().toLowerCase());
|
|
28109
28124
|
}
|
|
28110
|
-
function classifyImportedMcpServer(
|
|
28125
|
+
function classifyImportedMcpServer(input) {
|
|
28126
|
+
const declaration = input.declaration;
|
|
28127
|
+
if (!declaration || isCodeOwnedHostMcpName(input.name))
|
|
28128
|
+
return "host";
|
|
28129
|
+
const publicKeys = /* @__PURE__ */ new Set(["url", "enabled", "startup_timeout_sec", "tool_timeout_sec"]);
|
|
28130
|
+
if (Object.keys(declaration).some((key) => !publicKeys.has(key)))
|
|
28131
|
+
return "host";
|
|
28132
|
+
if (typeof declaration.url !== "string")
|
|
28133
|
+
return "host";
|
|
28134
|
+
try {
|
|
28135
|
+
const url = new URL(declaration.url);
|
|
28136
|
+
if (url.protocol === "https:" && url.hostname.includes(".") && !url.hostname.endsWith(".local") && !isIP(url.hostname) && !url.username && !url.password && !url.search && !url.hash)
|
|
28137
|
+
return "local";
|
|
28138
|
+
} catch {
|
|
28139
|
+
}
|
|
28111
28140
|
return "host";
|
|
28112
28141
|
}
|
|
28113
28142
|
function hasSquireBrokerEnvironment(declaration) {
|
|
@@ -28458,7 +28487,7 @@ async function installRegistryMcp(input) {
|
|
|
28458
28487
|
if (typeof client.client_id !== "string")
|
|
28459
28488
|
throw new Error("dynamic client registration returned no client id");
|
|
28460
28489
|
const verifier = base64url(randomBytes(48));
|
|
28461
|
-
const challenge3 = base64url(
|
|
28490
|
+
const challenge3 = base64url(createHash3("sha256").update(verifier).digest());
|
|
28462
28491
|
const resourceIndicator = safeUrl(resource.resource) ?? remoteUrl;
|
|
28463
28492
|
const scope = Array.isArray(resource.scopes_supported) ? resource.scopes_supported.filter((item) => typeof item === "string").join(" ") : challengeParameter(initialize.headers.get("www-authenticate"), "scope");
|
|
28464
28493
|
const authorizationUrl = new URL(authorizationEndpoint);
|
|
@@ -29004,7 +29033,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
29004
29033
|
})();
|
|
29005
29034
|
}
|
|
29006
29035
|
squire() {
|
|
29007
|
-
this.mcp ??= defaultSquireMcpClient();
|
|
29036
|
+
this.mcp ??= defaultSquireMcpClient({ agentId: this.agentId, roomId: "connector" });
|
|
29008
29037
|
return this.mcp;
|
|
29009
29038
|
}
|
|
29010
29039
|
async handle(assignment) {
|
|
@@ -30053,6 +30082,7 @@ var DaemonApiClient = class {
|
|
|
30053
30082
|
connectorAssignmentListener;
|
|
30054
30083
|
cornerCompleteListener;
|
|
30055
30084
|
cornerRestartListener;
|
|
30085
|
+
helperReleaseListener;
|
|
30056
30086
|
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
|
|
30057
30087
|
this.baseUrl = baseUrl;
|
|
30058
30088
|
this.daemonToken = daemonToken;
|
|
@@ -30127,6 +30157,9 @@ var DaemonApiClient = class {
|
|
|
30127
30157
|
setCornerRestartListener(listener) {
|
|
30128
30158
|
this.cornerRestartListener = listener;
|
|
30129
30159
|
}
|
|
30160
|
+
setHelperReleaseListener(listener) {
|
|
30161
|
+
this.helperReleaseListener = listener;
|
|
30162
|
+
}
|
|
30130
30163
|
updateLiveCursor(roomId, cursor3) {
|
|
30131
30164
|
const room = this.liveRooms.get(roomId);
|
|
30132
30165
|
if (room && cursor3)
|
|
@@ -30180,6 +30213,10 @@ var DaemonApiClient = class {
|
|
|
30180
30213
|
if (!value || typeof value !== "object")
|
|
30181
30214
|
return;
|
|
30182
30215
|
const event = value;
|
|
30216
|
+
if (event.type === "helper-release" && typeof event.version === "string" && typeof event.sha === "string") {
|
|
30217
|
+
this.helperReleaseListener?.({ version: event.version, sha: event.sha });
|
|
30218
|
+
return;
|
|
30219
|
+
}
|
|
30183
30220
|
if (event.type === "rooms-changed") {
|
|
30184
30221
|
this.roomsChangedListener?.(membershipChange(event));
|
|
30185
30222
|
return;
|
|
@@ -30321,7 +30358,7 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
30321
30358
|
|
|
30322
30359
|
// apps/body/dist/room-runtime.js
|
|
30323
30360
|
import { execFile as execFile9 } from "node:child_process";
|
|
30324
|
-
import { createHash as
|
|
30361
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
30325
30362
|
import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "node:fs";
|
|
30326
30363
|
import { mkdir as mkdir15, realpath as realpath2, rm as rm7 } from "node:fs/promises";
|
|
30327
30364
|
import { dirname as dirname15, relative as relative3, resolve as resolve25, sep as sep2 } from "node:path";
|
|
@@ -30329,7 +30366,7 @@ import { promisify as promisify5 } from "node:util";
|
|
|
30329
30366
|
|
|
30330
30367
|
// apps/body/dist/grant-runner.js
|
|
30331
30368
|
import { execFile as execFile5 } from "node:child_process";
|
|
30332
|
-
import { createHash as
|
|
30369
|
+
import { createHash as createHash4, randomBytes as randomBytes7 } from "node:crypto";
|
|
30333
30370
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
30334
30371
|
import { createServer as createServer2 } from "node:http";
|
|
30335
30372
|
import { dirname as dirname8, resolve as resolve12 } from "node:path";
|
|
@@ -31260,7 +31297,7 @@ ${ROOM_WRITE_REFUSED_NOTE}` : outcome.output, secrets), cap);
|
|
|
31260
31297
|
if (!script || script.path !== argument.path) {
|
|
31261
31298
|
throw new Error(`${argument.path} is a script no human has read: this grant was approved without it. Ask with request_grant kind=command so the card carries what the command runs.`);
|
|
31262
31299
|
}
|
|
31263
|
-
if (
|
|
31300
|
+
if (createHash4("sha256").update(bytes).digest("hex") !== script.sha256) {
|
|
31264
31301
|
throw new Error(`${argument.path} changed after it was approved, so the approval no longer covers it. Ask with request_grant kind=command again so a human reads what runs now.`);
|
|
31265
31302
|
}
|
|
31266
31303
|
}
|
|
@@ -31816,7 +31853,7 @@ async function runServerCommandIntake(options) {
|
|
|
31816
31853
|
|
|
31817
31854
|
// apps/body/dist/monolith-corner-turn.js
|
|
31818
31855
|
import { execFile as execFile8 } from "node:child_process";
|
|
31819
|
-
import { createHash as
|
|
31856
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
31820
31857
|
import { mkdir as mkdir14, readFile as readFile12, writeFile as writeFile11 } from "node:fs/promises";
|
|
31821
31858
|
import { homedir as homedir11, hostname as hostname2 } from "node:os";
|
|
31822
31859
|
import { dirname as dirname14, join as join13, resolve as resolve24 } from "node:path";
|
|
@@ -32621,7 +32658,7 @@ function stringify(obj, { maxDepth = 1e3, numbersAsFloat = false } = {}) {
|
|
|
32621
32658
|
}
|
|
32622
32659
|
|
|
32623
32660
|
// apps/body/dist/agent-home.js
|
|
32624
|
-
import { createHash as
|
|
32661
|
+
import { createHash as createHash5, randomUUID as randomUUID5 } from "node:crypto";
|
|
32625
32662
|
import { chmod as chmod3, copyFile, lstat as lstat2, mkdir as mkdir6, readFile as readFile9, readlink, readdir as readdir3, realpath, rename as rename3, rm as rm4, symlink, unlink as unlink3, writeFile as writeFile7 } from "node:fs/promises";
|
|
32626
32663
|
import { homedir as homedir9 } from "node:os";
|
|
32627
32664
|
import { basename as basename4, dirname as dirname10, join as join9, relative as relative2, resolve as resolve17, sep } from "node:path";
|
|
@@ -32763,7 +32800,7 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
32763
32800
|
"Tag another agent only when you need something from them: a question, a handoff, a task. Never tag to acknowledge, agree, or say you are ready. If nothing is actionable, do not reply.",
|
|
32764
32801
|
"Tag the user only when you need a decision or input, or when the task they asked for is finished. Never tag for progress, acknowledgement, or questions the transcript already answers.",
|
|
32765
32802
|
"If a shell command is refused, say so plainly rather than retrying it, and continue with read-only inspection: call beeline-readonly-mcp.search_text to find code and beeline-readonly-mcp.read_file to read it. Use CodeGraph first when it is available for indexed code relationships.",
|
|
32766
|
-
"
|
|
32803
|
+
"An agent may use its own owner\u2019s tools and resources regardless of who started the turn or whether yolo is on. Access to another person\u2019s files or tools outside the sandbox asks that resource owner. Wallet access keeps its separate approval rule. The two command hard stops remain: credential files named by argv and scripts nobody has read. Delegation and follow-up work retain the original requester. Network web search is enabled.",
|
|
32767
32804
|
BEELINE_AMBIENT_CONNECTOR_CAPABILITY,
|
|
32768
32805
|
"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.",
|
|
32769
32806
|
"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.",
|
|
@@ -32782,7 +32819,7 @@ var BEELINE_DM_CAPABILITIES = [
|
|
|
32782
32819
|
"This is a private direct-message conversation with one person. Every message they send is addressed to you; reply without tagging.",
|
|
32783
32820
|
"This Room is strictly conversational: there is no repository binding and no corner can be opened from here.",
|
|
32784
32821
|
"The repository filesystem is read-only in this session.",
|
|
32785
|
-
"
|
|
32822
|
+
"An agent may use its own owner\u2019s tools and resources regardless of who started the turn or whether yolo is on. Access to another person\u2019s files or tools outside the sandbox asks that resource owner. Wallet access keeps its separate approval rule. The two command hard stops remain: credential files named by argv and scripts nobody has read. Delegation and follow-up work retain the original requester. Network web search is enabled.",
|
|
32786
32823
|
BEELINE_AMBIENT_CONNECTOR_CAPABILITY,
|
|
32787
32824
|
"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.",
|
|
32788
32825
|
"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.",
|
|
@@ -33201,14 +33238,14 @@ function grantedSquireHostRouteNames(granted, declarations = {}) {
|
|
|
33201
33238
|
function hostRouteEnv(hostHome) {
|
|
33202
33239
|
return { XDG_CONFIG_HOME: join8(resolve15(hostHome), ".config") };
|
|
33203
33240
|
}
|
|
33204
|
-
function rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile) {
|
|
33241
|
+
function rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile, squireScope) {
|
|
33205
33242
|
const next = { ...declaration };
|
|
33206
33243
|
delete next[MCP_ROUTE_CLASS_KEY];
|
|
33207
33244
|
const explicitResourceTarget = typeof next[MCP_RESOURCE_TARGET_KEY] === "string" ? next[MCP_RESOURCE_TARGET_KEY] : void 0;
|
|
33208
33245
|
const transportGate = next[MCP_RESOURCE_GATE_KEY] === MCP_RESOURCE_GATE_TRANSPORT;
|
|
33209
33246
|
delete next[MCP_RESOURCE_TARGET_KEY];
|
|
33210
33247
|
delete next[MCP_RESOURCE_GATE_KEY];
|
|
33211
|
-
const launch = isSquireDeclaration(name, declaration) ? squireFacadeLaunch(hostHome) : void 0;
|
|
33248
|
+
const launch = isSquireDeclaration(name, declaration) ? squireFacadeLaunch(hostHome, requiredSquireScope(squireScope)) : void 0;
|
|
33212
33249
|
const routeEnv = launch?.env ?? hostRouteEnv(hostHome);
|
|
33213
33250
|
const gooseShape = "cmd" in declaration && !("command" in declaration);
|
|
33214
33251
|
if (launch) {
|
|
@@ -33240,7 +33277,12 @@ function rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile
|
|
|
33240
33277
|
}
|
|
33241
33278
|
return next;
|
|
33242
33279
|
}
|
|
33243
|
-
function
|
|
33280
|
+
function requiredSquireScope(scope) {
|
|
33281
|
+
if (!scope)
|
|
33282
|
+
throw new Error("Squire agent and Room scope is required");
|
|
33283
|
+
return scope;
|
|
33284
|
+
}
|
|
33285
|
+
function rewriteGrantedHostRoutes(declarations, granted, hostHome, resourceAuthFile, squireScope) {
|
|
33244
33286
|
const allowed = new Set(granted);
|
|
33245
33287
|
const rewritten = {};
|
|
33246
33288
|
for (const [name, declaration] of Object.entries(declarations)) {
|
|
@@ -33248,17 +33290,17 @@ function rewriteGrantedHostRoutes(declarations, granted, hostHome, resourceAuthF
|
|
|
33248
33290
|
continue;
|
|
33249
33291
|
if (classifyImportedMcpServer({ name, declaration }) !== "host")
|
|
33250
33292
|
continue;
|
|
33251
|
-
rewritten[name] = rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile);
|
|
33293
|
+
rewritten[name] = rewriteHostMcpDeclaration(name, declaration, hostHome, resourceAuthFile, squireScope);
|
|
33252
33294
|
}
|
|
33253
33295
|
for (const name of granted) {
|
|
33254
33296
|
if (rewritten[name] || !isCodeOwnedHostMcpName(name))
|
|
33255
33297
|
continue;
|
|
33256
|
-
rewritten[name] = rewriteHostMcpDeclaration(name, {}, hostHome, resourceAuthFile);
|
|
33298
|
+
rewritten[name] = rewriteHostMcpDeclaration(name, {}, hostHome, resourceAuthFile, squireScope);
|
|
33257
33299
|
}
|
|
33258
33300
|
return rewritten;
|
|
33259
33301
|
}
|
|
33260
|
-
function grantedHostRouteWires(granted, hostHome, declarations = {}, resourceAuthFile) {
|
|
33261
|
-
const routes = rewriteGrantedHostRoutes(declarations, granted, hostHome, resourceAuthFile);
|
|
33302
|
+
function grantedHostRouteWires(granted, hostHome, declarations = {}, resourceAuthFile, squireScope) {
|
|
33303
|
+
const routes = rewriteGrantedHostRoutes(declarations, granted, hostHome, resourceAuthFile, squireScope);
|
|
33262
33304
|
return Object.entries(routes).flatMap(([name, declaration]) => {
|
|
33263
33305
|
const command = typeof declaration.command === "string" && declaration.command || typeof declaration.cmd === "string" && declaration.cmd || "";
|
|
33264
33306
|
if (!command)
|
|
@@ -34043,7 +34085,7 @@ async function prepareRoomAgentHome(input) {
|
|
|
34043
34085
|
console.error(`[body] could not repair shared credentials under ${root}:`, error);
|
|
34044
34086
|
}
|
|
34045
34087
|
const prior = agentHomeProvisionQueues.get(root) ?? Promise.resolve();
|
|
34046
|
-
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed === true || Boolean(input.resourceAuthFile), input.sharedSkills ?? [], agentSkillDir(input.agentKind), input.openRouterRouting, input.isReviewer ?? false, input.grantedHostRoutes ?? [], input.extraHostRoutes ?? {}, input.agentKind, input.resourceAuthFile));
|
|
34088
|
+
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed === true || Boolean(input.resourceAuthFile), input.sharedSkills ?? [], agentSkillDir(input.agentKind), input.openRouterRouting, input.isReviewer ?? false, input.grantedHostRoutes ?? [], input.extraHostRoutes ?? {}, input.agentKind, input.resourceAuthFile, input.squireScope));
|
|
34047
34089
|
agentHomeProvisionQueues.set(root, provision);
|
|
34048
34090
|
try {
|
|
34049
34091
|
await provision;
|
|
@@ -34053,7 +34095,7 @@ async function prepareRoomAgentHome(input) {
|
|
|
34053
34095
|
}
|
|
34054
34096
|
return roomAgentHomeEnv(root);
|
|
34055
34097
|
}
|
|
34056
|
-
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting, isReviewer, grantedHostRoutes, extraHostRoutes, agentKind, resourceAuthFile) {
|
|
34098
|
+
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting, isReviewer, grantedHostRoutes, extraHostRoutes, agentKind, resourceAuthFile, squireScope) {
|
|
34057
34099
|
const managedSkills = [
|
|
34058
34100
|
{ name: DRAW_AVATAR_SKILL_NAME, content: drawAvatarSkillMarkdown(skillReleaseId) },
|
|
34059
34101
|
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
|
|
@@ -34137,19 +34179,19 @@ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, fa
|
|
|
34137
34179
|
throw error;
|
|
34138
34180
|
console.warn("[body] claude web-search settings provisioning failed:", error);
|
|
34139
34181
|
}
|
|
34140
|
-
await applyGrantedHostRoutes(root, operatorHome, grantedHostRoutes, agentKind, failClosed, resourceAuthFile, extraHostRoutes);
|
|
34182
|
+
await applyGrantedHostRoutes(root, operatorHome, grantedHostRoutes, agentKind, failClosed, resourceAuthFile, extraHostRoutes, squireScope);
|
|
34141
34183
|
await provisionPiCustomModelConfig(root, operatorHome, failClosed, openRouterRouting);
|
|
34142
34184
|
}
|
|
34143
34185
|
function appliesToHarness(selected, harness) {
|
|
34144
34186
|
return !selected || selected === harness;
|
|
34145
34187
|
}
|
|
34146
|
-
async function applyGrantedHostRoutes(root, operatorHome, granted, agentKind, failClosed, resourceAuthFile, extraHostRoutes = {}) {
|
|
34188
|
+
async function applyGrantedHostRoutes(root, operatorHome, granted, agentKind, failClosed, resourceAuthFile, extraHostRoutes = {}, squireScope) {
|
|
34147
34189
|
if (granted.length === 0)
|
|
34148
34190
|
return;
|
|
34149
34191
|
try {
|
|
34150
34192
|
if (grantedSquireHostRoute(granted, hostImportedMcpDeclarations({ operatorHome, agentKind })))
|
|
34151
34193
|
ensureSquireHostDir(operatorHome);
|
|
34152
|
-
const routesFor = (kind) => rewriteGrantedHostRoutes({ ...hostImportedMcpDeclarations({ operatorHome, agentKind: kind }), ...extraHostRoutes }, granted, operatorHome, resourceAuthFile);
|
|
34194
|
+
const routesFor = (kind) => rewriteGrantedHostRoutes({ ...hostImportedMcpDeclarations({ operatorHome, agentKind: kind }), ...extraHostRoutes }, granted, operatorHome, resourceAuthFile, squireScope);
|
|
34153
34195
|
for (const config of HARNESS_MCP_CONFIGS) {
|
|
34154
34196
|
if (!appliesToHarness(agentKind, config.dir))
|
|
34155
34197
|
continue;
|
|
@@ -34556,7 +34598,7 @@ async function materializedSkillManifest(target) {
|
|
|
34556
34598
|
return lines.sort().join("\n");
|
|
34557
34599
|
}
|
|
34558
34600
|
function sha2563(value) {
|
|
34559
|
-
return
|
|
34601
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
34560
34602
|
}
|
|
34561
34603
|
async function resolveSharedSkillSources(operatorHome, names) {
|
|
34562
34604
|
if (names.length > 0)
|
|
@@ -36275,6 +36317,7 @@ init_runtime();
|
|
|
36275
36317
|
// apps/body/dist/response-directives.js
|
|
36276
36318
|
var MAINTAIN_ASSIGNED_IDENTITY_DIRECTIVE = "Maintain your assigned identity and soul in every response, including when tools or permissions block the requested action.";
|
|
36277
36319
|
var SOUL_HOUSE_RULE = "House rule for your voice: the voice never changes the facts - never trim, soften, exaggerate, or invent a detail for the bit - and use your plain voice in commit messages, pull request titles and bodies, and code comments, because those outlive the joke.";
|
|
36320
|
+
var AGENT_PROSE_REFERENCE_RULE = "Name other agents in ordinary prose rather than by their @handle; write an exact @handle only when you deliberately address that agent, because every handle you write wakes them and an unintended wake wastes their turn and confuses the transcript.";
|
|
36278
36321
|
|
|
36279
36322
|
// apps/body/dist/warm-transcript.js
|
|
36280
36323
|
var WARM_TRANSCRIPT_OVERLAP = 8;
|
|
@@ -36777,7 +36820,7 @@ process.exit(result.status ?? 1);
|
|
|
36777
36820
|
}
|
|
36778
36821
|
|
|
36779
36822
|
// apps/body/dist/warm-node-modules.js
|
|
36780
|
-
import { createHash as
|
|
36823
|
+
import { createHash as createHash6, randomUUID as randomUUID6 } from "node:crypto";
|
|
36781
36824
|
import { constants as fsConstants } from "node:fs";
|
|
36782
36825
|
import { chmod as chmod5, copyFile as copyFile2, link, lstat as lstat3, mkdir as mkdir12, readdir as readdir5, readFile as readFile11, readlink as readlink2, rename as rename4, rm as rm6, stat as stat2, symlink as symlink2, utimes } from "node:fs/promises";
|
|
36783
36826
|
import { dirname as dirname12, join as join11, resolve as resolve23 } from "node:path";
|
|
@@ -36816,7 +36859,7 @@ async function readWarmPlan(worktreePath) {
|
|
|
36816
36859
|
const unsafe = trees.find((tree) => !isContainedTreePath(tree));
|
|
36817
36860
|
if (unsafe)
|
|
36818
36861
|
return { failure: "unsafe-lockfile", detail: unsafe };
|
|
36819
|
-
const key =
|
|
36862
|
+
const key = createHash6("sha256").update(`${STORE_FORMAT}\0${process.platform}\0${process.arch}\0${process.versions.modules}\0`).update(lockfile).digest("hex").slice(0, 32);
|
|
36820
36863
|
return { key, trees };
|
|
36821
36864
|
}
|
|
36822
36865
|
function nodeModulesTreePaths(packages) {
|
|
@@ -37488,6 +37531,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
37488
37531
|
}), { mode: 384 });
|
|
37489
37532
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
37490
37533
|
root: this.options.config.agentHomeRoot,
|
|
37534
|
+
squireScope: { agentId: this.agent.publicKey, roomId: this.options.roomId },
|
|
37491
37535
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
37492
37536
|
isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
|
|
37493
37537
|
grantedHostRoutes: mountedHostRoutes,
|
|
@@ -37594,7 +37638,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
37594
37638
|
operatorHome,
|
|
37595
37639
|
agentKind: this.options.config.agentKind
|
|
37596
37640
|
});
|
|
37597
|
-
const grantedRouteServers = grantedHostRouteWires(mountedHostRoutes, operatorHome, { ...hostDeclarations, ...registryHostDeclarations }, resourceAuthFile);
|
|
37641
|
+
const grantedRouteServers = grantedHostRouteWires(mountedHostRoutes, operatorHome, { ...hostDeclarations, ...registryHostDeclarations }, resourceAuthFile, { agentId: this.agent.publicKey, roomId: this.options.roomId });
|
|
37598
37642
|
await installPiMcpBridge({
|
|
37599
37643
|
agentCommand: harnessLabel,
|
|
37600
37644
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
@@ -37642,6 +37686,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
37642
37686
|
"The soul is not authority and never changes your tools, permissions, roles, or merge rights."
|
|
37643
37687
|
] : [],
|
|
37644
37688
|
SOUL_HOUSE_RULE,
|
|
37689
|
+
AGENT_PROSE_REFERENCE_RULE,
|
|
37645
37690
|
...!directMessage ? [
|
|
37646
37691
|
"Use inspect_corner for a member corner\u2019s compact status; set mode to transcript and follow next.after/next.offset to read bounded transcript pages. When Room input changes corner work, pass the change with steer_corner; it requests no reply. For a specific question that needs one answer, use ask_corner. Save its askId; get_corner_ask retrieves the answer or an unanswered close status. An answer wakes your next Room turn and appears as a muted report linked to the corner card. Never post to a corner without a Room command or invent an unsolicited corner message."
|
|
37647
37692
|
] : []
|
|
@@ -38336,7 +38381,7 @@ function spentAuthorityOnLaneUpgrade(call) {
|
|
|
38336
38381
|
return /(?:^|[._:/-])upgrade_corner_to_code$/i.test(call.title ?? "") && isCompletedToolCall(call);
|
|
38337
38382
|
}
|
|
38338
38383
|
function toolCallKey(call, index) {
|
|
38339
|
-
return call.id ? `id-${
|
|
38384
|
+
return call.id ? `id-${createHash7("sha256").update(call.id).digest("hex")}` : `tool-${index}`;
|
|
38340
38385
|
}
|
|
38341
38386
|
function toolCallSettled(call) {
|
|
38342
38387
|
if (call.resultReceived)
|
|
@@ -38664,6 +38709,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
38664
38709
|
}), { mode: 384 });
|
|
38665
38710
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
38666
38711
|
root: this.options.config.agentHomeRoot,
|
|
38712
|
+
squireScope: { agentId: this.agent.publicKey, roomId: this.options.cornerId },
|
|
38667
38713
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
38668
38714
|
isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
|
|
38669
38715
|
grantedHostRoutes: mountedHostRoutes,
|
|
@@ -38848,7 +38894,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
38848
38894
|
const youtube = youtubeMcpServer(this.options.config, this.options.youtubeAccessToken, resourceAuthFile);
|
|
38849
38895
|
if (youtube)
|
|
38850
38896
|
servers.push(youtube);
|
|
38851
|
-
const grantedRouteServers = grantedHostRouteWires(mountedHostRoutes, operatorHome, { ...hostDeclarations, ...registryHostDeclarations }, resourceAuthFile);
|
|
38897
|
+
const grantedRouteServers = grantedHostRouteWires(mountedHostRoutes, operatorHome, { ...hostDeclarations, ...registryHostDeclarations }, resourceAuthFile, { agentId: this.agent.publicKey, roomId: this.options.cornerId });
|
|
38852
38898
|
await installPiMcpBridge({
|
|
38853
38899
|
agentCommand: harnessLabel,
|
|
38854
38900
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
@@ -38858,7 +38904,8 @@ var MonolithCornerTurnLoop = class {
|
|
|
38858
38904
|
const identityInstructions = `Your Beeline identity is ${self?.name ?? this.agent.name}.`;
|
|
38859
38905
|
const personaInstructions = [
|
|
38860
38906
|
...persona?.instructions ? [`Human-authored Workspace persona: ${persona.name}. ${persona.instructions}`] : [],
|
|
38861
|
-
SOUL_HOUSE_RULE
|
|
38907
|
+
SOUL_HOUSE_RULE,
|
|
38908
|
+
AGENT_PROSE_REFERENCE_RULE
|
|
38862
38909
|
].join("\n");
|
|
38863
38910
|
this.turnIdentityInstructions = harnessHonorsSessionSystemPrompt(command) ? "" : [identityInstructions, personaInstructions].filter(Boolean).join("\n\n");
|
|
38864
38911
|
const opened = await this.client.sessionNew({
|
|
@@ -39091,7 +39138,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
39091
39138
|
const delivered = this.attachmentDir && allAttachments.length ? await deliverAttachments(allAttachments, join13(this.attachmentDir, requestId.replace(/[^\w-]/g, "_")), this.options.fetchImpl) : [];
|
|
39092
39139
|
const briefFileLines = await Promise.all((restored.brief?.attachments ?? []).map(async (file, index) => {
|
|
39093
39140
|
const entry = delivered.find((item) => item.attachment === briefAttachments[index]);
|
|
39094
|
-
const verified = entry?.path &&
|
|
39141
|
+
const verified = entry?.path && createHash7("sha256").update(await readFile12(entry.path)).digest("hex") === file.sha256;
|
|
39095
39142
|
return `- ${file.title} (${file.purpose}; ${file.required ? "required" : "optional"}; sha256 ${file.sha256}): ${verified ? entry.path : "UNAVAILABLE OR CONTENT MISMATCH"}`;
|
|
39096
39143
|
}));
|
|
39097
39144
|
const missingRequiredBriefFile = (restored.brief?.attachments ?? []).some((file, index) => file.required && briefFileLines[index]?.includes("UNAVAILABLE OR CONTENT MISMATCH"));
|
|
@@ -40012,7 +40059,7 @@ function cornerStartConfigKey(repository, objective) {
|
|
|
40012
40059
|
}
|
|
40013
40060
|
function cornerRepositoryCacheDir(supervisorRoot, remote) {
|
|
40014
40061
|
const normalizedRemote = roomCheckoutRemote(remote);
|
|
40015
|
-
const repositoryHash =
|
|
40062
|
+
const repositoryHash = createHash8("sha256").update(normalizedRemote).digest("hex").slice(0, 24);
|
|
40016
40063
|
return resolve25(supervisorRoot, "beeline", "repositories", `${repositoryHash}.git`);
|
|
40017
40064
|
}
|
|
40018
40065
|
function isStandingCornerStartFault(error) {
|
|
@@ -40689,7 +40736,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
40689
40736
|
return this.roomRoot(roomId);
|
|
40690
40737
|
const remote = roomCheckoutRemote(repository.remote);
|
|
40691
40738
|
const targetBranch = repository.targetBranch || "main";
|
|
40692
|
-
const checkoutId =
|
|
40739
|
+
const checkoutId = createHash8("sha256").update(`${roomId}\0${remote}\0${targetBranch}`).digest("hex").slice(0, 24);
|
|
40693
40740
|
const path = resolve25(this.runtime.supervisorRoot, "beeline", "room-checkouts", checkoutId);
|
|
40694
40741
|
await mkdir15(dirname15(path), { recursive: true, mode: 448 });
|
|
40695
40742
|
const token = remote.startsWith("https://github.com/") ? await this.options.daemonApi.execute("getRoomGitHubToken", { roomId }) : void 0;
|
|
@@ -41330,21 +41377,332 @@ init_runtime();
|
|
|
41330
41377
|
init_launchd();
|
|
41331
41378
|
init_systemd();
|
|
41332
41379
|
import { spawn as spawn11 } from "node:child_process";
|
|
41333
|
-
import { mkdir as
|
|
41334
|
-
import { dirname as dirname21, resolve as
|
|
41380
|
+
import { mkdir as mkdir22, rm as rm11, stat as stat5, writeFile as writeFile16 } from "node:fs/promises";
|
|
41381
|
+
import { dirname as dirname21, resolve as resolve32 } from "node:path";
|
|
41382
|
+
|
|
41383
|
+
// apps/body/dist/update-functional-probe.js
|
|
41384
|
+
import { mkdir as mkdir20, rm as rm10 } from "node:fs/promises";
|
|
41385
|
+
import { homedir as homedir15 } from "node:os";
|
|
41386
|
+
import { resolve as resolve30 } from "node:path";
|
|
41387
|
+
init_cursor_acp_bridge();
|
|
41388
|
+
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
41389
|
+
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
41390
|
+
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
41391
|
+
var UPDATE_PROBE_RETRY_DELAY_MS = 2e3;
|
|
41392
|
+
var ACP_ERROR_CODE = /\bACP error (-\d+):/;
|
|
41393
|
+
var ACP_PROMPT_INACTIVITY = /\bACP session\/prompt timed out after \d+ms of inactivity\b/;
|
|
41394
|
+
function isServerInternalCode(code) {
|
|
41395
|
+
return code === -32603 || code <= -32e3 && code >= -32099;
|
|
41396
|
+
}
|
|
41397
|
+
function classifyAcpTurnFailure(error) {
|
|
41398
|
+
const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
|
|
41399
|
+
if (!message)
|
|
41400
|
+
return void 0;
|
|
41401
|
+
if (ACP_PROMPT_INACTIVITY.test(message))
|
|
41402
|
+
return { kind: "prompt-inactivity" };
|
|
41403
|
+
const code = Number(ACP_ERROR_CODE.exec(message)?.[1]);
|
|
41404
|
+
if (Number.isFinite(code) && isServerInternalCode(code))
|
|
41405
|
+
return { kind: "server-internal", code };
|
|
41406
|
+
return void 0;
|
|
41407
|
+
}
|
|
41408
|
+
function sameAcpTurnFailure(one, other) {
|
|
41409
|
+
if (one.kind === "server-internal") {
|
|
41410
|
+
return other.kind === "server-internal" && one.code === other.code;
|
|
41411
|
+
}
|
|
41412
|
+
return other.kind === one.kind;
|
|
41413
|
+
}
|
|
41414
|
+
var UpdateFunctionalProbeError = class extends Error {
|
|
41415
|
+
reason;
|
|
41416
|
+
code = "BEELINE_UPDATE_FUNCTIONAL_PROBE_FAILED";
|
|
41417
|
+
/** Present when the turn failed on a provider refusal with an HTTP status. */
|
|
41418
|
+
providerRefusal;
|
|
41419
|
+
constructor(reason, detail, options) {
|
|
41420
|
+
super(`functional update probe failed (${reason}): ${detail}`, options);
|
|
41421
|
+
this.reason = reason;
|
|
41422
|
+
this.name = "UpdateFunctionalProbeError";
|
|
41423
|
+
this.providerRefusal = options?.providerRefusal;
|
|
41424
|
+
}
|
|
41425
|
+
};
|
|
41426
|
+
async function probeOutcome(run2) {
|
|
41427
|
+
try {
|
|
41428
|
+
await run2();
|
|
41429
|
+
return { kind: "served" };
|
|
41430
|
+
} catch (error) {
|
|
41431
|
+
if (error instanceof UpdateFunctionalProbeError && error.reason === "sandbox-unavailable") {
|
|
41432
|
+
return { kind: "sandbox-unavailable", reason: error.message };
|
|
41433
|
+
}
|
|
41434
|
+
if (error instanceof UpdateFunctionalProbeError && error.providerRefusal) {
|
|
41435
|
+
return { kind: "refused", ...error.providerRefusal };
|
|
41436
|
+
}
|
|
41437
|
+
return { kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
41438
|
+
}
|
|
41439
|
+
}
|
|
41440
|
+
function describeCurrentReleaseOutcome(outcome) {
|
|
41441
|
+
switch (outcome.kind) {
|
|
41442
|
+
case "served":
|
|
41443
|
+
return "the current release answered";
|
|
41444
|
+
case "refused":
|
|
41445
|
+
return `the current release got a different refusal (${outcome.reason})`;
|
|
41446
|
+
case "sandbox-unavailable":
|
|
41447
|
+
return `the current release has the same unavailable sandbox (${outcome.reason})`;
|
|
41448
|
+
case "model-unavailable":
|
|
41449
|
+
return `the current release cannot serve that model either (${outcome.reason})`;
|
|
41450
|
+
case "unavailable":
|
|
41451
|
+
return `the current release could not be compared (${outcome.reason})`;
|
|
41452
|
+
}
|
|
41453
|
+
}
|
|
41454
|
+
async function runUpdateFunctionalProbe(input) {
|
|
41455
|
+
const command = input.config.agentCommand ?? input.config.agentBinary;
|
|
41456
|
+
const harnessLabel = harnessIdentityLabel({
|
|
41457
|
+
kind: input.config.agentKind,
|
|
41458
|
+
command
|
|
41459
|
+
});
|
|
41460
|
+
const harness = input.config.agentKind ?? command;
|
|
41461
|
+
if (input.config.modelUnavailable) {
|
|
41462
|
+
const detail = input.config.modelUnavailable.detail;
|
|
41463
|
+
if (!input.compareWithCurrentRelease) {
|
|
41464
|
+
throw new UpdateFunctionalProbeError("model-unavailable", detail);
|
|
41465
|
+
}
|
|
41466
|
+
const current = await input.compareWithCurrentRelease({
|
|
41467
|
+
kind: "model-unavailable",
|
|
41468
|
+
reason: detail
|
|
41469
|
+
});
|
|
41470
|
+
if (current.kind !== "model-unavailable" && current.kind !== "unavailable") {
|
|
41471
|
+
throw new UpdateFunctionalProbeError("model-unavailable", `${detail}; ${describeCurrentReleaseOutcome(current)}`);
|
|
41472
|
+
}
|
|
41473
|
+
console.warn("[body] update probe: the configured model is unavailable to this release and the current release alike; that is the account catalog, not the bundle, so the probe passes without a model answer");
|
|
41474
|
+
return {
|
|
41475
|
+
harness,
|
|
41476
|
+
sandboxed: false,
|
|
41477
|
+
sessionStarted: false,
|
|
41478
|
+
turnCompleted: false,
|
|
41479
|
+
nativeTools: [],
|
|
41480
|
+
modelAnswer: "unavailable",
|
|
41481
|
+
modelAnswerReason: `${detail} (the current release cannot serve that model either)`
|
|
41482
|
+
};
|
|
41483
|
+
}
|
|
41484
|
+
if (input.sandboxRequired && !input.config.bwrapPath) {
|
|
41485
|
+
const detail = input.sandboxUnavailableDetail ?? "the configured bubblewrap boundary did not pass its startup self-test";
|
|
41486
|
+
if (!input.compareWithCurrentRelease) {
|
|
41487
|
+
throw new UpdateFunctionalProbeError("sandbox-unavailable", detail);
|
|
41488
|
+
}
|
|
41489
|
+
const current = await input.compareWithCurrentRelease({
|
|
41490
|
+
kind: "sandbox-unavailable",
|
|
41491
|
+
reason: detail
|
|
41492
|
+
});
|
|
41493
|
+
if (current.kind !== "sandbox-unavailable") {
|
|
41494
|
+
throw new UpdateFunctionalProbeError("sandbox-unavailable", `${detail}; ${describeCurrentReleaseOutcome(current)}`);
|
|
41495
|
+
}
|
|
41496
|
+
console.warn("[body] update probe: the sandbox is unavailable to this release and the current release alike; preserving fail-closed sandboxing and accepting the bundle as inconclusive");
|
|
41497
|
+
return {
|
|
41498
|
+
harness,
|
|
41499
|
+
sandboxed: false,
|
|
41500
|
+
sessionStarted: false,
|
|
41501
|
+
turnCompleted: false,
|
|
41502
|
+
nativeTools: [],
|
|
41503
|
+
modelAnswer: "unavailable",
|
|
41504
|
+
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
41505
|
+
};
|
|
41506
|
+
}
|
|
41507
|
+
const root = input.probeRoot ?? resolve30(input.runtimeDir, "update-functional-probe");
|
|
41508
|
+
const cwd = resolve30(root, "checkout");
|
|
41509
|
+
const homeRoot = resolve30(root, "agent-home");
|
|
41510
|
+
await rm10(root, { recursive: true, force: true });
|
|
41511
|
+
await mkdir20(cwd, { recursive: true, mode: 448 });
|
|
41512
|
+
let client;
|
|
41513
|
+
try {
|
|
41514
|
+
const agentEnv = {
|
|
41515
|
+
...input.config.agentEnv,
|
|
41516
|
+
...await prepareRoomAgentHome({
|
|
41517
|
+
root: homeRoot,
|
|
41518
|
+
operatorHome: input.config.operatorHome ?? homedir15(),
|
|
41519
|
+
sharedSkills: input.config.sharedSkills ?? [],
|
|
41520
|
+
...input.config.agentKind ? { agentKind: input.config.agentKind } : {},
|
|
41521
|
+
skillReleaseId: input.releaseId,
|
|
41522
|
+
failClosed: true,
|
|
41523
|
+
...openRouterRoutingInput({ ...input.config, openRouterRoutingCacheDir: openRouterRoutingCacheDir(input.runtimeDir) }, input.config.modelSelection)
|
|
41524
|
+
})
|
|
41525
|
+
};
|
|
41526
|
+
const selectedAgent = {
|
|
41527
|
+
kind: input.config.agentKind,
|
|
41528
|
+
command,
|
|
41529
|
+
args: input.config.agentArgs ?? []
|
|
41530
|
+
};
|
|
41531
|
+
let spawnCommand = {
|
|
41532
|
+
command,
|
|
41533
|
+
args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
|
|
41534
|
+
};
|
|
41535
|
+
let modelAnswer = {};
|
|
41536
|
+
let turnCompleted = true;
|
|
41537
|
+
if (input.config.bwrapPath) {
|
|
41538
|
+
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
41539
|
+
const operatorHome = input.config.operatorHome ?? homedir15();
|
|
41540
|
+
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
41541
|
+
await Promise.all(homeStateDirs.map((dir) => mkdir20(dir, { recursive: true })));
|
|
41542
|
+
spawnCommand = wrapAgentCommand({
|
|
41543
|
+
bwrapPath: input.config.bwrapPath,
|
|
41544
|
+
spec: {
|
|
41545
|
+
mode: "readonly",
|
|
41546
|
+
cwd,
|
|
41547
|
+
harnessStateDirs: stateDirs,
|
|
41548
|
+
harnessHomeStateDirs: homeStateDirs,
|
|
41549
|
+
maskPaths: credentialMaskPaths(input.config.sandboxMaskPaths, operatorHome),
|
|
41550
|
+
...tmpDir ? { tmpDir } : {}
|
|
41551
|
+
},
|
|
41552
|
+
command,
|
|
41553
|
+
args: spawnCommand.args
|
|
41554
|
+
});
|
|
41555
|
+
}
|
|
41556
|
+
client = new AcpClient({
|
|
41557
|
+
agentCommand: spawnCommand.command,
|
|
41558
|
+
agentArgs: spawnCommand.args,
|
|
41559
|
+
agentLabel: harnessLabel,
|
|
41560
|
+
agentEnv,
|
|
41561
|
+
agentCwd: cwd,
|
|
41562
|
+
autoApprovePermissions: true
|
|
41563
|
+
});
|
|
41564
|
+
const sessionTimeoutMs = input.sessionTimeoutMs ?? UPDATE_PROBE_SESSION_TIMEOUT_MS;
|
|
41565
|
+
const sessionOpenTimeoutMs = input.sessionOpenTimeoutMs ?? input.sessionTimeoutMs ?? UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS;
|
|
41566
|
+
const openSessionAndPrompt = async () => {
|
|
41567
|
+
const opened = await client.sessionNew({
|
|
41568
|
+
cwd,
|
|
41569
|
+
mcpServers: [],
|
|
41570
|
+
systemPrompt: "This is Beeline update validation. Answer only READY.",
|
|
41571
|
+
mode: "readonly",
|
|
41572
|
+
timeoutMs: sessionOpenTimeoutMs
|
|
41573
|
+
});
|
|
41574
|
+
if (input.config.modelSelection) {
|
|
41575
|
+
await applyAgentModelSelection(client, opened.sessionId, parseAdvertisedConfigOptions(opened.raw, input.config.modelSelection.model, isGrokAgentCommand(selectedAgent)), input.config.modelSelection);
|
|
41576
|
+
}
|
|
41577
|
+
return {
|
|
41578
|
+
sessionId: opened.sessionId,
|
|
41579
|
+
served: await client.sessionPrompt(opened.sessionId, "Reply READY.", input.turnTimeoutMs ?? UPDATE_PROBE_TURN_TIMEOUT_MS)
|
|
41580
|
+
};
|
|
41581
|
+
};
|
|
41582
|
+
try {
|
|
41583
|
+
await client.start(sessionTimeoutMs);
|
|
41584
|
+
try {
|
|
41585
|
+
const { sessionId, served } = await openSessionAndPrompt();
|
|
41586
|
+
if (served.agentText.trim()) {
|
|
41587
|
+
modelAnswer = { modelAnswer: "served" };
|
|
41588
|
+
} else {
|
|
41589
|
+
const explained = await explainEmptyAgentTurn({
|
|
41590
|
+
agentLabel: harnessLabel,
|
|
41591
|
+
agentEnv,
|
|
41592
|
+
sessionId,
|
|
41593
|
+
result: served
|
|
41594
|
+
});
|
|
41595
|
+
const modelSide = isAccountOrProviderRefusal(explained.record) || explained.record?.kind === "empty";
|
|
41596
|
+
if (!modelSide) {
|
|
41597
|
+
const refusal = explained.record?.kind === "error" && explained.record.status !== void 0 ? { status: explained.record.status, reason: explained.record.reason } : void 0;
|
|
41598
|
+
const detail = `the harness completed a session/prompt without an agent answer: ${explained.reason}`;
|
|
41599
|
+
if (!refusal || !input.compareWithCurrentRelease) {
|
|
41600
|
+
throw new UpdateFunctionalProbeError("turn-failed", detail, {
|
|
41601
|
+
...refusal ? { providerRefusal: refusal } : {}
|
|
41602
|
+
});
|
|
41603
|
+
}
|
|
41604
|
+
const current = await input.compareWithCurrentRelease({
|
|
41605
|
+
kind: "provider-refusal",
|
|
41606
|
+
reason: refusal.reason,
|
|
41607
|
+
refusal
|
|
41608
|
+
});
|
|
41609
|
+
if (current.kind !== "refused" || current.status !== refusal.status) {
|
|
41610
|
+
throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { providerRefusal: refusal });
|
|
41611
|
+
}
|
|
41612
|
+
const reason = `${explained.reason} (the current release gets the same ${refusal.status})`;
|
|
41613
|
+
console.warn(`[body] update probe: the provider refused this release and the current release alike (${refusal.reason}); that refusal is not this bundle's doing, so the probe passes as inconclusive`);
|
|
41614
|
+
modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: reason };
|
|
41615
|
+
} else {
|
|
41616
|
+
console.warn(`[body] update probe: the bundle reached the model boundary but the model answered nothing (${explained.reason}); that is the model's own answer on any release, so the probe passes without one`);
|
|
41617
|
+
modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: explained.reason };
|
|
41618
|
+
}
|
|
41619
|
+
}
|
|
41620
|
+
} catch (initialError) {
|
|
41621
|
+
if (initialError instanceof UpdateFunctionalProbeError)
|
|
41622
|
+
throw initialError;
|
|
41623
|
+
if (initialError instanceof ModelSelectionUnavailableError) {
|
|
41624
|
+
throw new UpdateFunctionalProbeError("model-unavailable", initialError.message, {
|
|
41625
|
+
cause: initialError
|
|
41626
|
+
});
|
|
41627
|
+
}
|
|
41628
|
+
let error = initialError;
|
|
41629
|
+
let detail = error instanceof Error ? error.message : String(error);
|
|
41630
|
+
let failure2 = classifyAcpTurnFailure(error);
|
|
41631
|
+
if (failure2) {
|
|
41632
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, input.retryDelayMs ?? UPDATE_PROBE_RETRY_DELAY_MS));
|
|
41633
|
+
try {
|
|
41634
|
+
const retried = await openSessionAndPrompt();
|
|
41635
|
+
if (retried.served.agentText.trim()) {
|
|
41636
|
+
turnCompleted = true;
|
|
41637
|
+
modelAnswer = { modelAnswer: "served" };
|
|
41638
|
+
console.warn(`[body] update probe: the probe turn failed once (${detail}) but a fresh retry answered; treating the first failure as the provider's, not this bundle's`);
|
|
41639
|
+
error = void 0;
|
|
41640
|
+
}
|
|
41641
|
+
} catch (retryError) {
|
|
41642
|
+
const retryDetail = retryError instanceof Error ? retryError.message : String(retryError);
|
|
41643
|
+
const retryFailure = classifyAcpTurnFailure(retryError);
|
|
41644
|
+
if (!retryFailure || !sameAcpTurnFailure(failure2, retryFailure)) {
|
|
41645
|
+
throw new UpdateFunctionalProbeError("turn-failed", retryDetail, {
|
|
41646
|
+
cause: retryError
|
|
41647
|
+
});
|
|
41648
|
+
}
|
|
41649
|
+
error = retryError;
|
|
41650
|
+
detail = retryDetail;
|
|
41651
|
+
failure2 = retryFailure;
|
|
41652
|
+
}
|
|
41653
|
+
}
|
|
41654
|
+
if (error === void 0) {
|
|
41655
|
+
} else if (!failure2 || !input.compareWithCurrentRelease) {
|
|
41656
|
+
throw new UpdateFunctionalProbeError("turn-failed", detail, { cause: error });
|
|
41657
|
+
} else {
|
|
41658
|
+
const current = await input.compareWithCurrentRelease({
|
|
41659
|
+
kind: "acp-turn-failure",
|
|
41660
|
+
reason: detail,
|
|
41661
|
+
failure: failure2
|
|
41662
|
+
});
|
|
41663
|
+
const currentFailure = current.kind === "unavailable" ? classifyAcpTurnFailure(current.reason) : void 0;
|
|
41664
|
+
if (!currentFailure || !sameAcpTurnFailure(failure2, currentFailure)) {
|
|
41665
|
+
throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { cause: error });
|
|
41666
|
+
}
|
|
41667
|
+
console.warn(`[body] update probe: the probe turn failed the same way on this release and the current release (${detail}); that failure is not this bundle's doing, so the probe passes as inconclusive`);
|
|
41668
|
+
turnCompleted = false;
|
|
41669
|
+
modelAnswer = {
|
|
41670
|
+
modelAnswer: "unavailable",
|
|
41671
|
+
modelAnswerReason: `${detail} (the current release fails the same way)`
|
|
41672
|
+
};
|
|
41673
|
+
}
|
|
41674
|
+
}
|
|
41675
|
+
} catch (error) {
|
|
41676
|
+
if (error instanceof UpdateFunctionalProbeError)
|
|
41677
|
+
throw error;
|
|
41678
|
+
throw new UpdateFunctionalProbeError("session-start-failed", error instanceof Error ? error.message : String(error), { cause: error });
|
|
41679
|
+
}
|
|
41680
|
+
return {
|
|
41681
|
+
harness,
|
|
41682
|
+
sandboxed: Boolean(input.config.bwrapPath),
|
|
41683
|
+
sessionStarted: true,
|
|
41684
|
+
turnCompleted,
|
|
41685
|
+
nativeTools: [],
|
|
41686
|
+
...modelAnswer
|
|
41687
|
+
};
|
|
41688
|
+
} finally {
|
|
41689
|
+
await client?.stop().catch(() => void 0);
|
|
41690
|
+
await rm10(root, { recursive: true, force: true }).catch(() => void 0);
|
|
41691
|
+
}
|
|
41692
|
+
}
|
|
41335
41693
|
|
|
41336
41694
|
// apps/body/dist/update-rollback-alert.js
|
|
41337
|
-
import { mkdir as
|
|
41338
|
-
import { dirname as dirname20, resolve as
|
|
41695
|
+
import { mkdir as mkdir21, readFile as readFile16, rename as rename8, unlink as unlink4, writeFile as writeFile15 } from "node:fs/promises";
|
|
41696
|
+
import { dirname as dirname20, resolve as resolve31 } from "node:path";
|
|
41339
41697
|
var REPORT_INTERVAL_MS = 60 * 60 * 1e3;
|
|
41340
41698
|
var lastLogged = /* @__PURE__ */ new Map();
|
|
41341
41699
|
function updateRollbackAlertPath(runtimeDir) {
|
|
41342
|
-
return
|
|
41700
|
+
return resolve31(runtimeDir, "update-rollback-alert.json");
|
|
41343
41701
|
}
|
|
41344
41702
|
async function writeAlert(runtimeDir, alert) {
|
|
41345
41703
|
const path = updateRollbackAlertPath(runtimeDir);
|
|
41346
41704
|
const staged = `${path}.${process.pid}.tmp`;
|
|
41347
|
-
await
|
|
41705
|
+
await mkdir21(dirname20(path), { recursive: true });
|
|
41348
41706
|
await writeFile15(staged, `${JSON.stringify(alert, null, 2)}
|
|
41349
41707
|
`, { mode: 384 });
|
|
41350
41708
|
await rename8(staged, path);
|
|
@@ -41408,13 +41766,13 @@ var LOCK_STALE_MS = UPDATE_WORKER_DEADLINE_MS + 5 * 6e4;
|
|
|
41408
41766
|
var DEFAULT_UPDATE_INITIAL_DELAY_MS = 0;
|
|
41409
41767
|
async function withInstallLock(layout, work, options = {}) {
|
|
41410
41768
|
const now2 = options.now ?? Date.now;
|
|
41411
|
-
const lock =
|
|
41769
|
+
const lock = resolve32(layout.releasesRoot, ".state", "install.lock");
|
|
41412
41770
|
const deadline = now2() + (options.waitMs ?? 1e4);
|
|
41413
|
-
await
|
|
41771
|
+
await mkdir22(dirname21(lock), { recursive: true });
|
|
41414
41772
|
for (; ; ) {
|
|
41415
41773
|
try {
|
|
41416
|
-
await
|
|
41417
|
-
await writeFile16(
|
|
41774
|
+
await mkdir22(lock);
|
|
41775
|
+
await writeFile16(resolve32(lock, "owner"), `${process.pid}
|
|
41418
41776
|
${now2()}
|
|
41419
41777
|
`, "utf8");
|
|
41420
41778
|
break;
|
|
@@ -41423,7 +41781,7 @@ ${now2()}
|
|
|
41423
41781
|
throw error;
|
|
41424
41782
|
const age = now2() - await stat5(lock).then((value) => value.mtimeMs).catch(() => now2());
|
|
41425
41783
|
if (age > LOCK_STALE_MS) {
|
|
41426
|
-
await
|
|
41784
|
+
await rm11(lock, { recursive: true, force: true });
|
|
41427
41785
|
continue;
|
|
41428
41786
|
}
|
|
41429
41787
|
if (now2() >= deadline)
|
|
@@ -41434,7 +41792,7 @@ ${now2()}
|
|
|
41434
41792
|
try {
|
|
41435
41793
|
return await work();
|
|
41436
41794
|
} finally {
|
|
41437
|
-
await
|
|
41795
|
+
await rm11(lock, { recursive: true, force: true });
|
|
41438
41796
|
}
|
|
41439
41797
|
}
|
|
41440
41798
|
var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
@@ -41451,6 +41809,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41451
41809
|
#requested = false;
|
|
41452
41810
|
#restartRequest;
|
|
41453
41811
|
#stagedReleaseId;
|
|
41812
|
+
#notifiedReleaseKey;
|
|
41454
41813
|
constructor(options) {
|
|
41455
41814
|
this.#layout = options.layout;
|
|
41456
41815
|
this.#loadedRelease = options.loadedRelease;
|
|
@@ -41471,6 +41830,13 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41471
41830
|
...options
|
|
41472
41831
|
});
|
|
41473
41832
|
}
|
|
41833
|
+
/** The live server socket wakes this install immediately; the manifest stays authoritative. */
|
|
41834
|
+
notifyReleaseAvailable(releaseKey) {
|
|
41835
|
+
if (!releaseKey || this.#notifiedReleaseKey === releaseKey)
|
|
41836
|
+
return;
|
|
41837
|
+
this.#notifiedReleaseKey = releaseKey;
|
|
41838
|
+
this.#nextUpdateCheckAt = 0;
|
|
41839
|
+
}
|
|
41474
41840
|
/**
|
|
41475
41841
|
* Return true once either a verified release is staged behind the serving
|
|
41476
41842
|
* daemon's idle gate or desired-release drift is represented by the single
|
|
@@ -41519,9 +41885,15 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41519
41885
|
return;
|
|
41520
41886
|
const state2 = await readUpdateState(this.#layout);
|
|
41521
41887
|
const interval = numberEnv(this.#env, "BEELINE_UPDATE_INTERVAL_MS", DEFAULT_UPDATE_INTERVAL_MS);
|
|
41522
|
-
if (state2.lastCheckAt !== void 0 && now2 - state2.lastCheckAt < interval)
|
|
41888
|
+
if (state2.notifiedReleaseKey === this.#notifiedReleaseKey && state2.lastCheckAt !== void 0 && now2 - state2.lastCheckAt < interval)
|
|
41523
41889
|
return;
|
|
41524
41890
|
await this.#runUpdateWorker();
|
|
41891
|
+
if (this.#notifiedReleaseKey) {
|
|
41892
|
+
await writeUpdateState(this.#layout, {
|
|
41893
|
+
...await readUpdateState(this.#layout),
|
|
41894
|
+
notifiedReleaseKey: this.#notifiedReleaseKey
|
|
41895
|
+
});
|
|
41896
|
+
}
|
|
41525
41897
|
}).catch((error) => {
|
|
41526
41898
|
console.error(`[thin-core] update worker failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
41527
41899
|
}).finally(() => {
|
|
@@ -41538,7 +41910,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41538
41910
|
if (!attempt || attempt.releaseId !== desiredRelease || attempt.status !== "pending") {
|
|
41539
41911
|
const from = await readInstalledBundleIdentity({
|
|
41540
41912
|
...this.#layout,
|
|
41541
|
-
libDir:
|
|
41913
|
+
libDir: resolve32(this.#layout.releasesRoot, this.#loadedRelease)
|
|
41542
41914
|
}).catch(() => void 0) ?? {};
|
|
41543
41915
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
41544
41916
|
const record6 = {
|
|
@@ -41570,7 +41942,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41570
41942
|
drainDeadlineAt
|
|
41571
41943
|
};
|
|
41572
41944
|
this.#requested = true;
|
|
41573
|
-
console.log(`[thin-core] update restart armed: loaded release ${this.#loadedRelease} -> ${desiredRelease};
|
|
41945
|
+
console.log(`[thin-core] update restart armed: loaded release ${this.#loadedRelease} -> ${desiredRelease}; drain target ${new Date(drainDeadlineAt).toISOString()}`);
|
|
41574
41946
|
return true;
|
|
41575
41947
|
}
|
|
41576
41948
|
/**
|
|
@@ -41578,10 +41950,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41578
41950
|
* Busy means a turn is executing right now; serving Rooms and corners with
|
|
41579
41951
|
* no turn in flight is idle. `quiesceIfIdle` closes intake in the same
|
|
41580
41952
|
* synchronous transition that proves idle, so a new turn cannot race
|
|
41581
|
-
* activation. A staged release stays inert
|
|
41582
|
-
* absolute drain deadline, when the restart is forced (`forced: true`) and
|
|
41583
|
-
* the caller cancels the running turn; the convergence contract outranks a
|
|
41584
|
-
* stuck turn. `ManagedUpdateDrain` enforces that deadline with a timer.
|
|
41953
|
+
* activation. A staged release stays inert for as long as a turn runs.
|
|
41585
41954
|
*/
|
|
41586
41955
|
async restartRequest(quiesceIfIdle) {
|
|
41587
41956
|
const activeDrift = await this.check();
|
|
@@ -41590,11 +41959,8 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41590
41959
|
const request = this.#restartRequest;
|
|
41591
41960
|
if (!request)
|
|
41592
41961
|
throw new Error("update drift was detected without an in-memory restart request");
|
|
41593
|
-
let forced = false;
|
|
41594
41962
|
if (!quiesceIfIdle()) {
|
|
41595
|
-
|
|
41596
|
-
return { kind: "waiting", request };
|
|
41597
|
-
forced = true;
|
|
41963
|
+
return { kind: "waiting", request };
|
|
41598
41964
|
}
|
|
41599
41965
|
if (this.#stagedReleaseId) {
|
|
41600
41966
|
const stagedReleaseId = this.#stagedReleaseId;
|
|
@@ -41627,7 +41993,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
41627
41993
|
this.#restartRequest = void 0;
|
|
41628
41994
|
await this.#journalDrift(stagedReleaseId);
|
|
41629
41995
|
}
|
|
41630
|
-
return { kind: "restart", request: this.#restartRequest ?? request
|
|
41996
|
+
return { kind: "restart", request: this.#restartRequest ?? request };
|
|
41631
41997
|
}
|
|
41632
41998
|
};
|
|
41633
41999
|
async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, waiting = async () => void 0) {
|
|
@@ -41638,23 +42004,20 @@ async function coordinateManagedUpdateHandoff(update, quiesceIfIdle, restart, wa
|
|
|
41638
42004
|
await waiting(next.request);
|
|
41639
42005
|
return "waiting-for-idle";
|
|
41640
42006
|
}
|
|
41641
|
-
await restart(next.request,
|
|
42007
|
+
await restart(next.request, "drained");
|
|
41642
42008
|
return "restarting";
|
|
41643
42009
|
}
|
|
41644
42010
|
var UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS = 6e4;
|
|
41645
42011
|
var ManagedUpdateDrain = class {
|
|
41646
42012
|
#options;
|
|
41647
|
-
#now;
|
|
41648
42013
|
#log;
|
|
41649
42014
|
#setTimer;
|
|
41650
42015
|
#clearTimer;
|
|
41651
|
-
#deadlineTimer;
|
|
41652
42016
|
#waitLogTimer;
|
|
41653
42017
|
#resolving;
|
|
41654
42018
|
#restarting = false;
|
|
41655
42019
|
constructor(options) {
|
|
41656
42020
|
this.#options = options;
|
|
41657
|
-
this.#now = options.now ?? Date.now;
|
|
41658
42021
|
this.#log = options.log ?? ((line) => console.log(line));
|
|
41659
42022
|
this.#setTimer = options.setTimer ?? ((callback, ms) => setTimeout(callback, ms));
|
|
41660
42023
|
this.#clearTimer = options.clearTimer ?? ((handle) => clearTimeout(handle));
|
|
@@ -41674,49 +42037,39 @@ var ManagedUpdateDrain = class {
|
|
|
41674
42037
|
return this.#resolving;
|
|
41675
42038
|
}
|
|
41676
42039
|
async #step() {
|
|
41677
|
-
const { update, quiesceIfIdle
|
|
42040
|
+
const { update, quiesceIfIdle } = this.#options;
|
|
41678
42041
|
let armed;
|
|
41679
42042
|
const progress = await coordinateManagedUpdateHandoff(update, quiesceIfIdle, async (request, mode) => {
|
|
41680
42043
|
this.#disarm();
|
|
41681
|
-
if (mode === "forced") {
|
|
41682
|
-
this.#log(`[thin-core] update restart forced: drain deadline reached with ${activeTurnCount()} active turn(s); cancelling them and restarting onto ${request.desiredRelease}`);
|
|
41683
|
-
}
|
|
41684
42044
|
await this.#options.restart(request, mode);
|
|
41685
42045
|
this.#restarting = true;
|
|
41686
42046
|
}, async (request) => {
|
|
41687
42047
|
armed = request;
|
|
41688
42048
|
await this.#options.waiting?.(request);
|
|
41689
42049
|
});
|
|
41690
|
-
if (armed && !this.#
|
|
41691
|
-
this.#arm(
|
|
42050
|
+
if (armed && !this.#waitLogTimer)
|
|
42051
|
+
this.#arm();
|
|
41692
42052
|
return progress;
|
|
41693
42053
|
}
|
|
41694
|
-
#arm(
|
|
41695
|
-
this.#logWaiting(
|
|
41696
|
-
this.#scheduleWaitLog(
|
|
41697
|
-
this.#deadlineTimer = this.#setTimer(() => void this.#resolve().catch((error) => {
|
|
41698
|
-
this.#log(`[thin-core] update restart at the drain deadline failed; retrying on the next tick: ${error instanceof Error ? error.message : String(error)}`);
|
|
41699
|
-
}), Math.max(0, request.drainDeadlineAt - this.#now()));
|
|
42054
|
+
#arm() {
|
|
42055
|
+
this.#logWaiting();
|
|
42056
|
+
this.#scheduleWaitLog();
|
|
41700
42057
|
}
|
|
41701
|
-
#scheduleWaitLog(
|
|
42058
|
+
#scheduleWaitLog() {
|
|
41702
42059
|
this.#waitLogTimer = this.#setTimer(() => {
|
|
41703
42060
|
this.#waitLogTimer = void 0;
|
|
41704
42061
|
if (this.#restarting)
|
|
41705
42062
|
return;
|
|
41706
|
-
this.#logWaiting(
|
|
41707
|
-
this.#scheduleWaitLog(
|
|
42063
|
+
this.#logWaiting();
|
|
42064
|
+
this.#scheduleWaitLog();
|
|
41708
42065
|
}, UPDATE_DRAIN_WAIT_LOG_INTERVAL_MS);
|
|
41709
42066
|
}
|
|
41710
|
-
#logWaiting(
|
|
41711
|
-
|
|
41712
|
-
this.#log(`[thin-core] update restart waiting: ${this.#options.activeTurnCount()} active turn(s); deadline in ${minutesLeft}m`);
|
|
42067
|
+
#logWaiting() {
|
|
42068
|
+
this.#log(`[thin-core] update restart waiting: ${this.#options.activeTurnCount()} active turn(s); will resume after work finishes`);
|
|
41713
42069
|
}
|
|
41714
42070
|
#disarm() {
|
|
41715
|
-
if (this.#deadlineTimer !== void 0)
|
|
41716
|
-
this.#clearTimer(this.#deadlineTimer);
|
|
41717
42071
|
if (this.#waitLogTimer !== void 0)
|
|
41718
42072
|
this.#clearTimer(this.#waitLogTimer);
|
|
41719
|
-
this.#deadlineTimer = void 0;
|
|
41720
42073
|
this.#waitLogTimer = void 0;
|
|
41721
42074
|
}
|
|
41722
42075
|
};
|
|
@@ -41829,16 +42182,17 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
41829
42182
|
return false;
|
|
41830
42183
|
const confirmed = [.../* @__PURE__ */ new Set([...current.confirmedProbeIds ?? [], probeId])].sort();
|
|
41831
42184
|
const required = current.requiredProbeIds ?? [probeId];
|
|
42185
|
+
const satisfied = [...confirmed, ...current.unavailableProbeIds ?? []];
|
|
41832
42186
|
await replaceUpdateAttempt(layout, {
|
|
41833
42187
|
...current,
|
|
41834
42188
|
confirmedProbeIds: confirmed,
|
|
41835
|
-
...required.every((id) =>
|
|
42189
|
+
...required.every((id) => satisfied.includes(id)) ? { status: "confirmed" } : {}
|
|
41836
42190
|
});
|
|
41837
42191
|
return true;
|
|
41838
42192
|
});
|
|
41839
42193
|
if (!accepted)
|
|
41840
42194
|
return false;
|
|
41841
|
-
await writeFile16(
|
|
42195
|
+
await writeFile16(resolve32(runtimeDir, "daemon-ready.json"), `${JSON.stringify({
|
|
41842
42196
|
readyAt: (options.now ?? Date.now)(),
|
|
41843
42197
|
loadedRelease,
|
|
41844
42198
|
functionalProof: options.functionalProof
|
|
@@ -41846,6 +42200,22 @@ async function proveLoadedReleaseReady(layout, runtimeDir, loadedRelease, option
|
|
|
41846
42200
|
`, { mode: 384 });
|
|
41847
42201
|
return true;
|
|
41848
42202
|
}
|
|
42203
|
+
async function recordUnavailableProbe(input) {
|
|
42204
|
+
return withInstallLock(input.layout, async () => {
|
|
42205
|
+
const current = await readUpdateAttempt(input.layout);
|
|
42206
|
+
if (!current || current.status !== "pending" || current.releaseId !== input.loadedRelease || await activeReleaseId(input.layout) !== input.loadedRelease)
|
|
42207
|
+
return false;
|
|
42208
|
+
const unavailableProbeIds = [.../* @__PURE__ */ new Set([...current.unavailableProbeIds ?? [], input.probeId])].sort();
|
|
42209
|
+
const required = current.requiredProbeIds ?? [input.probeId];
|
|
42210
|
+
const satisfied = [...current.confirmedProbeIds ?? [], ...unavailableProbeIds];
|
|
42211
|
+
await replaceUpdateAttempt(input.layout, {
|
|
42212
|
+
...current,
|
|
42213
|
+
unavailableProbeIds,
|
|
42214
|
+
...required.every((id) => satisfied.includes(id)) ? { status: "confirmed" } : {}
|
|
42215
|
+
});
|
|
42216
|
+
return true;
|
|
42217
|
+
});
|
|
42218
|
+
}
|
|
41849
42219
|
function siblingRevertNote(attempt, loadedRelease) {
|
|
41850
42220
|
if (!loadedRelease || attempt?.releaseId !== loadedRelease || attempt.status !== "reverted") {
|
|
41851
42221
|
return void 0;
|
|
@@ -41877,6 +42247,9 @@ async function gateManagedSuccessor(input) {
|
|
|
41877
42247
|
}
|
|
41878
42248
|
return { kind: "passed", proof };
|
|
41879
42249
|
} catch (error) {
|
|
42250
|
+
if (error instanceof UpdateFunctionalProbeError && error.reason === "model-unavailable" && await recordUnavailableProbe(input)) {
|
|
42251
|
+
return { kind: "agent-unavailable", error };
|
|
42252
|
+
}
|
|
41880
42253
|
return {
|
|
41881
42254
|
kind: "failed",
|
|
41882
42255
|
error,
|
|
@@ -42228,10 +42601,10 @@ async function runStartCommand(args, interactiveUi, dependencyOverrides = {}) {
|
|
|
42228
42601
|
|
|
42229
42602
|
// apps/body/dist/connect-command.js
|
|
42230
42603
|
import { spawn as spawn12 } from "node:child_process";
|
|
42231
|
-
import { createHash as
|
|
42232
|
-
import { chmod as chmod8, mkdir as
|
|
42233
|
-
import { homedir as
|
|
42234
|
-
import { dirname as dirname23, resolve as
|
|
42604
|
+
import { createHash as createHash10, randomUUID as randomUUID7 } from "node:crypto";
|
|
42605
|
+
import { chmod as chmod8, mkdir as mkdir23, readFile as readFile17, unlink as unlink5, writeFile as writeFile17 } from "node:fs/promises";
|
|
42606
|
+
import { homedir as homedir16, hostname as hostname3 } from "node:os";
|
|
42607
|
+
import { dirname as dirname23, resolve as resolve33 } from "node:path";
|
|
42235
42608
|
import { stdin as stdin3, stdout as stdout4 } from "node:process";
|
|
42236
42609
|
init_access_policy();
|
|
42237
42610
|
init_agent_command();
|
|
@@ -42897,8 +43270,8 @@ function parseConnectSubscriptions(value) {
|
|
|
42897
43270
|
];
|
|
42898
43271
|
}
|
|
42899
43272
|
async function readMachineId(env = process.env) {
|
|
42900
|
-
const configDir =
|
|
42901
|
-
const machineIdPath =
|
|
43273
|
+
const configDir = resolve33(env.XDG_CONFIG_HOME ?? resolve33(homedir16(), ".config"), "beeline");
|
|
43274
|
+
const machineIdPath = resolve33(configDir, "machine-id");
|
|
42902
43275
|
let machineId;
|
|
42903
43276
|
let machineName = hostname3();
|
|
42904
43277
|
try {
|
|
@@ -42909,11 +43282,11 @@ async function readMachineId(env = process.env) {
|
|
|
42909
43282
|
} catch {
|
|
42910
43283
|
machineId = randomUUID7();
|
|
42911
43284
|
try {
|
|
42912
|
-
await
|
|
43285
|
+
await mkdir23(configDir, { recursive: true, mode: 448 });
|
|
42913
43286
|
await writeFile17(machineIdPath, `${machineId}
|
|
42914
43287
|
`, { mode: 384 });
|
|
42915
43288
|
} catch {
|
|
42916
|
-
machineId =
|
|
43289
|
+
machineId = createHash10("sha256").update(machineName).digest("hex").slice(0, 36);
|
|
42917
43290
|
}
|
|
42918
43291
|
}
|
|
42919
43292
|
return { machineId, machineName };
|
|
@@ -42922,7 +43295,7 @@ function requestConnectGrant(baseUrl, pairingCode, selection, fetchImpl, machine
|
|
|
42922
43295
|
const normalizedPairingCode = normalizeAgentPairingCode(pairingCode);
|
|
42923
43296
|
if (!normalizedPairingCode)
|
|
42924
43297
|
throw new Error("invalid pairing code");
|
|
42925
|
-
const avatarSeed =
|
|
43298
|
+
const avatarSeed = createHash10("sha256").update(normalizedPairingCode.toUpperCase()).digest("hex").slice(0, 32);
|
|
42926
43299
|
return jsonRequest(`${baseUrl}/auth/agent/connect`, {
|
|
42927
43300
|
pairing_code: normalizedPairingCode,
|
|
42928
43301
|
harness: selection.harness,
|
|
@@ -42971,7 +43344,7 @@ async function installCurrentRelease(fetchImpl) {
|
|
|
42971
43344
|
});
|
|
42972
43345
|
await activateRelease(layout, releaseId);
|
|
42973
43346
|
return {
|
|
42974
|
-
binary:
|
|
43347
|
+
binary: resolve33(layout.binDir, "beeline"),
|
|
42975
43348
|
version: published.version ?? releaseId
|
|
42976
43349
|
};
|
|
42977
43350
|
}
|
|
@@ -42994,7 +43367,7 @@ function providerEnvironment(selection) {
|
|
|
42994
43367
|
};
|
|
42995
43368
|
}
|
|
42996
43369
|
async function writePrivateJson(path, value) {
|
|
42997
|
-
await
|
|
43370
|
+
await mkdir23(dirname23(path), { recursive: true, mode: 448 });
|
|
42998
43371
|
await writeFile17(path, `${JSON.stringify(value, null, 2)}
|
|
42999
43372
|
`, { mode: 384 });
|
|
43000
43373
|
await chmod8(path, 384);
|
|
@@ -43003,8 +43376,8 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
43003
43376
|
const values = providerEnvironment(selection);
|
|
43004
43377
|
if (Object.keys(values).length === 0)
|
|
43005
43378
|
return void 0;
|
|
43006
|
-
const path =
|
|
43007
|
-
await
|
|
43379
|
+
const path = resolve33(defaultSupervisorRoot(process.env), "beeline", "connect", `${agentPubkey}.env`);
|
|
43380
|
+
await mkdir23(dirname23(path), { recursive: true, mode: 448 });
|
|
43008
43381
|
const contents = Object.entries(values).map(([key, value]) => `${key}=${JSON.stringify(value)}`).join("\n");
|
|
43009
43382
|
await writeFile17(path, `${contents}
|
|
43010
43383
|
`, { mode: 384 });
|
|
@@ -43073,7 +43446,7 @@ async function runConnectWizard(code, fetchImpl, eventSubscriptions, accessPolic
|
|
|
43073
43446
|
await finishConnectedAgentPairing(baseUrl, pairingCode, grant.workspace_joined, eventSubscriptions, fetchImpl);
|
|
43074
43447
|
const installedRelease = await brassSpinner("Installing the Beeline daemon\u2026", () => installCurrentRelease(fetchImpl), (release) => `Installed Beeline helper ${release.version}`);
|
|
43075
43448
|
const llmEnvFile = await writeProviderEnv(selection, grant.agent_pubkey);
|
|
43076
|
-
const grantPath =
|
|
43449
|
+
const grantPath = resolve33(defaultSupervisorRoot(process.env), "beeline", "connect", `grant-${process.pid}-${Date.now()}.json`);
|
|
43077
43450
|
await writePrivateJson(grantPath, {
|
|
43078
43451
|
agentSecretKey: grant.agent_secret_key,
|
|
43079
43452
|
bodySecretKey: grant.body_secret_key,
|
|
@@ -43158,11 +43531,11 @@ async function runConnectFinishCommand(path) {
|
|
|
43158
43531
|
throw new Error("connect-finish may run only from the canonical installed Beeline launcher");
|
|
43159
43532
|
}
|
|
43160
43533
|
try {
|
|
43161
|
-
const grant = JSON.parse(await readFile17(
|
|
43534
|
+
const grant = JSON.parse(await readFile17(resolve33(path), "utf8"));
|
|
43162
43535
|
if (!isDevicePairingGrant(grant))
|
|
43163
43536
|
throw new Error("device connection grant is invalid");
|
|
43164
43537
|
const connected = await completeDevicePairing(grant);
|
|
43165
|
-
await unlink5(
|
|
43538
|
+
await unlink5(resolve33(path));
|
|
43166
43539
|
const providerEnv = grant.llmEnvFile ? await readFile17(grant.llmEnvFile, "utf8").catch(() => "") : "";
|
|
43167
43540
|
const apiKey = (/^OPENROUTER_API_KEY=(\S+)/m.exec(providerEnv)?.[1] ?? "").replace(/^["']|["']$/g, "");
|
|
43168
43541
|
const model = openRouterModelId(grant.model, { OPENROUTER_API_KEY: apiKey });
|
|
@@ -43187,12 +43560,12 @@ async function runConnectFinishCommand(path) {
|
|
|
43187
43560
|
init_self_update();
|
|
43188
43561
|
|
|
43189
43562
|
// apps/body/dist/daemon-failure.js
|
|
43190
|
-
import { mkdir as
|
|
43191
|
-
import { dirname as dirname24, resolve as
|
|
43563
|
+
import { mkdir as mkdir24, readFile as readFile18, rename as rename9, rm as rm12, writeFile as writeFile18 } from "node:fs/promises";
|
|
43564
|
+
import { dirname as dirname24, resolve as resolve34 } from "node:path";
|
|
43192
43565
|
var DAEMON_FAILURE_LIMIT = 3;
|
|
43193
43566
|
var DAEMON_FAILURE_WINDOW_MS = 5 * 6e4;
|
|
43194
43567
|
function daemonFailurePath(runtimeDir) {
|
|
43195
|
-
return
|
|
43568
|
+
return resolve34(runtimeDir, "daemon-distress.json");
|
|
43196
43569
|
}
|
|
43197
43570
|
async function readFailureRecord(runtimeDir) {
|
|
43198
43571
|
try {
|
|
@@ -43208,7 +43581,7 @@ async function readFailureRecord(runtimeDir) {
|
|
|
43208
43581
|
async function writeFailureRecord(runtimeDir, record6) {
|
|
43209
43582
|
const path = daemonFailurePath(runtimeDir);
|
|
43210
43583
|
const staged = `${path}.${process.pid}.tmp`;
|
|
43211
|
-
await
|
|
43584
|
+
await mkdir24(dirname24(path), { recursive: true, mode: 448 });
|
|
43212
43585
|
await writeFile18(staged, `${JSON.stringify(record6, null, 2)}
|
|
43213
43586
|
`, { mode: 384 });
|
|
43214
43587
|
await rename9(staged, path);
|
|
@@ -43227,318 +43600,12 @@ async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
|
43227
43600
|
return { distressed, count: failures.length, path: daemonFailurePath(runtimeDir) };
|
|
43228
43601
|
}
|
|
43229
43602
|
async function clearDaemonStartFailures(runtimeDir) {
|
|
43230
|
-
await
|
|
43603
|
+
await rm12(daemonFailurePath(runtimeDir), { force: true });
|
|
43231
43604
|
}
|
|
43232
43605
|
|
|
43233
43606
|
// apps/body/dist/cli.js
|
|
43234
43607
|
init_systemd();
|
|
43235
43608
|
|
|
43236
|
-
// apps/body/dist/update-functional-probe.js
|
|
43237
|
-
import { mkdir as mkdir24, rm as rm12 } from "node:fs/promises";
|
|
43238
|
-
import { homedir as homedir16 } from "node:os";
|
|
43239
|
-
import { resolve as resolve34 } from "node:path";
|
|
43240
|
-
init_cursor_acp_bridge();
|
|
43241
|
-
var UPDATE_PROBE_SESSION_TIMEOUT_MS = 1e4;
|
|
43242
|
-
var UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS = 2e4;
|
|
43243
|
-
var UPDATE_PROBE_TURN_TIMEOUT_MS = 45e3;
|
|
43244
|
-
var UPDATE_PROBE_RETRY_DELAY_MS = 2e3;
|
|
43245
|
-
var ACP_ERROR_CODE = /\bACP error (-\d+):/;
|
|
43246
|
-
var ACP_PROMPT_INACTIVITY = /\bACP session\/prompt timed out after \d+ms of inactivity\b/;
|
|
43247
|
-
function isServerInternalCode(code) {
|
|
43248
|
-
return code === -32603 || code <= -32e3 && code >= -32099;
|
|
43249
|
-
}
|
|
43250
|
-
function classifyAcpTurnFailure(error) {
|
|
43251
|
-
const message = error instanceof Error ? error.message : typeof error === "string" ? error : void 0;
|
|
43252
|
-
if (!message)
|
|
43253
|
-
return void 0;
|
|
43254
|
-
if (ACP_PROMPT_INACTIVITY.test(message))
|
|
43255
|
-
return { kind: "prompt-inactivity" };
|
|
43256
|
-
const code = Number(ACP_ERROR_CODE.exec(message)?.[1]);
|
|
43257
|
-
if (Number.isFinite(code) && isServerInternalCode(code))
|
|
43258
|
-
return { kind: "server-internal", code };
|
|
43259
|
-
return void 0;
|
|
43260
|
-
}
|
|
43261
|
-
function sameAcpTurnFailure(one, other) {
|
|
43262
|
-
if (one.kind === "server-internal") {
|
|
43263
|
-
return other.kind === "server-internal" && one.code === other.code;
|
|
43264
|
-
}
|
|
43265
|
-
return other.kind === one.kind;
|
|
43266
|
-
}
|
|
43267
|
-
var UpdateFunctionalProbeError = class extends Error {
|
|
43268
|
-
reason;
|
|
43269
|
-
code = "BEELINE_UPDATE_FUNCTIONAL_PROBE_FAILED";
|
|
43270
|
-
/** Present when the turn failed on a provider refusal with an HTTP status. */
|
|
43271
|
-
providerRefusal;
|
|
43272
|
-
constructor(reason, detail, options) {
|
|
43273
|
-
super(`functional update probe failed (${reason}): ${detail}`, options);
|
|
43274
|
-
this.reason = reason;
|
|
43275
|
-
this.name = "UpdateFunctionalProbeError";
|
|
43276
|
-
this.providerRefusal = options?.providerRefusal;
|
|
43277
|
-
}
|
|
43278
|
-
};
|
|
43279
|
-
async function probeOutcome(run2) {
|
|
43280
|
-
try {
|
|
43281
|
-
await run2();
|
|
43282
|
-
return { kind: "served" };
|
|
43283
|
-
} catch (error) {
|
|
43284
|
-
if (error instanceof UpdateFunctionalProbeError && error.reason === "sandbox-unavailable") {
|
|
43285
|
-
return { kind: "sandbox-unavailable", reason: error.message };
|
|
43286
|
-
}
|
|
43287
|
-
if (error instanceof UpdateFunctionalProbeError && error.providerRefusal) {
|
|
43288
|
-
return { kind: "refused", ...error.providerRefusal };
|
|
43289
|
-
}
|
|
43290
|
-
return { kind: "unavailable", reason: error instanceof Error ? error.message : String(error) };
|
|
43291
|
-
}
|
|
43292
|
-
}
|
|
43293
|
-
function describeCurrentReleaseOutcome(outcome) {
|
|
43294
|
-
switch (outcome.kind) {
|
|
43295
|
-
case "served":
|
|
43296
|
-
return "the current release answered";
|
|
43297
|
-
case "refused":
|
|
43298
|
-
return `the current release got a different refusal (${outcome.reason})`;
|
|
43299
|
-
case "sandbox-unavailable":
|
|
43300
|
-
return `the current release has the same unavailable sandbox (${outcome.reason})`;
|
|
43301
|
-
case "model-unavailable":
|
|
43302
|
-
return `the current release cannot serve that model either (${outcome.reason})`;
|
|
43303
|
-
case "unavailable":
|
|
43304
|
-
return `the current release could not be compared (${outcome.reason})`;
|
|
43305
|
-
}
|
|
43306
|
-
}
|
|
43307
|
-
async function runUpdateFunctionalProbe(input) {
|
|
43308
|
-
const command = input.config.agentCommand ?? input.config.agentBinary;
|
|
43309
|
-
const harnessLabel = harnessIdentityLabel({
|
|
43310
|
-
kind: input.config.agentKind,
|
|
43311
|
-
command
|
|
43312
|
-
});
|
|
43313
|
-
const harness = input.config.agentKind ?? command;
|
|
43314
|
-
if (input.config.modelUnavailable) {
|
|
43315
|
-
const detail = input.config.modelUnavailable.detail;
|
|
43316
|
-
if (!input.compareWithCurrentRelease) {
|
|
43317
|
-
throw new UpdateFunctionalProbeError("model-unavailable", detail);
|
|
43318
|
-
}
|
|
43319
|
-
const current = await input.compareWithCurrentRelease({
|
|
43320
|
-
kind: "model-unavailable",
|
|
43321
|
-
reason: detail
|
|
43322
|
-
});
|
|
43323
|
-
if (current.kind !== "model-unavailable" && current.kind !== "unavailable") {
|
|
43324
|
-
throw new UpdateFunctionalProbeError("model-unavailable", `${detail}; ${describeCurrentReleaseOutcome(current)}`);
|
|
43325
|
-
}
|
|
43326
|
-
console.warn("[body] update probe: the configured model is unavailable to this release and the current release alike; that is the account catalog, not the bundle, so the probe passes without a model answer");
|
|
43327
|
-
return {
|
|
43328
|
-
harness,
|
|
43329
|
-
sandboxed: false,
|
|
43330
|
-
sessionStarted: false,
|
|
43331
|
-
turnCompleted: false,
|
|
43332
|
-
nativeTools: [],
|
|
43333
|
-
modelAnswer: "unavailable",
|
|
43334
|
-
modelAnswerReason: `${detail} (the current release cannot serve that model either)`
|
|
43335
|
-
};
|
|
43336
|
-
}
|
|
43337
|
-
if (input.sandboxRequired && !input.config.bwrapPath) {
|
|
43338
|
-
const detail = input.sandboxUnavailableDetail ?? "the configured bubblewrap boundary did not pass its startup self-test";
|
|
43339
|
-
if (!input.compareWithCurrentRelease) {
|
|
43340
|
-
throw new UpdateFunctionalProbeError("sandbox-unavailable", detail);
|
|
43341
|
-
}
|
|
43342
|
-
const current = await input.compareWithCurrentRelease({
|
|
43343
|
-
kind: "sandbox-unavailable",
|
|
43344
|
-
reason: detail
|
|
43345
|
-
});
|
|
43346
|
-
if (current.kind !== "sandbox-unavailable") {
|
|
43347
|
-
throw new UpdateFunctionalProbeError("sandbox-unavailable", `${detail}; ${describeCurrentReleaseOutcome(current)}`);
|
|
43348
|
-
}
|
|
43349
|
-
console.warn("[body] update probe: the sandbox is unavailable to this release and the current release alike; preserving fail-closed sandboxing and accepting the bundle as inconclusive");
|
|
43350
|
-
return {
|
|
43351
|
-
harness,
|
|
43352
|
-
sandboxed: false,
|
|
43353
|
-
sessionStarted: false,
|
|
43354
|
-
turnCompleted: false,
|
|
43355
|
-
nativeTools: [],
|
|
43356
|
-
modelAnswer: "unavailable",
|
|
43357
|
-
modelAnswerReason: `${detail} (the current release has the same host sandbox failure)`
|
|
43358
|
-
};
|
|
43359
|
-
}
|
|
43360
|
-
const root = input.probeRoot ?? resolve34(input.runtimeDir, "update-functional-probe");
|
|
43361
|
-
const cwd = resolve34(root, "checkout");
|
|
43362
|
-
const homeRoot = resolve34(root, "agent-home");
|
|
43363
|
-
await rm12(root, { recursive: true, force: true });
|
|
43364
|
-
await mkdir24(cwd, { recursive: true, mode: 448 });
|
|
43365
|
-
let client;
|
|
43366
|
-
try {
|
|
43367
|
-
const agentEnv = {
|
|
43368
|
-
...input.config.agentEnv,
|
|
43369
|
-
...await prepareRoomAgentHome({
|
|
43370
|
-
root: homeRoot,
|
|
43371
|
-
operatorHome: input.config.operatorHome ?? homedir16(),
|
|
43372
|
-
sharedSkills: input.config.sharedSkills ?? [],
|
|
43373
|
-
...input.config.agentKind ? { agentKind: input.config.agentKind } : {},
|
|
43374
|
-
skillReleaseId: input.releaseId,
|
|
43375
|
-
failClosed: true,
|
|
43376
|
-
...openRouterRoutingInput({ ...input.config, openRouterRoutingCacheDir: openRouterRoutingCacheDir(input.runtimeDir) }, input.config.modelSelection)
|
|
43377
|
-
})
|
|
43378
|
-
};
|
|
43379
|
-
const selectedAgent = {
|
|
43380
|
-
kind: input.config.agentKind,
|
|
43381
|
-
command,
|
|
43382
|
-
args: input.config.agentArgs ?? []
|
|
43383
|
-
};
|
|
43384
|
-
let spawnCommand = {
|
|
43385
|
-
command,
|
|
43386
|
-
args: agentArgsWithModelSelection(selectedAgent, input.config.modelSelection)
|
|
43387
|
-
};
|
|
43388
|
-
let modelAnswer = {};
|
|
43389
|
-
let turnCompleted = true;
|
|
43390
|
-
if (input.config.bwrapPath) {
|
|
43391
|
-
const { stateDirs, tmpDir } = harnessStateDirsFromEnv(agentEnv);
|
|
43392
|
-
const operatorHome = input.config.operatorHome ?? homedir16();
|
|
43393
|
-
const homeStateDirs = harnessHomeStateDirs(harnessLabel, agentEnv.HOME ?? operatorHome);
|
|
43394
|
-
await Promise.all(homeStateDirs.map((dir) => mkdir24(dir, { recursive: true })));
|
|
43395
|
-
spawnCommand = wrapAgentCommand({
|
|
43396
|
-
bwrapPath: input.config.bwrapPath,
|
|
43397
|
-
spec: {
|
|
43398
|
-
mode: "readonly",
|
|
43399
|
-
cwd,
|
|
43400
|
-
harnessStateDirs: stateDirs,
|
|
43401
|
-
harnessHomeStateDirs: homeStateDirs,
|
|
43402
|
-
maskPaths: credentialMaskPaths(input.config.sandboxMaskPaths, operatorHome),
|
|
43403
|
-
...tmpDir ? { tmpDir } : {}
|
|
43404
|
-
},
|
|
43405
|
-
command,
|
|
43406
|
-
args: spawnCommand.args
|
|
43407
|
-
});
|
|
43408
|
-
}
|
|
43409
|
-
client = new AcpClient({
|
|
43410
|
-
agentCommand: spawnCommand.command,
|
|
43411
|
-
agentArgs: spawnCommand.args,
|
|
43412
|
-
agentLabel: harnessLabel,
|
|
43413
|
-
agentEnv,
|
|
43414
|
-
agentCwd: cwd,
|
|
43415
|
-
autoApprovePermissions: true
|
|
43416
|
-
});
|
|
43417
|
-
const sessionTimeoutMs = input.sessionTimeoutMs ?? UPDATE_PROBE_SESSION_TIMEOUT_MS;
|
|
43418
|
-
const sessionOpenTimeoutMs = input.sessionOpenTimeoutMs ?? input.sessionTimeoutMs ?? UPDATE_PROBE_SESSION_OPEN_TIMEOUT_MS;
|
|
43419
|
-
const openSessionAndPrompt = async () => {
|
|
43420
|
-
const opened = await client.sessionNew({
|
|
43421
|
-
cwd,
|
|
43422
|
-
mcpServers: [],
|
|
43423
|
-
systemPrompt: "This is Beeline update validation. Answer only READY.",
|
|
43424
|
-
mode: "readonly",
|
|
43425
|
-
timeoutMs: sessionOpenTimeoutMs
|
|
43426
|
-
});
|
|
43427
|
-
if (input.config.modelSelection) {
|
|
43428
|
-
await applyAgentModelSelection(client, opened.sessionId, parseAdvertisedConfigOptions(opened.raw, input.config.modelSelection.model, isGrokAgentCommand(selectedAgent)), input.config.modelSelection);
|
|
43429
|
-
}
|
|
43430
|
-
return {
|
|
43431
|
-
sessionId: opened.sessionId,
|
|
43432
|
-
served: await client.sessionPrompt(opened.sessionId, "Reply READY.", input.turnTimeoutMs ?? UPDATE_PROBE_TURN_TIMEOUT_MS)
|
|
43433
|
-
};
|
|
43434
|
-
};
|
|
43435
|
-
try {
|
|
43436
|
-
await client.start(sessionTimeoutMs);
|
|
43437
|
-
try {
|
|
43438
|
-
const { sessionId, served } = await openSessionAndPrompt();
|
|
43439
|
-
if (served.agentText.trim()) {
|
|
43440
|
-
modelAnswer = { modelAnswer: "served" };
|
|
43441
|
-
} else {
|
|
43442
|
-
const explained = await explainEmptyAgentTurn({
|
|
43443
|
-
agentLabel: harnessLabel,
|
|
43444
|
-
agentEnv,
|
|
43445
|
-
sessionId,
|
|
43446
|
-
result: served
|
|
43447
|
-
});
|
|
43448
|
-
const modelSide = isAccountOrProviderRefusal(explained.record) || explained.record?.kind === "empty";
|
|
43449
|
-
if (!modelSide) {
|
|
43450
|
-
const refusal = explained.record?.kind === "error" && explained.record.status !== void 0 ? { status: explained.record.status, reason: explained.record.reason } : void 0;
|
|
43451
|
-
const detail = `the harness completed a session/prompt without an agent answer: ${explained.reason}`;
|
|
43452
|
-
if (!refusal || !input.compareWithCurrentRelease) {
|
|
43453
|
-
throw new UpdateFunctionalProbeError("turn-failed", detail, {
|
|
43454
|
-
...refusal ? { providerRefusal: refusal } : {}
|
|
43455
|
-
});
|
|
43456
|
-
}
|
|
43457
|
-
const current = await input.compareWithCurrentRelease({
|
|
43458
|
-
kind: "provider-refusal",
|
|
43459
|
-
reason: refusal.reason,
|
|
43460
|
-
refusal
|
|
43461
|
-
});
|
|
43462
|
-
if (current.kind !== "refused" || current.status !== refusal.status) {
|
|
43463
|
-
throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { providerRefusal: refusal });
|
|
43464
|
-
}
|
|
43465
|
-
const reason = `${explained.reason} (the current release gets the same ${refusal.status})`;
|
|
43466
|
-
console.warn(`[body] update probe: the provider refused this release and the current release alike (${refusal.reason}); that refusal is not this bundle's doing, so the probe passes as inconclusive`);
|
|
43467
|
-
modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: reason };
|
|
43468
|
-
} else {
|
|
43469
|
-
console.warn(`[body] update probe: the bundle reached the model boundary but the model answered nothing (${explained.reason}); that is the model's own answer on any release, so the probe passes without one`);
|
|
43470
|
-
modelAnswer = { modelAnswer: "unavailable", modelAnswerReason: explained.reason };
|
|
43471
|
-
}
|
|
43472
|
-
}
|
|
43473
|
-
} catch (initialError) {
|
|
43474
|
-
if (initialError instanceof UpdateFunctionalProbeError)
|
|
43475
|
-
throw initialError;
|
|
43476
|
-
let error = initialError;
|
|
43477
|
-
let detail = error instanceof Error ? error.message : String(error);
|
|
43478
|
-
let failure2 = classifyAcpTurnFailure(error);
|
|
43479
|
-
if (failure2) {
|
|
43480
|
-
await new Promise((resolveWait) => setTimeout(resolveWait, input.retryDelayMs ?? UPDATE_PROBE_RETRY_DELAY_MS));
|
|
43481
|
-
try {
|
|
43482
|
-
const retried = await openSessionAndPrompt();
|
|
43483
|
-
if (retried.served.agentText.trim()) {
|
|
43484
|
-
turnCompleted = true;
|
|
43485
|
-
modelAnswer = { modelAnswer: "served" };
|
|
43486
|
-
console.warn(`[body] update probe: the probe turn failed once (${detail}) but a fresh retry answered; treating the first failure as the provider's, not this bundle's`);
|
|
43487
|
-
error = void 0;
|
|
43488
|
-
}
|
|
43489
|
-
} catch (retryError) {
|
|
43490
|
-
const retryDetail = retryError instanceof Error ? retryError.message : String(retryError);
|
|
43491
|
-
const retryFailure = classifyAcpTurnFailure(retryError);
|
|
43492
|
-
if (!retryFailure || !sameAcpTurnFailure(failure2, retryFailure)) {
|
|
43493
|
-
throw new UpdateFunctionalProbeError("turn-failed", retryDetail, {
|
|
43494
|
-
cause: retryError
|
|
43495
|
-
});
|
|
43496
|
-
}
|
|
43497
|
-
error = retryError;
|
|
43498
|
-
detail = retryDetail;
|
|
43499
|
-
failure2 = retryFailure;
|
|
43500
|
-
}
|
|
43501
|
-
}
|
|
43502
|
-
if (error === void 0) {
|
|
43503
|
-
} else if (!failure2 || !input.compareWithCurrentRelease) {
|
|
43504
|
-
throw new UpdateFunctionalProbeError("turn-failed", detail, { cause: error });
|
|
43505
|
-
} else {
|
|
43506
|
-
const current = await input.compareWithCurrentRelease({
|
|
43507
|
-
kind: "acp-turn-failure",
|
|
43508
|
-
reason: detail,
|
|
43509
|
-
failure: failure2
|
|
43510
|
-
});
|
|
43511
|
-
const currentFailure = current.kind === "unavailable" ? classifyAcpTurnFailure(current.reason) : void 0;
|
|
43512
|
-
if (!currentFailure || !sameAcpTurnFailure(failure2, currentFailure)) {
|
|
43513
|
-
throw new UpdateFunctionalProbeError("turn-failed", `${detail}; ${describeCurrentReleaseOutcome(current)}`, { cause: error });
|
|
43514
|
-
}
|
|
43515
|
-
console.warn(`[body] update probe: the probe turn failed the same way on this release and the current release (${detail}); that failure is not this bundle's doing, so the probe passes as inconclusive`);
|
|
43516
|
-
turnCompleted = false;
|
|
43517
|
-
modelAnswer = {
|
|
43518
|
-
modelAnswer: "unavailable",
|
|
43519
|
-
modelAnswerReason: `${detail} (the current release fails the same way)`
|
|
43520
|
-
};
|
|
43521
|
-
}
|
|
43522
|
-
}
|
|
43523
|
-
} catch (error) {
|
|
43524
|
-
if (error instanceof UpdateFunctionalProbeError)
|
|
43525
|
-
throw error;
|
|
43526
|
-
throw new UpdateFunctionalProbeError("session-start-failed", error instanceof Error ? error.message : String(error), { cause: error });
|
|
43527
|
-
}
|
|
43528
|
-
return {
|
|
43529
|
-
harness,
|
|
43530
|
-
sandboxed: Boolean(input.config.bwrapPath),
|
|
43531
|
-
sessionStarted: true,
|
|
43532
|
-
turnCompleted,
|
|
43533
|
-
nativeTools: [],
|
|
43534
|
-
...modelAnswer
|
|
43535
|
-
};
|
|
43536
|
-
} finally {
|
|
43537
|
-
await client?.stop().catch(() => void 0);
|
|
43538
|
-
await rm12(root, { recursive: true, force: true }).catch(() => void 0);
|
|
43539
|
-
}
|
|
43540
|
-
}
|
|
43541
|
-
|
|
43542
43609
|
// apps/body/dist/current-release-probe.js
|
|
43543
43610
|
import { spawn as spawn13 } from "node:child_process";
|
|
43544
43611
|
import { dirname as dirname25, join as join15 } from "node:path";
|
|
@@ -44049,18 +44116,20 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
44049
44116
|
update,
|
|
44050
44117
|
quiesceIfIdle: () => core.quiesceForUpdateIfIdle(),
|
|
44051
44118
|
activeTurnCount: () => core.activeTurnCount(),
|
|
44052
|
-
restart: async ({ desiredRelease, drainDeadlineAt }
|
|
44053
|
-
if (mode === "forced")
|
|
44054
|
-
await core.prepareForForcedUpdateRestart();
|
|
44119
|
+
restart: async ({ desiredRelease, drainDeadlineAt }) => {
|
|
44055
44120
|
core.setDrainDeadlineAt(drainDeadlineAt);
|
|
44056
|
-
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease};
|
|
44121
|
+
stoppingStatus = `update pending, converging; loaded_release=${loadedRelease ?? "unknown"}; desired_release=${desiredRelease}; active work drained; intake quiesced`;
|
|
44057
44122
|
await notifier.stopping(stoppingStatus);
|
|
44058
44123
|
controller.abort();
|
|
44059
44124
|
},
|
|
44060
|
-
waiting: async ({ desiredRelease
|
|
44061
|
-
await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}
|
|
44125
|
+
waiting: async ({ desiredRelease }) => {
|
|
44126
|
+
await notifier.progress(`loaded_release=${loadedRelease ?? "unknown"}; update ready; active agent work is still running; handoff deferred; desired_release=${desiredRelease}`);
|
|
44062
44127
|
}
|
|
44063
44128
|
}) : void 0;
|
|
44129
|
+
daemonApi.setHelperReleaseListener(({ version, sha }) => {
|
|
44130
|
+
update?.notifyReleaseAvailable(`${version}:${sha}`);
|
|
44131
|
+
void updateDrain?.tick();
|
|
44132
|
+
});
|
|
44064
44133
|
const result = await core.run({
|
|
44065
44134
|
signal: controller.signal,
|
|
44066
44135
|
onEstablished: async () => {
|
|
@@ -44095,10 +44164,18 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
44095
44164
|
successorRolledBack = gate.rolledBack;
|
|
44096
44165
|
throw gate.error;
|
|
44097
44166
|
}
|
|
44098
|
-
|
|
44167
|
+
if (gate.kind === "agent-unavailable") {
|
|
44168
|
+
if (!config.modelUnavailable) {
|
|
44169
|
+
config.modelUnavailable = modelUnavailableState(config.modelSelection ?? runtime.modelSelection ?? {}, gate.error.cause ?? gate.error);
|
|
44170
|
+
}
|
|
44171
|
+
console.warn(`[thin-core] new release retained; this agent's selected model is unavailable: ${gate.error.message}`);
|
|
44172
|
+
} else {
|
|
44173
|
+
functionalProof = gate.proof;
|
|
44174
|
+
}
|
|
44099
44175
|
pendingSuccessor = false;
|
|
44100
44176
|
await clearUpdateRollbackAlert(runtimeDir);
|
|
44101
|
-
|
|
44177
|
+
if (functionalProof)
|
|
44178
|
+
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})` : ""));
|
|
44102
44179
|
}
|
|
44103
44180
|
await clearDaemonStartFailures(runtimeDir);
|
|
44104
44181
|
await writeDaemonReleaseStatus(runtimeDir, runtime.agent.publicKey, loadedReleaseIdentity);
|