usebeeline 0.0.104 → 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 CHANGED
@@ -141,7 +141,7 @@ Two MCP surfaces are mounted into every agent session.
141
141
  | ------------------------------------------------------ | --------------- | ------------------------------------------------------------- |
142
142
  | `open_corner` | Top-level Rooms | Open one write-enabled corner with a ≤24-word objective |
143
143
  | `pr_checks_status` | Corners | Read checks, human hold, and PR/head-bound merge approval |
144
- | `attach_file` | Everywhere | Attach one file from the checkout or scratch dir to the reply |
144
+ | `post_artifact` | Everywhere | Upload one file (path or html/bytes) as an attachment |
145
145
  | `create_schedule`, `list_schedules`, `delete_schedule` | Everywhere | Run a prompt again later — interval minutes or a 5-field cron |
146
146
  | `request_grant` | Everywhere | Ask the owner for reach outside the sandbox |
147
147
  | `run_granted_command` | Everywhere | Run a command an approved grant covers, outside the sandbox |
@@ -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 spawn4 } from "node:child_process";
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 = spawn4(command, args, { stdio: ["ignore", "ignore", "pipe"] });
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 = spawn4("tar", ["-tzf", tempArchive], { stdio: ["ignore", "pipe", "inherit"] });
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");
@@ -6898,14 +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 withoutOneTrailingLineEnding(text2) {
6902
- if (/\r?\n\r?\n$/.test(text2))
6903
- return text2;
6904
- const stripped = text2.replace(/\r?\n$/, "");
6905
- return stripped || text2;
6906
- }
6907
- function normalizeStreamDelta(text2, agentLabel) {
6908
- return agentLabel && PI_ACP_HARNESS.test(agentLabel) ? withoutOneTrailingLineEnding(text2) : text2;
6901
+ function normalizeStreamDelta(text2, _agentLabel) {
6902
+ return text2;
6909
6903
  }
6910
6904
  function agentMessageRuns(updates, agentLabel) {
6911
6905
  const runs = [];
@@ -8162,6 +8156,483 @@ async function syncAgentModelCatalog(input) {
8162
8156
  }
8163
8157
  }
8164
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
+
8165
8636
  // packages/api-contract/dist/daemon-operations.js
8166
8637
  function isAgentCommand(value) {
8167
8638
  if (!value || typeof value !== "object")
@@ -8185,7 +8656,7 @@ function isAgentCommand(value) {
8185
8656
  }
8186
8657
 
8187
8658
  // packages/api-contract/dist/artifacts.js
8188
- var ARTIFACT_MAXIMUM_BYTES = 2 * 1024 * 1024;
8659
+ var ARTIFACT_MAXIMUM_BYTES = 25 * 1024 * 1024;
8189
8660
 
8190
8661
  // packages/api-contract/dist/system-events.js
8191
8662
  var SERVER_EVENT_KINDS = [
@@ -8216,12 +8687,12 @@ var wrapper_default = import_websocket.default;
8216
8687
 
8217
8688
  // apps/body/dist/runtime.js
8218
8689
  import { randomBytes as randomBytes5 } from "node:crypto";
8219
- import { execFile } from "node:child_process";
8690
+ import { execFile as execFile2 } from "node:child_process";
8220
8691
  import { closeSync, openSync } from "node:fs";
8221
8692
  import { mkdir, readFile as readFile2, readdir, rename, stat, writeFile as writeFile2 } from "node:fs/promises";
8222
8693
  import { homedir as homedir2 } from "node:os";
8223
8694
  import { dirname as dirname2, resolve as resolve6 } from "node:path";
8224
- import { spawn as spawn2 } from "node:child_process";
8695
+ import { spawn as spawn3 } from "node:child_process";
8225
8696
  import { promisify } from "node:util";
8226
8697
 
8227
8698
  // node_modules/@noble/hashes/_u64.js
@@ -16344,13 +16815,13 @@ var NegentropyStorageVector = class {
16344
16815
  let count = last - first;
16345
16816
  while (count > 0) {
16346
16817
  let it = first;
16347
- let step = Math.floor(count / 2);
16348
- it += step;
16818
+ let step2 = Math.floor(count / 2);
16819
+ it += step2;
16349
16820
  if (cmp(arr[it])) {
16350
16821
  first = ++it;
16351
- count -= step + 1;
16822
+ count -= step2 + 1;
16352
16823
  } else {
16353
- count = step;
16824
+ count = step2;
16354
16825
  }
16355
16826
  }
16356
16827
  return first;
@@ -16752,7 +17223,7 @@ function decodeNsec(nsec) {
16752
17223
  }
16753
17224
 
16754
17225
  // apps/body/dist/runtime.js
16755
- var execFileAsync = promisify(execFile);
17226
+ var execFileAsync = promisify(execFile2);
16756
17227
  var DEFAULT_AGENT_IDENTITY_NAME = "beeline-agent";
16757
17228
  var DEFAULT_BODY_IDENTITY_NAME = "beeline-body";
16758
17229
  var DEFAULT_DAEMON_MONOLITH_BASE_URL = "https://server.usebeeline.app";
@@ -16942,7 +17413,7 @@ async function launchRuntimeDaemon(configPath, opts = {}) {
16942
17413
  const entrypoint = opts.entrypoint ?? process.argv[1];
16943
17414
  if (!entrypoint)
16944
17415
  throw new Error("cannot resolve daemon CLI entrypoint");
16945
- const child = spawn2(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve6(configPath)], {
17416
+ const child = spawn3(process.execPath, [...opts.execArgv ?? [], entrypoint, "daemon", "--config", resolve6(configPath)], {
16946
17417
  cwd: directory,
16947
17418
  env: opts.env ?? process.env,
16948
17419
  detached: !foreground,
@@ -17213,7 +17684,7 @@ async function activateDaemonTransport(path, fetchImpl = fetch) {
17213
17684
  }
17214
17685
 
17215
17686
  // apps/body/dist/room-runtime.js
17216
- import { execFile as execFile5 } from "node:child_process";
17687
+ import { execFile as execFile6 } from "node:child_process";
17217
17688
  import { createHash as createHash6 } from "node:crypto";
17218
17689
  import { existsSync as existsSync4, mkdirSync } from "node:fs";
17219
17690
  import { mkdir as mkdir13, rm as rm5 } from "node:fs/promises";
@@ -17221,7 +17692,7 @@ import { dirname as dirname8, resolve as resolve20 } from "node:path";
17221
17692
  import { promisify as promisify4 } from "node:util";
17222
17693
 
17223
17694
  // apps/body/dist/grant-runner.js
17224
- import { execFile as execFile2 } from "node:child_process";
17695
+ import { execFile as execFile3 } from "node:child_process";
17225
17696
  import { createHash as createHash2, randomBytes as randomBytes6 } from "node:crypto";
17226
17697
  import { readFile as readFile4 } from "node:fs/promises";
17227
17698
  import { createServer } from "node:http";
@@ -17880,9 +18351,9 @@ var GrantCommandRunner = class {
17880
18351
  ...Object.fromEntries(secrets)
17881
18352
  };
17882
18353
  const cap = this.options.outputCapBytes ?? GRANT_COMMAND_OUTPUT_CAP_BYTES;
17883
- const spawn8 = surfaceAllows(policy.surface, "run-host-command") ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv);
18354
+ const spawn9 = surfaceAllows(policy.surface, "run-host-command") ? { command: argv[0], args: argv.slice(1) } : roomSandboxCommand(policy, room.cwd, argv);
17884
18355
  const outcome = await new Promise((resolveRun) => {
17885
- const child = execFile2(spawn8.command, spawn8.args, {
18356
+ const child = execFile3(spawn9.command, spawn9.args, {
17886
18357
  cwd: room.cwd,
17887
18358
  env,
17888
18359
  timeout: this.options.timeoutMs ?? GRANT_COMMAND_TIMEOUT_MS,
@@ -18318,7 +18789,7 @@ async function runServerCommandIntake(options) {
18318
18789
  }
18319
18790
 
18320
18791
  // apps/body/dist/monolith-corner-turn.js
18321
- import { execFile as execFile4 } from "node:child_process";
18792
+ import { execFile as execFile5 } from "node:child_process";
18322
18793
  import { createHash as createHash5 } from "node:crypto";
18323
18794
  import { mkdir as mkdir12 } from "node:fs/promises";
18324
18795
  import { homedir as homedir7 } from "node:os";
@@ -18352,6 +18823,9 @@ function isAgentPairingCode(value) {
18352
18823
  // apps/body/dist/beeline-skill.js
18353
18824
  var USING_BEELINE_SKILL_NAME = "using-beeline";
18354
18825
  var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
18826
+ function isConfiguredReviewer(agentHandle, reviewerHandle) {
18827
+ return Boolean(agentHandle && reviewerHandle && agentHandle.replace(/^@/, "") === reviewerHandle.replace(/^@/, ""));
18828
+ }
18355
18829
  var BEELINE_ROOM_CAPABILITIES = [
18356
18830
  "The repository filesystem is read-only in this Room session.",
18357
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.",
@@ -18359,7 +18833,7 @@ var BEELINE_ROOM_CAPABILITIES = [
18359
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.",
18360
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.",
18361
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.",
18362
- "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 attach_file 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); it is attached to your reply. 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.",
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.",
18363
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.",
18364
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.`,
18365
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.",
@@ -18375,7 +18849,7 @@ var BEELINE_DM_CAPABILITIES = [
18375
18849
  "The repository filesystem is read-only in this session.",
18376
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.",
18377
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.",
18378
- "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 attach_file 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); it is attached to your reply. 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.",
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.",
18379
18853
  "Tag the person only when you need a decision or input, or when the task they asked for is finished.",
18380
18854
  "Never claim an action or reply happened unless the prompt or a tool result proves it."
18381
18855
  ].join(" ");
@@ -18417,6 +18891,8 @@ description: How to answer inside a Beeline Room.
18417
18891
 
18418
18892
  You are answering inside a Room whose filesystem is read-only. ${BEELINE_ROOM_CAPABILITIES}
18419
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
+
18420
18896
  ## Tools and the Workbench
18421
18897
 
18422
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.
@@ -18507,7 +18983,7 @@ Then take exactly one action:
18507
18983
 
18508
18984
  - FAIL: reply \`@author\` with the confirmed findings to fix.
18509
18985
  - PASS: call \`approve_merge\` with the reviewed head SHA, then reply \`@author approved <reviewed sha>, merge\`.
18510
- - Never merge the pull request yourself.
18986
+ - Approving is your last step as reviewer. The author merges it; you never do, and nothing merges it automatically.
18511
18987
  `;
18512
18988
  }
18513
18989
 
@@ -19177,7 +19653,7 @@ async function prepareRoomAgentHome(input) {
19177
19653
  await symlink(source, target).catch(() => void 0);
19178
19654
  }
19179
19655
  const prior = agentHomeProvisionQueues.get(root) ?? Promise.resolve();
19180
- 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));
19181
19657
  agentHomeProvisionQueues.set(root, provision);
19182
19658
  try {
19183
19659
  await provision;
@@ -19187,10 +19663,10 @@ async function prepareRoomAgentHome(input) {
19187
19663
  }
19188
19664
  return roomAgentHomeEnv(root);
19189
19665
  }
19190
- async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting) {
19666
+ async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting, isReviewer) {
19191
19667
  const managedSkills = [
19192
19668
  { name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
19193
- { name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }
19669
+ ...isReviewer ? [{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }] : []
19194
19670
  ];
19195
19671
  const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
19196
19672
  await provisionManagedSkillsDir(resolve13(root, skillDir, "skills"), managedSkills, shared, sharedSkills.length === 0);
@@ -20245,9 +20721,9 @@ export default async function (pi) {
20245
20721
  `;
20246
20722
 
20247
20723
  // apps/body/dist/corner-branch-sync.js
20248
- import { execFile as execFile3 } from "node:child_process";
20724
+ import { execFile as execFile4 } from "node:child_process";
20249
20725
  import { promisify as promisify2 } from "node:util";
20250
- var execFileAsync2 = promisify2(execFile3);
20726
+ var execFileAsync2 = promisify2(execFile4);
20251
20727
  async function syncCornerBranch(input) {
20252
20728
  const git = input.git ?? (async (args) => (await execFileAsync2("git", ["-C", input.worktreePath, ...args], {
20253
20729
  ...input.env ? { env: input.env } : {},
@@ -20426,7 +20902,7 @@ function isMountedMcpToolPermissionRequest(request, mountedServers = ROOM_MOUNTE
20426
20902
  var AGENT_SURFACE_TOOL_NAMES = [
20427
20903
  "open_corner",
20428
20904
  "pr_checks_status",
20429
- "attach_file",
20905
+ "post_artifact",
20430
20906
  "write_scratch_file"
20431
20907
  ];
20432
20908
  var SQUIRE_TITLE_PREFIXES = [
@@ -21267,7 +21743,7 @@ async function seedWarmNodeModules(input) {
21267
21743
  for (const target of placed) {
21268
21744
  await rm4(target, { recursive: true, force: true }).catch(() => void 0);
21269
21745
  }
21270
- return { reason: "failed", key: plan.key, detail: describe(error) };
21746
+ return { reason: "failed", key: plan.key, detail: describe2(error) };
21271
21747
  } finally {
21272
21748
  await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
21273
21749
  }
@@ -21310,7 +21786,7 @@ async function harvestWarmNodeModules(input) {
21310
21786
  } catch (error) {
21311
21787
  if (await pathExists(entry))
21312
21788
  return { reason: "already-warm", key: plan.key };
21313
- return { reason: "failed", key: plan.key, detail: describe(error) };
21789
+ return { reason: "failed", key: plan.key, detail: describe2(error) };
21314
21790
  } finally {
21315
21791
  await rm4(staging, { recursive: true, force: true }).catch(() => void 0);
21316
21792
  }
@@ -21453,7 +21929,7 @@ async function deviceOf(path) {
21453
21929
  async function isDirectory(path) {
21454
21930
  return stat2(path).then((info) => info.isDirectory(), () => false);
21455
21931
  }
21456
- function describe(error) {
21932
+ function describe2(error) {
21457
21933
  return error instanceof Error ? error.message : String(error);
21458
21934
  }
21459
21935
 
@@ -21731,10 +22207,13 @@ var MonolithRoomTurnLoop = class {
21731
22207
  });
21732
22208
  const directMessage = Array.isArray(repositoryState.directParticipants) && repositoryState.directParticipants.length === 2;
21733
22209
  await mkdir11(this.options.cwd, { recursive: true });
21734
- const selection = configuration.model || configuration.effort ? { model: configuration.model, effort: configuration.effort } : this.options.config.modelSelection;
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;
21735
22213
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
21736
22214
  root: this.options.config.agentHomeRoot,
21737
22215
  sharedSkills: this.options.config.sharedSkills ?? [],
22216
+ isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
21738
22217
  ...this.options.config.agentKind ? { agentKind: this.options.config.agentKind } : {},
21739
22218
  ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
21740
22219
  ...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl, {
@@ -21957,6 +22436,7 @@ var MonolithRoomTurnLoop = class {
21957
22436
  this.busy = true;
21958
22437
  const trace = this.beginTurnTrace(item.id);
21959
22438
  let liveStream;
22439
+ let liveCornerOpened = false;
21960
22440
  try {
21961
22441
  if (!this.memberNames.has(item.authorId))
21962
22442
  await this.roster().catch(() => void 0);
@@ -22038,7 +22518,11 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
22038
22518
  result2 = await this.client.sessionPrompt(this.sessionId, nextPrompt, ROOM_PROMPT_INACTIVITY_TIMEOUT_MS, (delta, full) => {
22039
22519
  trace.firstModelOutput();
22040
22520
  stream.onChunk(delta, full);
22041
- }, void 0, (calls) => trace.toolCalls(calls));
22521
+ }, void 0, (calls) => {
22522
+ trace.toolCalls(calls);
22523
+ if (openedACorner(openCornerToolCall(calls)))
22524
+ liveCornerOpened = true;
22525
+ });
22042
22526
  } catch (error) {
22043
22527
  promptError = error;
22044
22528
  }
@@ -22132,6 +22616,22 @@ ${JSON.stringify((corners.corners ?? []).filter((corner) => !corner.archived))}`
22132
22616
  await trace.finish("cancelled");
22133
22617
  return;
22134
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
+ }
22135
22635
  await liveStream?.retract().catch((retractError) => {
22136
22636
  console.error(`[thin-core] monolith Room ${this.options.roomId} draft retract failed:`, retractError);
22137
22637
  });
@@ -22201,7 +22701,7 @@ function roomMessagePrompt(author, body, attachments, delivered, harnessAcceptsI
22201
22701
  }
22202
22702
 
22203
22703
  // apps/body/dist/monolith-corner-turn.js
22204
- var execFileAsync3 = promisify3(execFile4);
22704
+ var execFileAsync3 = promisify3(execFile5);
22205
22705
  var TOOL_ARGUMENT_MAX_BYTES = 1200;
22206
22706
  var TOOL_OUTPUT_MAX_BYTES = 3200;
22207
22707
  var TOOL_PATH_LIMIT = 12;
@@ -22592,6 +23092,7 @@ var MonolithCornerTurnLoop = class {
22592
23092
  const homeOverlay = this.options.config.agentHomeRoot ? await prepareRoomAgentHome({
22593
23093
  root: this.options.config.agentHomeRoot,
22594
23094
  sharedSkills: this.options.config.sharedSkills ?? [],
23095
+ isReviewer: isConfiguredReviewer(self?.handle, configuration.reviewerHandle),
22595
23096
  ...this.options.config.agentKind ? { agentKind: this.options.config.agentKind } : {},
22596
23097
  ...this.options.config.operatorHome ? { operatorHome: this.options.config.operatorHome } : {},
22597
23098
  ...openRouterRoutingInput(this.options.config, selection, this.options.fetchImpl, {
@@ -22742,7 +23243,7 @@ var MonolithCornerTurnLoop = class {
22742
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."
22743
23244
  ] : [
22744
23245
  "This is a chat-only corner with no repository or GitHub workflow.",
22745
- "Work in this corner's writable workspace. Use write_scratch_file or ordinary tools to create files, then attach_file to send them back to the corner.",
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.",
22746
23247
  "Do not initialize a repository, create a branch, push, open a pull request, or wait for GitHub checks."
22747
23248
  ]
22748
23249
  ].filter(Boolean).join("\n\n")
@@ -23729,7 +24230,7 @@ async function removeCornerWorktreeAndBranches(worktree) {
23729
24230
  ]);
23730
24231
  }
23731
24232
  }
23732
- var execFileAsync4 = promisify4(execFile5);
24233
+ var execFileAsync4 = promisify4(execFile6);
23733
24234
  async function removeCornerScratchWorkspace(input) {
23734
24235
  const expected = resolve20(input.roomRoot, "scratch");
23735
24236
  if (resolve20(input.scratchPath) !== expected) {
@@ -24464,13 +24965,13 @@ var ThinDaemonCore = class {
24464
24965
  };
24465
24966
 
24466
24967
  // apps/body/dist/systemd.js
24467
- import { execFile as execFile6 } from "node:child_process";
24968
+ import { execFile as execFile7 } from "node:child_process";
24468
24969
  import { mkdir as mkdir14, readFile as readFile9, writeFile as writeFile9 } from "node:fs/promises";
24469
24970
  import { homedir as homedir8 } from "node:os";
24470
24971
  import { dirname as dirname9, resolve as resolve21 } from "node:path";
24471
24972
  import { setTimeout as sleep } from "node:timers/promises";
24472
24973
  import { promisify as promisify5 } from "node:util";
24473
- var execFileAsync5 = promisify5(execFile6);
24974
+ var execFileAsync5 = promisify5(execFile7);
24474
24975
  var DELIBERATE_REMOVAL_EXIT_STATUS = 78;
24475
24976
  var DAEMON_DISTRESS_EXIT_STATUS = 77;
24476
24977
  var UNKNOWN_AGENT_EXIT_STATUS = 79;
@@ -24709,7 +25210,7 @@ async function runStartCommand(args, interactiveUi) {
24709
25210
  }
24710
25211
 
24711
25212
  // apps/body/dist/connect-command.js
24712
- import { spawn as spawn5 } from "node:child_process";
25213
+ import { spawn as spawn6 } from "node:child_process";
24713
25214
  import { createHash as createHash8 } from "node:crypto";
24714
25215
  import { chmod as chmod6, mkdir as mkdir16, readFile as readFile11, unlink as unlink2, writeFile as writeFile11 } from "node:fs/promises";
24715
25216
  import { dirname as dirname12, resolve as resolve23 } from "node:path";
@@ -24796,7 +25297,7 @@ async function verifyProviderKey(input) {
24796
25297
  }
24797
25298
 
24798
25299
  // apps/body/dist/pair-agent-selection.js
24799
- import { spawn as spawn3 } from "node:child_process";
25300
+ import { spawn as spawn4 } from "node:child_process";
24800
25301
  import { stdin as stdin2, stdout as stdout3 } from "node:process";
24801
25302
  var NO_AGENT_MESSAGE = `No supported ACP-capable coding agent was detected.
24802
25303
  Install one of these supported agents:
@@ -24824,7 +25325,7 @@ async function clackSelectAgent(candidates) {
24824
25325
  }
24825
25326
  async function installAdapter(install, opts) {
24826
25327
  await new Promise((resolveInstall, rejectInstall) => {
24827
- const child = spawn3(install.command, install.args, {
25328
+ const child = spawn4(install.command, install.args, {
24828
25329
  cwd: opts.cwd,
24829
25330
  env: opts.env ?? process.env,
24830
25331
  stdio: "inherit"
@@ -25419,7 +25920,7 @@ async function writeProviderEnv(selection, agentPubkey) {
25419
25920
  }
25420
25921
  async function runInstalledFinish(binary, grantPath) {
25421
25922
  await new Promise((resolveRun, rejectRun) => {
25422
- const child = spawn5(binary, ["connect-finish", grantPath], {
25923
+ const child = spawn6(binary, ["connect-finish", grantPath], {
25423
25924
  stdio: ["ignore", "pipe", "pipe"]
25424
25925
  });
25425
25926
  let diagnostic = "";
@@ -25592,7 +26093,7 @@ init_self_update_manifest();
25592
26093
 
25593
26094
  // apps/body/dist/managed-update.js
25594
26095
  init_self_update();
25595
- import { spawn as spawn6 } from "node:child_process";
26096
+ import { spawn as spawn7 } from "node:child_process";
25596
26097
  import { mkdir as mkdir18, rm as rm7, stat as stat3, writeFile as writeFile13 } from "node:fs/promises";
25597
26098
  import { dirname as dirname14, resolve as resolve25 } from "node:path";
25598
26099
 
@@ -26006,7 +26507,7 @@ async function runManagedUpdateWorkerProcess() {
26006
26507
  if (!entrypoint)
26007
26508
  throw new Error("cannot resolve the current Beeline entrypoint");
26008
26509
  await new Promise((resolveWorker, rejectWorker) => {
26009
- const child = spawn6(process.execPath, [entrypoint, "managed-update-worker"], {
26510
+ const child = spawn7(process.execPath, [entrypoint, "managed-update-worker"], {
26010
26511
  detached: true,
26011
26512
  env: { ...process.env, BEELINE_INTERNAL_UPDATE_WORKER: "1" },
26012
26513
  stdio: ["ignore", "pipe", "pipe"]
@@ -26638,7 +27139,7 @@ async function runUpdateFunctionalProbe(input) {
26638
27139
  }
26639
27140
 
26640
27141
  // apps/body/dist/current-release-probe.js
26641
- import { spawn as spawn7 } from "node:child_process";
27142
+ import { spawn as spawn8 } from "node:child_process";
26642
27143
  import { dirname as dirname16, join as join10 } from "node:path";
26643
27144
  init_self_update();
26644
27145
  var CURRENT_RELEASE_PROBE_TIMEOUT_MS = 12e4;
@@ -26689,7 +27190,7 @@ async function probeReleaseInSubprocess(input) {
26689
27190
  }
26690
27191
  const timeoutMs = input.timeoutMs ?? CURRENT_RELEASE_PROBE_TIMEOUT_MS;
26691
27192
  return new Promise((resolve31) => {
26692
- const child = spawn7(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
27193
+ const child = spawn8(input.execPath ?? process.execPath, [entrypoint, UPDATE_PROBE_COMMAND, "--config", input.runtimeConfigPath], { env: input.env ?? process.env, stdio: ["ignore", "pipe", "pipe"] });
26693
27194
  let stdout6 = "";
26694
27195
  let stderr = "";
26695
27196
  let settled = false;
@@ -27034,6 +27535,7 @@ async function runStoredDaemon(pathOrPointer) {
27034
27535
  const scratchSweepTimer = setInterval(() => runScratchSweepLogged(runtimeDir), SCRATCH_SWEEP_INTERVAL_MS);
27035
27536
  scratchSweepTimer.unref();
27036
27537
  let ready = false;
27538
+ let connectorLoop;
27037
27539
  let stoppingStatus = "daemon stopped";
27038
27540
  try {
27039
27541
  const core = new ThinDaemonCore(runtime, configPath, config, { daemonApi });
@@ -27106,6 +27608,12 @@ async function runStoredDaemon(pathOrPointer) {
27106
27608
  ...runtime.modelSelection ? { runtimeSelection: runtime.modelSelection } : {},
27107
27609
  ...config.modelUnavailable ? { startupUnavailable: config.modelUnavailable.unavailable.label } : {}
27108
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();
27109
27617
  },
27110
27618
  onProgress: async (status) => {
27111
27619
  void drainRollbackAlert(core.activeRoomIds()[0] ?? runtime.rooms[0]?.channelId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.104",
3
+ "version": "0.0.105",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {