usebeeline 0.0.108 → 0.0.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/usebeeline.mjs +196 -27
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -8476,7 +8476,7 @@ async function installGoogleTool(options) {
|
|
|
8476
8476
|
return fail("connector type", `${options.connectorType} is not a Google tool connector`);
|
|
8477
8477
|
}
|
|
8478
8478
|
push(step("Google credentials resolved", "running", { output: "resolving\u2026" }));
|
|
8479
|
-
const resolved = options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
8479
|
+
const resolved = options.resolvedCredentials ? await options.resolvedCredentials : options.resolveCredentials ? await options.resolveCredentials() : await (async () => {
|
|
8480
8480
|
const oneClick = await readGoogleCredentialsFromVault(options.squire);
|
|
8481
8481
|
return oneClick.source === "squire" ? { ...oneClick } : { ...loadManualGoogleCredentials(options.home, options.env) };
|
|
8482
8482
|
})();
|
|
@@ -8515,7 +8515,31 @@ function describe(error) {
|
|
|
8515
8515
|
|
|
8516
8516
|
// apps/body/dist/connector-squire.js
|
|
8517
8517
|
import { execFile, spawn as spawn2 } from "node:child_process";
|
|
8518
|
-
var
|
|
8518
|
+
var SQUIRE_MCP_NAME = "@trusty-squire/mcp";
|
|
8519
|
+
var SQUIRE_CONNECT_PACKAGE = `${SQUIRE_MCP_NAME}@latest`;
|
|
8520
|
+
var activeConnectSession;
|
|
8521
|
+
function isProcessAlive(pid) {
|
|
8522
|
+
if (pid === void 0 || pid <= 0)
|
|
8523
|
+
return false;
|
|
8524
|
+
try {
|
|
8525
|
+
process.kill(pid, 0);
|
|
8526
|
+
return true;
|
|
8527
|
+
} catch (error) {
|
|
8528
|
+
return error?.code === "EPERM";
|
|
8529
|
+
}
|
|
8530
|
+
}
|
|
8531
|
+
function releaseSquireConnectSession(log2) {
|
|
8532
|
+
const claim = activeConnectSession;
|
|
8533
|
+
activeConnectSession = void 0;
|
|
8534
|
+
if (!claim)
|
|
8535
|
+
return;
|
|
8536
|
+
if (isProcessAlive(claim.pid)) {
|
|
8537
|
+
log2?.(`released trusty-squire connect session claim (pid ${String(claim.pid)} still live \u2014 a fresh connect owns the browser now)`);
|
|
8538
|
+
claim.abort();
|
|
8539
|
+
} else {
|
|
8540
|
+
log2?.("cleared dead trusty-squire connect session claim (owner process gone)");
|
|
8541
|
+
}
|
|
8542
|
+
}
|
|
8519
8543
|
var defaultShellRunner = (command, args) => new Promise((resolve31) => {
|
|
8520
8544
|
execFile(command, [...args], { timeout: 12e4, maxBuffer: 4 * 1024 * 1024, encoding: "utf8" }, (error, stdout6, stderr) => {
|
|
8521
8545
|
const code = error?.code;
|
|
@@ -8537,7 +8561,7 @@ var defaultStreamedRunner = (command, args) => new Promise((resolve31) => {
|
|
|
8537
8561
|
const safetyTimer = setTimeout(() => {
|
|
8538
8562
|
if (!resolved) {
|
|
8539
8563
|
resolved = true;
|
|
8540
|
-
resolve31({ stdout: stdout6, stderr, signIn: void 0, abort: () => {
|
|
8564
|
+
resolve31({ stdout: stdout6, stderr, pid: child.pid ?? void 0, signIn: void 0, abort: () => {
|
|
8541
8565
|
} });
|
|
8542
8566
|
}
|
|
8543
8567
|
child.kill();
|
|
@@ -8546,6 +8570,12 @@ var defaultStreamedRunner = (command, args) => new Promise((resolve31) => {
|
|
|
8546
8570
|
clearTimeout(safetyTimer);
|
|
8547
8571
|
child.kill();
|
|
8548
8572
|
};
|
|
8573
|
+
const claim = {
|
|
8574
|
+
pid: child.pid ?? void 0,
|
|
8575
|
+
claimedAt: Date.now(),
|
|
8576
|
+
abort
|
|
8577
|
+
};
|
|
8578
|
+
activeConnectSession = claim;
|
|
8549
8579
|
const finish = (result) => {
|
|
8550
8580
|
if (!resolved) {
|
|
8551
8581
|
resolved = true;
|
|
@@ -8570,11 +8600,11 @@ ${stderr}`;
|
|
|
8570
8600
|
checkOutput();
|
|
8571
8601
|
});
|
|
8572
8602
|
child.on("close", () => {
|
|
8573
|
-
finish({ stdout: stdout6, stderr, signIn: void 0, abort: () => {
|
|
8603
|
+
finish({ stdout: stdout6, stderr, pid: child.pid ?? void 0, signIn: void 0, abort: () => {
|
|
8574
8604
|
} });
|
|
8575
8605
|
});
|
|
8576
8606
|
child.on("error", () => {
|
|
8577
|
-
finish({ stdout: stdout6, stderr, signIn: void 0, abort: () => {
|
|
8607
|
+
finish({ stdout: stdout6, stderr, pid: child.pid ?? void 0, signIn: void 0, abort: () => {
|
|
8578
8608
|
} });
|
|
8579
8609
|
});
|
|
8580
8610
|
});
|
|
@@ -8595,17 +8625,57 @@ function parseConnectOutput(output) {
|
|
|
8595
8625
|
}
|
|
8596
8626
|
return { method: "streamed-page", url };
|
|
8597
8627
|
}
|
|
8598
|
-
async function installedSquireVersion(run2) {
|
|
8599
|
-
const probe = await run2("npx", [
|
|
8628
|
+
async function installedSquireVersion(run2, spec = SQUIRE_CONNECT_PACKAGE, preferOnline = false) {
|
|
8629
|
+
const probe = await run2("npx", [
|
|
8630
|
+
...preferOnline ? ["--prefer-online"] : [],
|
|
8631
|
+
"-y",
|
|
8632
|
+
spec,
|
|
8633
|
+
"--version"
|
|
8634
|
+
]);
|
|
8600
8635
|
const version = probe.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0];
|
|
8601
8636
|
return version;
|
|
8602
8637
|
}
|
|
8638
|
+
async function currentSquireRelease(run2) {
|
|
8639
|
+
const probe = await run2("npm", ["view", SQUIRE_MCP_NAME, "version"]);
|
|
8640
|
+
if (probe.code !== 0)
|
|
8641
|
+
return void 0;
|
|
8642
|
+
return probe.stdout.match(/\d+\.\d+\.\d+[^\s]*/)?.[0];
|
|
8643
|
+
}
|
|
8644
|
+
async function resolveSquireConnectSpec(run2) {
|
|
8645
|
+
const currentRelease = await currentSquireRelease(run2);
|
|
8646
|
+
const first = await installedSquireVersion(run2);
|
|
8647
|
+
if (currentRelease === void 0 || first === currentRelease) {
|
|
8648
|
+
return {
|
|
8649
|
+
npxArgs: ["-y", SQUIRE_CONNECT_PACKAGE],
|
|
8650
|
+
...first !== void 0 ? { resolvedVersion: first } : {},
|
|
8651
|
+
...currentRelease !== void 0 ? { currentRelease } : {},
|
|
8652
|
+
reResolved: false
|
|
8653
|
+
};
|
|
8654
|
+
}
|
|
8655
|
+
const fresh = await installedSquireVersion(run2, SQUIRE_CONNECT_PACKAGE, true);
|
|
8656
|
+
if (fresh === currentRelease) {
|
|
8657
|
+
return {
|
|
8658
|
+
npxArgs: ["--prefer-online", "-y", SQUIRE_CONNECT_PACKAGE],
|
|
8659
|
+
resolvedVersion: fresh,
|
|
8660
|
+
currentRelease,
|
|
8661
|
+
reResolved: true
|
|
8662
|
+
};
|
|
8663
|
+
}
|
|
8664
|
+
return {
|
|
8665
|
+
npxArgs: ["-y", `${SQUIRE_MCP_NAME}@${currentRelease}`],
|
|
8666
|
+
...fresh !== void 0 ? { resolvedVersion: fresh } : {},
|
|
8667
|
+
currentRelease,
|
|
8668
|
+
reResolved: true
|
|
8669
|
+
};
|
|
8670
|
+
}
|
|
8603
8671
|
function parseSignedInAs(output) {
|
|
8604
8672
|
return output.match(/signed in as ([^\s,;]+)/i)?.[1];
|
|
8605
8673
|
}
|
|
8606
8674
|
async function installSquire(options) {
|
|
8607
8675
|
const run2 = options.run ?? defaultShellRunner;
|
|
8608
8676
|
const streamRun = options.streamRun ?? defaultStreamedRunner;
|
|
8677
|
+
const log2 = options.log ?? (() => {
|
|
8678
|
+
});
|
|
8609
8679
|
const steps = [step2("helper reached", "done")];
|
|
8610
8680
|
const emit = () => options.onProgress?.([...steps]);
|
|
8611
8681
|
const push = (next) => {
|
|
@@ -8618,9 +8688,13 @@ async function installSquire(options) {
|
|
|
8618
8688
|
return { status: "error", steps, errorMessage: reason };
|
|
8619
8689
|
};
|
|
8620
8690
|
emit();
|
|
8691
|
+
releaseSquireConnectSession(log2);
|
|
8692
|
+
const resolution = await resolveSquireConnectSpec(run2);
|
|
8693
|
+
if (resolution.reResolved) {
|
|
8694
|
+
log2(`trusty-squire stale copy ${resolution.resolvedVersion ?? "unknown"} re-resolved against current release ${resolution.currentRelease}`);
|
|
8695
|
+
}
|
|
8621
8696
|
const install = await streamRun("npx", [
|
|
8622
|
-
|
|
8623
|
-
SQUIRE_CONNECT_PACKAGE,
|
|
8697
|
+
...resolution.npxArgs,
|
|
8624
8698
|
"connect",
|
|
8625
8699
|
"--force-relogin=google",
|
|
8626
8700
|
"--target=codex",
|
|
@@ -8631,7 +8705,7 @@ async function installSquire(options) {
|
|
|
8631
8705
|
push(step2("trusty-squire installed", "failed", stderr || "connect printed no sign-in URL"));
|
|
8632
8706
|
return fail(stderr || "the trusty-squire connect command printed no sign-in surface");
|
|
8633
8707
|
}
|
|
8634
|
-
const version =
|
|
8708
|
+
const version = resolution.resolvedVersion;
|
|
8635
8709
|
push(step2(`trusty-squire${version ? ` ${version}` : ""} installed`, "done"));
|
|
8636
8710
|
const signIn = install.signIn;
|
|
8637
8711
|
const signedInAs = parseSignedInAs(`${install.stdout}
|
|
@@ -8788,9 +8862,10 @@ var StdioSquireMcpClient = class {
|
|
|
8788
8862
|
}
|
|
8789
8863
|
initialize() {
|
|
8790
8864
|
const child = (this.options.spawn ?? spawn3)(this.options.command ?? "npx", [
|
|
8791
|
-
// `@
|
|
8792
|
-
//
|
|
8793
|
-
|
|
8865
|
+
// `@latest`: the current release, never a source-level version pin
|
|
8866
|
+
// (captain). Only the vault MCP server the agent spawns; the connect
|
|
8867
|
+
// install path verifies freshness itself (connector-squire.ts).
|
|
8868
|
+
...this.options.args ?? ["-y", "@trusty-squire/mcp@latest"]
|
|
8794
8869
|
]);
|
|
8795
8870
|
this.child = child;
|
|
8796
8871
|
this.buffer = "";
|
|
@@ -8899,7 +8974,12 @@ var ConnectorAssignmentLoop = class {
|
|
|
8899
8974
|
this.log = options.log ?? (() => {
|
|
8900
8975
|
});
|
|
8901
8976
|
this.install = options.install ?? installSquire;
|
|
8902
|
-
this.installGoogle = options.installGoogle ?? ((connectorType, onProgress) => installGoogleTool({
|
|
8977
|
+
this.installGoogle = options.installGoogle ?? ((connectorType, onProgress, sharedCredentials) => installGoogleTool({
|
|
8978
|
+
connectorType,
|
|
8979
|
+
home: this.googleHome(),
|
|
8980
|
+
onProgress,
|
|
8981
|
+
resolvedCredentials: sharedCredentials ? sharedCredentials() : this.resolveGoogleCredentials()
|
|
8982
|
+
}));
|
|
8903
8983
|
this.googleHomeDir = options.googleHome ?? process.env.BEELINE_AGENT_HOME ?? process.cwd();
|
|
8904
8984
|
this.readVaultFn = options.readVault ?? readVault;
|
|
8905
8985
|
this.revokeGrantsFn = options.revokeGrants ?? revokeGrants;
|
|
@@ -8941,15 +9021,60 @@ var ConnectorAssignmentLoop = class {
|
|
|
8941
9021
|
this.log(`connector assignments unavailable: ${describe2(error)}`);
|
|
8942
9022
|
return;
|
|
8943
9023
|
}
|
|
9024
|
+
let googleBatch;
|
|
8944
9025
|
for (const assignment of assignments) {
|
|
8945
9026
|
const key = `${assignment.kind}:${assignment.connectorId}`;
|
|
8946
9027
|
if (assignment.kind === "uninstall")
|
|
8947
9028
|
continue;
|
|
9029
|
+
if (assignment.kind === "install" && isGoogleToolConnectorType(assignment.connectorType)) {
|
|
9030
|
+
if (this.inFlight.has(key))
|
|
9031
|
+
continue;
|
|
9032
|
+
this.inFlight.add(key);
|
|
9033
|
+
(googleBatch ??= []).push(assignment);
|
|
9034
|
+
continue;
|
|
9035
|
+
}
|
|
8948
9036
|
if (this.inFlight.has(key))
|
|
8949
9037
|
continue;
|
|
8950
9038
|
this.inFlight.add(key);
|
|
8951
9039
|
void this.handle(assignment).catch((error) => this.log(`connector assignment ${key} failed: ${describe2(error)}`)).finally(() => this.inFlight.delete(key));
|
|
8952
9040
|
}
|
|
9041
|
+
if (googleBatch)
|
|
9042
|
+
this.flushGoogleBatch(googleBatch);
|
|
9043
|
+
}
|
|
9044
|
+
flushGoogleBatch(batch) {
|
|
9045
|
+
const keys = batch.map((a2) => `${a2.kind}:${a2.connectorId}`);
|
|
9046
|
+
void this.runGoogleBatch(batch).catch((error) => this.log(`google connector installs failed: ${describe2(error)}`)).finally(() => {
|
|
9047
|
+
for (const key of keys)
|
|
9048
|
+
this.inFlight.delete(key);
|
|
9049
|
+
});
|
|
9050
|
+
}
|
|
9051
|
+
/** The Google tool connectors ride ONE grant: every install in the batch
|
|
9052
|
+
* shares one credential resolution (the single Google consent) and the
|
|
9053
|
+
* installs run one at a time. Each tool still verifies and fails
|
|
9054
|
+
* independently — a grant that cannot serve one tool's scope refuses that
|
|
9055
|
+
* tool alone, never its siblings. */
|
|
9056
|
+
async runGoogleBatch(batch) {
|
|
9057
|
+
let shared;
|
|
9058
|
+
const sharedCredentials = () => shared ??= this.resolveGoogleCredentials();
|
|
9059
|
+
for (const assignment of batch) {
|
|
9060
|
+
await this.runGoogleInstall(assignment.connectorId, assignment.connectorType, sharedCredentials);
|
|
9061
|
+
}
|
|
9062
|
+
}
|
|
9063
|
+
/** The ONE grant resolution shared by every Google tool install of a
|
|
9064
|
+
* drain: the Squire one-click vault path first, then the manual
|
|
9065
|
+
* credentials path. Never rejects — a failure resolves as an unusable
|
|
9066
|
+
* grant each install reports through its own steps. */
|
|
9067
|
+
resolveGoogleCredentials() {
|
|
9068
|
+
return (async () => {
|
|
9069
|
+
try {
|
|
9070
|
+
const oneClick = await readGoogleCredentialsFromVault(this.squire());
|
|
9071
|
+
if (oneClick.source === "squire")
|
|
9072
|
+
return oneClick;
|
|
9073
|
+
} catch (error) {
|
|
9074
|
+
this.log(`google one-click grant lookup failed: ${describe2(error)}`);
|
|
9075
|
+
}
|
|
9076
|
+
return loadManualGoogleCredentials(this.googleHome(), process.env);
|
|
9077
|
+
})();
|
|
8953
9078
|
}
|
|
8954
9079
|
squire() {
|
|
8955
9080
|
this.mcp ??= defaultSquireMcpClient();
|
|
@@ -8957,11 +9082,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
8957
9082
|
}
|
|
8958
9083
|
async handle(assignment) {
|
|
8959
9084
|
if (assignment.kind === "install") {
|
|
8960
|
-
|
|
8961
|
-
await this.runGoogleInstall(assignment.connectorId, assignment.connectorType);
|
|
8962
|
-
} else {
|
|
8963
|
-
await this.runInstall(assignment.connectorId);
|
|
8964
|
-
}
|
|
9085
|
+
await this.runInstall(assignment.connectorId);
|
|
8965
9086
|
} else if (assignment.kind === "sync")
|
|
8966
9087
|
await this.runSync();
|
|
8967
9088
|
else if (assignment.kind === "revoke-grants")
|
|
@@ -8971,8 +9092,9 @@ var ConnectorAssignmentLoop = class {
|
|
|
8971
9092
|
googleHome() {
|
|
8972
9093
|
return this.googleHomeDir;
|
|
8973
9094
|
}
|
|
8974
|
-
/** Install one Google tool connector (Gmail/Calendar/Drive/YouTube)
|
|
8975
|
-
|
|
9095
|
+
/** Install one Google tool connector (Gmail/Calendar/Drive/YouTube),
|
|
9096
|
+
* riding the drain's shared grant resolution. */
|
|
9097
|
+
async runGoogleInstall(connectorId, connectorType, sharedCredentials) {
|
|
8976
9098
|
const report = async (steps) => {
|
|
8977
9099
|
try {
|
|
8978
9100
|
await this.api.execute("postConnectorStatus", { agentId: this.agentId, connectorId, steps });
|
|
@@ -8980,7 +9102,7 @@ var ConnectorAssignmentLoop = class {
|
|
|
8980
9102
|
this.log(`step report failed: ${describe2(error)}`);
|
|
8981
9103
|
}
|
|
8982
9104
|
};
|
|
8983
|
-
const result = await this.installGoogle(connectorType, report);
|
|
9105
|
+
const result = await this.installGoogle(connectorType, report, sharedCredentials);
|
|
8984
9106
|
if (result.status === "error") {
|
|
8985
9107
|
await this.api.execute("postConnectorStatus", {
|
|
8986
9108
|
agentId: this.agentId,
|
|
@@ -9008,7 +9130,8 @@ var ConnectorAssignmentLoop = class {
|
|
|
9008
9130
|
const result = await this.install({
|
|
9009
9131
|
workspaceId: this.agentId,
|
|
9010
9132
|
mcp: this.squire(),
|
|
9011
|
-
onProgress: report
|
|
9133
|
+
onProgress: report,
|
|
9134
|
+
log: (message) => this.log(`[trusty-squire] ${message}`)
|
|
9012
9135
|
});
|
|
9013
9136
|
if (result.status === "error") {
|
|
9014
9137
|
await this.api.execute("postConnectorStatus", {
|
|
@@ -17902,6 +18025,7 @@ var DaemonApiClient = class {
|
|
|
17902
18025
|
liveReconnect;
|
|
17903
18026
|
liveReconnectDelayMs = 1e3;
|
|
17904
18027
|
liveRooms = /* @__PURE__ */ new Map();
|
|
18028
|
+
roomsChangedListener;
|
|
17905
18029
|
constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
|
|
17906
18030
|
this.baseUrl = baseUrl;
|
|
17907
18031
|
this.daemonToken = daemonToken;
|
|
@@ -17948,6 +18072,12 @@ var DaemonApiClient = class {
|
|
|
17948
18072
|
}
|
|
17949
18073
|
};
|
|
17950
18074
|
}
|
|
18075
|
+
/** Register the one listener invoked when the server reports this agent's
|
|
18076
|
+
* Room/corner memberships changed — the wake that discovers a freshly
|
|
18077
|
+
* created Room without waiting for the reconciliation heartbeat. */
|
|
18078
|
+
setRoomsChangedListener(listener) {
|
|
18079
|
+
this.roomsChangedListener = listener;
|
|
18080
|
+
}
|
|
17951
18081
|
updateLiveCursor(roomId, cursor3) {
|
|
17952
18082
|
const room = this.liveRooms.get(roomId);
|
|
17953
18083
|
if (room && cursor3)
|
|
@@ -17981,6 +18111,7 @@ var DaemonApiClient = class {
|
|
|
17981
18111
|
this.liveReconnectDelayMs = 1e3;
|
|
17982
18112
|
for (const roomId of this.liveRooms.keys())
|
|
17983
18113
|
this.sendLiveSubscription(roomId);
|
|
18114
|
+
this.roomsChangedListener?.();
|
|
17984
18115
|
};
|
|
17985
18116
|
socket.onmessage = (message) => {
|
|
17986
18117
|
let value;
|
|
@@ -17992,6 +18123,10 @@ var DaemonApiClient = class {
|
|
|
17992
18123
|
if (!value || typeof value !== "object")
|
|
17993
18124
|
return;
|
|
17994
18125
|
const event = value;
|
|
18126
|
+
if (event.type === "rooms-changed") {
|
|
18127
|
+
this.roomsChangedListener?.();
|
|
18128
|
+
return;
|
|
18129
|
+
}
|
|
17995
18130
|
if (event.type === "subscribed" && typeof event.roomId === "string") {
|
|
17996
18131
|
const capabilities = event.capabilities;
|
|
17997
18132
|
this.liveRooms.get(event.roomId)?.onState?.(true, {
|
|
@@ -19259,7 +19394,7 @@ var BEELINE_ROOM_CAPABILITIES = [
|
|
|
19259
19394
|
"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.",
|
|
19260
19395
|
"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.",
|
|
19261
19396
|
"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.",
|
|
19262
|
-
`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.`,
|
|
19397
|
+
`To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
|
|
19263
19398
|
"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.",
|
|
19264
19399
|
"When repository work is needed, you MUST call beeline-agent open_corner with a name of at most three words - it titles the corner everywhere - and a complete objective of no more than 24 words. The host-governed call is the only way to start write work.",
|
|
19265
19400
|
"Never open a corner from your own reading of a person's ask. For every ask, first reply on one line `Proposed corner: <name> \u2014 <objective>`, using the exact title and objective you would pass to open_corner, then stop and wait. If one message contains several asks, list one numbered `Proposed corner:` line per ask; `go on 1 and 3` opens exactly those objectives and leaves the others proposed. A person's `go`, `yes`, or equivalent opens exactly the proposed corner; if they edit it, their edited text is the objective. Skip this ceremony only when the message itself explicitly commands a corner and states its scope, such as `open a corner and do X` or `go build X in a corner`; open that scope immediately.",
|
|
@@ -19412,7 +19547,7 @@ Then take exactly one action:
|
|
|
19412
19547
|
}
|
|
19413
19548
|
|
|
19414
19549
|
// apps/body/dist/external-mcp-capabilities.js
|
|
19415
|
-
var SQUIRE_MCP_VERSION = "
|
|
19550
|
+
var SQUIRE_MCP_VERSION = "latest";
|
|
19416
19551
|
var SQUIRE_MCP_PACKAGE = `@trusty-squire/mcp@${SQUIRE_MCP_VERSION}`;
|
|
19417
19552
|
function isTrustySquireMcpLaunch(command, args = []) {
|
|
19418
19553
|
return [command, ...args].some((value) => {
|
|
@@ -24509,6 +24644,25 @@ var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
|
|
|
24509
24644
|
var DEFAULT_RECONCILE_HEARTBEAT_MS = 6e4;
|
|
24510
24645
|
var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
|
|
24511
24646
|
var CORNER_BRANCH_DELETE_ATTEMPTS = 3;
|
|
24647
|
+
var DiscoveryWakes = class {
|
|
24648
|
+
arrived = 0;
|
|
24649
|
+
served = 0;
|
|
24650
|
+
/** Called for every agent-directed `rooms-changed` wake. */
|
|
24651
|
+
wake() {
|
|
24652
|
+
this.arrived += 1;
|
|
24653
|
+
}
|
|
24654
|
+
needsFastReconcile() {
|
|
24655
|
+
return this.arrived !== this.served;
|
|
24656
|
+
}
|
|
24657
|
+
/** Snapshot at reconcile entry: the wake count its reads will cover. */
|
|
24658
|
+
beginReconcile() {
|
|
24659
|
+
return this.arrived;
|
|
24660
|
+
}
|
|
24661
|
+
/** Clear only wakes the completed start pass actually served. */
|
|
24662
|
+
completeReconcile(covered) {
|
|
24663
|
+
this.served = Math.max(this.served, covered);
|
|
24664
|
+
}
|
|
24665
|
+
};
|
|
24512
24666
|
function shouldPostInitialCornerWorkingState(restore, isOpener = true) {
|
|
24513
24667
|
return isOpener && !restore.featureBranch && !restore.lifecycle?.branch && !restore.lifecycle?.pr;
|
|
24514
24668
|
}
|
|
@@ -24685,6 +24839,11 @@ var RoomRuntimeCoordinator = class {
|
|
|
24685
24839
|
workspaceRemovalConfirmations = 0;
|
|
24686
24840
|
roomRemovalConfirmations = /* @__PURE__ */ new Map();
|
|
24687
24841
|
confirmationPending = false;
|
|
24842
|
+
/** Agent-directed discovery wakes (#1369), counted instead of flagged: a
|
|
24843
|
+
* wake that arrives while a reconcile is already running must survive its
|
|
24844
|
+
* start-pass clearing, or a corner opened mid-reconcile waits a heartbeat
|
|
24845
|
+
* for a wake the daemon already received. */
|
|
24846
|
+
discoveryWakes = new DiscoveryWakes();
|
|
24688
24847
|
/** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
|
|
24689
24848
|
grantRunner;
|
|
24690
24849
|
grantRunnerServer;
|
|
@@ -24705,6 +24864,9 @@ var RoomRuntimeCoordinator = class {
|
|
|
24705
24864
|
});
|
|
24706
24865
|
this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
|
|
24707
24866
|
this.connectorUsage = new ConnectorUsageRecorder();
|
|
24867
|
+
this.options.daemonApi.setRoomsChangedListener?.(() => {
|
|
24868
|
+
this.discoveryWakes.wake();
|
|
24869
|
+
});
|
|
24708
24870
|
this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
|
|
24709
24871
|
this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
|
|
24710
24872
|
this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
|
|
@@ -24735,7 +24897,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
24735
24897
|
return this.scheduler.snapshot();
|
|
24736
24898
|
}
|
|
24737
24899
|
needsFastReconcile() {
|
|
24738
|
-
return this.confirmationPending;
|
|
24900
|
+
return this.confirmationPending || this.discoveryWakes.needsFastReconcile();
|
|
24739
24901
|
}
|
|
24740
24902
|
reconcileHeartbeatIntervalMs() {
|
|
24741
24903
|
return this.reconcileHeartbeatMs;
|
|
@@ -24771,6 +24933,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
24771
24933
|
}
|
|
24772
24934
|
async reconcile() {
|
|
24773
24935
|
this.confirmationPending = false;
|
|
24936
|
+
const coveredWakes = this.discoveryWakes.beginReconcile();
|
|
24774
24937
|
const bootstrap = await this.options.daemonApi.execute("getDaemonBootstrap", {
|
|
24775
24938
|
agentId: this.agent.publicKey
|
|
24776
24939
|
});
|
|
@@ -24838,8 +25001,13 @@ var RoomRuntimeCoordinator = class {
|
|
|
24838
25001
|
}
|
|
24839
25002
|
}
|
|
24840
25003
|
await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
|
|
24841
|
-
if (
|
|
25004
|
+
if (this.running.has(roomId))
|
|
25005
|
+
return;
|
|
25006
|
+
try {
|
|
24842
25007
|
await this.startRoom(roomId);
|
|
25008
|
+
} catch (error) {
|
|
25009
|
+
console.error(`[thin-core] failed to start Room ${roomId}:`, error);
|
|
25010
|
+
}
|
|
24843
25011
|
});
|
|
24844
25012
|
await mapWithConcurrency([...desiredCorners.values()], ROOM_JOIN_CONCURRENCY, async (corner) => {
|
|
24845
25013
|
if (!this.running.has(corner.cornerId))
|
|
@@ -24847,6 +25015,7 @@ var RoomRuntimeCoordinator = class {
|
|
|
24847
25015
|
});
|
|
24848
25016
|
for (const running of this.running.values())
|
|
24849
25017
|
running.body.requestReconciliation();
|
|
25018
|
+
this.discoveryWakes.completeReconcile(coveredWakes);
|
|
24850
25019
|
return "member";
|
|
24851
25020
|
}
|
|
24852
25021
|
roomRecord(roomId) {
|