usebeeline 0.0.100 → 0.0.105
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 +1 -1
- package/dist/usebeeline.mjs +775 -132
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -3987,7 +3987,7 @@ __export(self_update_exports, {
|
|
|
3987
3987
|
import { createHash as createHash7 } from "node:crypto";
|
|
3988
3988
|
import { constants as fsConstants2 } from "node:fs";
|
|
3989
3989
|
import { access, chmod as chmod5, lstat as lstat3, mkdir as mkdir15, open, readFile as readFile10, rename as rename4, rm as rm6, symlink as symlink3, writeFile as writeFile10 } from "node:fs/promises";
|
|
3990
|
-
import { spawn as
|
|
3990
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
3991
3991
|
import { homedir as homedir9 } from "node:os";
|
|
3992
3992
|
import { dirname as dirname11, join as join9, resolve as resolve22 } from "node:path";
|
|
3993
3993
|
function anchorLayout(rawLibDir) {
|
|
@@ -4137,7 +4137,7 @@ async function fetchText(url, fetchImpl) {
|
|
|
4137
4137
|
}
|
|
4138
4138
|
function run(command, args, timeoutMs) {
|
|
4139
4139
|
return new Promise((resolveRun) => {
|
|
4140
|
-
const child =
|
|
4140
|
+
const child = spawn5(command, args, { stdio: ["ignore", "ignore", "pipe"] });
|
|
4141
4141
|
let stderr = "";
|
|
4142
4142
|
child.stderr?.setEncoding("utf8");
|
|
4143
4143
|
child.stderr?.on("data", (chunk) => {
|
|
@@ -4210,7 +4210,7 @@ async function stageRelease(layout, manifestUrl, published, opts = {}) {
|
|
|
4210
4210
|
}
|
|
4211
4211
|
await writeFile10(tempArchive, Buffer.concat(chunks), { mode: 384 });
|
|
4212
4212
|
const entries = (await new Promise((resolveList, rejectList) => {
|
|
4213
|
-
const child =
|
|
4213
|
+
const child = spawn5("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
|
|
4214
4214
|
let out = "";
|
|
4215
4215
|
child.stdout?.on("data", (chunk) => {
|
|
4216
4216
|
out += chunk.toString("utf8");
|
|
@@ -4389,44 +4389,44 @@ async function readUpdateAttempt(layout) {
|
|
|
4389
4389
|
return void 0;
|
|
4390
4390
|
}
|
|
4391
4391
|
}
|
|
4392
|
-
async function writeUpdateAttempt(layout,
|
|
4392
|
+
async function writeUpdateAttempt(layout, record3) {
|
|
4393
4393
|
await mkdir15(join9(layout.releasesRoot, ".state"), { recursive: true });
|
|
4394
4394
|
const path = updateAttemptPath(layout);
|
|
4395
4395
|
const staged = `${path}.${process.pid}.tmp`;
|
|
4396
|
-
await writeFile10(staged, `${JSON.stringify(
|
|
4396
|
+
await writeFile10(staged, `${JSON.stringify(record3, null, 2)}
|
|
4397
4397
|
`, { mode: 384 });
|
|
4398
4398
|
await rename4(staged, path);
|
|
4399
4399
|
}
|
|
4400
|
-
async function replaceUpdateAttempt(layout,
|
|
4401
|
-
await writeUpdateAttempt(layout,
|
|
4400
|
+
async function replaceUpdateAttempt(layout, record3) {
|
|
4401
|
+
await writeUpdateAttempt(layout, record3);
|
|
4402
4402
|
}
|
|
4403
|
-
async function writeUpdateAttemptFixture(layout,
|
|
4403
|
+
async function writeUpdateAttemptFixture(layout, record3) {
|
|
4404
4404
|
await writeUpdateAttempt(layout, {
|
|
4405
|
-
...
|
|
4406
|
-
version:
|
|
4407
|
-
confirmBy:
|
|
4408
|
-
status:
|
|
4405
|
+
...record3,
|
|
4406
|
+
version: record3.version ?? 1,
|
|
4407
|
+
confirmBy: record3.confirmBy ?? record3.appliedAt + DEFAULT_UPDATE_CONFIRM_WINDOW_MS,
|
|
4408
|
+
status: record3.status ?? "pending"
|
|
4409
4409
|
});
|
|
4410
4410
|
}
|
|
4411
4411
|
async function settleUpdateAttemptOnStart(layout, opts = {}) {
|
|
4412
|
-
const
|
|
4413
|
-
if (!
|
|
4412
|
+
const record3 = await readUpdateAttempt(layout);
|
|
4413
|
+
if (!record3 || record3.status !== "pending")
|
|
4414
4414
|
return { kind: "none" };
|
|
4415
4415
|
const now2 = opts.now ?? Date.now;
|
|
4416
|
-
const confirmAt = opts.confirmWindowMs ?
|
|
4416
|
+
const confirmAt = opts.confirmWindowMs ? record3.appliedAt + opts.confirmWindowMs : record3.confirmBy;
|
|
4417
4417
|
if (now2() <= confirmAt) {
|
|
4418
|
-
return { kind: "pending", confirmAt, record:
|
|
4418
|
+
return { kind: "pending", confirmAt, record: record3 };
|
|
4419
4419
|
}
|
|
4420
|
-
if (
|
|
4421
|
-
await rollbackToPreviousRelease(layout,
|
|
4420
|
+
if (record3.previousReleaseId) {
|
|
4421
|
+
await rollbackToPreviousRelease(layout, record3.previousReleaseId);
|
|
4422
4422
|
}
|
|
4423
4423
|
await writeUpdateAttempt(layout, {
|
|
4424
|
-
...
|
|
4424
|
+
...record3,
|
|
4425
4425
|
confirmBy: confirmAt,
|
|
4426
4426
|
status: "reverted",
|
|
4427
4427
|
failure: "update did not serve a verified turn before its confirmation deadline"
|
|
4428
4428
|
});
|
|
4429
|
-
return { kind: "rolled-back", record:
|
|
4429
|
+
return { kind: "rolled-back", record: record3 };
|
|
4430
4430
|
}
|
|
4431
4431
|
function describeIdentity(identity) {
|
|
4432
4432
|
if (!identity)
|
|
@@ -6864,13 +6864,13 @@ function parseAvailableCommands(value) {
|
|
|
6864
6864
|
for (const entry of value) {
|
|
6865
6865
|
if (!entry || typeof entry !== "object")
|
|
6866
6866
|
continue;
|
|
6867
|
-
const
|
|
6868
|
-
const rawName = typeof
|
|
6867
|
+
const record3 = entry;
|
|
6868
|
+
const rawName = typeof record3.name === "string" ? record3.name.trim().replace(/^\/+/, "") : "";
|
|
6869
6869
|
if (!rawName || rawName.length > 80 || seen.has(rawName))
|
|
6870
6870
|
continue;
|
|
6871
6871
|
seen.add(rawName);
|
|
6872
|
-
const description = typeof
|
|
6873
|
-
const input =
|
|
6872
|
+
const description = typeof record3.description === "string" && record3.description.trim() ? record3.description.trim().slice(0, 300) : void 0;
|
|
6873
|
+
const input = record3.input;
|
|
6874
6874
|
const hintRaw = input && typeof input.hint === "string" ? input.hint : void 0;
|
|
6875
6875
|
const inputHint = hintRaw && hintRaw.trim() ? hintRaw.trim().slice(0, 120) : void 0;
|
|
6876
6876
|
result.push({
|
|
@@ -6898,13 +6898,8 @@ var PI_ACP_HARNESS = /(^|[/\\])pi-acp(?:\.[a-z]+)?$/i;
|
|
|
6898
6898
|
function isPiAcpHarness(agentLabel) {
|
|
6899
6899
|
return Boolean(agentLabel && PI_ACP_HARNESS.test(agentLabel));
|
|
6900
6900
|
}
|
|
6901
|
-
function
|
|
6902
|
-
|
|
6903
|
-
return text2;
|
|
6904
|
-
return text2.replace(/\r?\n$/, "");
|
|
6905
|
-
}
|
|
6906
|
-
function normalizeStreamDelta(text2, agentLabel) {
|
|
6907
|
-
return agentLabel && PI_ACP_HARNESS.test(agentLabel) ? withoutOneTrailingLineEnding(text2) : text2;
|
|
6901
|
+
function normalizeStreamDelta(text2, _agentLabel) {
|
|
6902
|
+
return text2;
|
|
6908
6903
|
}
|
|
6909
6904
|
function agentMessageRuns(updates, agentLabel) {
|
|
6910
6905
|
const runs = [];
|
|
@@ -7727,12 +7722,12 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
|
|
|
7727
7722
|
for (const entry of Array.isArray(configOptions) ? configOptions : []) {
|
|
7728
7723
|
if (!entry || typeof entry !== "object")
|
|
7729
7724
|
continue;
|
|
7730
|
-
const
|
|
7731
|
-
const id =
|
|
7732
|
-
const category =
|
|
7725
|
+
const record3 = entry;
|
|
7726
|
+
const id = record3.id;
|
|
7727
|
+
const category = record3.category;
|
|
7733
7728
|
if (typeof id !== "string" || typeof category !== "string")
|
|
7734
7729
|
continue;
|
|
7735
|
-
const rawChoices = Array.isArray(
|
|
7730
|
+
const rawChoices = Array.isArray(record3.options) ? record3.options : [];
|
|
7736
7731
|
const choices = [];
|
|
7737
7732
|
for (const rawChoice of rawChoices) {
|
|
7738
7733
|
if (!rawChoice || typeof rawChoice !== "object")
|
|
@@ -7749,7 +7744,7 @@ function parseAdvertisedConfigOptions(raw, preferredModelId, includeLaunchEffort
|
|
|
7749
7744
|
result.push({
|
|
7750
7745
|
id,
|
|
7751
7746
|
category,
|
|
7752
|
-
...typeof
|
|
7747
|
+
...typeof record3.currentValue === "string" ? { currentValue: record3.currentValue } : {},
|
|
7753
7748
|
options: category === "model" ? sortModelChoicesNewestFirst(choices) : choices
|
|
7754
7749
|
});
|
|
7755
7750
|
}
|
|
@@ -7857,8 +7852,8 @@ function credentialedPiCustomProviders(env) {
|
|
|
7857
7852
|
};
|
|
7858
7853
|
if (Array.isArray(providers)) {
|
|
7859
7854
|
for (const provider of providers) {
|
|
7860
|
-
const
|
|
7861
|
-
remember(
|
|
7855
|
+
const record3 = provider;
|
|
7856
|
+
remember(record3?.name ?? record3?.id, record3);
|
|
7862
7857
|
}
|
|
7863
7858
|
} else if (providers && typeof providers === "object") {
|
|
7864
7859
|
for (const [name, config] of Object.entries(providers)) {
|
|
@@ -8161,6 +8156,483 @@ async function syncAgentModelCatalog(input) {
|
|
|
8161
8156
|
}
|
|
8162
8157
|
}
|
|
8163
8158
|
|
|
8159
|
+
// apps/body/dist/connector-squire.js
|
|
8160
|
+
import { execFile } from "node:child_process";
|
|
8161
|
+
var REMOTE_LOGIN_BINARIES = ["Xvfb", "x11vnc", "websockify", "cloudflared"];
|
|
8162
|
+
var SQUIRE_CONNECT_PACKAGE = "@trusty-squire/mcp";
|
|
8163
|
+
var defaultShellRunner = (command, args) => new Promise((resolve31) => {
|
|
8164
|
+
execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
|
|
8165
|
+
const code = error?.code;
|
|
8166
|
+
resolve31({
|
|
8167
|
+
code: typeof code === "number" ? code : error ? 1 : 0,
|
|
8168
|
+
stdout: String(stdout6 ?? ""),
|
|
8169
|
+
stderr: String(stderr ?? "")
|
|
8170
|
+
});
|
|
8171
|
+
});
|
|
8172
|
+
});
|
|
8173
|
+
var step = (label, status, reason) => ({
|
|
8174
|
+
label,
|
|
8175
|
+
status,
|
|
8176
|
+
...reason ? { reason } : {}
|
|
8177
|
+
});
|
|
8178
|
+
function binaryExists(binary) {
|
|
8179
|
+
return new Promise((resolve31) => {
|
|
8180
|
+
execFile("sh", ["-c", `command -v ${JSON.stringify(binary)}`], (error, stdout6) => {
|
|
8181
|
+
const path = String(stdout6 ?? "").trim();
|
|
8182
|
+
resolve31({ binary, found: !error && path.length > 0, ...path ? { path } : {} });
|
|
8183
|
+
});
|
|
8184
|
+
});
|
|
8185
|
+
}
|
|
8186
|
+
async function checkRemoteLoginPrerequisites(probe = binaryExists) {
|
|
8187
|
+
return Promise.all(REMOTE_LOGIN_BINARIES.map(probe));
|
|
8188
|
+
}
|
|
8189
|
+
function missingPrerequisiteStep(checks) {
|
|
8190
|
+
const missing = checks.filter((check) => !check.found).map((check) => check.binary);
|
|
8191
|
+
return step("remote sign-in prerequisites", "failed", `missing on this helper: ${missing.join(", ")}`);
|
|
8192
|
+
}
|
|
8193
|
+
function parseConnectOutput(output) {
|
|
8194
|
+
const url = output.match(/https:\/\/[^\s"'<>]+/)?.[0];
|
|
8195
|
+
if (!url)
|
|
8196
|
+
return void 0;
|
|
8197
|
+
if (/oauth|authorize/i.test(url) || /oauth/i.test(output)) {
|
|
8198
|
+
return { method: "oauth", url };
|
|
8199
|
+
}
|
|
8200
|
+
if (/novnc|vnc\.html|remote|stream/i.test(url) || /novnc|remote login|vnc/i.test(output)) {
|
|
8201
|
+
return { method: "streamed-page", url };
|
|
8202
|
+
}
|
|
8203
|
+
return { method: "streamed-page", url };
|
|
8204
|
+
}
|
|
8205
|
+
async function installedSquireVersion(run2) {
|
|
8206
|
+
const probe = await run2("npx", ["-y", SQUIRE_CONNECT_PACKAGE, "--version"]);
|
|
8207
|
+
const version = probe.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0];
|
|
8208
|
+
return version;
|
|
8209
|
+
}
|
|
8210
|
+
function parseSignedInAs(output) {
|
|
8211
|
+
return output.match(/signed in as ([^\s,;]+)/i)?.[1];
|
|
8212
|
+
}
|
|
8213
|
+
async function installSquire(options) {
|
|
8214
|
+
const run2 = options.run ?? defaultShellRunner;
|
|
8215
|
+
const steps = [step("helper reached", "done")];
|
|
8216
|
+
const emit = () => options.onProgress?.([...steps]);
|
|
8217
|
+
const push = (next) => {
|
|
8218
|
+
steps.push(next);
|
|
8219
|
+
emit();
|
|
8220
|
+
};
|
|
8221
|
+
const fail = (reason) => {
|
|
8222
|
+
steps.push(step("waiting for sign-in", "pending"));
|
|
8223
|
+
emit();
|
|
8224
|
+
return { status: "error", steps, errorMessage: reason };
|
|
8225
|
+
};
|
|
8226
|
+
emit();
|
|
8227
|
+
const checks = await checkRemoteLoginPrerequisites(options.probeBinary);
|
|
8228
|
+
if (checks.some((check) => !check.found)) {
|
|
8229
|
+
push(missingPrerequisiteStep(checks));
|
|
8230
|
+
return fail("this helper cannot host the remote sign-in surface");
|
|
8231
|
+
}
|
|
8232
|
+
push(step("remote sign-in prerequisites", "done"));
|
|
8233
|
+
const install = await run2("npx", [
|
|
8234
|
+
"-y",
|
|
8235
|
+
SQUIRE_CONNECT_PACKAGE,
|
|
8236
|
+
"connect",
|
|
8237
|
+
"--target=pi",
|
|
8238
|
+
"--skip-browser"
|
|
8239
|
+
]);
|
|
8240
|
+
if (install.code !== 0) {
|
|
8241
|
+
push(step("trusty-squire installed", "failed", install.stderr.trim() || "connect failed"));
|
|
8242
|
+
return fail("the trusty-squire install command failed");
|
|
8243
|
+
}
|
|
8244
|
+
const version = await installedSquireVersion(run2);
|
|
8245
|
+
push(step(`trusty-squire${version ? ` ${version}` : ""} installed`, "done"));
|
|
8246
|
+
const output = `${install.stdout}
|
|
8247
|
+
${install.stderr}`;
|
|
8248
|
+
const signIn = parseConnectOutput(output);
|
|
8249
|
+
if (!signIn) {
|
|
8250
|
+
push(step("waiting for sign-in", "failed", "connect printed no sign-in URL"));
|
|
8251
|
+
return fail("the trusty-squire connect command printed no sign-in surface");
|
|
8252
|
+
}
|
|
8253
|
+
push(step("waiting for sign-in", "done"));
|
|
8254
|
+
const signedInAs = parseSignedInAs(output);
|
|
8255
|
+
const pair = await pairSquire(options.mcp, options.workspaceId);
|
|
8256
|
+
if (!pair.ok) {
|
|
8257
|
+
push(step("paired to workspace", "failed", pair.reason));
|
|
8258
|
+
return {
|
|
8259
|
+
status: "installing",
|
|
8260
|
+
steps,
|
|
8261
|
+
signIn,
|
|
8262
|
+
...version ? { squireVersion: version } : {},
|
|
8263
|
+
...signedInAs ? { signedInAs } : {}
|
|
8264
|
+
};
|
|
8265
|
+
}
|
|
8266
|
+
push(step("paired to workspace", "done"));
|
|
8267
|
+
return {
|
|
8268
|
+
status: "connected",
|
|
8269
|
+
steps,
|
|
8270
|
+
signIn,
|
|
8271
|
+
...version ? { squireVersion: version } : {},
|
|
8272
|
+
...signedInAs ? { signedInAs } : {}
|
|
8273
|
+
};
|
|
8274
|
+
}
|
|
8275
|
+
async function pairSquire(mcp, _workspaceId) {
|
|
8276
|
+
if (!mcp)
|
|
8277
|
+
return { ok: false, reason: "the Squire MCP surface is not mounted on this helper" };
|
|
8278
|
+
try {
|
|
8279
|
+
await mcp.call("list_credentials", { fields: "summary" });
|
|
8280
|
+
return { ok: true };
|
|
8281
|
+
} catch (error) {
|
|
8282
|
+
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
8283
|
+
}
|
|
8284
|
+
}
|
|
8285
|
+
function asRecord(value) {
|
|
8286
|
+
return value && typeof value === "object" ? value : {};
|
|
8287
|
+
}
|
|
8288
|
+
function asArray(value) {
|
|
8289
|
+
return Array.isArray(value) ? value : [];
|
|
8290
|
+
}
|
|
8291
|
+
function stringList(value) {
|
|
8292
|
+
return asArray(value).filter((entry) => typeof entry === "string");
|
|
8293
|
+
}
|
|
8294
|
+
function numberOrNull(value) {
|
|
8295
|
+
return typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
8296
|
+
}
|
|
8297
|
+
function vaultConnectionMeta(raw) {
|
|
8298
|
+
const record3 = asRecord(raw);
|
|
8299
|
+
const reference = String(record3.reference ?? record3.id ?? "");
|
|
8300
|
+
return {
|
|
8301
|
+
reference,
|
|
8302
|
+
service: typeof record3.service === "string" ? record3.service : null,
|
|
8303
|
+
label: String(record3.label ?? record3.service ?? reference),
|
|
8304
|
+
fieldNames: stringList(record3.field_names ?? record3.fieldNames),
|
|
8305
|
+
allowedHosts: stringList(record3.allowed_hosts ?? record3.allowedHosts ?? record3.login_hosts),
|
|
8306
|
+
createdAt: numberOrNull(record3.created_at ?? record3.createdAt) ?? 0,
|
|
8307
|
+
stale: record3.stale === true,
|
|
8308
|
+
state: record3.state === "error" ? "error" : "active"
|
|
8309
|
+
};
|
|
8310
|
+
}
|
|
8311
|
+
async function readVault(mcp) {
|
|
8312
|
+
const result = asRecord(await mcp.call("list_credentials"));
|
|
8313
|
+
return asArray(result.credentials ?? result.items ?? result).map(vaultConnectionMeta);
|
|
8314
|
+
}
|
|
8315
|
+
function connectionGrant(raw) {
|
|
8316
|
+
const record3 = asRecord(raw);
|
|
8317
|
+
return {
|
|
8318
|
+
grantId: String(record3.grant_id ?? record3.grantId ?? record3.id ?? ""),
|
|
8319
|
+
credentialRef: String(record3.credential_ref ?? record3.credentialRef ?? record3.reference ?? ""),
|
|
8320
|
+
createdAt: numberOrNull(record3.created_at ?? record3.createdAt) ?? 0,
|
|
8321
|
+
...numberOrNull(record3.revoked_at ?? record3.revokedAt) !== null ? { revokedAt: numberOrNull(record3.revoked_at ?? record3.revokedAt) } : {},
|
|
8322
|
+
...numberOrNull(record3.rate_limit_per_hour) !== null ? { rateLimitPerHour: numberOrNull(record3.rate_limit_per_hour) } : {},
|
|
8323
|
+
...numberOrNull(record3.spend_cap_usd) !== null ? { spendCapUsd: numberOrNull(record3.spend_cap_usd) } : {}
|
|
8324
|
+
};
|
|
8325
|
+
}
|
|
8326
|
+
async function readGrants(mcp, ref) {
|
|
8327
|
+
const result = asRecord(await mcp.call("list_app_access", {}));
|
|
8328
|
+
return asArray(result.grants ?? result.items ?? result).map(connectionGrant).filter((grant) => grant.credentialRef === ref && grant.revokedAt === void 0);
|
|
8329
|
+
}
|
|
8330
|
+
async function revokeGrants(mcp, ref) {
|
|
8331
|
+
const grants = await readGrants(mcp, ref);
|
|
8332
|
+
let revoked = 0;
|
|
8333
|
+
let failed = 0;
|
|
8334
|
+
for (const grant of grants) {
|
|
8335
|
+
try {
|
|
8336
|
+
const result = asRecord(await mcp.call("revoke_app_access", { grant_id: grant.grantId }));
|
|
8337
|
+
if (result.revoked === false)
|
|
8338
|
+
failed += 1;
|
|
8339
|
+
else
|
|
8340
|
+
revoked += 1;
|
|
8341
|
+
} catch {
|
|
8342
|
+
failed += 1;
|
|
8343
|
+
}
|
|
8344
|
+
}
|
|
8345
|
+
return { revoked, failed };
|
|
8346
|
+
}
|
|
8347
|
+
|
|
8348
|
+
// apps/body/dist/squire-mcp-client.js
|
|
8349
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
8350
|
+
var INITIALIZE_TIMEOUT_MS = 3e4;
|
|
8351
|
+
var CALL_TIMEOUT_MS = 12e4;
|
|
8352
|
+
var StdioSquireMcpClient = class {
|
|
8353
|
+
options;
|
|
8354
|
+
child;
|
|
8355
|
+
nextId = 1;
|
|
8356
|
+
pending = /* @__PURE__ */ new Map();
|
|
8357
|
+
buffer = "";
|
|
8358
|
+
initialized;
|
|
8359
|
+
closed = false;
|
|
8360
|
+
log;
|
|
8361
|
+
constructor(options = {}) {
|
|
8362
|
+
this.options = options;
|
|
8363
|
+
this.log = options.log ?? (() => {
|
|
8364
|
+
});
|
|
8365
|
+
}
|
|
8366
|
+
/** One Squire MCP tool call; resolves with the tool's parsed result. */
|
|
8367
|
+
async call(tool, args = {}) {
|
|
8368
|
+
await this.ensureSession();
|
|
8369
|
+
const response = await this.request("tools/call", { name: tool, arguments: args });
|
|
8370
|
+
if (response.isError) {
|
|
8371
|
+
const text3 = response.content?.find((entry) => entry.type === "text")?.text ?? "tool error";
|
|
8372
|
+
throw new Error(`${tool} failed: ${text3}`);
|
|
8373
|
+
}
|
|
8374
|
+
const text2 = response.content?.find((entry) => entry.type === "text")?.text;
|
|
8375
|
+
if (typeof text2 !== "string")
|
|
8376
|
+
return response;
|
|
8377
|
+
try {
|
|
8378
|
+
return JSON.parse(text2);
|
|
8379
|
+
} catch {
|
|
8380
|
+
return response;
|
|
8381
|
+
}
|
|
8382
|
+
}
|
|
8383
|
+
/** Tear the session down; safe to call repeatedly. */
|
|
8384
|
+
close() {
|
|
8385
|
+
this.closed = true;
|
|
8386
|
+
this.initialized = void 0;
|
|
8387
|
+
for (const entry of this.pending.values()) {
|
|
8388
|
+
clearTimeout(entry.timer);
|
|
8389
|
+
entry.reject(new Error("Squire MCP session closed"));
|
|
8390
|
+
}
|
|
8391
|
+
this.pending.clear();
|
|
8392
|
+
this.child?.kill();
|
|
8393
|
+
this.child = void 0;
|
|
8394
|
+
}
|
|
8395
|
+
ensureSession() {
|
|
8396
|
+
if (this.closed)
|
|
8397
|
+
throw new Error("Squire MCP client is closed");
|
|
8398
|
+
this.initialized ??= this.initialize().catch((error) => {
|
|
8399
|
+
this.initialized = void 0;
|
|
8400
|
+
throw error;
|
|
8401
|
+
});
|
|
8402
|
+
return this.initialized;
|
|
8403
|
+
}
|
|
8404
|
+
initialize() {
|
|
8405
|
+
const child = (this.options.spawn ?? spawn2)(this.options.command ?? "npx", [
|
|
8406
|
+
...this.options.args ?? ["-y", "@trusty-squire/mcp"]
|
|
8407
|
+
]);
|
|
8408
|
+
this.child = child;
|
|
8409
|
+
this.buffer = "";
|
|
8410
|
+
child.stdout.setEncoding("utf8");
|
|
8411
|
+
child.stderr.setEncoding("utf8");
|
|
8412
|
+
child.stdout.on("data", (chunk) => this.onData(chunk));
|
|
8413
|
+
child.stderr.on("data", (chunk) => this.log(`squire mcp stderr: ${chunk.trim()}`));
|
|
8414
|
+
child.on("exit", (code) => {
|
|
8415
|
+
this.log(`squire mcp exited (${String(code)})`);
|
|
8416
|
+
this.initialized = void 0;
|
|
8417
|
+
this.child = void 0;
|
|
8418
|
+
for (const entry of this.pending.values()) {
|
|
8419
|
+
clearTimeout(entry.timer);
|
|
8420
|
+
entry.reject(new Error("Squire MCP server exited"));
|
|
8421
|
+
}
|
|
8422
|
+
this.pending.clear();
|
|
8423
|
+
});
|
|
8424
|
+
return this.request("initialize", {
|
|
8425
|
+
protocolVersion: "2024-11-05",
|
|
8426
|
+
capabilities: {},
|
|
8427
|
+
clientInfo: { name: "beeline-helper", version: "1.0.0" }
|
|
8428
|
+
}).then(() => {
|
|
8429
|
+
this.notify("notifications/initialized");
|
|
8430
|
+
});
|
|
8431
|
+
}
|
|
8432
|
+
notify(method, params) {
|
|
8433
|
+
this.child?.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", method, ...params ? { params } : {} })}
|
|
8434
|
+
`);
|
|
8435
|
+
}
|
|
8436
|
+
request(method, params) {
|
|
8437
|
+
const id = this.nextId++;
|
|
8438
|
+
const child = this.child;
|
|
8439
|
+
if (!child)
|
|
8440
|
+
return Promise.reject(new Error("Squire MCP session is not running"));
|
|
8441
|
+
return new Promise((resolve31, reject) => {
|
|
8442
|
+
const timer = setTimeout(() => {
|
|
8443
|
+
this.pending.delete(id);
|
|
8444
|
+
reject(new Error(`${method} timed out`));
|
|
8445
|
+
}, method === "initialize" ? INITIALIZE_TIMEOUT_MS : CALL_TIMEOUT_MS);
|
|
8446
|
+
this.pending.set(id, { resolve: resolve31, reject, timer });
|
|
8447
|
+
child.stdin.write(`${JSON.stringify({ jsonrpc: "2.0", id, method, params })}
|
|
8448
|
+
`);
|
|
8449
|
+
});
|
|
8450
|
+
}
|
|
8451
|
+
onData(chunk) {
|
|
8452
|
+
this.buffer += chunk;
|
|
8453
|
+
for (; ; ) {
|
|
8454
|
+
const newline = this.buffer.indexOf("\n");
|
|
8455
|
+
if (newline < 0)
|
|
8456
|
+
return;
|
|
8457
|
+
const line = this.buffer.slice(0, newline).trim();
|
|
8458
|
+
this.buffer = this.buffer.slice(newline + 1);
|
|
8459
|
+
if (!line)
|
|
8460
|
+
continue;
|
|
8461
|
+
let message;
|
|
8462
|
+
try {
|
|
8463
|
+
message = JSON.parse(line);
|
|
8464
|
+
} catch {
|
|
8465
|
+
continue;
|
|
8466
|
+
}
|
|
8467
|
+
const id = typeof message.id === "number" ? message.id : void 0;
|
|
8468
|
+
if (id === void 0)
|
|
8469
|
+
continue;
|
|
8470
|
+
const entry = this.pending.get(id);
|
|
8471
|
+
if (!entry)
|
|
8472
|
+
continue;
|
|
8473
|
+
this.pending.delete(id);
|
|
8474
|
+
clearTimeout(entry.timer);
|
|
8475
|
+
if (message.error) {
|
|
8476
|
+
const error = message.error;
|
|
8477
|
+
entry.reject(new Error(error.message ?? "Squire MCP error"));
|
|
8478
|
+
} else {
|
|
8479
|
+
entry.resolve(message.result);
|
|
8480
|
+
}
|
|
8481
|
+
}
|
|
8482
|
+
}
|
|
8483
|
+
};
|
|
8484
|
+
function defaultSquireMcpClient() {
|
|
8485
|
+
return new StdioSquireMcpClient();
|
|
8486
|
+
}
|
|
8487
|
+
|
|
8488
|
+
// apps/body/dist/connector-assignments.js
|
|
8489
|
+
var CONNECTOR_POLL_INTERVAL_MS = 1e4;
|
|
8490
|
+
var ConnectorAssignmentLoop = class {
|
|
8491
|
+
agentId;
|
|
8492
|
+
api;
|
|
8493
|
+
intervalMs;
|
|
8494
|
+
log;
|
|
8495
|
+
install;
|
|
8496
|
+
readVaultFn;
|
|
8497
|
+
revokeGrantsFn;
|
|
8498
|
+
schedule;
|
|
8499
|
+
cancel;
|
|
8500
|
+
timer;
|
|
8501
|
+
started = false;
|
|
8502
|
+
stopped = false;
|
|
8503
|
+
/** One install at a time per connector; other polls skip it. */
|
|
8504
|
+
inFlight = /* @__PURE__ */ new Set();
|
|
8505
|
+
mcp;
|
|
8506
|
+
constructor(options) {
|
|
8507
|
+
this.agentId = options.agentId;
|
|
8508
|
+
this.api = options.api;
|
|
8509
|
+
this.intervalMs = options.intervalMs ?? CONNECTOR_POLL_INTERVAL_MS;
|
|
8510
|
+
this.log = options.log ?? (() => {
|
|
8511
|
+
});
|
|
8512
|
+
this.install = options.install ?? installSquire;
|
|
8513
|
+
this.readVaultFn = options.readVault ?? readVault;
|
|
8514
|
+
this.revokeGrantsFn = options.revokeGrants ?? revokeGrants;
|
|
8515
|
+
this.schedule = options.schedule ?? ((fn, ms) => {
|
|
8516
|
+
const timer = setTimeout(fn, ms);
|
|
8517
|
+
timer.unref?.();
|
|
8518
|
+
return timer;
|
|
8519
|
+
});
|
|
8520
|
+
this.cancel = options.cancel ?? ((handle) => clearTimeout(handle));
|
|
8521
|
+
}
|
|
8522
|
+
start() {
|
|
8523
|
+
if (this.stopped || this.started)
|
|
8524
|
+
return;
|
|
8525
|
+
this.started = true;
|
|
8526
|
+
void this.runOnce();
|
|
8527
|
+
this.timer = this.schedule(() => this.poll(), this.intervalMs);
|
|
8528
|
+
}
|
|
8529
|
+
stop() {
|
|
8530
|
+
this.stopped = true;
|
|
8531
|
+
if (this.timer !== void 0) {
|
|
8532
|
+
this.cancel(this.timer);
|
|
8533
|
+
this.timer = void 0;
|
|
8534
|
+
}
|
|
8535
|
+
}
|
|
8536
|
+
/** One interval tick: poll, then re-arm. */
|
|
8537
|
+
poll() {
|
|
8538
|
+
if (this.stopped)
|
|
8539
|
+
return;
|
|
8540
|
+
void this.runOnce();
|
|
8541
|
+
this.timer = this.schedule(() => this.poll(), this.intervalMs);
|
|
8542
|
+
}
|
|
8543
|
+
/** Drain the queue once; every failure is logged, never raised. */
|
|
8544
|
+
async runOnce() {
|
|
8545
|
+
let assignments;
|
|
8546
|
+
try {
|
|
8547
|
+
const result = await this.api.execute("getConnectorAssignments", { agentId: this.agentId });
|
|
8548
|
+
assignments = result.assignments;
|
|
8549
|
+
} catch (error) {
|
|
8550
|
+
this.log(`connector assignments unavailable: ${describe(error)}`);
|
|
8551
|
+
return;
|
|
8552
|
+
}
|
|
8553
|
+
for (const assignment of assignments) {
|
|
8554
|
+
const key = `${assignment.kind}:${assignment.connectorId}`;
|
|
8555
|
+
if (assignment.kind === "uninstall")
|
|
8556
|
+
continue;
|
|
8557
|
+
if (this.inFlight.has(key))
|
|
8558
|
+
continue;
|
|
8559
|
+
this.inFlight.add(key);
|
|
8560
|
+
void this.handle(assignment).catch((error) => this.log(`connector assignment ${key} failed: ${describe(error)}`)).finally(() => this.inFlight.delete(key));
|
|
8561
|
+
}
|
|
8562
|
+
}
|
|
8563
|
+
squire() {
|
|
8564
|
+
this.mcp ??= defaultSquireMcpClient();
|
|
8565
|
+
return this.mcp;
|
|
8566
|
+
}
|
|
8567
|
+
async handle(assignment) {
|
|
8568
|
+
if (assignment.kind === "install")
|
|
8569
|
+
await this.runInstall(assignment.connectorId);
|
|
8570
|
+
else if (assignment.kind === "sync")
|
|
8571
|
+
await this.runSync();
|
|
8572
|
+
else if (assignment.kind === "revoke-grants")
|
|
8573
|
+
await this.runRevoke(assignment.connectorId, assignment.reference);
|
|
8574
|
+
}
|
|
8575
|
+
/** Install Trusty Squire, reporting every step as it settles. */
|
|
8576
|
+
async runInstall(connectorId) {
|
|
8577
|
+
const report = async (steps) => {
|
|
8578
|
+
try {
|
|
8579
|
+
await this.api.execute("postConnectorStatus", { agentId: this.agentId, connectorId, steps });
|
|
8580
|
+
} catch (error) {
|
|
8581
|
+
this.log(`step report failed: ${describe(error)}`);
|
|
8582
|
+
}
|
|
8583
|
+
};
|
|
8584
|
+
const result = await this.install({
|
|
8585
|
+
workspaceId: this.agentId,
|
|
8586
|
+
mcp: this.squire(),
|
|
8587
|
+
onProgress: report
|
|
8588
|
+
});
|
|
8589
|
+
if (result.status === "error") {
|
|
8590
|
+
await this.api.execute("postConnectorStatus", {
|
|
8591
|
+
agentId: this.agentId,
|
|
8592
|
+
connectorId,
|
|
8593
|
+
steps: result.steps,
|
|
8594
|
+
errorMessage: result.errorMessage
|
|
8595
|
+
});
|
|
8596
|
+
return;
|
|
8597
|
+
}
|
|
8598
|
+
if (result.status === "connected") {
|
|
8599
|
+
await this.api.execute("installConnector", {
|
|
8600
|
+
agentId: this.agentId,
|
|
8601
|
+
connectorId,
|
|
8602
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {},
|
|
8603
|
+
...result.signedInAs ? { signedInAs: result.signedInAs } : {},
|
|
8604
|
+
...result.signIn ? { signIn: result.signIn } : {}
|
|
8605
|
+
});
|
|
8606
|
+
await this.reportVault(connectorId);
|
|
8607
|
+
return;
|
|
8608
|
+
}
|
|
8609
|
+
await this.api.execute("postConnectorStatus", {
|
|
8610
|
+
agentId: this.agentId,
|
|
8611
|
+
connectorId,
|
|
8612
|
+
steps: result.steps,
|
|
8613
|
+
...result.squireVersion ? { squireVersion: result.squireVersion } : {},
|
|
8614
|
+
...result.signedInAs ? { signedInAs: result.signedInAs } : {},
|
|
8615
|
+
...result.signIn ? { signIn: result.signIn } : {}
|
|
8616
|
+
});
|
|
8617
|
+
}
|
|
8618
|
+
/** One vault report covers every live trusty-squire connector on this helper. */
|
|
8619
|
+
async runSync() {
|
|
8620
|
+
await this.reportVault();
|
|
8621
|
+
}
|
|
8622
|
+
async runRevoke(connectorId, reference) {
|
|
8623
|
+
const outcome = await this.revokeGrantsFn(this.squire(), reference);
|
|
8624
|
+
this.log(`revoked ${outcome.revoked} grant(s) on ${reference}` + (outcome.failed ? `, ${outcome.failed} failed` : ""));
|
|
8625
|
+
void connectorId;
|
|
8626
|
+
}
|
|
8627
|
+
async reportVault(_connectorId) {
|
|
8628
|
+
const connections = await this.readVaultFn(this.squire());
|
|
8629
|
+
await this.api.execute("postConnectorVault", { agentId: this.agentId, connections });
|
|
8630
|
+
}
|
|
8631
|
+
};
|
|
8632
|
+
function describe(error) {
|
|
8633
|
+
return error instanceof Error ? error.message : String(error);
|
|
8634
|
+
}
|
|
8635
|
+
|
|
8164
8636
|
// packages/api-contract/dist/daemon-operations.js
|
|
8165
8637
|
function isAgentCommand(value) {
|
|
8166
8638
|
if (!value || typeof value !== "object")
|
|
@@ -8183,6 +8655,9 @@ function isAgentCommand(value) {
|
|
|
8183
8655
|
return Boolean(source && typeof source.id === "string" && typeof source.authorId === "string" && typeof source.body === "string" && typeof source.createdAt === "number" && Array.isArray(source.attachments));
|
|
8184
8656
|
}
|
|
8185
8657
|
|
|
8658
|
+
// packages/api-contract/dist/artifacts.js
|
|
8659
|
+
var ARTIFACT_MAXIMUM_BYTES = 25 * 1024 * 1024;
|
|
8660
|
+
|
|
8186
8661
|
// packages/api-contract/dist/system-events.js
|
|
8187
8662
|
var SERVER_EVENT_KINDS = [
|
|
8188
8663
|
"joined",
|
|
@@ -8212,12 +8687,12 @@ var wrapper_default = import_websocket.default;
|
|
|
8212
8687
|
|
|
8213
8688
|
// apps/body/dist/runtime.js
|
|
8214
8689
|
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
8215
|
-
import { execFile } from "node:child_process";
|
|
8690
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
8216
8691
|
import { closeSync, openSync } from "node:fs";
|
|
8217
8692
|
import { mkdir, readFile as readFile2, readdir, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
|
|
8218
8693
|
import { homedir as homedir2 } from "node:os";
|
|
8219
8694
|
import { dirname as dirname2, resolve as resolve6 } from "node:path";
|
|
8220
|
-
import { spawn as
|
|
8695
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
8221
8696
|
import { promisify } from "node:util";
|
|
8222
8697
|
|
|
8223
8698
|
// node_modules/@noble/hashes/_u64.js
|
|
@@ -16340,13 +16815,13 @@ var NegentropyStorageVector = class {
|
|
|
16340
16815
|
let count = last - first;
|
|
16341
16816
|
while (count > 0) {
|
|
16342
16817
|
let it = first;
|
|
16343
|
-
let
|
|
16344
|
-
it +=
|
|
16818
|
+
let step2 = Math.floor(count / 2);
|
|
16819
|
+
it += step2;
|
|
16345
16820
|
if (cmp(arr[it])) {
|
|
16346
16821
|
first = ++it;
|
|
16347
|
-
count -=
|
|
16822
|
+
count -= step2 + 1;
|
|
16348
16823
|
} else {
|
|
16349
|
-
count =
|
|
16824
|
+
count = step2;
|
|
16350
16825
|
}
|
|
16351
16826
|
}
|
|
16352
16827
|
return first;
|
|
@@ -16748,7 +17223,7 @@ function decodeNsec(nsec) {
|
|
|
16748
17223
|
}
|
|
16749
17224
|
|
|
16750
17225
|
// apps/body/dist/runtime.js
|
|
16751
|
-
var execFileAsync = promisify(
|
|
17226
|
+
var execFileAsync = promisify(execFile2);
|
|
16752
17227
|
var DEFAULT_AGENT_IDENTITY_NAME = "beeline-agent";
|
|
16753
17228
|
var DEFAULT_BODY_IDENTITY_NAME = "beeline-body";
|
|
16754
17229
|
var DEFAULT_DAEMON_MONOLITH_BASE_URL = "https://server.usebeeline.app";
|
|
@@ -16938,7 +17413,7 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
|
|
|
16938
17413
|
const entrypoint = opts.entrypoint ?? process.argv[1];
|
|
16939
17414
|
if (!entrypoint)
|
|
16940
17415
|
throw new Error("cannot resolve daemon CLI entrypoint");
|
|
16941
|
-
const child =
|
|
17416
|
+
const child = spawn3(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve6(configPath)], {
|
|
16942
17417
|
cwd: directory,
|
|
16943
17418
|
env: opts.env ?? process.env,
|
|
16944
17419
|
detached: !foreground,
|
|
@@ -17209,7 +17684,7 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
|
|
|
17209
17684
|
}
|
|
17210
17685
|
|
|
17211
17686
|
// apps/body/dist/room-runtime.js
|
|
17212
|
-
import { execFile as
|
|
17687
|
+
import { execFile as execFile6 } from "node:child_process";
|
|
17213
17688
|
import { createHash as createHash6 } from "node:crypto";
|
|
17214
17689
|
import { existsSync as existsSync4, mkdirSync } from "node:fs";
|
|
17215
17690
|
import { mkdir as mkdir13, rm as rm5 } from "node:fs/promises";
|
|
@@ -17217,7 +17692,7 @@ import { dirname as dirname8, resolve as resolve20 } from "node:path";
|
|
|
17217
17692
|
import { promisify as promisify4 } from "node:util";
|
|
17218
17693
|
|
|
17219
17694
|
// apps/body/dist/grant-runner.js
|
|
17220
|
-
import { execFile as
|
|
17695
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
17221
17696
|
import { createHash as createHash2, randomBytes as randomBytes6 } from "node:crypto";
|
|
17222
17697
|
import { readFile as readFile4 } from "node:fs/promises";
|
|
17223
17698
|
import { createServer } from "node:http";
|
|
@@ -17876,9 +18351,9 @@ var GrantCommandRunner = class {
|
|
|
17876
18351
|
...Object.fromEntries(secrets)
|
|
17877
18352
|
};
|
|
17878
18353
|
const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
|
|
17879
|
-
const
|
|
18354
|
+
const spawn9 = surfaceAllows(policy.surface, "run-host-command") ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv);
|
|
17880
18355
|
const outcome = await new Promise((resolveRun) => {
|
|
17881
|
-
const child =
|
|
18356
|
+
const child = execFile3(spawn9.command, spawn9.args, {
|
|
17882
18357
|
cwd: room.cwd,
|
|
17883
18358
|
env,
|
|
17884
18359
|
timeout: this.options.timeoutMs ?? GRANT_COMMAND_TIMEOUT_MS,
|
|
@@ -18047,6 +18522,106 @@ var GrantRunnerServer = class {
|
|
|
18047
18522
|
}
|
|
18048
18523
|
};
|
|
18049
18524
|
|
|
18525
|
+
// apps/body/dist/connector-runner.js
|
|
18526
|
+
function toolName(call) {
|
|
18527
|
+
const raw = `${call.title ?? ""} ${call.kind ?? ""}`;
|
|
18528
|
+
const match = raw.match(/(?:^|__|\b)(use_credential|fetch_credential|grant_app_access|revoke_app_access)\b/);
|
|
18529
|
+
return match?.[1] ?? "";
|
|
18530
|
+
}
|
|
18531
|
+
var SQUIRE_EVENT_CLASS = {
|
|
18532
|
+
fetch_credential: "approval",
|
|
18533
|
+
// the credential was read in clear
|
|
18534
|
+
grant_app_access: "approval",
|
|
18535
|
+
// a host was added to a key
|
|
18536
|
+
revoke_app_access: "vault-change"
|
|
18537
|
+
// access removed on the owner's behalf
|
|
18538
|
+
};
|
|
18539
|
+
function record(call) {
|
|
18540
|
+
return call && typeof call === "object" ? call : {};
|
|
18541
|
+
}
|
|
18542
|
+
function serializedSize(value) {
|
|
18543
|
+
if (value == null)
|
|
18544
|
+
return 0;
|
|
18545
|
+
if (typeof value === "string")
|
|
18546
|
+
return value.length;
|
|
18547
|
+
try {
|
|
18548
|
+
return JSON.stringify(value).length;
|
|
18549
|
+
} catch {
|
|
18550
|
+
return 0;
|
|
18551
|
+
}
|
|
18552
|
+
}
|
|
18553
|
+
function squireUsageFromToolCall(call) {
|
|
18554
|
+
const tool = toolName(call);
|
|
18555
|
+
if (!tool)
|
|
18556
|
+
return void 0;
|
|
18557
|
+
const args = record(call.rawInput);
|
|
18558
|
+
const result = record(call.content ?? call.rawOutput);
|
|
18559
|
+
const resultPayload = record(result.content ?? result);
|
|
18560
|
+
const url = typeof args.url === "string" ? args.url : "";
|
|
18561
|
+
const method = typeof args.method === "string" ? args.method.toUpperCase() : tool;
|
|
18562
|
+
const operation = url ? `${method} ${url}` : `${method} ${String(args.reference ?? "")}`.trim();
|
|
18563
|
+
const statusCode = typeof resultPayload.status === "number" ? resultPayload.status : /failed|error|denied/i.test(call.status ?? "") ? 0 : 200;
|
|
18564
|
+
return {
|
|
18565
|
+
ref: String(args.reference ?? args.service ?? ""),
|
|
18566
|
+
service: typeof args.service === "string" ? args.service : null,
|
|
18567
|
+
operation: operation.slice(0, 300),
|
|
18568
|
+
statusCode,
|
|
18569
|
+
bytes: serializedSize(resultPayload.body ?? resultPayload),
|
|
18570
|
+
...typeof args.grant_id === "string" ? { grantId: args.grant_id } : {},
|
|
18571
|
+
...SQUIRE_EVENT_CLASS[tool] ? { eventClass: SQUIRE_EVENT_CLASS[tool] } : {}
|
|
18572
|
+
};
|
|
18573
|
+
}
|
|
18574
|
+
var ConnectorUsageRecorder = class {
|
|
18575
|
+
batches = /* @__PURE__ */ new Map();
|
|
18576
|
+
turns = /* @__PURE__ */ new Map();
|
|
18577
|
+
record(turn, usage2) {
|
|
18578
|
+
if (!usage2.ref)
|
|
18579
|
+
return;
|
|
18580
|
+
const existing = this.batches.get(turn.requestId) ?? [];
|
|
18581
|
+
existing.push(usage2);
|
|
18582
|
+
this.batches.set(turn.requestId, existing);
|
|
18583
|
+
if (!this.turns.has(turn.requestId))
|
|
18584
|
+
this.turns.set(turn.requestId, turn);
|
|
18585
|
+
}
|
|
18586
|
+
/** Everything recorded for this turn so far; used by tests and diagnostics. */
|
|
18587
|
+
pending(requestId) {
|
|
18588
|
+
return this.batches.get(requestId) ?? [];
|
|
18589
|
+
}
|
|
18590
|
+
/**
|
|
18591
|
+
* Publish the turn's whole batch as ONE operation call and clear it.
|
|
18592
|
+
* Returns the published record count (0 when nothing was recorded or the
|
|
18593
|
+
* publish failed — a failed usage report never fails the agent turn).
|
|
18594
|
+
*/
|
|
18595
|
+
async flush(api, requestId) {
|
|
18596
|
+
const usage2 = this.batches.get(requestId);
|
|
18597
|
+
const turn = this.turns.get(requestId);
|
|
18598
|
+
if (!usage2?.length || !turn)
|
|
18599
|
+
return 0;
|
|
18600
|
+
this.batches.delete(requestId);
|
|
18601
|
+
this.turns.delete(requestId);
|
|
18602
|
+
try {
|
|
18603
|
+
await api.execute("postConnectionUsage", {
|
|
18604
|
+
requestId: turn.requestId,
|
|
18605
|
+
agentId: turn.agentId,
|
|
18606
|
+
...turn.roomId ? { roomId: turn.roomId } : {},
|
|
18607
|
+
...turn.cornerId ? { cornerId: turn.cornerId } : {},
|
|
18608
|
+
usage: usage2
|
|
18609
|
+
});
|
|
18610
|
+
return usage2.length;
|
|
18611
|
+
} catch (error) {
|
|
18612
|
+
console.error("[connector] connection usage report failed:", error);
|
|
18613
|
+
return 0;
|
|
18614
|
+
}
|
|
18615
|
+
}
|
|
18616
|
+
};
|
|
18617
|
+
function captureConnectionUsage(recorder, turn, calls) {
|
|
18618
|
+
for (const call of calls) {
|
|
18619
|
+
const usage2 = squireUsageFromToolCall(call);
|
|
18620
|
+
if (usage2)
|
|
18621
|
+
recorder.record(turn, usage2);
|
|
18622
|
+
}
|
|
18623
|
+
}
|
|
18624
|
+
|
|
18050
18625
|
// apps/body/dist/server-command-intake.js
|
|
18051
18626
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
18052
18627
|
import { mkdir as mkdir3, writeFile as writeFile4 } from "node:fs/promises";
|
|
@@ -18214,7 +18789,7 @@ async function runServerCommandIntake(options) {
|
|
|
18214
18789
|
}
|
|
18215
18790
|
|
|
18216
18791
|
// apps/body/dist/monolith-corner-turn.js
|
|
18217
|
-
import { execFile as
|
|
18792
|
+
import { execFile as execFile5 } from "node:child_process";
|
|
18218
18793
|
import { createHash as createHash5 } from "node:crypto";
|
|
18219
18794
|
import { mkdir as mkdir12 } from "node:fs/promises";
|
|
18220
18795
|
import { homedir as homedir7 } from "node:os";
|
|
@@ -18248,6 +18823,9 @@ function isAgentPairingCode(value) {
|
|
|
18248
18823
|
// apps/body/dist/beeline-skill.js
|
|
18249
18824
|
var USING_BEELINE_SKILL_NAME = "using-beeline";
|
|
18250
18825
|
var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
|
|
18826
|
+
function isConfiguredReviewer(agentHandle, reviewerHandle) {
|
|
18827
|
+
return Boolean(agentHandle && reviewerHandle && agentHandle.replace(/^@/, "") === reviewerHandle.replace(/^@/, ""));
|
|
18828
|
+
}
|
|
18251
18829
|
var BEELINE_ROOM_CAPABILITIES = [
|
|
18252
18830
|
"The repository filesystem is read-only in this Room session.",
|
|
18253
18831
|
"You may address any Room member, including another agent, by writing @name in your reply; the server routes that mention to them. Each turn prompt lists the Room members and the exact spelling that tags each one - use those spellings, and never guess or reuse one from an older message.",
|
|
@@ -18255,7 +18833,7 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
18255
18833
|
"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.",
|
|
18256
18834
|
"Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
|
|
18257
18835
|
"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.",
|
|
18258
|
-
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent
|
|
18836
|
+
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture - turning it into a raster image needs a converter, which needs shell, which this Room does not have.",
|
|
18259
18837
|
"To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
|
|
18260
18838
|
`To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
|
|
18261
18839
|
"To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
|
|
@@ -18271,7 +18849,7 @@ var BEELINE_DM_CAPABILITIES = [
|
|
|
18271
18849
|
"The repository filesystem is read-only in this session.",
|
|
18272
18850
|
"Every MCP server mounted into this session is approved tool by tool - use operator and host tools freely; the read-only filesystem sandbox is the boundary, not a tool list. Network web search is enabled.",
|
|
18273
18851
|
"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.",
|
|
18274
|
-
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent
|
|
18852
|
+
"To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture - turning it into a raster image needs a converter, which needs shell, which this Room does not have.",
|
|
18275
18853
|
"Tag the person only when you need a decision or input, or when the task they asked for is finished.",
|
|
18276
18854
|
"Never claim an action or reply happened unless the prompt or a tool result proves it."
|
|
18277
18855
|
].join(" ");
|
|
@@ -18312,6 +18890,22 @@ description: How to answer inside a Beeline Room.
|
|
|
18312
18890
|
# Using Beeline
|
|
18313
18891
|
|
|
18314
18892
|
You are answering inside a Room whose filesystem is read-only. ${BEELINE_ROOM_CAPABILITIES}
|
|
18893
|
+
|
|
18894
|
+
When your corner's pull request is ready, merging is your step: once the configured reviewer approves and tags you, you run \`gh pr merge\` yourself - nothing merges it for you.
|
|
18895
|
+
|
|
18896
|
+
## Tools and the Workbench
|
|
18897
|
+
|
|
18898
|
+
A **tool** is something you can use once a human pairs it; a **key** is the credential that tool holds for that human. You spend a key through the mounted connector and never see the credential itself.
|
|
18899
|
+
|
|
18900
|
+
This build knows one tool: Trusty Squire - vaulted credentials and a browser that signs in for you. Wallet and Tailscale are named in the Workbench but not yet available.
|
|
18901
|
+
|
|
18902
|
+
You NEVER pair a tool and never ask for a raw credential in chat. When a task needs one you do not have, say so plainly, name the tool that would solve it, and tell the person exactly where to go: Settings \u2192 Workbench \u2192 Tools.
|
|
18903
|
+
|
|
18904
|
+
Keys are sovereign: they belong to the human who provisioned them. You cannot use another member's key and must not ask a member to share one.
|
|
18905
|
+
|
|
18906
|
+
## Showing a mock
|
|
18907
|
+
|
|
18908
|
+
When a design decision needs eyes, show it instead of describing it. Build ONE self-contained HTML page and post it with beeline-agent post_artifact (mime "text/html", pass the document as html). Everything is inline: a single <style> element for all CSS and data: URLs for any image - no script, no external dependencies, no network references. The validator refuses every <script>, <link>, <iframe>, <object>, <embed>, <form>, inline event handler, and http(s) URL, so a page that reaches for the network never posts. Use the Obsidian Refined tokens: grayscale surfaces, one brass accent #d7af5f, 3px radii, IBM Plex Sans/Mono type. Lay the user stories out as frames - one bordered, labelled block per story, so each story can be judged on its own. After posting, ask for feedback here in the corner: the artifact is the thing people react to, not your prose.
|
|
18315
18909
|
`;
|
|
18316
18910
|
}
|
|
18317
18911
|
function beelineReviewSkillMarkdown(releaseId) {
|
|
@@ -18389,7 +18983,7 @@ Then take exactly one action:
|
|
|
18389
18983
|
|
|
18390
18984
|
- FAIL: reply \`@author\` with the confirmed findings to fix.
|
|
18391
18985
|
- PASS: call \`approve_merge\` with the reviewed head SHA, then reply \`@author approved <reviewed sha>, merge\`.
|
|
18392
|
-
-
|
|
18986
|
+
- Approving is your last step as reviewer. The author merges it; you never do, and nothing merges it automatically.
|
|
18393
18987
|
`;
|
|
18394
18988
|
}
|
|
18395
18989
|
|
|
@@ -18437,8 +19031,8 @@ function openRouterModelId(model, env = {}) {
|
|
|
18437
19031
|
}
|
|
18438
19032
|
function parseOpenRouterEndpoints(payload) {
|
|
18439
19033
|
const data = payload?.data;
|
|
18440
|
-
const
|
|
18441
|
-
const raw = Array.isArray(
|
|
19034
|
+
const record3 = data && typeof data === "object" ? data : {};
|
|
19035
|
+
const raw = Array.isArray(record3.endpoints) ? record3.endpoints : [];
|
|
18442
19036
|
const endpoints = [];
|
|
18443
19037
|
for (const entry of raw) {
|
|
18444
19038
|
if (!entry || typeof entry !== "object")
|
|
@@ -18453,11 +19047,11 @@ function parseOpenRouterEndpoints(payload) {
|
|
|
18453
19047
|
const supported = Array.isArray(endpoint2.supported_parameters) ? endpoint2.supported_parameters : [];
|
|
18454
19048
|
endpoints.push({ provider, uptime, contextLength, tools: supported.includes("tools") });
|
|
18455
19049
|
}
|
|
18456
|
-
const architecture =
|
|
19050
|
+
const architecture = record3.architecture && typeof record3.architecture === "object" ? record3.architecture : {};
|
|
18457
19051
|
const modalities = Array.isArray(architecture.input_modalities) ? architecture.input_modalities.filter((value) => typeof value === "string" && value.length > 0) : void 0;
|
|
18458
19052
|
return {
|
|
18459
19053
|
endpoints,
|
|
18460
|
-
contextLength: typeof
|
|
19054
|
+
contextLength: typeof record3.context_length === "number" ? record3.context_length : advertisedContextLength(endpoints),
|
|
18461
19055
|
inputModalities: modalities?.length ? modalities : void 0
|
|
18462
19056
|
};
|
|
18463
19057
|
}
|
|
@@ -19059,7 +19653,7 @@ async function prepareRoomAgentHome(input) {
|
|
|
19059
19653
|
await symlink(source, target).catch(() => void 0);
|
|
19060
19654
|
}
|
|
19061
19655
|
const prior = agentHomeProvisionQueues.get(root) ?? Promise.resolve();
|
|
19062
|
-
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? [], agentSkillDir(input.agentKind), input.openRouterRouting));
|
|
19656
|
+
const provision = prior.catch(() => void 0).then(() => provisionAgentSkillsAndMcp(root, operatorHome, input.skillReleaseId ?? runningBeelineReleaseId(), input.failClosed ?? false, input.sharedSkills ?? [], agentSkillDir(input.agentKind), input.openRouterRouting, input.isReviewer ?? false));
|
|
19063
19657
|
agentHomeProvisionQueues.set(root, provision);
|
|
19064
19658
|
try {
|
|
19065
19659
|
await provision;
|
|
@@ -19069,10 +19663,10 @@ async function prepareRoomAgentHome(input) {
|
|
|
19069
19663
|
}
|
|
19070
19664
|
return roomAgentHomeEnv(root);
|
|
19071
19665
|
}
|
|
19072
|
-
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting) {
|
|
19666
|
+
async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting, isReviewer) {
|
|
19073
19667
|
const managedSkills = [
|
|
19074
19668
|
{ name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
|
|
19075
|
-
{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }
|
|
19669
|
+
...isReviewer ? [{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }] : []
|
|
19076
19670
|
];
|
|
19077
19671
|
const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
|
|
19078
19672
|
await provisionManagedSkillsDir(resolve13(root, skillDir, "skills"), managedSkills, shared, sharedSkills.length === 0);
|
|
@@ -19904,8 +20498,8 @@ function toolCallText(content) {
|
|
|
19904
20498
|
}
|
|
19905
20499
|
if (!value || typeof value !== "object")
|
|
19906
20500
|
return;
|
|
19907
|
-
const
|
|
19908
|
-
for (const [key, nested] of Object.entries(
|
|
20501
|
+
const record3 = value;
|
|
20502
|
+
for (const [key, nested] of Object.entries(record3)) {
|
|
19909
20503
|
if (["text", "content", "message", "error", "output"].includes(key.toLowerCase())) {
|
|
19910
20504
|
walk(nested, depth + 1);
|
|
19911
20505
|
}
|
|
@@ -20127,9 +20721,9 @@ export default async function (pi) {
|
|
|
20127
20721
|
`;
|
|
20128
20722
|
|
|
20129
20723
|
// apps/body/dist/corner-branch-sync.js
|
|
20130
|
-
import { execFile as
|
|
20724
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
20131
20725
|
import { promisify as promisify2 } from "node:util";
|
|
20132
|
-
var execFileAsync2 = promisify2(
|
|
20726
|
+
var execFileAsync2 = promisify2(execFile4);
|
|
20133
20727
|
async function syncCornerBranch(input) {
|
|
20134
20728
|
const git = input.git ?? (async (args) => (await execFileAsync2("git", ["-C", input.worktreePath, ...args], {
|
|
20135
20729
|
...input.env ? { env: input.env } : {},
|
|
@@ -20204,8 +20798,8 @@ function shellPayload(toolCall) {
|
|
|
20204
20798
|
return true;
|
|
20205
20799
|
if (!rawInput || typeof rawInput !== "object" || Array.isArray(rawInput))
|
|
20206
20800
|
return false;
|
|
20207
|
-
const
|
|
20208
|
-
return typeof
|
|
20801
|
+
const record3 = rawInput;
|
|
20802
|
+
return typeof record3.command === "string" || typeof record3.cmd === "string";
|
|
20209
20803
|
}
|
|
20210
20804
|
var ROOM_MOUNTED_MCP_SERVERS = [
|
|
20211
20805
|
READ_ONLY_MCP_SERVER_NAME,
|
|
@@ -20217,10 +20811,10 @@ var TOOL_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;
|
|
|
20217
20811
|
function permissionRecord(rawInput) {
|
|
20218
20812
|
return rawInput && typeof rawInput === "object" && !Array.isArray(rawInput) ? rawInput : void 0;
|
|
20219
20813
|
}
|
|
20220
|
-
function dispatchedPayloadIsShell(
|
|
20221
|
-
if (!
|
|
20814
|
+
function dispatchedPayloadIsShell(record3) {
|
|
20815
|
+
if (!record3)
|
|
20222
20816
|
return false;
|
|
20223
|
-
return DISPATCHER_TOOL_INPUT_KEYS.some((key) => key in
|
|
20817
|
+
return DISPATCHER_TOOL_INPUT_KEYS.some((key) => key in record3 && shellPayload({ rawInput: record3[key] }));
|
|
20224
20818
|
}
|
|
20225
20819
|
function toolIdentityCandidates(toolCall) {
|
|
20226
20820
|
const candidates = [];
|
|
@@ -20229,9 +20823,9 @@ function toolIdentityCandidates(toolCall) {
|
|
|
20229
20823
|
candidates.push(value.trim());
|
|
20230
20824
|
};
|
|
20231
20825
|
add(toolCall?.title);
|
|
20232
|
-
const
|
|
20826
|
+
const record3 = permissionRecord(toolCall?.rawInput);
|
|
20233
20827
|
for (const key of DISPATCHER_TOOL_NAME_KEYS)
|
|
20234
|
-
add(
|
|
20828
|
+
add(record3?.[key]);
|
|
20235
20829
|
return candidates;
|
|
20236
20830
|
}
|
|
20237
20831
|
function normalizedServerName(name) {
|
|
@@ -20241,8 +20835,8 @@ function resolveMountedMcpToolCall(request, mountedServers) {
|
|
|
20241
20835
|
const toolCall = request.toolCall;
|
|
20242
20836
|
if (shellPayload(toolCall))
|
|
20243
20837
|
return void 0;
|
|
20244
|
-
const
|
|
20245
|
-
if (dispatchedPayloadIsShell(
|
|
20838
|
+
const record3 = permissionRecord(toolCall?.rawInput);
|
|
20839
|
+
if (dispatchedPayloadIsShell(record3))
|
|
20246
20840
|
return void 0;
|
|
20247
20841
|
const candidates = toolIdentityCandidates(toolCall);
|
|
20248
20842
|
for (const server of mountedServers) {
|
|
@@ -20308,7 +20902,7 @@ function isMountedMcpToolPermissionRequest(request, mountedServers = ROOM_MOUNTE
|
|
|
20308
20902
|
var AGENT_SURFACE_TOOL_NAMES = [
|
|
20309
20903
|
"open_corner",
|
|
20310
20904
|
"pr_checks_status",
|
|
20311
|
-
"
|
|
20905
|
+
"post_artifact",
|
|
20312
20906
|
"write_scratch_file"
|
|
20313
20907
|
];
|
|
20314
20908
|
var SQUIRE_TITLE_PREFIXES = [
|
|
@@ -20519,25 +21113,25 @@ async function explainEmptyAgentTurn(input) {
|
|
|
20519
21113
|
const streamFact = describeEmptyTurn(input.result, input.agentLabel);
|
|
20520
21114
|
if (!isPiAcpHarness(input.agentLabel))
|
|
20521
21115
|
return { reason: streamFact };
|
|
20522
|
-
const
|
|
20523
|
-
if (!
|
|
21116
|
+
const record3 = await readPiTurnRecord({ agentEnv: input.agentEnv, sessionId: input.sessionId });
|
|
21117
|
+
if (!record3)
|
|
20524
21118
|
return { reason: `pi left no readable turn record; ${streamFact}` };
|
|
20525
|
-
switch (
|
|
21119
|
+
switch (record3.kind) {
|
|
20526
21120
|
case "error":
|
|
20527
|
-
return { reason:
|
|
21121
|
+
return { reason: record3.reason, record: record3 };
|
|
20528
21122
|
case "empty":
|
|
20529
21123
|
return {
|
|
20530
|
-
reason: `the model ended its turn with no text (stop reason ${
|
|
20531
|
-
record:
|
|
21124
|
+
reason: `the model ended its turn with no text (stop reason ${record3.stopReason})`,
|
|
21125
|
+
record: record3
|
|
20532
21126
|
};
|
|
20533
21127
|
case "answer":
|
|
20534
21128
|
return {
|
|
20535
|
-
recoveredText:
|
|
21129
|
+
recoveredText: record3.text,
|
|
20536
21130
|
reason: "pi recorded the answer but the ACP stream delivered no text",
|
|
20537
|
-
record:
|
|
21131
|
+
record: record3
|
|
20538
21132
|
};
|
|
20539
21133
|
case "missing":
|
|
20540
|
-
return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record:
|
|
21134
|
+
return { reason: `pi recorded no assistant message for this turn; ${streamFact}`, record: record3 };
|
|
20541
21135
|
}
|
|
20542
21136
|
}
|
|
20543
21137
|
function shouldRetryEmptyTurn(explanation) {
|
|
@@ -20564,10 +21158,10 @@ function turnFailureReasonWithProvider(reason, providers) {
|
|
|
20564
21158
|
const served = describeTurnProviders(providers);
|
|
20565
21159
|
return served ? `${reason} \xB7 ${served}` : reason;
|
|
20566
21160
|
}
|
|
20567
|
-
function isAccountOrProviderRefusal(
|
|
20568
|
-
if (!
|
|
21161
|
+
function isAccountOrProviderRefusal(record3) {
|
|
21162
|
+
if (!record3 || record3.kind !== "error" || record3.status === void 0)
|
|
20569
21163
|
return false;
|
|
20570
|
-
return [401, 402, 403, 407, 408, 429].includes(
|
|
21164
|
+
return [401, 402, 403, 407, 408, 429].includes(record3.status) || record3.status >= 500;
|
|
20571
21165
|
}
|
|
20572
21166
|
|
|
20573
21167
|
// apps/body/dist/response-directives.js
|
|
@@ -20824,11 +21418,11 @@ var TurnTrace = class {
|
|
|
20824
21418
|
this.end(phase);
|
|
20825
21419
|
this.promptSettled();
|
|
20826
21420
|
}
|
|
20827
|
-
const
|
|
20828
|
-
await this.options.sink?.write(
|
|
21421
|
+
const record3 = this.snapshot(outcome, reason);
|
|
21422
|
+
await this.options.sink?.write(record3).catch((error) => {
|
|
20829
21423
|
console.error("[thin-core] turn trace write failed:", error);
|
|
20830
21424
|
});
|
|
20831
|
-
return
|
|
21425
|
+
return record3;
|
|
20832
21426
|
}
|
|
20833
21427
|
};
|
|
20834
21428
|
function round(ms) {
|
|
@@ -20853,9 +21447,9 @@ function formatTurnAttempt(attempt) {
|
|
|
20853
21447
|
parts.push(`${attempt.toolCalls} tool call${attempt.toolCalls === 1 ? "" : "s"}`);
|
|
20854
21448
|
return parts.join(" \xB7 ");
|
|
20855
21449
|
}
|
|
20856
|
-
function formatTurnTraceLine(
|
|
20857
|
-
const head = `${
|
|
20858
|
-
return [head, ...
|
|
21450
|
+
function formatTurnTraceLine(record3) {
|
|
21451
|
+
const head = `${record3.surface} ${record3.roomId} turn ${record3.requestId} ${record3.outcome} ${formatDuration(record3.totalMs)}`;
|
|
21452
|
+
return [head, ...record3.attempts.map((attempt) => formatTurnAttempt(attempt))].join("\n ");
|
|
20859
21453
|
}
|
|
20860
21454
|
function turnTraceDirectory(runtimeDir) {
|
|
20861
21455
|
return resolve17(runtimeDir, "turn-traces");
|
|
@@ -20877,16 +21471,16 @@ var TurnTraceFile = class {
|
|
|
20877
21471
|
path(now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))()) {
|
|
20878
21472
|
return resolve17(this.directory, traceFileName(now2));
|
|
20879
21473
|
}
|
|
20880
|
-
write(
|
|
21474
|
+
write(record3) {
|
|
20881
21475
|
this.tail = this.tail.catch(() => void 0).then(async () => {
|
|
20882
21476
|
const now2 = (this.options.clock ?? (() => /* @__PURE__ */ new Date()))();
|
|
20883
21477
|
const path = this.path(now2);
|
|
20884
21478
|
await mkdir8(this.directory, { recursive: true, mode: 448 });
|
|
20885
|
-
await appendFile(path, `${JSON.stringify(
|
|
21479
|
+
await appendFile(path, `${JSON.stringify(record3)}
|
|
20886
21480
|
`, { mode: 384 });
|
|
20887
21481
|
await this.prune(now2);
|
|
20888
21482
|
const log2 = this.options.log ?? ((line) => console.log(line));
|
|
20889
|
-
log2(`[thin-core] turn trace ${formatTurnTraceLine(
|
|
21483
|
+
log2(`[thin-core] turn trace ${formatTurnTraceLine(record3)}${this.announced ? "" : `
|
|
20890
21484
|
recorded in ${path}`}`);
|
|
20891
21485
|
this.announced = true;
|
|
20892
21486
|
});
|
|
@@ -21149,7 +21743,7 @@ async function seedWarmNodeModules(input) {
|
|
|
21149
21743
|
for (const target of placed) {
|
|
21150
21744
|
await rm4(target, { recursive: true, force: true }).catch(() => void 0);
|
|
21151
21745
|
}
|
|
21152
|
-
return { reason: "failed", key: plan.key, detail:
|
|
21746
|
+
return { reason: "failed", key: plan.key, detail: describe2(error) };
|
|
21153
21747
|
} finally {
|
|
21154
21748
|
await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
21155
21749
|
}
|
|
@@ -21192,7 +21786,7 @@ async function harvestWarmNodeModules(input) {
|
|
|
21192
21786
|
} catch (error) {
|
|
21193
21787
|
if (await pathExists(entry))
|
|
21194
21788
|
return { reason: "already-warm", key: plan.key };
|
|
21195
|
-
return { reason: "failed", key: plan.key, detail:
|
|
21789
|
+
return { reason: "failed", key: plan.key, detail: describe2(error) };
|
|
21196
21790
|
} finally {
|
|
21197
21791
|
await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
|
|
21198
21792
|
}
|
|
@@ -21335,7 +21929,7 @@ async function deviceOf(path) {
|
|
|
21335
21929
|
async function isDirectory(path) {
|
|
21336
21930
|
return stat2(path).then((info) => info.isDirectory(), () => false);
|
|
21337
21931
|
}
|
|
21338
|
-
function
|
|
21932
|
+
function describe2(error) {
|
|
21339
21933
|
return error instanceof Error ? error.message : String(error);
|
|
21340
21934
|
}
|
|
21341
21935
|
|
|
@@ -21613,10 +22207,13 @@ var MonolithRoomTurnLoop = class {
|
|
|
21613
22207
|
});
|
|
21614
22208
|
const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
|
|
21615
22209
|
await mkdir11(this.options.cwd, { recursive: true });
|
|
21616
|
-
const
|
|
22210
|
+
const selectionModel = configuration.model ?? this.options.config.modelSelection?.model;
|
|
22211
|
+
const selectionEffort = configuration.effort ?? this.options.config.modelSelection?.effort;
|
|
22212
|
+
const selection = selectionModel || selectionEffort ? { model: selectionModel, effort: selectionEffort } : void 0;
|
|
21617
22213
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
21618
22214
|
root: this.options.config.agentHomeRoot,
|
|
21619
22215
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
22216
|
+
isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
|
|
21620
22217
|
...this.options.config.agentKind ? { agentKind: this.options.config.agentKind } : {},
|
|
21621
22218
|
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
|
|
21622
22219
|
...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl, {
|
|
@@ -21839,6 +22436,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
21839
22436
|
this.busy = true;
|
|
21840
22437
|
const trace = this.beginTurnTrace(item.id);
|
|
21841
22438
|
let liveStream;
|
|
22439
|
+
let liveCornerOpened = false;
|
|
21842
22440
|
try {
|
|
21843
22441
|
if (!this.memberNames.has(item.authorId))
|
|
21844
22442
|
await this.roster().catch(() => void 0);
|
|
@@ -21920,7 +22518,11 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
21920
22518
|
result2 = await this.client.sessionPrompt(this.sessionId, nextPrompt, ROOM_PROMPT_INACTIVITY_TIMEOUT_MS, (delta, full) => {
|
|
21921
22519
|
trace.firstModelOutput();
|
|
21922
22520
|
stream.onChunk(delta, full);
|
|
21923
|
-
}, void 0, (calls) =>
|
|
22521
|
+
}, void 0, (calls) => {
|
|
22522
|
+
trace.toolCalls(calls);
|
|
22523
|
+
if (openedACorner(openCornerToolCall(calls)))
|
|
22524
|
+
liveCornerOpened = true;
|
|
22525
|
+
});
|
|
21924
22526
|
} catch (error) {
|
|
21925
22527
|
promptError = error;
|
|
21926
22528
|
}
|
|
@@ -22014,6 +22616,22 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
|
|
|
22014
22616
|
await trace.finish("cancelled");
|
|
22015
22617
|
return;
|
|
22016
22618
|
}
|
|
22619
|
+
if (liveCornerOpened && error instanceof AcpRequestTimeoutError && error.inactivity && error.method === "session/prompt") {
|
|
22620
|
+
console.log(`[thin-core] monolith Room ${this.options.roomId} turn ${item.id}: inactivity timeout after opening a corner; the work continues in the corner`);
|
|
22621
|
+
this.options.onCornerOpened?.();
|
|
22622
|
+
await liveStream?.retract().catch((retractError) => {
|
|
22623
|
+
console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
|
|
22624
|
+
});
|
|
22625
|
+
await api.execute("postAgentTurnReceipt", {
|
|
22626
|
+
agentId: this.agent.publicKey,
|
|
22627
|
+
roomId: this.options.roomId,
|
|
22628
|
+
requestId: item.id,
|
|
22629
|
+
status: "complete",
|
|
22630
|
+
generationId: this.commandContext.generationId
|
|
22631
|
+
});
|
|
22632
|
+
await trace.finish("complete");
|
|
22633
|
+
return;
|
|
22634
|
+
}
|
|
22017
22635
|
await liveStream?.retract().catch((retractError) => {
|
|
22018
22636
|
console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
|
|
22019
22637
|
});
|
|
@@ -22083,7 +22701,7 @@ function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsI
|
|
|
22083
22701
|
}
|
|
22084
22702
|
|
|
22085
22703
|
// apps/body/dist/monolith-corner-turn.js
|
|
22086
|
-
var execFileAsync3 = promisify3(
|
|
22704
|
+
var execFileAsync3 = promisify3(execFile5);
|
|
22087
22705
|
var TOOL_ARGUMENT_MAX_BYTES = 1200;
|
|
22088
22706
|
var TOOL_OUTPUT_MAX_BYTES = 3200;
|
|
22089
22707
|
var TOOL_PATH_LIMIT = 12;
|
|
@@ -22100,6 +22718,11 @@ function cornerReviewerInstruction(input) {
|
|
|
22100
22718
|
const headSha = input.headSha ?? "<head sha>";
|
|
22101
22719
|
return `Checks are green on PR #${number} at ${headSha}. Review it now with the beeline-review skill against that exact head. FAIL: reply \`@${author}\` with the confirmed findings to fix. PASS: call the approve_merge tool for ${headSha}, then reply \`@${author} approved ${headSha}, merge\`. Never merge yourself. Never say you are holding or waiting for checks.`;
|
|
22102
22720
|
}
|
|
22721
|
+
function cornerSelfReviewerInstruction(input) {
|
|
22722
|
+
if (!input.reviewerHandle || !input.agentHandle || !input.openedByAgent || input.agentHandle.replace(/^@/, "") !== input.reviewerHandle.replace(/^@/, ""))
|
|
22723
|
+
return void 0;
|
|
22724
|
+
return "You are this Room's reviewer, so your own pull request needs no review: do not request one, do not tag any agent for review, and merge yourself with gh once checks pass and no hold exists.";
|
|
22725
|
+
}
|
|
22103
22726
|
var CORNER_AUTHOR_CONTRACT = `The objective text is the user's ask. Keep it verbatim in your head and do not reinterpret it.
|
|
22104
22727
|
Before any code, write its end-user story in one sentence: "a person who does X sees Y".
|
|
22105
22728
|
If the objective reports a defect, reproduce it first at the layer where it lives: the command, request, or tap sequence, and what was observed.
|
|
@@ -22159,7 +22782,7 @@ function serialized(value) {
|
|
|
22159
22782
|
return "";
|
|
22160
22783
|
}
|
|
22161
22784
|
}
|
|
22162
|
-
function
|
|
22785
|
+
function record2(value) {
|
|
22163
22786
|
return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
22164
22787
|
}
|
|
22165
22788
|
function clampBytes(value, maxBytes) {
|
|
@@ -22199,7 +22822,7 @@ function filePaths(value, worktreePath) {
|
|
|
22199
22822
|
candidate.forEach((entry) => visit(entry));
|
|
22200
22823
|
return;
|
|
22201
22824
|
}
|
|
22202
|
-
const object =
|
|
22825
|
+
const object = record2(candidate);
|
|
22203
22826
|
if (object)
|
|
22204
22827
|
Object.entries(object).forEach(([entryKey, entry]) => visit(entry, entryKey));
|
|
22205
22828
|
};
|
|
@@ -22207,9 +22830,9 @@ function filePaths(value, worktreePath) {
|
|
|
22207
22830
|
return [...paths];
|
|
22208
22831
|
}
|
|
22209
22832
|
function resultStatus(call) {
|
|
22210
|
-
const content =
|
|
22833
|
+
const content = record2(call.content) ?? (typeof call.content === "string" ? (() => {
|
|
22211
22834
|
try {
|
|
22212
|
-
return
|
|
22835
|
+
return record2(JSON.parse(call.content));
|
|
22213
22836
|
} catch {
|
|
22214
22837
|
return void 0;
|
|
22215
22838
|
}
|
|
@@ -22224,7 +22847,7 @@ function resultStatus(call) {
|
|
|
22224
22847
|
return /(?:failed|error|denied)/i.test(call.status ?? "") ? "error" : "ok";
|
|
22225
22848
|
}
|
|
22226
22849
|
function toolArguments(call) {
|
|
22227
|
-
const raw =
|
|
22850
|
+
const raw = record2(call.rawInput);
|
|
22228
22851
|
const command = typeof call.rawInput === "string" ? call.rawInput : typeof raw?.command === "string" ? raw.command : typeof raw?.cmd === "string" ? raw.cmd : void 0;
|
|
22229
22852
|
if (command)
|
|
22230
22853
|
return { command: clampBytes(redactToolDetail(command), TOOL_ARGUMENT_MAX_BYTES) };
|
|
@@ -22462,12 +23085,14 @@ var MonolithCornerTurnLoop = class {
|
|
|
22462
23085
|
headSha: restore.lifecycle?.pr?.headSha
|
|
22463
23086
|
});
|
|
22464
23087
|
}
|
|
22465
|
-
|
|
23088
|
+
const selfReviewerInstruction = cornerSelfReviewerInstruction(reviewerInput);
|
|
23089
|
+
this.cornerTurnEndNudge = reviewerInstruction ?? selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle);
|
|
22466
23090
|
await mkdir12(this.options.worktreePath, { recursive: true });
|
|
22467
23091
|
const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
|
|
22468
23092
|
const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
|
|
22469
23093
|
root: this.options.config.agentHomeRoot,
|
|
22470
23094
|
sharedSkills: this.options.config.sharedSkills ?? [],
|
|
23095
|
+
isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
|
|
22471
23096
|
...this.options.config.agentKind ? { agentKind: this.options.config.agentKind } : {},
|
|
22472
23097
|
...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
|
|
22473
23098
|
...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl, {
|
|
@@ -22612,13 +23237,13 @@ var MonolithCornerTurnLoop = class {
|
|
|
22612
23237
|
...reviewerInstruction ? [reviewerInstruction] : [
|
|
22613
23238
|
configuration.reviewerHandle ? `Once the pull request exists, reply with its full URL and end the turn; do not tag the reviewer, check, or wait for CI.` : 'Once the pull request exists, reply only with its full URL and end the turn; do not check or wait for CI. On a later checks turn, call pr_checks_status. Merge only when checks="passed", held=false, and approvalPending=false; if checks="unknown", reply in this corner with the tool reason and stop instead of retrying. Only a later explicit human resume clears a hold.',
|
|
22614
23239
|
CORNER_AUTHOR_CONTRACT,
|
|
22615
|
-
cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
|
|
23240
|
+
selfReviewerInstruction ?? cornerMergeInstruction(configuration.yoloMode, configuration.reviewerHandle)
|
|
22616
23241
|
],
|
|
22617
23242
|
"Do not tag the user when a corner turn finishes: the server posts the merge summary card and its push already cover completion. Tag a human only mid-turn, and only when you need a decision or input.",
|
|
22618
|
-
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request."
|
|
23243
|
+
"Never restate server check or merge notes. On a checks turn, say nothing unless you merge or push a fix, then use one short line. Never merge while approvalPending is true. When approval is pending, wait for the reviewer to tag you. Never merge another pull request. Never create a schedule to poll pr_checks_status or the merge gate: the green transition wakes the reviewer and the reviewer's approval tag wakes you, and tagging any agent other than the configured reviewer cannot clear the gate. If a schedule wakes you in this corner anyway, follow the same rule as a checks turn: say nothing unless you merge, push a fix, or report a genuinely new blocker."
|
|
22619
23244
|
] : [
|
|
22620
23245
|
"This is a chat-only corner with no repository or GitHub workflow.",
|
|
22621
|
-
"Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then
|
|
23246
|
+
"Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then post_artifact with the path to send them back to the corner.",
|
|
22622
23247
|
"Do not initialize a repository, create a branch, push, open a pull request, or wait for GitHub checks."
|
|
22623
23248
|
]
|
|
22624
23249
|
].filter(Boolean).join("\n\n")
|
|
@@ -22781,7 +23406,7 @@ ${trigger}`,
|
|
|
22781
23406
|
const pendingToolActivities = /* @__PURE__ */ new Map();
|
|
22782
23407
|
let lastNarratedToolCall;
|
|
22783
23408
|
let activityAttempt = 0;
|
|
22784
|
-
const publishToolCalls = (calls, settledOnly) => {
|
|
23409
|
+
const publishToolCalls = (calls, settledOnly, exceptKey) => {
|
|
22785
23410
|
calls.forEach((call, index) => {
|
|
22786
23411
|
const key = `${activityAttempt}:${toolCallKey(call, index)}`;
|
|
22787
23412
|
if (settledOnly && !observedToolCalls.has(key)) {
|
|
@@ -22791,7 +23416,7 @@ ${trigger}`,
|
|
|
22791
23416
|
if (narration2)
|
|
22792
23417
|
lastNarratedToolCall = key;
|
|
22793
23418
|
}
|
|
22794
|
-
if (settledOnly || publishedToolCalls.has(key) || !toolCallSettled(call))
|
|
23419
|
+
if (settledOnly || publishedToolCalls.has(key) || !toolCallSettled(call) || key === exceptKey)
|
|
22795
23420
|
return;
|
|
22796
23421
|
publishedToolCalls.add(key);
|
|
22797
23422
|
const narration = pendingToolNarrations.get(key) ?? "";
|
|
@@ -22827,12 +23452,18 @@ ${trigger}`,
|
|
|
22827
23452
|
});
|
|
22828
23453
|
});
|
|
22829
23454
|
};
|
|
22830
|
-
const flushToolCalls = async (calls, finalReply) => {
|
|
23455
|
+
const flushToolCalls = async (calls, finalReply, final = false) => {
|
|
22831
23456
|
await this.activityTail;
|
|
22832
23457
|
if (lastNarratedToolCall && pendingToolNarrations.get(lastNarratedToolCall) === finalReply)
|
|
22833
23458
|
pendingToolNarrations.delete(lastNarratedToolCall);
|
|
22834
23459
|
publishToolCalls(calls, false);
|
|
22835
23460
|
await this.activityTail;
|
|
23461
|
+
if (this.options.connectorUsage) {
|
|
23462
|
+
captureConnectionUsage(this.options.connectorUsage, { requestId, agentId: this.agent.publicKey, cornerId }, calls);
|
|
23463
|
+
if (final)
|
|
23464
|
+
await this.options.connectorUsage.flush(api, requestId);
|
|
23465
|
+
}
|
|
23466
|
+
await this.activityTail;
|
|
22836
23467
|
publishToolCalls(calls, false);
|
|
22837
23468
|
await this.activityTail;
|
|
22838
23469
|
};
|
|
@@ -22864,6 +23495,7 @@ ${trigger}`,
|
|
|
22864
23495
|
}, void 0, (calls) => {
|
|
22865
23496
|
trace.toolCalls(calls);
|
|
22866
23497
|
publishToolCalls(calls, true);
|
|
23498
|
+
publishToolCalls(calls, false, lastNarratedToolCall);
|
|
22867
23499
|
});
|
|
22868
23500
|
};
|
|
22869
23501
|
let result = await runPrompt();
|
|
@@ -22901,7 +23533,7 @@ ${trigger}`,
|
|
|
22901
23533
|
if (!reply && explained?.recoveredText)
|
|
22902
23534
|
reply = durableReplyText(explained.recoveredText);
|
|
22903
23535
|
reply = [replyBeforeNudge, reply].filter(Boolean).join("\n\n");
|
|
22904
|
-
await flushToolCalls(result.toolCalls, reply);
|
|
23536
|
+
await flushToolCalls(result.toolCalls, reply, true);
|
|
22905
23537
|
for (const call of result.toolCalls) {
|
|
22906
23538
|
const failure = toolCallFailureLine(call);
|
|
22907
23539
|
if (failure)
|
|
@@ -23598,7 +24230,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
|
|
|
23598
24230
|
]);
|
|
23599
24231
|
}
|
|
23600
24232
|
}
|
|
23601
|
-
var execFileAsync4 = promisify4(
|
|
24233
|
+
var execFileAsync4 = promisify4(execFile6);
|
|
23602
24234
|
async function removeCornerScratchWorkspace(input) {
|
|
23603
24235
|
const expected = resolve20(input.roomRoot, "scratch");
|
|
23604
24236
|
if (resolve20(input.scratchPath) !== expected) {
|
|
@@ -23632,6 +24264,8 @@ var RoomRuntimeCoordinator = class {
|
|
|
23632
24264
|
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
23633
24265
|
grantRunner;
|
|
23634
24266
|
grantRunnerServer;
|
|
24267
|
+
/** Connection usage capture, batched per agent turn. */
|
|
24268
|
+
connectorUsage;
|
|
23635
24269
|
constructor(runtime, configPath, baseConfig, options) {
|
|
23636
24270
|
this.configPath = configPath;
|
|
23637
24271
|
this.baseConfig = baseConfig;
|
|
@@ -23646,6 +24280,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
23646
24280
|
agentId: this.agent.publicKey
|
|
23647
24281
|
});
|
|
23648
24282
|
this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
|
|
24283
|
+
this.connectorUsage = new ConnectorUsageRecorder();
|
|
23649
24284
|
this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
|
|
23650
24285
|
this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
|
|
23651
24286
|
this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
|
|
@@ -23962,6 +24597,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
23962
24597
|
cornerId: corner.cornerId,
|
|
23963
24598
|
grantRunner: this.grantRunner,
|
|
23964
24599
|
...grantRunnerEndpoint ? { grantRunnerEndpoint } : {},
|
|
24600
|
+
connectorUsage: this.connectorUsage,
|
|
23965
24601
|
parentRoomId: corner.parentRoomId,
|
|
23966
24602
|
workspaceId: this.runtime.communityId,
|
|
23967
24603
|
...corner.openedBy ? { openedBy: corner.openedBy } : {},
|
|
@@ -24329,13 +24965,13 @@ var ThinDaemonCore = class {
|
|
|
24329
24965
|
};
|
|
24330
24966
|
|
|
24331
24967
|
// apps/body/dist/systemd.js
|
|
24332
|
-
import { execFile as
|
|
24968
|
+
import { execFile as execFile7 } from "node:child_process";
|
|
24333
24969
|
import { mkdir as mkdir14, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
|
|
24334
24970
|
import { homedir as homedir8 } from "node:os";
|
|
24335
24971
|
import { dirname as dirname9, resolve as resolve21 } from "node:path";
|
|
24336
24972
|
import { setTimeout as sleep } from "node:timers/promises";
|
|
24337
24973
|
import { promisify as promisify5 } from "node:util";
|
|
24338
|
-
var execFileAsync5 = promisify5(
|
|
24974
|
+
var execFileAsync5 = promisify5(execFile7);
|
|
24339
24975
|
var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
|
|
24340
24976
|
var DAEMON_DISTRESS_EXIT_STATUS = 77;
|
|
24341
24977
|
var UNKNOWN_AGENT_EXIT_STATUS = 79;
|
|
@@ -24574,7 +25210,7 @@ async function runStartCommand(args, interactiveUi) {
|
|
|
24574
25210
|
}
|
|
24575
25211
|
|
|
24576
25212
|
// apps/body/dist/connect-command.js
|
|
24577
|
-
import { spawn as
|
|
25213
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
24578
25214
|
import { createHash as createHash8 } from "node:crypto";
|
|
24579
25215
|
import { chmod as chmod6, mkdir as mkdir16, readFile as readFile11, unlink as unlink2, writeFile as writeFile11 } from "node:fs/promises";
|
|
24580
25216
|
import { dirname as dirname12, resolve as resolve23 } from "node:path";
|
|
@@ -24661,7 +25297,7 @@ async function verifyProviderKey(input) {
|
|
|
24661
25297
|
}
|
|
24662
25298
|
|
|
24663
25299
|
// apps/body/dist/pair-agent-selection.js
|
|
24664
|
-
import { spawn as
|
|
25300
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
24665
25301
|
import { stdin as stdin2, stdout as stdout3 } from "node:process";
|
|
24666
25302
|
var NO_AGENT_MESSAGE = `No supported ACP-capable coding agent was detected.
|
|
24667
25303
|
Install one of these supported agents:
|
|
@@ -24689,7 +25325,7 @@ async function clackSelectAgent(candidates) {
|
|
|
24689
25325
|
}
|
|
24690
25326
|
async function installAdapter(install, opts) {
|
|
24691
25327
|
await new Promise((resolveInstall, rejectInstall) => {
|
|
24692
|
-
const child =
|
|
25328
|
+
const child = spawn4(install.command, install.args, {
|
|
24693
25329
|
cwd: opts.cwd,
|
|
24694
25330
|
env: opts.env ?? process.env,
|
|
24695
25331
|
stdio: "inherit"
|
|
@@ -25284,7 +25920,7 @@ async function writeProviderEnv(selection, agentPubkey) {
|
|
|
25284
25920
|
}
|
|
25285
25921
|
async function runInstalledFinish(binary, grantPath) {
|
|
25286
25922
|
await new Promise((resolveRun, rejectRun) => {
|
|
25287
|
-
const child =
|
|
25923
|
+
const child = spawn6(binary, ["connect-finish", grantPath], {
|
|
25288
25924
|
stdio: ["ignore", "pipe", "pipe"]
|
|
25289
25925
|
});
|
|
25290
25926
|
let diagnostic = "";
|
|
@@ -25457,7 +26093,7 @@ init_self_update_manifest();
|
|
|
25457
26093
|
|
|
25458
26094
|
// apps/body/dist/managed-update.js
|
|
25459
26095
|
init_self_update();
|
|
25460
|
-
import { spawn as
|
|
26096
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
25461
26097
|
import { mkdir as mkdir18, rm as rm7, stat as stat3, writeFile as writeFile13 } from "node:fs/promises";
|
|
25462
26098
|
import { dirname as dirname14, resolve as resolve25 } from "node:path";
|
|
25463
26099
|
|
|
@@ -25668,7 +26304,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
25668
26304
|
libDir: resolve25(this.#layout.releasesRoot, this.#loadedRelease)
|
|
25669
26305
|
}).catch(() => void 0) ?? {};
|
|
25670
26306
|
const to = await readInstalledBundleIdentity(this.#layout).catch(() => void 0) ?? {};
|
|
25671
|
-
const
|
|
26307
|
+
const record3 = {
|
|
25672
26308
|
version: 1,
|
|
25673
26309
|
from,
|
|
25674
26310
|
to,
|
|
@@ -25679,7 +26315,7 @@ var ManagedUpdateHandoff = class _ManagedUpdateHandoff {
|
|
|
25679
26315
|
status: "pending",
|
|
25680
26316
|
...this.#requiredProbeIds.length > 0 ? { requiredProbeIds: this.#requiredProbeIds, confirmedProbeIds: [] } : {}
|
|
25681
26317
|
};
|
|
25682
|
-
await writeUpdateAttempt(this.#layout,
|
|
26318
|
+
await writeUpdateAttempt(this.#layout, record3);
|
|
25683
26319
|
} else if (this.#requiredProbeIds.length > 0) {
|
|
25684
26320
|
const requiredProbeIds = [
|
|
25685
26321
|
.../* @__PURE__ */ new Set([...attempt.requiredProbeIds ?? [], ...this.#requiredProbeIds])
|
|
@@ -25871,7 +26507,7 @@ async function runManagedUpdateWorkerProcess() {
|
|
|
25871
26507
|
if (!entrypoint)
|
|
25872
26508
|
throw new Error("cannot resolve the current Beeline entrypoint");
|
|
25873
26509
|
await new Promise((resolveWorker, rejectWorker) => {
|
|
25874
|
-
const child =
|
|
26510
|
+
const child = spawn7(process.execPath, [entrypoint, "managed-update-worker"], {
|
|
25875
26511
|
detached: true,
|
|
25876
26512
|
env: { ...process.env, BEELINE_INTERNAL_UPDATE_WORKER: "1" },
|
|
25877
26513
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -26198,11 +26834,11 @@ async function readFailureRecord(runtimeDir) {
|
|
|
26198
26834
|
return void 0;
|
|
26199
26835
|
}
|
|
26200
26836
|
}
|
|
26201
|
-
async function writeFailureRecord(runtimeDir,
|
|
26837
|
+
async function writeFailureRecord(runtimeDir, record3) {
|
|
26202
26838
|
const path = daemonFailurePath(runtimeDir);
|
|
26203
26839
|
const staged = `${path}.${process.pid}.tmp`;
|
|
26204
26840
|
await mkdir19(dirname15(path), { recursive: true, mode: 448 });
|
|
26205
|
-
await writeFile14(staged, `${JSON.stringify(
|
|
26841
|
+
await writeFile14(staged, `${JSON.stringify(record3, null, 2)}
|
|
26206
26842
|
`, { mode: 384 });
|
|
26207
26843
|
await rename6(staged, path);
|
|
26208
26844
|
}
|
|
@@ -26210,13 +26846,13 @@ async function recordDaemonStartFailure(runtimeDir, error, now2 = Date.now()) {
|
|
|
26210
26846
|
const prior = await readFailureRecord(runtimeDir);
|
|
26211
26847
|
const failures = [...prior?.failures ?? [], now2].filter((failure) => now2 - failure <= DAEMON_FAILURE_WINDOW_MS);
|
|
26212
26848
|
const distressed = failures.length >= DAEMON_FAILURE_LIMIT;
|
|
26213
|
-
const
|
|
26849
|
+
const record3 = {
|
|
26214
26850
|
version: 1,
|
|
26215
26851
|
failures,
|
|
26216
26852
|
lastError: (error instanceof Error ? error.message : String(error)).slice(0, 1e3),
|
|
26217
26853
|
...distressed ? { distressedAt: now2 } : {}
|
|
26218
26854
|
};
|
|
26219
|
-
await writeFailureRecord(runtimeDir,
|
|
26855
|
+
await writeFailureRecord(runtimeDir, record3);
|
|
26220
26856
|
return { distressed, count: failures.length, path: daemonFailurePath(runtimeDir) };
|
|
26221
26857
|
}
|
|
26222
26858
|
async function clearDaemonStartFailures(runtimeDir) {
|
|
@@ -26503,7 +27139,7 @@ async function runUpdateFunctionalProbe(input) {
|
|
|
26503
27139
|
}
|
|
26504
27140
|
|
|
26505
27141
|
// apps/body/dist/current-release-probe.js
|
|
26506
|
-
import { spawn as
|
|
27142
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
26507
27143
|
import { dirname as dirname16, join as join10 } from "node:path";
|
|
26508
27144
|
init_self_update();
|
|
26509
27145
|
var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
|
|
@@ -26554,7 +27190,7 @@ async function probeReleaseInSubprocess(input) {
|
|
|
26554
27190
|
}
|
|
26555
27191
|
const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
|
|
26556
27192
|
return new Promise((resolve31) => {
|
|
26557
|
-
const child =
|
|
27193
|
+
const child = spawn8(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
|
|
26558
27194
|
let stdout6 = "";
|
|
26559
27195
|
let stderr = "";
|
|
26560
27196
|
let settled = false;
|
|
@@ -26899,6 +27535,7 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26899
27535
|
const scratchSweepTimer = setInterval(() => runScratchSweepLogged(runtimeDir), SCRATCH_SWEEP_INTERVAL_MS);
|
|
26900
27536
|
scratchSweepTimer.unref();
|
|
26901
27537
|
let ready = false;
|
|
27538
|
+
let connectorLoop;
|
|
26902
27539
|
let stoppingStatus = "daemon stopped";
|
|
26903
27540
|
try {
|
|
26904
27541
|
const core = new ThinDaemonCore(runtime, configPath, config, { daemonApi });
|
|
@@ -26971,6 +27608,12 @@ async function runStoredDaemon(pathOrPointer) {
|
|
|
26971
27608
|
...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {},
|
|
26972
27609
|
...config.modelUnavailable ? { startupUnavailable: config.modelUnavailable.unavailable.label } : {}
|
|
26973
27610
|
});
|
|
27611
|
+
connectorLoop ??= new ConnectorAssignmentLoop({
|
|
27612
|
+
api: daemonApi,
|
|
27613
|
+
agentId: runtime.agent.publicKey,
|
|
27614
|
+
log: (message) => console.log(`[body] connector: ${message}`)
|
|
27615
|
+
});
|
|
27616
|
+
connectorLoop.start();
|
|
26974
27617
|
},
|
|
26975
27618
|
onProgress: async (status) => {
|
|
26976
27619
|
void drainRollbackAlert(core.activeRoomIds()[0] ?? runtime.rooms[0]?.channelId);
|