usebeeline 0.1.22 → 0.1.23
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 +140 -43
- package/package.json +1 -1
package/dist/usebeeline.mjs
CHANGED
|
@@ -16722,6 +16722,32 @@ function defaultSquireMcpClient(scope) {
|
|
|
16722
16722
|
return new StdioSquireMcpClient({ scope });
|
|
16723
16723
|
}
|
|
16724
16724
|
|
|
16725
|
+
// apps/body/dist/squire-session-registry.js
|
|
16726
|
+
var owners = /* @__PURE__ */ new Map();
|
|
16727
|
+
function registerSquireSession(sessionId, owner) {
|
|
16728
|
+
if (owners.get(sessionId)?.client === owner.client)
|
|
16729
|
+
return;
|
|
16730
|
+
owners.set(sessionId, { ...owner, scheduleIds: /* @__PURE__ */ new Set() });
|
|
16731
|
+
}
|
|
16732
|
+
function forgetSquireSessions(sessionIds2) {
|
|
16733
|
+
for (const sessionId of sessionIds2)
|
|
16734
|
+
owners.delete(sessionId);
|
|
16735
|
+
}
|
|
16736
|
+
function squireSessionOwner(sessionId) {
|
|
16737
|
+
return owners.get(sessionId);
|
|
16738
|
+
}
|
|
16739
|
+
function tagSquireSessionSchedule(sessionId, scheduleId) {
|
|
16740
|
+
owners.get(sessionId)?.scheduleIds.add(scheduleId);
|
|
16741
|
+
}
|
|
16742
|
+
function squireSessionInheritable(sessionId, agentId, conversationId, scheduleId) {
|
|
16743
|
+
const owner = owners.get(sessionId);
|
|
16744
|
+
if (!owner || owner.agentId !== agentId)
|
|
16745
|
+
return false;
|
|
16746
|
+
if (owner.scheduleIds.has(scheduleId))
|
|
16747
|
+
return true;
|
|
16748
|
+
return owner.scheduleIds.size === 0 && owner.conversationId === conversationId;
|
|
16749
|
+
}
|
|
16750
|
+
|
|
16725
16751
|
// apps/body/dist/squire-task-relay.js
|
|
16726
16752
|
var SQUIRE_TASK_IDLE_LEASE_MS = 15 * 6e4;
|
|
16727
16753
|
function sessionIds(value, depth = 0) {
|
|
@@ -16796,6 +16822,8 @@ var SquireTaskRelay = class {
|
|
|
16796
16822
|
url;
|
|
16797
16823
|
token = randomUUID3();
|
|
16798
16824
|
active;
|
|
16825
|
+
/** The schedule a `schedule` command is a run of, if any. */
|
|
16826
|
+
activeScheduleId;
|
|
16799
16827
|
task;
|
|
16800
16828
|
leaseTimer;
|
|
16801
16829
|
approvalRequestId;
|
|
@@ -16864,6 +16892,7 @@ var SquireTaskRelay = class {
|
|
|
16864
16892
|
taskId: command.rootCommandId,
|
|
16865
16893
|
generationId
|
|
16866
16894
|
};
|
|
16895
|
+
this.activeScheduleId = command.scheduleId ?? command.source?.systemEvent?.scheduleId;
|
|
16867
16896
|
if (this.task) {
|
|
16868
16897
|
this.task.taskId = command.rootCommandId;
|
|
16869
16898
|
this.task.lastRequestId = command.turnRequestId;
|
|
@@ -16956,6 +16985,7 @@ var SquireTaskRelay = class {
|
|
|
16956
16985
|
exitCode: code,
|
|
16957
16986
|
sessionId: redactedSessionId(task.sessions.values().next().value)
|
|
16958
16987
|
});
|
|
16988
|
+
forgetSquireSessions(task.sessions);
|
|
16959
16989
|
task.sessions.clear();
|
|
16960
16990
|
task.pendingApprovals.clear();
|
|
16961
16991
|
}
|
|
@@ -16963,6 +16993,38 @@ var SquireTaskRelay = class {
|
|
|
16963
16993
|
this.task = task;
|
|
16964
16994
|
return task;
|
|
16965
16995
|
}
|
|
16996
|
+
/**
|
|
16997
|
+
* The client that may drive the requested sessions. Own sessions use this
|
|
16998
|
+
* relay's connection. A schedule run may borrow a live session from its
|
|
16999
|
+
* creating conversation or an earlier run of the same schedule, and the call
|
|
17000
|
+
* then runs on that session's owning connection. Anything else returns this
|
|
17001
|
+
* relay's client so the ordinary ownership refusal fires.
|
|
17002
|
+
*/
|
|
17003
|
+
inheritedCallClient(task, borrowed, requestedCount) {
|
|
17004
|
+
if (!borrowed.length)
|
|
17005
|
+
return task.client;
|
|
17006
|
+
if (borrowed.length !== requestedCount)
|
|
17007
|
+
return task.client;
|
|
17008
|
+
const scheduleId = this.activeScheduleId;
|
|
17009
|
+
if (!scheduleId)
|
|
17010
|
+
return task.client;
|
|
17011
|
+
const owner = squireSessionOwner(borrowed[0]);
|
|
17012
|
+
if (!owner || !borrowed.every((id) => squireSessionOwner(id)?.client === owner.client && squireSessionInheritable(id, this.agentId, this.roomId, scheduleId)))
|
|
17013
|
+
return task.client;
|
|
17014
|
+
return owner.client;
|
|
17015
|
+
}
|
|
17016
|
+
/**
|
|
17017
|
+
* Record every session this connection holds, so another conversation can
|
|
17018
|
+
* find the owning client when a schedule it created runs there. The owning
|
|
17019
|
+
* relay stays the one that issues the call.
|
|
17020
|
+
*/
|
|
17021
|
+
rememberSessions(client, ids, scheduleId) {
|
|
17022
|
+
for (const id of ids) {
|
|
17023
|
+
registerSquireSession(id, { agentId: this.agentId, conversationId: this.roomId, client });
|
|
17024
|
+
if (scheduleId)
|
|
17025
|
+
tagSquireSessionSchedule(id, scheduleId);
|
|
17026
|
+
}
|
|
17027
|
+
}
|
|
16966
17028
|
retire(reason) {
|
|
16967
17029
|
if (this.leaseTimer)
|
|
16968
17030
|
clearTimeout(this.leaseTimer);
|
|
@@ -16974,6 +17036,7 @@ var SquireTaskRelay = class {
|
|
|
16974
17036
|
sessionId: redactedSessionId(this.task.sessions.values().next().value)
|
|
16975
17037
|
});
|
|
16976
17038
|
this.task.client.close();
|
|
17039
|
+
forgetSquireSessions(this.task.sessions);
|
|
16977
17040
|
this.task.sessions.clear();
|
|
16978
17041
|
this.task.pendingApprovals.clear();
|
|
16979
17042
|
this.task = void 0;
|
|
@@ -17016,11 +17079,15 @@ var SquireTaskRelay = class {
|
|
|
17016
17079
|
this.log("call-end", task, { method: input.method, source: "cached" });
|
|
17017
17080
|
return task.tools;
|
|
17018
17081
|
}
|
|
17019
|
-
|
|
17082
|
+
const borrowed = input.method === "tools/call" ? requestedIds.filter((id) => !task.sessions.has(id)) : [];
|
|
17083
|
+
let callClient = this.inheritedCallClient(task, borrowed, requestedIds.length);
|
|
17084
|
+
const usingOwnConnection = callClient === task.client;
|
|
17085
|
+
if (input.method === "tools/call" && usingOwnConnection && task.dead && toolName2 !== "operate_start") {
|
|
17020
17086
|
this.log("call-refused", task, { reason: "connection-died" });
|
|
17021
17087
|
throw new Error("Squire MCP connection died and its browser session is gone; call operate_start");
|
|
17022
17088
|
}
|
|
17023
|
-
|
|
17089
|
+
const scheduleId = this.activeScheduleId;
|
|
17090
|
+
if (input.method === "tools/call" && (!safeToolName || usingOwnConnection && requestedIds.some((id) => !task.sessions.has(id)) || scheduleId && requestedIds.some((id) => !squireSessionInheritable(id, this.agentId, this.roomId, scheduleId)))) {
|
|
17024
17091
|
this.log("call-refused", task, { reason: "session-not-owned" });
|
|
17025
17092
|
throw new Error("Squire browser session is no longer owned by this conversation; call operate_start");
|
|
17026
17093
|
}
|
|
@@ -17032,16 +17099,17 @@ var SquireTaskRelay = class {
|
|
|
17032
17099
|
this.log("call-refused", task, { reason: "resource-denied" });
|
|
17033
17100
|
throw new Error("Squire call is not authorized for this task");
|
|
17034
17101
|
}
|
|
17035
|
-
if (task.dead) {
|
|
17102
|
+
if (usingOwnConnection && task.dead) {
|
|
17036
17103
|
if (toolName2 !== "operate_start" || requestedIds.length) {
|
|
17037
17104
|
this.log("call-refused", task, { reason: "restart-required" });
|
|
17038
17105
|
throw new Error("Squire MCP connection died; call operate_start for a fresh browser session");
|
|
17039
17106
|
}
|
|
17040
17107
|
this.retire("restart-required");
|
|
17041
17108
|
task = this.connection(active.taskId);
|
|
17109
|
+
callClient = task.client;
|
|
17042
17110
|
}
|
|
17043
17111
|
try {
|
|
17044
|
-
const result = await
|
|
17112
|
+
const result = await callClient.requestMcp(input.method, input.params ?? {});
|
|
17045
17113
|
if (this.closed || this.task !== task || active && (this.active?.taskId !== active.taskId || this.active?.requestId !== active.requestId))
|
|
17046
17114
|
throw new Error("Squire task ended while the call was running");
|
|
17047
17115
|
if (input.method === "tools/list")
|
|
@@ -17051,6 +17119,7 @@ var SquireTaskRelay = class {
|
|
|
17051
17119
|
if (staleLeaseResult(result)) {
|
|
17052
17120
|
for (const id of requestedIds)
|
|
17053
17121
|
task.sessions.delete(id);
|
|
17122
|
+
forgetSquireSessions(requestedIds);
|
|
17054
17123
|
this.releasePendingApprovals(task, requestedIds);
|
|
17055
17124
|
this.log("call-error", task, {
|
|
17056
17125
|
method: input.method,
|
|
@@ -17065,11 +17134,17 @@ var SquireTaskRelay = class {
|
|
|
17065
17134
|
if (safeToolName === "operate_finish" && !(result && typeof result === "object" && result.isError === true)) {
|
|
17066
17135
|
for (const id of requestedIds)
|
|
17067
17136
|
task.sessions.delete(id);
|
|
17137
|
+
forgetSquireSessions(requestedIds);
|
|
17068
17138
|
this.releasePendingApprovals(task, requestedIds);
|
|
17069
|
-
} else {
|
|
17070
|
-
|
|
17139
|
+
} else if (usingOwnConnection) {
|
|
17140
|
+
const created = sessionIds(result);
|
|
17141
|
+
for (const id of created)
|
|
17071
17142
|
task.sessions.add(id);
|
|
17143
|
+
this.rememberSessions(callClient, created, this.activeScheduleId);
|
|
17072
17144
|
}
|
|
17145
|
+
if (this.activeScheduleId)
|
|
17146
|
+
for (const id of requestedIds)
|
|
17147
|
+
tagSquireSessionSchedule(id, this.activeScheduleId);
|
|
17073
17148
|
const approval = input.method === "tools/call" && squireApprovalFromMcp({ id: 1, method: input.method, params: input.params }, { id: 1, result });
|
|
17074
17149
|
if (approval) {
|
|
17075
17150
|
const ids = requestedIds.length ? requestedIds : [...task.sessions];
|
|
@@ -17746,7 +17821,7 @@ async function refresh(state, transport) {
|
|
|
17746
17821
|
};
|
|
17747
17822
|
}
|
|
17748
17823
|
var REGISTRY_MCP_APPROVAL_REFUSAL = "Resource access requires approval from its owner for this requester, or the resource is unavailable.";
|
|
17749
|
-
var REGISTRY_MCP_UNAVAILABLE = "Registry MCP provider is unavailable; reconnect it in
|
|
17824
|
+
var REGISTRY_MCP_UNAVAILABLE = "Registry MCP provider is unavailable; reconnect it in Connectors.";
|
|
17750
17825
|
var RegistryMcpRefusal = class extends Error {
|
|
17751
17826
|
constructor() {
|
|
17752
17827
|
super(REGISTRY_MCP_APPROVAL_REFUSAL);
|
|
@@ -18191,7 +18266,7 @@ function classifyTurnSilence(reason, reasonKind) {
|
|
|
18191
18266
|
if (isContextOverflowFault(text2)) {
|
|
18192
18267
|
return { kind: "context-overflow" };
|
|
18193
18268
|
}
|
|
18194
|
-
if (/\bresource_limit: ACP session exceeded its \d+ MiB memory limit\b/i.test(text2)) {
|
|
18269
|
+
if (/\bresource_limit: ACP session (?:exceeded|likely hit) its \d+ MiB memory limit\b/i.test(text2)) {
|
|
18195
18270
|
return { kind: "resource-limit" };
|
|
18196
18271
|
}
|
|
18197
18272
|
if (/\b402\b/.test(text2) || /usage limit/i.test(text2) || /more credits/i.test(text2) || /quota/i.test(text2) || /allowance is spent/i.test(text2)) {
|
|
@@ -18223,6 +18298,7 @@ function isStandingWorkspaceConfigurationFault(text2) {
|
|
|
18223
18298
|
}
|
|
18224
18299
|
|
|
18225
18300
|
// packages/api-contract/dist/institutional-memory.js
|
|
18301
|
+
var WORKSPACE_CORE_SKILL_SLUG = "workspace-core";
|
|
18226
18302
|
var WORKSPACE_SKILL_MARKDOWN_MAX_BYTES = 32 * 1024;
|
|
18227
18303
|
|
|
18228
18304
|
// packages/api-contract/dist/workflow-contracts.js
|
|
@@ -29672,7 +29748,9 @@ var AcpResourceScope = class {
|
|
|
29672
29748
|
`--unit=${this.unit}`,
|
|
29673
29749
|
`--property=MemoryMax=${this.memoryMaxBytes}`,
|
|
29674
29750
|
"--property=MemorySwapMax=0",
|
|
29675
|
-
|
|
29751
|
+
// Let the kernel kill one offending process. `kill` sets
|
|
29752
|
+
// memory.oom.group=1 and ends the ACP runtime with the command.
|
|
29753
|
+
"--property=OOMPolicy=continue",
|
|
29676
29754
|
"--property=KillMode=control-group",
|
|
29677
29755
|
"--property=TimeoutStopSec=1s",
|
|
29678
29756
|
...owner ? [`--property=BindsTo=${owner}`, `--property=After=${owner}`] : [],
|
|
@@ -29685,15 +29763,6 @@ var AcpResourceScope = class {
|
|
|
29685
29763
|
env
|
|
29686
29764
|
};
|
|
29687
29765
|
}
|
|
29688
|
-
/** An OOM result survives process exit until reset-failed unloads the unit. */
|
|
29689
|
-
async exceededMemoryLimit() {
|
|
29690
|
-
try {
|
|
29691
|
-
const { stdout: stdout6 } = await execFileAsync5("systemctl", ["--user", "show", this.unit, "--property=Result", "--value"], { timeout: 5e3 });
|
|
29692
|
-
return stdout6.trim() === "oom-kill";
|
|
29693
|
-
} catch {
|
|
29694
|
-
return false;
|
|
29695
|
-
}
|
|
29696
|
-
}
|
|
29697
29766
|
async stop() {
|
|
29698
29767
|
return this.#stop ??= (async () => {
|
|
29699
29768
|
try {
|
|
@@ -30039,7 +30108,7 @@ var AcpRuntimeExitedError = class extends Error {
|
|
|
30039
30108
|
var AcpResourceLimitError = class extends Error {
|
|
30040
30109
|
cause = "resource_limit";
|
|
30041
30110
|
constructor(memoryMaxBytes) {
|
|
30042
|
-
super(`resource_limit: ACP session
|
|
30111
|
+
super(`resource_limit: ACP session likely hit its ${Math.floor(memoryMaxBytes / 1024 / 1024)} MiB memory limit (exit 137 or signal 9)`);
|
|
30043
30112
|
this.name = "AcpResourceLimitError";
|
|
30044
30113
|
}
|
|
30045
30114
|
};
|
|
@@ -30445,9 +30514,9 @@ ${String(error)}`);
|
|
|
30445
30514
|
}
|
|
30446
30515
|
/**
|
|
30447
30516
|
* One child exit: reject every in-flight request so the turn fails and is
|
|
30448
|
-
* reported instead of hanging, then publish the exit. A scoped
|
|
30449
|
-
*
|
|
30450
|
-
*
|
|
30517
|
+
* reported instead of hanging, then publish the exit. A scoped SIGKILL/137
|
|
30518
|
+
* is a likely memory-limit hit, while an unscoped SIGKILL uses the daemon
|
|
30519
|
+
* cgroup's OOM counter to distinguish it from a deliberate kill.
|
|
30451
30520
|
*/
|
|
30452
30521
|
async handleChildExit(code, signal) {
|
|
30453
30522
|
const child = this.child;
|
|
@@ -30455,9 +30524,9 @@ ${String(error)}`);
|
|
|
30455
30524
|
for (const p of this.pending.values())
|
|
30456
30525
|
this.clearTimer(p);
|
|
30457
30526
|
const scope = this.resourceScope;
|
|
30458
|
-
const
|
|
30527
|
+
const memoryLimitLikely = Boolean(scope && !this.stopping && (signal === "SIGKILL" || code === 137));
|
|
30459
30528
|
let oomKilled = false;
|
|
30460
|
-
if (!
|
|
30529
|
+
if (!scope && signal === "SIGKILL" && !this.stopping) {
|
|
30461
30530
|
let diagnosticTimer;
|
|
30462
30531
|
try {
|
|
30463
30532
|
oomKilled = await Promise.race([
|
|
@@ -30475,7 +30544,7 @@ ${String(error)}`);
|
|
|
30475
30544
|
const stderrSuffix = meaningful ? `: ${meaningful}` : "";
|
|
30476
30545
|
for (const [, p] of this.pending) {
|
|
30477
30546
|
this.clearTimer(p);
|
|
30478
|
-
p.reject(
|
|
30547
|
+
p.reject(memoryLimitLikely ? new AcpResourceLimitError(scope.memoryMaxBytes) : new AcpRuntimeExitedError(`${exitText}${stderrSuffix}`));
|
|
30479
30548
|
}
|
|
30480
30549
|
this.pending.clear();
|
|
30481
30550
|
this.activeRunIds.clear();
|
|
@@ -32486,29 +32555,29 @@ These mechanics apply in Rooms and corners. ${BEELINE_ROOM_MECHANICS}
|
|
|
32486
32555
|
|
|
32487
32556
|
Follow your own owner first, then this Workspace's owner and admins, then members. A higher-tier instruction overrides a lower-tier hold. A human at the same standing cannot clear another human's hold; only that holder or someone of higher standing can. Never tell a higher-tier human that a lower-tier hold binds them. Reason from the conversation. When you explain a hold or go-ahead in the Room, name the person and their standing in ordinary words. Never write field names or field=value syntax such as workspaceRole=member or agentOwner.
|
|
32488
32557
|
|
|
32489
|
-
## Tools and the
|
|
32558
|
+
## Tools and the Connectors
|
|
32490
32559
|
|
|
32491
32560
|
A **tool** is something you can use once a human adds 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.
|
|
32492
32561
|
|
|
32493
|
-
The tools this build knows, what each is for, and which the owner of this machine already has are one call away: beeline-agent
|
|
32562
|
+
The tools this build knows, what each is for, and which the owner of this machine already has are one call away: beeline-agent connectors_status. That read is the Connectors page for the machine and owner you actually run on, not the person who asked. Connected apps are listed once per app with a stable ID. Their sign-in and credentials stay with the server. Trusty Squire signs up and signs in to any website hands-off for the person; its vault holds keys when needed. operate_drive drives a whole flow, and flat steps (operate_click, operate_type, operate_select, operate_press, operate_scroll, operate_wait, operate_navigate, operate_read_inbox, operate_extract, and the rest of its flat operate_* actions) handle one action at a time. Tailscale installs its CLI on the selected helper and opens Tailscale's own browser sign-in; once connected, use the CLI for tailnet resources and tailscale file cp or tailscale file get for Taildrop. The wallet is created only from the Connectors page. When you cannot reach a tool, say: I can't reach X on this machine because Y; to fix it, Z. Give one explanation, then take or offer Z.
|
|
32494
32563
|
|
|
32495
32564
|
**Inspect your own host before answering.** When a question is about a system on the machine you run on, inspect its live state with the tools you have: processes, logs, local endpoints or service status. Say what you checked. Memory and Room history are not evidence of current state.
|
|
32496
32565
|
|
|
32497
|
-
**Use available tools before asking the person.** Before asking a person for a code, a status or a file, call
|
|
32566
|
+
**Use available tools before asking the person.** Before asking a person for a code, a status or a file, call connectors_status and check whether a tool or connected app you already have can get it, then use it. This includes an emailed verification code in an already-connected inbox. Ask only when none can, and say why. The key-ownership and grant rules still apply.
|
|
32498
32567
|
|
|
32499
|
-
**Offer the tool at the moment you need it.** When the work in front of you needs a tool the person does not have, do not send them to a settings page and do not stop at naming it. Call
|
|
32568
|
+
**Offer the tool at the moment you need it.** When the work in front of you needs a tool the person does not have, do not send them to a settings page and do not stop at naming it. Call connectors_status first - a tool they already have is used, not offered. In a Room or DM, call offer_connector with the connectorType and one short reason: a card appears there, spoken by you, addressed to the person you are answering, with one action. Connector offers are unavailable in corners; use a Room or DM for those. When the person wants Squire on a different Google account (or its signed-in account is the wrong one), call offer_connector with connectorType trusty-squire and intent "switch" - a plain connect keeps the account it already has. Your turn pauses on the card - say in prose what you are waiting for and end the turn; you are woken when it is added, and then you carry on.
|
|
32500
32569
|
|
|
32501
32570
|
**Research before you offer, and say so.** Never offer a tool you cannot describe. If someone asks you to install or add something you do not already know - by name, by purpose, or by what it will hold - say plainly that you are looking it up first, find out what it is and what it does with credentials, and state what you learned in your reply BEFORE the card appears. A person reading "Add X?" must already have read, in your own words, what X is. Refusing to act blind is part of being trusted with keys.
|
|
32502
32571
|
|
|
32503
32572
|
An offer is setup, never authority: it does not replace a grant, write permission, target-branch confirmation, or the merge gate, and it never needs a raw credential in chat. You never pair a tool yourself and never ask anyone for a key value; once a tool is added, provisioning happens inside it and receipts reach the person through the tool's own status message.
|
|
32504
32573
|
|
|
32505
|
-
Keys belong to the human who provisioned them. Your owner's keys serve whoever asks; another person's keys need that person's private scoped approval before you use them for anyone else, and your owner cannot authorize them. Use the connector without exposing or sharing raw keys. The
|
|
32574
|
+
Keys belong to the human who provisioned them. Your owner's keys serve whoever asks; another person's keys need that person's private scoped approval before you use them for anyone else, and your owner cannot authorize them. Use the connector without exposing or sharing raw keys. The Connectors page remains the place a person manages tools and keys by hand (Settings \u2192 Connectors); you point there to MANAGE what exists, not to add what you need.
|
|
32506
32575
|
|
|
32507
32576
|
## Where you run
|
|
32508
32577
|
|
|
32509
32578
|
Each agent runs on one owner's machine, with that owner's keys, hosts, and network. Agents in one Room can differ in what they reach. Memory is shared across all machines.
|
|
32510
32579
|
|
|
32511
|
-
- Check your own capabilities with
|
|
32580
|
+
- Check your own capabilities with connectors_status and your tools. Never infer another agent's.
|
|
32512
32581
|
- A failure saved from another machine is not proof here. Try it first.
|
|
32513
32582
|
- If you cannot do X here, say "I can't do X on this machine because Y", then hand off to an agent that memory shows has done X, or ask the requester who has access.
|
|
32514
32583
|
|
|
@@ -32525,16 +32594,16 @@ You keep memory yourself. No background reviewer exists: what you do not keep cu
|
|
|
32525
32594
|
| Workflows | Roles and steps for a run across agents | save_workflow; run with start_workflow |
|
|
32526
32595
|
| History | Every message in the Workspace | Nothing; read with search_history |
|
|
32527
32596
|
|
|
32528
|
-
- **Look up.**
|
|
32597
|
+
- **Look up.** Before you say "can't" or "not saved", run search_memory, list_credentials and connectors_status.
|
|
32529
32598
|
- **Use.** Call report_memory_used for what your answer used. When a saved item fails, fix it in the same turn.
|
|
32530
32599
|
- **Save.** Put each fact in one store only. Never save "X is impossible". Save the specific failure and the machine: "On Niglet's host, the highroll Fly token returns 403."
|
|
32531
32600
|
- **Prune.** Rewrite \`workspace-core\` whole and keep it at most 2,200 characters. An agent-saved Workspace fact that nobody uses expires in the weekly pass. Replace a procedure or workflow by saving its slug again; remove it with delete_skill.
|
|
32532
32601
|
|
|
32533
32602
|
## Connect an app
|
|
32534
32603
|
|
|
32535
|
-
When a request needs an app or service, call beeline-agent \`
|
|
32604
|
+
When a request needs an app or service, call beeline-agent \`connectors_status\` first. For a connected app via composio, use its ID with \`list_app_tools\`, then call \`execute_app_tool\` with the selected tool slug and arguments. For an app via squire-api, call its API with \`use_credential\` (service = the app key); via squire-browser, use the \`operate_*\` tools; via registry-mcp, use its mounted MCP tools. The server executes against that person\u2019s account and decides permission on every call. If tool discovery reports an error, follow its cause; \`needs_connection\` means the account must be reconnected, and \`needs_permission\` means wait for the connected person's decision. Do not switch paths after denial or provider unavailability. No app token, provider key, or sign-in URL belongs in chat or a model prompt.
|
|
32536
32605
|
|
|
32537
|
-
For an app that is not connected, call \`connect_app\` with its name, one short reason, and a request-specific \`continuation\`: one sentence stating what you will do right after sign-in. Do not include a link, credential, account identifier, or private data in that sentence. The server chooses the route and returns \`next\`. In a corner, call it from that corner so the sign-in card and connection wake return there. A managed OAuth app presents a sign-in card to its owner; end the turn and wait for the connection wake.
|
|
32606
|
+
For an app that is not connected, call \`connect_app\` with its name, one short reason, and a request-specific \`continuation\`: one sentence stating what you will do right after sign-in. Do not include a link, credential, account identifier, or private data in that sentence. The server chooses the route and returns \`next\`. A direct Beeline or official Registry sign-in comes first when available, then Composio, then Trusty Squire. In a corner, call it from that corner so the sign-in card and connection wake return there. A managed OAuth app presents a sign-in card to its owner; end the turn and wait for the connection wake. A provider outage is unavailable, not permission to change routes. After the person connects, read Connectors again and use the app ID. Every use is authorized as \`app:<key>\` and recorded once.
|
|
32538
32607
|
|
|
32539
32608
|
## Showing a mock
|
|
32540
32609
|
|
|
@@ -41694,12 +41763,15 @@ Only when the brief calls for repository changes, follow the PR procedure below.
|
|
|
41694
41763
|
Before the PR, demonstrate Y: run the built app or affected service and perform X. If no interactive surface is reachable, run the narrowest test or script exercising the exact user path and printing Y. An inner-function unit test, log line or code read is not a demonstration.
|
|
41695
41764
|
The PR body MUST have ## Reproduced and ## Demonstrated. Cite Reproduction <id> in both, with steps, the wrong result and the now-passing result. In ## Demonstrated, when none was obtained, state that plainly ("not obtained") and show the regression; for feature work say "not a defect report".
|
|
41696
41765
|
No unrequested features, flags, compatibility shims or refactors.`;
|
|
41697
|
-
var CORNER_REVIEWER_SESSION_INSTRUCTION = "You are this Room's configured reviewer.
|
|
41698
|
-
var CORNER_REVIEWER_UNSTABLE_HEAD_INSTRUCTION = "
|
|
41766
|
+
var CORNER_REVIEWER_SESSION_INSTRUCTION = "You are this Room's configured reviewer. Your PASS is approve_merge on the current green PR head, which your turn prompt names. Call it when that head is ready to merge. Never merge a pull request you review.";
|
|
41767
|
+
var CORNER_REVIEWER_UNSTABLE_HEAD_INSTRUCTION = "The current PR head is not green, so approve_merge on it fails now. The server wakes you when checks pass on the current head.";
|
|
41699
41768
|
var CORNER_DELIVERY_NUDGE = "Only when you write code in this corner and the brief calls for repository changes, inspect the repository state and finish delivering the work unless a human hold stands: commit and push the intended changes and open the pull request if one does not exist. Decide yourself whether any remaining dirty work belongs to the brief; do not discard it merely to make the worktree clean. The pull request body must carry ## Reproduced and ## Demonstrated; if they are missing, add them before ending the turn. Otherwise do not commit, push or open a PR; deliver with post_artifact.";
|
|
41700
41769
|
var CORNER_LAND_RULE = "Then, if a human's question or proposal in the corner has no answer, name it and do not merge; otherwise call merge_corner.";
|
|
41701
41770
|
var CORNER_CHECKS_NUDGE = `Call pr_checks_status now. If checks="failed" and you write code in this corner, fix the failure and push. If checks="unknown", reply in this corner with the tool reason and stop instead of retrying. Otherwise stop: a reviewer's PASS wakes you to merge with merge_corner. Never merge it any other way.`;
|
|
41702
41771
|
var handle = (value) => value.replace(/^@/, "");
|
|
41772
|
+
function memoryListsWorkspaceCoreProcedure(memory) {
|
|
41773
|
+
return (memory ?? "").split("\n").some((line) => line.startsWith(`- Procedure ${WORKSPACE_CORE_SKILL_SLUG} (load_workspace_skill):`));
|
|
41774
|
+
}
|
|
41703
41775
|
function cornerMergeInstruction(reviewerHandle) {
|
|
41704
41776
|
if (!reviewerHandle)
|
|
41705
41777
|
return "Once the pull request exists, reply with its full URL and end the turn; do not check or wait for CI. This Room has no configured reviewer, so it merges only on a person's yes: ask a Workspace owner or admin to approve the merge, then record their yes with order_corner_merge. You are woken to fix failing checks or a merge GitHub refused.";
|
|
@@ -41746,6 +41818,15 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
41746
41818
|
surfaces: EVERYWHERE,
|
|
41747
41819
|
render: () => "Your voice never changes the facts: never trim, soften, exaggerate, or invent a detail for style. Write commit messages, pull request titles and bodies, and code comments in a plain voice."
|
|
41748
41820
|
},
|
|
41821
|
+
{
|
|
41822
|
+
id: "core.memory",
|
|
41823
|
+
topic: "memory",
|
|
41824
|
+
why: "A command can exhaust the session cgroup before the agent can adapt its workload.",
|
|
41825
|
+
budgetBytes: 160,
|
|
41826
|
+
layer: "core",
|
|
41827
|
+
surfaces: EVERYWHERE,
|
|
41828
|
+
render: ({ memoryMaxBytes }) => `Memory cap: ${Number(((memoryMaxBytes ?? ACP_SESSION_MEMORY_MAX_BYTES) / 1024 ** 3).toFixed(2))} GiB per session. Size builds, tests and data jobs to fit it; split heavy work. Exit 137/signal 9 likely means a command hit this cap.`
|
|
41829
|
+
},
|
|
41749
41830
|
{
|
|
41750
41831
|
id: "core.proactive",
|
|
41751
41832
|
topic: "proactivity",
|
|
@@ -41762,7 +41843,7 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
41762
41843
|
budgetBytes: 360,
|
|
41763
41844
|
layer: "core",
|
|
41764
41845
|
surfaces: EVERYWHERE,
|
|
41765
|
-
render: () => "Finish the work before you end the turn.
|
|
41846
|
+
render: () => "Finish the work before you end the turn. Take the next step a tool result gives you, including sign-in or retry. Wait only for a person, CI, a reviewer, or a real block."
|
|
41766
41847
|
},
|
|
41767
41848
|
{
|
|
41768
41849
|
id: "core.truth",
|
|
@@ -41825,7 +41906,7 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
41825
41906
|
budgetBytes: 240,
|
|
41826
41907
|
layer: "surface",
|
|
41827
41908
|
surfaces: EVERYWHERE,
|
|
41828
|
-
render: ({ surface }) => surface === "room" || surface === "dm" ? "When you need a tool you do not have, call
|
|
41909
|
+
render: ({ surface }) => surface === "room" || surface === "dm" ? "When you need a tool you do not have, call connectors_status; offer_connector the tool it lists, otherwise call connect_app for the app." : "In corners, connectors_status discovers apps; connect_app starts sign-in here. Connector offers stay in Rooms and DMs."
|
|
41829
41910
|
},
|
|
41830
41911
|
{
|
|
41831
41912
|
id: "surface.squire-drive",
|
|
@@ -41854,13 +41935,13 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
41854
41935
|
id: "room.corners",
|
|
41855
41936
|
topic: "repository-work",
|
|
41856
41937
|
why: "Repository work must start through the host-governed corner, with a brief as its authority.",
|
|
41857
|
-
budgetBytes:
|
|
41938
|
+
budgetBytes: 1150,
|
|
41858
41939
|
layer: "surface",
|
|
41859
41940
|
surfaces: ["room"],
|
|
41860
41941
|
render: () => [
|
|
41861
41942
|
"Repository changes happen only in a corner. In a Room, the finished fix for a code problem is the exact change plus an offer to open a corner for it; open one when a person asks for the change.",
|
|
41862
41943
|
"Before opening one, consult beeline-triage and beeline-spec, then call open_corner with a name of at most three words joined by hyphens (no whitespace), an objective of at most 24 words, and the brief (a spec plus the approving message). The objective only titles the work; the brief is its authority. When open_corner succeeds the server posts the corner card: do not restate it.",
|
|
41863
|
-
"For corners under this Room, use inspect_corner for status, steer_corner to pass Room input down, ask_corner for one answer, and post_to_corner for a note that wakes nobody.",
|
|
41944
|
+
"For corners under this Room, use inspect_corner for status (its activity and awaiting fields show whether one waits on you), steer_corner to pass Room input down, ask_corner for one answer, and post_to_corner for a note that wakes nobody.",
|
|
41864
41945
|
"A corner ends when its pull request merges; since a person may merge minutes after it opens, anything still needed must already live in a different corner before that pull request opens, not after. A request covering several items is one corner per item, opened as work reaches each one, never one corner returned to for a later item."
|
|
41865
41946
|
].join(" ")
|
|
41866
41947
|
},
|
|
@@ -42185,6 +42266,16 @@ ${transcript.lines.join("\n")}`;
|
|
|
42185
42266
|
surfaces: EVERYWHERE,
|
|
42186
42267
|
render: ({ memory }) => memory ?? ""
|
|
42187
42268
|
},
|
|
42269
|
+
{
|
|
42270
|
+
id: "turn.workspace-core",
|
|
42271
|
+
topic: "workspace-core",
|
|
42272
|
+
why: "Most Workspaces never saved a workspace-core procedure; only a Workspace that lists it should be told to load it at task start.",
|
|
42273
|
+
budgetBytes: 120,
|
|
42274
|
+
layer: "turn",
|
|
42275
|
+
surfaces: EVERYWHERE,
|
|
42276
|
+
when: ({ memory }) => memoryListsWorkspaceCoreProcedure(memory),
|
|
42277
|
+
render: () => "At task start, call load_workspace_skill for `workspace-core`."
|
|
42278
|
+
},
|
|
42188
42279
|
{
|
|
42189
42280
|
id: "turn.corners",
|
|
42190
42281
|
topic: "corners",
|
|
@@ -43020,9 +43111,13 @@ var MonolithRoomTurnLoop = class {
|
|
|
43020
43111
|
* fixed when it opens: they cannot be corrected in place, so a changed one
|
|
43021
43112
|
* has to cost a respawn. The check is one round trip — the roster half of
|
|
43022
43113
|
* which the turn was going to fetch anyway — against a cold spawn measured
|
|
43023
|
-
* in seconds.
|
|
43114
|
+
* in seconds. A harness process that has exited (a memory-limit kill, a
|
|
43115
|
+
* crash) is never current: handing it back fails the turn with
|
|
43116
|
+
* `AcpClient not started` instead of spawning a fresh one.
|
|
43024
43117
|
*/
|
|
43025
43118
|
async sessionIsCurrent() {
|
|
43119
|
+
if (!this.client?.isAlive)
|
|
43120
|
+
return false;
|
|
43026
43121
|
await this.refreshTurnCheckout();
|
|
43027
43122
|
return this.sessionCwd === this.options.cwd && this.sessionCommit === this.turnCheckout?.commit && await this.currentSessionFingerprint() === this.sessionFingerprint;
|
|
43028
43123
|
}
|
|
@@ -45128,8 +45223,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
45128
45223
|
}
|
|
45129
45224
|
/** See `MonolithRoomTurnLoop.sessionIsCurrent`: retention never keeps a
|
|
45130
45225
|
* session whose persona, model pin, or mounted MCP set the operator has
|
|
45131
|
-
* since changed. */
|
|
45226
|
+
* since changed, or whose harness process has exited. */
|
|
45132
45227
|
async sessionIsCurrent() {
|
|
45228
|
+
if (!this.client?.isAlive)
|
|
45229
|
+
return false;
|
|
45133
45230
|
return await this.currentSessionFingerprint() === this.sessionFingerprint;
|
|
45134
45231
|
}
|
|
45135
45232
|
async currentSessionFingerprint() {
|
|
@@ -45721,7 +45818,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
45721
45818
|
}),
|
|
45722
45819
|
this.roster(),
|
|
45723
45820
|
api.execute("getCornerRestoreState", { cornerId }),
|
|
45724
|
-
|
|
45821
|
+
this.activeReviewerInstruction(),
|
|
45725
45822
|
awaitInstitutionalContext(institutionalContextFetch, (message) => console.warn(`[thin-core] corner ${cornerId}: ${message}`)),
|
|
45726
45823
|
api.execute("listRoomCorners", { roomId: this.options.parentRoomId })
|
|
45727
45824
|
]));
|