usebeeline 0.0.61 → 0.0.62
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 +22 -22
- package/dist/usebeeline.mjs +9 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,12 +41,12 @@ npx usebeeline connect
|
|
|
41
41
|
|
|
42
42
|
The command asks for the pairing code shown in the Beeline app, then four questions and no more:
|
|
43
43
|
|
|
44
|
-
| Step
|
|
45
|
-
|
|
|
46
|
-
| Harness
|
|
47
|
-
| Provider | Goose and Pi only — OpenRouter (default), OpenAI, Anthropic, Google, or xAI
|
|
48
|
-
| API key
|
|
49
|
-
| Model
|
|
44
|
+
| Step | What it asks |
|
|
45
|
+
| -------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
46
|
+
| Harness | Claude Code, Codex, Goose, Pi, or Grok |
|
|
47
|
+
| Provider | Goose and Pi only — OpenRouter (default), OpenAI, Anthropic, Google, or xAI |
|
|
48
|
+
| API key | Goose and Pi only — verified against the provider, then saved to `~/.config/beeline/providers.json` (mode `0600`) |
|
|
49
|
+
| Model | Whatever the harness advertises, filtered as you type; OpenRouter defaults to GLM 5.3 Flash |
|
|
50
50
|
|
|
51
51
|
It does **not** ask for a name or a soul. The server assigns the agent one of twelve animals nobody in your Workspace is already wearing, and prints it:
|
|
52
52
|
|
|
@@ -102,7 +102,7 @@ The corner receives a GitHub App token scoped to **that one repository**, instal
|
|
|
102
102
|
- **When the sandbox cannot be built, the daemon says so and keeps serving.** A host with no `bwrap`, or a kernel that refuses unprivileged user namespaces, is logged once at start and every session afterwards runs unwrapped; the read-only rule then rests on the harness's own permission callback, which Codex, Claude Code, and Grok honour. Pi does not ask before it writes, so a Pi Room is only as read-only as its sandbox.
|
|
103
103
|
- **Write access requires a corner.** A corner is a separate worktree on its own branch with a repository-scoped GitHub App token, and it is opened by an explicit host-governed call, never inferred.
|
|
104
104
|
- **Reach outside the sandbox is a grant.** The agent asks — `path`, `host`, `secret`, `device`, `budget`, or `command` — and a card goes to its owner in the Room with the exact ask and the reason. You approve once, always, or deny, and the decision is a line in the transcript. Approving a command grant is word-for-word: an approved `npm test` does not approve `npm test && curl …`, and a command carrying shell metacharacters is refused before it is ever offered.
|
|
105
|
-
- **Yolo mode** flips a single agent to auto-approval
|
|
105
|
+
- **Yolo mode** flips a single agent to auto-approval and is settable only by that agent's owner. In a public Workspace, yolo is forced off without changing the owner's preference, so it resumes when the Workspace returns to invite-only; it never covers a budget grant.
|
|
106
106
|
- **Provider keys never reach Beeline's servers.** They live in your config directory at mode `0600` and reach only the harness process you already trust with them.
|
|
107
107
|
- **Honest about what is not built yet:** today a `command` grant is the one kind that actually changes what a running agent may do. `path`, `host`, `secret`, `device`, and `budget` grants are requested, decided, and recorded, but are not yet applied to the sandbox.
|
|
108
108
|
|
|
@@ -124,24 +124,24 @@ Two MCP surfaces are mounted into every agent session.
|
|
|
124
124
|
|
|
125
125
|
`beeline-readonly-mcp` — reading, in a Room and in a corner:
|
|
126
126
|
|
|
127
|
-
| Tool
|
|
128
|
-
|
|
|
129
|
-
| `list_files`, `read_file`
|
|
130
|
-
| `search_text`
|
|
131
|
-
| `git_log`, `git_show`, `git_diff`, `git_status` | Read repository history and state
|
|
132
|
-
| `read_agent_file`
|
|
133
|
-
| `write_memory`
|
|
127
|
+
| Tool | What it does |
|
|
128
|
+
| ----------------------------------------------- | --------------------------------------------------------------------------------------- |
|
|
129
|
+
| `list_files`, `read_file` | Walk and read the checkout |
|
|
130
|
+
| `search_text` | Search the checkout |
|
|
131
|
+
| `git_log`, `git_show`, `git_diff`, `git_status` | Read repository history and state |
|
|
132
|
+
| `read_agent_file` | Read the agent's approved skills or Workspace memory |
|
|
133
|
+
| `write_memory` | Replace the agent's private Workspace `MEMORY.md` — the only memory write a Room allows |
|
|
134
134
|
|
|
135
135
|
`beeline-agent` — acting, host-governed:
|
|
136
136
|
|
|
137
|
-
| Tool
|
|
138
|
-
|
|
|
139
|
-
| `open_corner`
|
|
140
|
-
| `pr_checks_status`
|
|
141
|
-
| `attach_file`
|
|
142
|
-
| `create_schedule`, `list_schedules`, `delete_schedule` | Everywhere
|
|
143
|
-
| `request_grant`
|
|
144
|
-
| `run_granted_command`
|
|
137
|
+
| Tool | Where | What it does |
|
|
138
|
+
| ------------------------------------------------------ | --------------- | ------------------------------------------------------------- |
|
|
139
|
+
| `open_corner` | Top-level Rooms | Open one write-enabled corner with a ≤24-word objective |
|
|
140
|
+
| `pr_checks_status` | Corners | Read the server-posted checks verdict and human hold state |
|
|
141
|
+
| `attach_file` | Everywhere | Attach one file from the checkout or scratch dir to the reply |
|
|
142
|
+
| `create_schedule`, `list_schedules`, `delete_schedule` | Everywhere | Run a prompt again later — interval minutes or a 5-field cron |
|
|
143
|
+
| `request_grant` | Everywhere | Ask the owner for reach outside the sandbox |
|
|
144
|
+
| `run_granted_command` | Everywhere | Run a command an approved grant covers, outside the sandbox |
|
|
145
145
|
|
|
146
146
|
## The app
|
|
147
147
|
|
package/dist/usebeeline.mjs
CHANGED
|
@@ -8126,6 +8126,7 @@ async function syncAgentModelCatalog(input) {
|
|
|
8126
8126
|
|
|
8127
8127
|
// apps/body/dist/daemon-api-client.js
|
|
8128
8128
|
import { resolve as resolve7 } from "node:path";
|
|
8129
|
+
import { randomUUID } from "node:crypto";
|
|
8129
8130
|
|
|
8130
8131
|
// node_modules/ws/wrapper.mjs
|
|
8131
8132
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -16940,6 +16941,7 @@ var DaemonApiClient = class {
|
|
|
16940
16941
|
agentId;
|
|
16941
16942
|
fetchImpl;
|
|
16942
16943
|
webSocketFactory;
|
|
16944
|
+
presenceLifecycleId = randomUUID();
|
|
16943
16945
|
liveSocket;
|
|
16944
16946
|
liveReconnect;
|
|
16945
16947
|
liveReconnectDelayMs = 1e3;
|
|
@@ -17012,7 +17014,9 @@ var DaemonApiClient = class {
|
|
|
17012
17014
|
ensureLiveSocket() {
|
|
17013
17015
|
if (this.liveSocket || !this.liveRooms.size)
|
|
17014
17016
|
return;
|
|
17015
|
-
const socket = this.webSocketFactory(this.baseUrl.replace(/^http/, "ws") + "/v1/phone/live", [
|
|
17017
|
+
const socket = this.webSocketFactory(this.baseUrl.replace(/^http/, "ws") + "/v1/phone/live", [
|
|
17018
|
+
`bearer.${this.daemonToken}`
|
|
17019
|
+
]);
|
|
17016
17020
|
this.liveSocket = socket;
|
|
17017
17021
|
socket.onopen = () => {
|
|
17018
17022
|
this.liveReconnectDelayMs = 1e3;
|
|
@@ -17081,6 +17085,7 @@ var DaemonApiClient = class {
|
|
|
17081
17085
|
this.liveSocket.send(JSON.stringify({
|
|
17082
17086
|
type: "subscribe",
|
|
17083
17087
|
roomId,
|
|
17088
|
+
lifecycleId: this.presenceLifecycleId,
|
|
17084
17089
|
...room.cursor ? { cursor: room.cursor } : {},
|
|
17085
17090
|
...room.presence
|
|
17086
17091
|
}));
|
|
@@ -17981,7 +17986,7 @@ import { promisify as promisify3 } from "node:util";
|
|
|
17981
17986
|
|
|
17982
17987
|
// apps/body/dist/agent-home.js
|
|
17983
17988
|
import { existsSync as existsSync3, readFileSync as readFileSync4 } from "node:fs";
|
|
17984
|
-
import { createHash as createHash3, randomUUID } from "node:crypto";
|
|
17989
|
+
import { createHash as createHash3, randomUUID as randomUUID2 } from "node:crypto";
|
|
17985
17990
|
import { chmod as chmod2, copyFile, lstat, mkdir as mkdir4, readFile as readFile6, readdir as readdir2, realpath, rename as rename2, rm as rm2, symlink, unlink, writeFile as writeFile5 } from "node:fs/promises";
|
|
17986
17991
|
import { homedir as homedir5 } from "node:os";
|
|
17987
17992
|
import { basename as basename3, dirname as dirname5, join as join3, relative as relative2, resolve as resolve13, sep } from "node:path";
|
|
@@ -18903,7 +18908,7 @@ async function provisionManagedSkillsDir(target, managedSkills, sharedSkills, op
|
|
|
18903
18908
|
const plan = await planManagedSkills(managedSkills, sharedSkills, optionalShares);
|
|
18904
18909
|
if (await materializedSkillManifest(target) === plan.manifest)
|
|
18905
18910
|
return;
|
|
18906
|
-
const staged = resolve13(parent, `.skills.${process.pid}.${
|
|
18911
|
+
const staged = resolve13(parent, `.skills.${process.pid}.${randomUUID2()}.tmp`);
|
|
18907
18912
|
await mkdir4(staged, { mode: 448 });
|
|
18908
18913
|
try {
|
|
18909
18914
|
for (const entry of plan.entries) {
|
|
@@ -19155,7 +19160,7 @@ async function writeIsolatedHarnessFile(path, content) {
|
|
|
19155
19160
|
if (!parentStats.isDirectory() || parentStats.isSymbolicLink()) {
|
|
19156
19161
|
throw new Error(`isolated harness parent is not a real directory: ${parent}`);
|
|
19157
19162
|
}
|
|
19158
|
-
const temporary = resolve13(parent, `.${basename3(path)}.${process.pid}.${
|
|
19163
|
+
const temporary = resolve13(parent, `.${basename3(path)}.${process.pid}.${randomUUID2()}.tmp`);
|
|
19159
19164
|
try {
|
|
19160
19165
|
await writeFile5(temporary, content, { mode: 384, flag: "wx" });
|
|
19161
19166
|
await chmod2(temporary, 384);
|
|
@@ -22231,42 +22236,12 @@ var MonolithRoomTurnLoop = class {
|
|
|
22231
22236
|
}
|
|
22232
22237
|
async run() {
|
|
22233
22238
|
const { api, roomId, signal } = this.options;
|
|
22234
|
-
const status = this.options.config.modelUnavailable ? "offline" : "online";
|
|
22235
|
-
let legacyPresence = false;
|
|
22236
|
-
let legacyPresenceHeartbeat;
|
|
22237
|
-
let legacyPresenceFallback;
|
|
22238
|
-
const postLegacyPresence = async (presence) => {
|
|
22239
|
-
await api.execute("postAgentPresence", {
|
|
22240
|
-
agentId: this.agent.publicKey,
|
|
22241
|
-
roomId,
|
|
22242
|
-
status: presence,
|
|
22243
|
-
...this.options.config.daemonReleaseVersion ? { releaseVersion: this.options.config.daemonReleaseVersion } : {},
|
|
22244
|
-
...this.options.config.daemonSourceSha ? { sourceSha: this.options.config.daemonSourceSha } : {}
|
|
22245
|
-
});
|
|
22246
|
-
};
|
|
22247
|
-
const useLegacyPresence = () => {
|
|
22248
|
-
if (legacyPresence)
|
|
22249
|
-
return;
|
|
22250
|
-
legacyPresence = true;
|
|
22251
|
-
void postLegacyPresence(status).catch((error) => console.error(`[thin-core] monolith Room ${roomId} presence fallback failed:`, error));
|
|
22252
|
-
legacyPresenceHeartbeat = setInterval(() => void postLegacyPresence(status).catch((error) => console.error(`[thin-core] monolith Room ${roomId} presence fallback failed:`, error)), 3e4);
|
|
22253
|
-
legacyPresenceHeartbeat.unref?.();
|
|
22254
|
-
};
|
|
22255
|
-
const stopLegacyPresence = () => {
|
|
22256
|
-
legacyPresence = false;
|
|
22257
|
-
clearTimeout(legacyPresenceFallback);
|
|
22258
|
-
legacyPresenceFallback = void 0;
|
|
22259
|
-
clearInterval(legacyPresenceHeartbeat);
|
|
22260
|
-
legacyPresenceHeartbeat = void 0;
|
|
22261
|
-
};
|
|
22262
22239
|
let cursor3;
|
|
22263
22240
|
const processedInboxIds = /* @__PURE__ */ new Set();
|
|
22264
22241
|
const pushedInbox = [];
|
|
22265
22242
|
let pendingPushedCursor;
|
|
22266
22243
|
let liveConnected = false;
|
|
22267
22244
|
let stopLive;
|
|
22268
|
-
legacyPresenceFallback = setTimeout(useLegacyPresence, 1e3);
|
|
22269
|
-
legacyPresenceFallback.unref?.();
|
|
22270
22245
|
try {
|
|
22271
22246
|
const activation = await api.execute("getRoomInbox", { roomId, startAtLatest: true });
|
|
22272
22247
|
cursor3 = activation.cursor;
|
|
@@ -22280,10 +22255,6 @@ var MonolithRoomTurnLoop = class {
|
|
|
22280
22255
|
this.wakeIntake = void 0;
|
|
22281
22256
|
}, (connected, capabilities) => {
|
|
22282
22257
|
liveConnected = connected && capabilities?.pushIntake === true;
|
|
22283
|
-
if (connected && capabilities?.connectionPresence !== true)
|
|
22284
|
-
useLegacyPresence();
|
|
22285
|
-
else if (connected)
|
|
22286
|
-
stopLegacyPresence();
|
|
22287
22258
|
this.options.health.presence(connected && !this.options.config.modelUnavailable ? "online" : "offline");
|
|
22288
22259
|
this.wakeIntake?.();
|
|
22289
22260
|
this.wakeIntake = void 0;
|
|
@@ -22362,11 +22333,6 @@ var MonolithRoomTurnLoop = class {
|
|
|
22362
22333
|
this.client.sessionCancel(this.sessionId);
|
|
22363
22334
|
}
|
|
22364
22335
|
await this.activeTurn?.promise;
|
|
22365
|
-
clearTimeout(legacyPresenceFallback);
|
|
22366
|
-
clearInterval(legacyPresenceHeartbeat);
|
|
22367
|
-
if (legacyPresence) {
|
|
22368
|
-
await postLegacyPresence("offline").catch((error) => console.error(`[thin-core] monolith Room ${roomId} offline presence failed:`, error));
|
|
22369
|
-
}
|
|
22370
22336
|
await this.options.scheduler.suspend(roomId);
|
|
22371
22337
|
}
|
|
22372
22338
|
}
|