usebeeline 0.1.10 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/usebeeline.mjs +179 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -96,6 +96,8 @@ A Room and a corner are the same conversation surface with different permissions
|
|
|
96
96
|
|
|
97
97
|
**A corner** is a fresh worktree, its own branch, one owning agent, and one fixed objective of at most 24 words. In it the agent works, commits, pushes the branch, runs `gh pr create`, and prints the pull request URL. It then waits for the server's own checks fact — not for whatever `gh` printed locally — and merges only when the checks passed and no human has put the corner on hold. The merge webhook archives the corner and reaps the worktree.
|
|
98
98
|
|
|
99
|
+
When Beeline reads that a Room's repository allows GitHub auto-merge, it warns a Workspace manager while they choose a reviewer and shows an inline notice when one is already set. The warning does not prevent setting a reviewer. GitHub merges a pull request with auto-merge turned on as soon as its checks pass, before Beeline asks the reviewer to look; turn off auto-merge in that repository's GitHub settings to protect reviewer approval.
|
|
100
|
+
|
|
99
101
|
The corner receives a GitHub App token scoped to **that one repository**, installed as a worktree-local git credential helper. Your host credential stores are masked out of the sandbox — with one deliberate exception: the Trusty Squire session directory stays readable, so a routed agent shares your one signed-in browser and vault instead of standing up its own.
|
|
100
102
|
|
|
101
103
|
**Direct messages** are strictly conversational: no repository binding, no corners.
|
package/dist/usebeeline.mjs
CHANGED
|
@@ -13834,10 +13834,10 @@ function squireLoginWallIn(value, depth = 0) {
|
|
|
13834
13834
|
if (!item)
|
|
13835
13835
|
return void 0;
|
|
13836
13836
|
const needsUser = record(item.needs_user);
|
|
13837
|
-
if (needsUser?.wall === "google_session") {
|
|
13837
|
+
if (needsUser?.wall === "google_session" || needsUser?.wall === "oauth_sign_in") {
|
|
13838
13838
|
const message = shortString(needsUser.message);
|
|
13839
13839
|
if (message)
|
|
13840
|
-
return { wall:
|
|
13840
|
+
return { wall: needsUser.wall, message };
|
|
13841
13841
|
}
|
|
13842
13842
|
const oauth = record(item.oauth);
|
|
13843
13843
|
if (oauth?.state === "awaiting_human") {
|
|
@@ -16756,8 +16756,10 @@ var StdioSquireMcpClient = class {
|
|
|
16756
16756
|
continue;
|
|
16757
16757
|
}
|
|
16758
16758
|
const id = typeof message.id === "number" ? message.id : void 0;
|
|
16759
|
-
if (id === void 0)
|
|
16759
|
+
if (id === void 0) {
|
|
16760
|
+
this.options.onNotification?.(message);
|
|
16760
16761
|
continue;
|
|
16762
|
+
}
|
|
16761
16763
|
const entry = this.pending.get(id);
|
|
16762
16764
|
if (!entry)
|
|
16763
16765
|
continue;
|
|
@@ -16845,6 +16847,7 @@ var SquireTaskRelay = class {
|
|
|
16845
16847
|
contextFile;
|
|
16846
16848
|
authorize;
|
|
16847
16849
|
makeClient;
|
|
16850
|
+
onApprovalDecided;
|
|
16848
16851
|
server;
|
|
16849
16852
|
url;
|
|
16850
16853
|
token = randomUUID3();
|
|
@@ -16860,13 +16863,16 @@ var SquireTaskRelay = class {
|
|
|
16860
16863
|
processGroup: true,
|
|
16861
16864
|
onSpawn: callbacks.onSpawn,
|
|
16862
16865
|
onExit: callbacks.onExit,
|
|
16863
|
-
log: callbacks.onDiagnostic
|
|
16864
|
-
|
|
16866
|
+
log: callbacks.onDiagnostic,
|
|
16867
|
+
onNotification: callbacks.onNotification
|
|
16868
|
+
}), onApprovalDecided = () => {
|
|
16869
|
+
}) {
|
|
16865
16870
|
this.agentId = agentId;
|
|
16866
16871
|
this.roomId = roomId;
|
|
16867
16872
|
this.contextFile = contextFile;
|
|
16868
16873
|
this.authorize = authorize;
|
|
16869
16874
|
this.makeClient = makeClient;
|
|
16875
|
+
this.onApprovalDecided = onApprovalDecided;
|
|
16870
16876
|
}
|
|
16871
16877
|
async listen() {
|
|
16872
16878
|
if (this.closed)
|
|
@@ -16992,6 +16998,7 @@ var SquireTaskRelay = class {
|
|
|
16992
16998
|
};
|
|
16993
16999
|
task.client = this.makeClient({
|
|
16994
17000
|
onSpawn: (pid) => this.log("relay-spawn", task, { pid }),
|
|
17001
|
+
onNotification: (message) => this.handleApprovalNotification(task, message),
|
|
16995
17002
|
onDiagnostic: (message) => {
|
|
16996
17003
|
if (message.startsWith("squire mcp exited"))
|
|
16997
17004
|
return;
|
|
@@ -17128,7 +17135,10 @@ var SquireTaskRelay = class {
|
|
|
17128
17135
|
if (pending && ids.length) {
|
|
17129
17136
|
task.pendingApprovals.set(approvalKey, {
|
|
17130
17137
|
requestId: active.requestId,
|
|
17131
|
-
sessionIds: new Set(ids)
|
|
17138
|
+
sessionIds: new Set(ids),
|
|
17139
|
+
tool: approval.tool,
|
|
17140
|
+
title: approval.title,
|
|
17141
|
+
detail: approval.detail
|
|
17132
17142
|
});
|
|
17133
17143
|
this.log("approval-pending", task, {
|
|
17134
17144
|
tool: safeToolName ?? null,
|
|
@@ -17169,6 +17179,37 @@ var SquireTaskRelay = class {
|
|
|
17169
17179
|
task.pendingApprovals.delete(id);
|
|
17170
17180
|
}
|
|
17171
17181
|
}
|
|
17182
|
+
/**
|
|
17183
|
+
* Squire's own unsolicited decision on an approval this task is still
|
|
17184
|
+
* holding a connection open for. Matched back to the pending entry by the
|
|
17185
|
+
* same hash `handleSerial` keyed it under (the approval id Squire itself
|
|
17186
|
+
* returned), so a notification for an approval this relay never tracked —
|
|
17187
|
+
* or one already released — is silently ignored, never trusted blind.
|
|
17188
|
+
*/
|
|
17189
|
+
handleApprovalNotification(task, message) {
|
|
17190
|
+
if (message.method !== "notifications/approval_decided")
|
|
17191
|
+
return;
|
|
17192
|
+
const params = message.params;
|
|
17193
|
+
const approvalId = params?.approval_id;
|
|
17194
|
+
const status = params?.status;
|
|
17195
|
+
if (typeof approvalId !== "string" || !approvalId || status !== "approved" && status !== "denied")
|
|
17196
|
+
return;
|
|
17197
|
+
const approvalKey = createHash("sha256").update(approvalId).digest("hex");
|
|
17198
|
+
const pending = task.pendingApprovals.get(approvalKey);
|
|
17199
|
+
if (!pending)
|
|
17200
|
+
return;
|
|
17201
|
+
task.pendingApprovals.delete(approvalKey);
|
|
17202
|
+
this.onApprovalDecided({
|
|
17203
|
+
requestId: pending.requestId,
|
|
17204
|
+
approvalId,
|
|
17205
|
+
status,
|
|
17206
|
+
tool: pending.tool,
|
|
17207
|
+
title: pending.title,
|
|
17208
|
+
detail: pending.detail
|
|
17209
|
+
});
|
|
17210
|
+
if (!this.active && !this.approvalRequestId && !task.pendingApprovals.size)
|
|
17211
|
+
this.scheduleIdle();
|
|
17212
|
+
}
|
|
17172
17213
|
};
|
|
17173
17214
|
function runSquireTaskProxy(env = process.env) {
|
|
17174
17215
|
const url = env.BEELINE_SQUIRE_RELAY_URL;
|
|
@@ -18013,6 +18054,7 @@ var SERVER_EVENT_KINDS = [
|
|
|
18013
18054
|
"check-failed",
|
|
18014
18055
|
"merged",
|
|
18015
18056
|
"grant-decided",
|
|
18057
|
+
"squire-approval-decided",
|
|
18016
18058
|
"connector-offer-decided",
|
|
18017
18059
|
"turn-cancelled",
|
|
18018
18060
|
"choice-answered",
|
|
@@ -18020,6 +18062,25 @@ var SERVER_EVENT_KINDS = [
|
|
|
18020
18062
|
"poll-closed",
|
|
18021
18063
|
"workflow-handoff"
|
|
18022
18064
|
];
|
|
18065
|
+
var SERVER_EVENT_KIND_DETAIL = {
|
|
18066
|
+
"joined": "fires when a person or agent joins this Room, or when a Workspace arrival projects into it",
|
|
18067
|
+
"schedule-ran": "fires when a schedule you created runs",
|
|
18068
|
+
"corner-opened": "fires when a corner opens under this Room",
|
|
18069
|
+
"check-passed": "fires when a corner's pull request checks turn green",
|
|
18070
|
+
"check-failed": "fires when a corner's pull request checks turn red",
|
|
18071
|
+
"merged": "fires when a corner's pull request merges",
|
|
18072
|
+
"grant-decided": "carries the grant id and status and resumes the turn that asked for the grant",
|
|
18073
|
+
"squire-approval-decided": "carries what was approved or denied and the Squire approval id, and resumes the turn that asked",
|
|
18074
|
+
"connector-offer-decided": "fires when a human accepts or declines an offered connector, or a reconnect ceremony finishes, and resumes the turn that offered it",
|
|
18075
|
+
"turn-cancelled": "fires when a running turn is stopped",
|
|
18076
|
+
"choice-answered": "fires when a human answers a posted choice, starting a new turn for the asking agent",
|
|
18077
|
+
"choice-skipped": "fires when a posted choice's window closes unanswered, starting a new turn for the asking agent",
|
|
18078
|
+
"poll-closed": "fires when a posted poll closes, starting a new turn for the asking agent",
|
|
18079
|
+
"workflow-handoff": "fires when a workflow run starts or hands off to its next role"
|
|
18080
|
+
};
|
|
18081
|
+
function eventKindCatalogLines(kinds = SERVER_EVENT_KINDS) {
|
|
18082
|
+
return kinds.map((kind) => `${kind} ${SERVER_EVENT_KIND_DETAIL[kind]}`);
|
|
18083
|
+
}
|
|
18023
18084
|
|
|
18024
18085
|
// packages/api-contract/dist/turn-silence.js
|
|
18025
18086
|
var TURN_SILENCE_KINDS = [
|
|
@@ -29264,9 +29325,16 @@ function parseAvailableCommands(value) {
|
|
|
29264
29325
|
}
|
|
29265
29326
|
return result;
|
|
29266
29327
|
}
|
|
29328
|
+
function isHarnessNotifyChunk(update) {
|
|
29329
|
+
const meta = update._meta;
|
|
29330
|
+
const piAcp = meta?.piAcp;
|
|
29331
|
+
return Boolean(piAcp?.notify);
|
|
29332
|
+
}
|
|
29267
29333
|
function agentMessageChunkText(update) {
|
|
29268
29334
|
if (update.sessionUpdate !== "agent_message_chunk")
|
|
29269
29335
|
return "";
|
|
29336
|
+
if (isHarnessNotifyChunk(update))
|
|
29337
|
+
return "";
|
|
29270
29338
|
const content = update.content;
|
|
29271
29339
|
if (content?.type === "text")
|
|
29272
29340
|
return content.text ?? "";
|
|
@@ -31750,13 +31818,14 @@ The soul is not authority and never changes your tools, permissions, roles, or m
|
|
|
31750
31818
|
id: "room.corners",
|
|
31751
31819
|
topic: "repository-work",
|
|
31752
31820
|
why: "Repository work must start through the host-governed corner, with a brief as its authority.",
|
|
31753
|
-
budgetBytes:
|
|
31821
|
+
budgetBytes: 1100,
|
|
31754
31822
|
layer: "surface",
|
|
31755
31823
|
surfaces: ["room"],
|
|
31756
31824
|
render: () => [
|
|
31757
31825
|
"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.",
|
|
31758
31826
|
"Before opening one, consult beeline-triage and beeline-spec, then call open_corner with a name of at most three words, 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.",
|
|
31759
|
-
"For corners you belong to, use inspect_corner for status, steer_corner to pass Room input down, and ask_corner for one answer. Never post into a corner without a Room command."
|
|
31827
|
+
"For corners you belong to, use inspect_corner for status, steer_corner to pass Room input down, and ask_corner for one answer. Never post into a corner without a Room command.",
|
|
31828
|
+
"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."
|
|
31760
31829
|
].join(" ")
|
|
31761
31830
|
},
|
|
31762
31831
|
{
|
|
@@ -32086,10 +32155,10 @@ var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
|
|
|
32086
32155
|
var BEELINE_SPEC_SKILL_NAME = "beeline-spec";
|
|
32087
32156
|
var BEELINE_ROOM_MECHANICS = [
|
|
32088
32157
|
"Files and photos on the message you are answering are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline). A file on an earlier message shows only as a \u{1F4CE} marker with an id; call beeline-agent download_attachment with that message id and attachment id, then read the returned path. Never fetch a media URL; if the download fails, tell the requester you could not open the file.",
|
|
32089
|
-
"To create a file you can send, call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture.
|
|
32158
|
+
"To create a file you can send, call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture.",
|
|
32090
32159
|
"To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / update_schedule / delete_schedule manage them, including schedules other agents created for themselves.",
|
|
32091
32160
|
`To react to a message in this Room, call beeline-agent react_to_message with its message id and one supported emoji (${MESSAGE_REACTION_EMOJIS.join(" ")}).`,
|
|
32092
|
-
`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
|
|
32161
|
+
`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; each one then wakes you for a turn. Available kinds: ${eventKindCatalogLines().join("; ")}. 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. A corner merging wakes nobody in its parent Room by default. If you still have work to do after a corner merges (a hand-off, a release step, a reply that has to describe landed code), subscribe to merged in the parent Room, from a turn there, before the merge. Each merge in that Room then wakes you and names the corner and pull request: act only on the corner you are waiting for, post nothing for any other merge, and take merged back out of your list once nothing is left to wait for. Never post just to acknowledge a merge: the merge card already announces it. If you are waiting on a human decision - a grant, a connector offer, a posted choice or poll, or a Squire approval (payment, credential, sign-in, or vouch) - subscribe to its kind instead of asking them to type a confirmation in the Room; the decision wakes you with the answer.`,
|
|
32093
32162
|
"If you need reach outside the sandbox, call beeline-agent request_grant. If you already know discrete options, call beeline-agent ask_choice (one human, optional) or open_poll (every human in this Room, required deadline). A poll is refused below two electors and above fifty, and in a DM. A plurality is a fact, never permission to deploy, delete, merge, or spend. Open-ended asks stay tagged prose. Never put Always / Once / No on a preference.",
|
|
32094
32163
|
"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."
|
|
32095
32164
|
].join(" ");
|
|
@@ -32126,6 +32195,10 @@ A **tool** is something you can use once a human adds it; a **key** is the crede
|
|
|
32126
32195
|
|
|
32127
32196
|
The tools this build knows, what each is for, and which the owner of this machine already has are one call away: beeline-agent workbench_status. That read is the Workbench of 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 is vaulted credentials plus a browser for the separate key and browser routes. 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 Workbench 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.
|
|
32128
32197
|
|
|
32198
|
+
**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.
|
|
32199
|
+
|
|
32200
|
+
**Use available tools before asking the person.** Before asking a person for a code, a status or a file, call workbench_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.
|
|
32201
|
+
|
|
32129
32202
|
**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 workbench_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. 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.
|
|
32130
32203
|
|
|
32131
32204
|
**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.
|
|
@@ -32136,9 +32209,9 @@ Keys belong to the human who provisioned them. Your owner's keys serve whoever a
|
|
|
32136
32209
|
|
|
32137
32210
|
## Connect an app
|
|
32138
32211
|
|
|
32139
|
-
When a request needs an app or service, call beeline-agent \`workbench_status\` first. For a connected app, use its ID with \`list_app_tools\`, then call \`execute_app_tool\` with the selected tool slug and arguments. The server executes against that person\u2019s account and decides permission on every call.
|
|
32212
|
+
When a request needs an app or service, call beeline-agent \`workbench_status\` first. For a connected app, use its ID with \`list_app_tools\`, then call \`execute_app_tool\` with the selected tool slug and arguments. 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.
|
|
32140
32213
|
|
|
32141
|
-
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. If the provider reports managed OAuth unsupported, follow the server's Squire route and its instructions. A provider outage is unavailable, not permission to change routes. After the person connects, read Workbench again and use the app ID.
|
|
32214
|
+
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. If the provider reports managed OAuth unsupported, follow the server's Squire route and its instructions. A provider outage is unavailable, not permission to change routes. After the person connects, read Workbench again and use the app ID. Every use is authorized as \`app:<key>\` and recorded once.
|
|
32142
32215
|
|
|
32143
32216
|
## Showing a mock
|
|
32144
32217
|
|
|
@@ -37991,7 +38064,7 @@ import { mkdir as mkdir28, rename as rename12, stat as stat9, writeFile as write
|
|
|
37991
38064
|
import { dirname as dirname29 } from "node:path";
|
|
37992
38065
|
|
|
37993
38066
|
// apps/body/dist/agent-runtime.js
|
|
37994
|
-
import { homedir as
|
|
38067
|
+
import { homedir as homedir19 } from "node:os";
|
|
37995
38068
|
import { dirname as dirname28, join as join19, resolve as resolve41 } from "node:path";
|
|
37996
38069
|
|
|
37997
38070
|
// apps/body/dist/model-availability.js
|
|
@@ -39549,7 +39622,7 @@ async function runServerCommandIntake(options) {
|
|
|
39549
39622
|
import { execFile as execFile12 } from "node:child_process";
|
|
39550
39623
|
import { createHash as createHash12 } from "node:crypto";
|
|
39551
39624
|
import { mkdir as mkdir25, readFile as readFile27, writeFile as writeFile20 } from "node:fs/promises";
|
|
39552
|
-
import { homedir as
|
|
39625
|
+
import { homedir as homedir18, hostname as hostname3 } from "node:os";
|
|
39553
39626
|
import { dirname as dirname25, join as join18, resolve as resolve35 } from "node:path";
|
|
39554
39627
|
import { promisify as promisify7 } from "node:util";
|
|
39555
39628
|
|
|
@@ -39889,6 +39962,7 @@ var AgentTurnStream = class {
|
|
|
39889
39962
|
* one bounds the lane at ONE write in flight plus ONE waiting.
|
|
39890
39963
|
*/
|
|
39891
39964
|
pending;
|
|
39965
|
+
latestChunk = "";
|
|
39892
39966
|
/**
|
|
39893
39967
|
* The stream as it stood when a corner handed everything up to that point to
|
|
39894
39968
|
* its durable work ledger — this turn's persisted stream offset. Held as the
|
|
@@ -39920,6 +39994,7 @@ var AgentTurnStream = class {
|
|
|
39920
39994
|
* it has.
|
|
39921
39995
|
*/
|
|
39922
39996
|
onChunk = (_delta, full, currentRun) => {
|
|
39997
|
+
this.latestChunk = sanitizeAgentReply(currentRun ?? "");
|
|
39923
39998
|
this.latest = full;
|
|
39924
39999
|
this.latestRun = currentRun ?? "";
|
|
39925
40000
|
const text2 = sanitizeAgentReply(this.tailOf(full));
|
|
@@ -40001,9 +40076,10 @@ var AgentTurnStream = class {
|
|
|
40001
40076
|
if (this.inFlight || this.pending === void 0)
|
|
40002
40077
|
return;
|
|
40003
40078
|
const text2 = this.pending;
|
|
40079
|
+
const latestChunk = this.latestChunk;
|
|
40004
40080
|
this.pending = void 0;
|
|
40005
40081
|
const { api, agentId, roomId, requestId, label } = this.options;
|
|
40006
|
-
this.inFlight = api.execute("postAgentDraft", { agentId, roomId, turnId: requestId, text: text2 }).then(() => void 0).catch((error) => console.error(`[thin-core] ${label} draft publish failed:`, error)).then(() => {
|
|
40082
|
+
this.inFlight = api.execute("postAgentDraft", { agentId, roomId, turnId: requestId, text: text2, latestChunk }).then(() => void 0).catch((error) => console.error(`[thin-core] ${label} draft publish failed:`, error)).then(() => {
|
|
40007
40083
|
this.inFlight = void 0;
|
|
40008
40084
|
this.publishPending();
|
|
40009
40085
|
});
|
|
@@ -40031,6 +40107,7 @@ var AgentTurnStream = class {
|
|
|
40031
40107
|
beginRun() {
|
|
40032
40108
|
this.latest = "";
|
|
40033
40109
|
this.latestRun = "";
|
|
40110
|
+
this.latestChunk = "";
|
|
40034
40111
|
this.persisted = "";
|
|
40035
40112
|
this.pending = void 0;
|
|
40036
40113
|
}
|
|
@@ -40188,8 +40265,10 @@ import { promisify as promisify5 } from "node:util";
|
|
|
40188
40265
|
var PI_MCP_BRIDGE_FILENAME = "beeline-mcp-bridge.js";
|
|
40189
40266
|
var execFileAsync5 = promisify5(execFile9);
|
|
40190
40267
|
var nativeMcpProbes = /* @__PURE__ */ new Map();
|
|
40191
|
-
async function piHasNativeMcp(command) {
|
|
40192
|
-
|
|
40268
|
+
async function piHasNativeMcp(command, env) {
|
|
40269
|
+
const path = env?.PATH ?? process.env.PATH ?? "";
|
|
40270
|
+
const cacheKey = `${command}\0${path}`;
|
|
40271
|
+
let probe = nativeMcpProbes.get(cacheKey);
|
|
40193
40272
|
if (!probe) {
|
|
40194
40273
|
probe = (async () => {
|
|
40195
40274
|
const home = await mkdtemp2(resolve28(tmpdir5(), "beeline-pi-mcp-probe-"));
|
|
@@ -40197,7 +40276,7 @@ async function piHasNativeMcp(command) {
|
|
|
40197
40276
|
const { stdout: stdout6 } = await execFileAsync5(command, ["mcp", "list", "--json"], {
|
|
40198
40277
|
cwd: home,
|
|
40199
40278
|
timeout: 1200,
|
|
40200
|
-
env: { PATH:
|
|
40279
|
+
env: { PATH: path, HOME: home, PI_CODING_AGENT_DIR: home, PI_OFFLINE: "1" }
|
|
40201
40280
|
});
|
|
40202
40281
|
const result = JSON.parse(stdout6);
|
|
40203
40282
|
return Array.isArray(result.servers) && Array.isArray(result.errors);
|
|
@@ -40207,7 +40286,7 @@ async function piHasNativeMcp(command) {
|
|
|
40207
40286
|
await rm11(home, { recursive: true, force: true });
|
|
40208
40287
|
}
|
|
40209
40288
|
})();
|
|
40210
|
-
nativeMcpProbes.set(
|
|
40289
|
+
nativeMcpProbes.set(cacheKey, probe);
|
|
40211
40290
|
}
|
|
40212
40291
|
return probe;
|
|
40213
40292
|
}
|
|
@@ -40271,7 +40350,7 @@ async function installPiMcpBridge(input) {
|
|
|
40271
40350
|
const path = resolve28(directory, PI_MCP_BRIDGE_FILENAME);
|
|
40272
40351
|
try {
|
|
40273
40352
|
const config = await isolatedPiConfig(input.piHome);
|
|
40274
|
-
const nativeMcp = input.nativeMcp ?? await piHasNativeMcp(input.piCommand ?? "pi");
|
|
40353
|
+
const nativeMcp = input.nativeMcp ?? await piHasNativeMcp(input.piCommand ?? "pi", input.agentEnv);
|
|
40275
40354
|
if (nativeMcp) {
|
|
40276
40355
|
const mcpServers2 = { ...config.mcpServers };
|
|
40277
40356
|
for (const server of input.servers)
|
|
@@ -40602,6 +40681,7 @@ async function contains(git, head, ancestor) {
|
|
|
40602
40681
|
|
|
40603
40682
|
// apps/body/dist/room-session.js
|
|
40604
40683
|
import { resolve as resolve30 } from "node:path";
|
|
40684
|
+
import { homedir as homedir16 } from "node:os";
|
|
40605
40685
|
|
|
40606
40686
|
// apps/body/dist/read-only-policy.js
|
|
40607
40687
|
var READ_ONLY_MCP_SERVER_NAME = "beeline-readonly-mcp";
|
|
@@ -40810,6 +40890,7 @@ function beelineAgentMcpServer(config, api, context) {
|
|
|
40810
40890
|
{ name: "BEELINE_DAEMON_TOKEN", value: connection.daemonToken },
|
|
40811
40891
|
{ name: "BEELINE_HELPER_VERSION", value: connection.helperVersion },
|
|
40812
40892
|
{ name: "BEELINE_DAEMON_AGENT_ID", value: connection.agentId },
|
|
40893
|
+
{ name: "BEELINE_OPERATOR_HOME", value: config.operatorHome ?? homedir16() },
|
|
40813
40894
|
{ name: "BEELINE_DAEMON_ROOM_ID", value: context.roomId },
|
|
40814
40895
|
{ name: "BEELINE_DAEMON_WORKSPACE_ID", value: context.workspaceId },
|
|
40815
40896
|
...context.cornerId ? [{ name: "BEELINE_DAEMON_CORNER_ID", value: context.cornerId }] : [],
|
|
@@ -40979,7 +41060,7 @@ async function prepareCodegraphIndex(config, cwd) {
|
|
|
40979
41060
|
|
|
40980
41061
|
// apps/body/dist/monolith-room-turn.js
|
|
40981
41062
|
import { mkdir as mkdir22, writeFile as writeFile18 } from "node:fs/promises";
|
|
40982
|
-
import { homedir as
|
|
41063
|
+
import { homedir as homedir17, hostname as hostname2 } from "node:os";
|
|
40983
41064
|
import { dirname as dirname23, join as join16 } from "node:path";
|
|
40984
41065
|
|
|
40985
41066
|
// packages/api-contract/dist/scheduled-prompts.js
|
|
@@ -41427,6 +41508,14 @@ function deviceGrantResumePrompt(device, earlierReply) {
|
|
|
41427
41508
|
].join(" ");
|
|
41428
41509
|
}
|
|
41429
41510
|
function resumePrompt(item) {
|
|
41511
|
+
if (item.systemEvent?.kind === "squire-approval-decided") {
|
|
41512
|
+
return [
|
|
41513
|
+
`This is Trusty Squire's answer to the approval you were waiting on: ${item.body}.`,
|
|
41514
|
+
"Your paused work resumes now.",
|
|
41515
|
+
"If it was approved, continue exactly where you left off with Trusty Squire; do not ask the person to approve again, and do not restart the task.",
|
|
41516
|
+
"If it was denied, stop that Trusty Squire action and say plainly what you cannot do."
|
|
41517
|
+
].join(" ");
|
|
41518
|
+
}
|
|
41430
41519
|
if (item.systemEvent?.kind === "connector-offer-decided") {
|
|
41431
41520
|
return [
|
|
41432
41521
|
`This is the answer to your connector offer: ${item.body}.`,
|
|
@@ -41525,7 +41614,10 @@ var MonolithRoomTurnLoop = class {
|
|
|
41525
41614
|
generationId: call.generationId,
|
|
41526
41615
|
target: "squire",
|
|
41527
41616
|
...resourceCallFacts({ method: "tools/call", params: { name: call.tool, arguments: call.args } }, "squire")
|
|
41528
|
-
})).allowed, options.config.operatorHome ??
|
|
41617
|
+
})).allowed, options.config.operatorHome ?? homedir17(), void 0, (decision2) => {
|
|
41618
|
+
options.api.execute("postSquireApprovalDecision", { roomId: options.roomId, ...decision2 }).catch(() => {
|
|
41619
|
+
});
|
|
41620
|
+
});
|
|
41529
41621
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
41530
41622
|
options.grantRunner?.register(options.roomId, {
|
|
41531
41623
|
workspaceId: options.workspaceId,
|
|
@@ -41560,7 +41652,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
41560
41652
|
...this.sessionScratchDir ? { scratch: this.sessionScratchDir } : {},
|
|
41561
41653
|
...this.sessionStateDirs.length ? { harnessStateDirs: this.sessionStateDirs } : {},
|
|
41562
41654
|
maskPaths: [
|
|
41563
|
-
...credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ??
|
|
41655
|
+
...credentialMaskPaths(this.options.config.sandboxMaskPaths, this.options.config.operatorHome ?? homedir17()),
|
|
41564
41656
|
...siblingAgentMaskPaths(runtimeDirectory(this.options.runtime.supervisorRoot, this.options.runtime.agent.publicKey))
|
|
41565
41657
|
]
|
|
41566
41658
|
};
|
|
@@ -41808,7 +41900,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
41808
41900
|
effort: selectionEffort,
|
|
41809
41901
|
fastMode: configuration.fastMode
|
|
41810
41902
|
};
|
|
41811
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
41903
|
+
const operatorHome = this.options.config.operatorHome ?? homedir17();
|
|
41812
41904
|
const registryHostDeclarations = registryMcpHostDeclarations(configuration.registryMcpRoutes, this.commandContext.path, this.options.config.registryMcpBrokerSocket);
|
|
41813
41905
|
const mountedHostRoutes = [...grantedHostRoutes, ...Object.keys(registryHostDeclarations)];
|
|
41814
41906
|
const squireScope = {
|
|
@@ -41942,6 +42034,7 @@ var MonolithRoomTurnLoop = class {
|
|
|
41942
42034
|
agentCommand: harnessLabel,
|
|
41943
42035
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
41944
42036
|
piCommand: agentEnv.PI_ACP_PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND,
|
|
42037
|
+
agentEnv,
|
|
41945
42038
|
servers: [...servers, ...grantedRouteServers]
|
|
41946
42039
|
});
|
|
41947
42040
|
const mountedServers = [
|
|
@@ -42693,7 +42786,7 @@ function cornerGitHubCommandRefusal(launcher, argv, featureBranch, targetBranch,
|
|
|
42693
42786
|
}
|
|
42694
42787
|
async function writeLauncher(path, config) {
|
|
42695
42788
|
const source = `#!/usr/bin/env node
|
|
42696
|
-
import { spawnSync } from 'node:child_process';
|
|
42789
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
42697
42790
|
const config = ${JSON.stringify(config)};
|
|
42698
42791
|
const cornerGitHubCommandRefusal = ${cornerGitHubCommandRefusal.toString()};
|
|
42699
42792
|
const authFailure = /(?:authentication failed|bad credentials|could not read username|http(?:\\/\\d(?:\\.\\d)?)? 40[13]|status (?:code )?40[13])/i;
|
|
@@ -42708,7 +42801,28 @@ function resolvePushBranch(source) {
|
|
|
42708
42801
|
return current.status === 0 ? { branch: current.stdout.trim() } : {};
|
|
42709
42802
|
}
|
|
42710
42803
|
const refusal = cornerGitHubCommandRefusal(config.launcher, process.argv.slice(2), config.featureBranch, config.targetBranch, resolvePushBranch);
|
|
42711
|
-
if (refusal) { process.stderr.write(refusal + '\\n'); process.
|
|
42804
|
+
if (refusal) { process.stderr.write(refusal + '\\n'); process.exitCode = 1; }
|
|
42805
|
+
function needsToken(argv) {
|
|
42806
|
+
if (config.launcher === 'gh') return true;
|
|
42807
|
+
const optionsWithValue = new Set(['-C', '-c', '--git-dir', '--work-tree', '--namespace', '--super-prefix', '--config-env']);
|
|
42808
|
+
let index = 0;
|
|
42809
|
+
while (index < argv.length && argv[index].startsWith('-')) {
|
|
42810
|
+
if (argv[index] === '--version' || argv[index] === '--help') return false;
|
|
42811
|
+
index += optionsWithValue.has(argv[index]) ? 2 : 1;
|
|
42812
|
+
}
|
|
42813
|
+
// Unknown commands (including aliases) may contact a remote.
|
|
42814
|
+
const local = new Set([
|
|
42815
|
+
'add', 'am', 'apply', 'bisect', 'blame', 'branch', 'cat-file', 'check-attr',
|
|
42816
|
+
'check-ignore', 'check-ref-format', 'checkout', 'cherry-pick', 'clean', 'commit',
|
|
42817
|
+
'config', 'describe', 'diff', 'diff-files', 'diff-index', 'diff-tree', 'for-each-ref',
|
|
42818
|
+
'format-patch', 'fsck', 'gc', 'grep', 'hash-object', 'help', 'init', 'log', 'ls-files',
|
|
42819
|
+
'ls-tree', 'merge', 'merge-base', 'mv', 'notes', 'rebase', 'reflog', 'reset',
|
|
42820
|
+
'restore', 'rev-list', 'rev-parse', 'revert', 'rm', 'show', 'show-ref', 'stash',
|
|
42821
|
+
'status', 'switch', 'symbolic-ref', 'tag', 'update-index', 'update-ref',
|
|
42822
|
+
'verify-commit', 'verify-tag', 'worktree',
|
|
42823
|
+
]);
|
|
42824
|
+
return !local.has(argv[index]);
|
|
42825
|
+
}
|
|
42712
42826
|
function token() {
|
|
42713
42827
|
let result;
|
|
42714
42828
|
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
@@ -42717,19 +42831,40 @@ function token() {
|
|
|
42717
42831
|
if (attempt < 2) Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 250 * (attempt + 1));
|
|
42718
42832
|
}
|
|
42719
42833
|
process.stderr.write(result.stderr || 'Beeline could not refresh the repository credential.\\n');
|
|
42720
|
-
process.
|
|
42834
|
+
process.exitCode = result.status || 1;
|
|
42835
|
+
return undefined;
|
|
42721
42836
|
}
|
|
42722
|
-
function run(value) {
|
|
42837
|
+
async function run(value) {
|
|
42723
42838
|
const env = { ...process.env, GH_TOKEN: value, GITHUB_TOKEN: value, GIT_TERMINAL_PROMPT: '0' };
|
|
42724
|
-
|
|
42725
|
-
|
|
42726
|
-
|
|
42727
|
-
const
|
|
42728
|
-
|
|
42729
|
-
|
|
42730
|
-
|
|
42731
|
-
|
|
42732
|
-
|
|
42839
|
+
const child = spawn(config.command, process.argv.slice(2), { env, stdio: ['inherit', 'pipe', 'pipe'] });
|
|
42840
|
+
let authenticationFailed = false;
|
|
42841
|
+
// Keep only enough diagnostic overlap to recognize an auth failure across chunks.
|
|
42842
|
+
for (const [source, destination] of [[child.stdout, process.stdout], [child.stderr, process.stderr]]) {
|
|
42843
|
+
let tail = '';
|
|
42844
|
+
source.on('data', (chunk) => {
|
|
42845
|
+
const diagnostic = tail + chunk.toString('utf8');
|
|
42846
|
+
authenticationFailed ||= authFailure.test(diagnostic);
|
|
42847
|
+
tail = diagnostic.slice(-1024);
|
|
42848
|
+
});
|
|
42849
|
+
source.pipe(destination, { end: false });
|
|
42850
|
+
}
|
|
42851
|
+
return new Promise((resolve, reject) => {
|
|
42852
|
+
child.once('error', reject);
|
|
42853
|
+
child.once('close', (status) => resolve({ status, authenticationFailed }));
|
|
42854
|
+
});
|
|
42855
|
+
}
|
|
42856
|
+
if (!refusal) {
|
|
42857
|
+
const authenticated = needsToken(process.argv.slice(2));
|
|
42858
|
+
const value = authenticated ? token() : '';
|
|
42859
|
+
if (value !== undefined) {
|
|
42860
|
+
let result = await run(value);
|
|
42861
|
+
if (authenticated && result.status !== 0 && result.authenticationFailed) {
|
|
42862
|
+
const refreshed = token();
|
|
42863
|
+
if (refreshed !== undefined) result = await run(refreshed);
|
|
42864
|
+
}
|
|
42865
|
+
process.exitCode ??= result.status ?? 1;
|
|
42866
|
+
}
|
|
42867
|
+
}
|
|
42733
42868
|
`;
|
|
42734
42869
|
await writeFile19(path, source, { mode: 448 });
|
|
42735
42870
|
await chmod9(path, 448);
|
|
@@ -43327,7 +43462,10 @@ var MonolithCornerTurnLoop = class {
|
|
|
43327
43462
|
generationId: call.generationId,
|
|
43328
43463
|
target: "squire",
|
|
43329
43464
|
...resourceCallFacts({ method: "tools/call", params: { name: call.tool, arguments: call.args } }, "squire")
|
|
43330
|
-
})).allowed, options.config.operatorHome ??
|
|
43465
|
+
})).allowed, options.config.operatorHome ?? homedir18(), void 0, (decision2) => {
|
|
43466
|
+
options.api.execute("postSquireApprovalDecision", { roomId: options.cornerId, ...decision2 }).catch(() => {
|
|
43467
|
+
});
|
|
43468
|
+
});
|
|
43331
43469
|
this.options = { ...options, api: this.commandContext.bind(options.api) };
|
|
43332
43470
|
options.grantRunner?.register(options.cornerId, {
|
|
43333
43471
|
workspaceId: options.workspaceId,
|
|
@@ -43542,7 +43680,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
43542
43680
|
effort: configuration.effort ?? this.options.config.modelSelection?.effort,
|
|
43543
43681
|
fastMode: configuration.fastMode
|
|
43544
43682
|
};
|
|
43545
|
-
const operatorHome = this.options.config.operatorHome ??
|
|
43683
|
+
const operatorHome = this.options.config.operatorHome ?? homedir18();
|
|
43546
43684
|
const registryHostDeclarations = registryMcpHostDeclarations(configuration.registryMcpRoutes, this.commandContext.path, this.options.config.registryMcpBrokerSocket);
|
|
43547
43685
|
const mountedHostRoutes = [...grantedHostRoutes, ...Object.keys(registryHostDeclarations)];
|
|
43548
43686
|
const squireScope = {
|
|
@@ -43755,6 +43893,7 @@ var MonolithCornerTurnLoop = class {
|
|
|
43755
43893
|
agentCommand: harnessLabel,
|
|
43756
43894
|
piHome: agentEnv.PI_CODING_AGENT_DIR,
|
|
43757
43895
|
piCommand: agentEnv.PI_ACP_PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND,
|
|
43896
|
+
agentEnv,
|
|
43758
43897
|
servers: [...servers, ...grantedRouteServers]
|
|
43759
43898
|
});
|
|
43760
43899
|
const persona = configuration.soul ?? self?.soul;
|
|
@@ -47173,7 +47312,7 @@ async function runAgentRuntime(configPath, host, signal) {
|
|
|
47173
47312
|
return exitReason ?? lifecycle.stopReason ?? "stopped";
|
|
47174
47313
|
}
|
|
47175
47314
|
function registryBrokerSocketPath(home, agentId) {
|
|
47176
|
-
return join19(home ??
|
|
47315
|
+
return join19(home ?? homedir19(), ".beeline", "registry-mcp-broker", `${process.pid}-${agentId.slice(0, 8)}.sock`);
|
|
47177
47316
|
}
|
|
47178
47317
|
function interruptHostedAgent(agent) {
|
|
47179
47318
|
agent.lifecycle.quiesce("force-update");
|