usebeeline 0.0.110 → 0.0.112

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  <p align="center"><strong>Team messaging for agents and humans.</strong></p>
10
10
  <p align="center">One Room for your people and your coding agents. Talk it through, hand off the work, watch it merge.</p>
11
11
 
12
- `usebeeline` connects **a coding agent you already run — Claude Code, Codex, Goose, Pi, or Grok — to a Room in the Beeline app on your phone**. One command on the machine where the agent lives, and it walks into the conversation as a member: it reads what your teammates actually said, answers when it is tagged, and takes work away when someone asks it to. In top-level Rooms and corners, it can also continue the conversation with the person it last addressed, without piling on from another agent. Nothing is retyped into a prompt box.
12
+ `usebeeline` connects **a coding agent you already run — Claude Code, Codex, Goose, Pi, Grok, or Cursor — to a Room in the Beeline app on your phone**. One command on the machine where the agent lives, and it walks into the conversation as a member: it reads what your teammates actually said, answers when it is tagged, and takes work away when someone asks it to. In top-level Rooms and corners, it can also continue the conversation with the person it last addressed, without piling on from another agent. Nothing is retyped into a prompt box.
13
13
 
14
14
  The agent stays on your machine. Your provider key stays on your machine. What crosses the wire is the conversation, and — when repository work starts — a pull request.
15
15
 
@@ -6092,16 +6092,18 @@ var AGENT_KINDS = [
6092
6092
  "goose",
6093
6093
  "pi",
6094
6094
  "grok",
6095
+ "cursor",
6095
6096
  "reference",
6096
6097
  "custom"
6097
6098
  ];
6098
- var AUTO_DETECT_AGENT_KINDS = ["codex", "claude", "goose", "pi", "grok"];
6099
+ var AUTO_DETECT_AGENT_KINDS = ["codex", "claude", "goose", "pi", "grok", "cursor"];
6099
6100
  var AGENT_EXECUTABLES = {
6100
6101
  codex: "codex",
6101
6102
  claude: "claude",
6102
6103
  goose: "goose",
6103
6104
  pi: "pi",
6104
- grok: "grok"
6105
+ grok: "grok",
6106
+ cursor: "cursor-agent"
6105
6107
  };
6106
6108
  var ADAPTER_INSTALL_COMMANDS = {
6107
6109
  codex: {
@@ -6115,6 +6117,10 @@ var ADAPTER_INSTALL_COMMANDS = {
6115
6117
  pi: {
6116
6118
  command: "npm",
6117
6119
  args: ["install", "-g", "pi-acp"]
6120
+ },
6121
+ cursor: {
6122
+ command: "npm",
6123
+ args: ["install", "-g", "cursor-agent-acp"]
6118
6124
  }
6119
6125
  };
6120
6126
  function adapterInstallHint(kind) {
@@ -6295,6 +6301,11 @@ function resolveAgentCommand(opts) {
6295
6301
  const command2 = requireExecutable("pi-acp", env, cwd, `Pi needs an ACP adapter. Install it with \`${adapterInstallHint("pi")}\`, then retry with \`--agent pi\`.`);
6296
6302
  return { kind: typedKind, command: command2, args: [] };
6297
6303
  }
6304
+ if (typedKind === "cursor") {
6305
+ requireExecutable("cursor-agent", env, cwd, "Cursor Agent CLI not found. Install it from https://cursor.com/docs/cli, then retry with `--agent cursor`.");
6306
+ const command2 = requireExecutable("cursor-agent-acp", env, cwd, `Cursor ACP adapter not found. Install it with \`${adapterInstallHint("cursor")}\`, then retry with \`--agent cursor\`.`);
6307
+ return { kind: typedKind, command: command2, args: [] };
6308
+ }
6298
6309
  if (typedKind === "grok") {
6299
6310
  const command2 = requireExecutable("grok", env, cwd, "Grok CLI not found. Install it with `curl -fsSL https://x.ai/cli/install.sh | bash`, then retry with `--agent grok`.");
6300
6311
  return { kind: typedKind, command: command2, args: ["agent", "stdio"] };
@@ -6762,6 +6773,13 @@ var PROFILES = [
6762
6773
  enforcement: "allowlisted",
6763
6774
  note: "buzz-agent mounts only the MCP servers the daemon passes; it has no operator-global tool config"
6764
6775
  }
6776
+ },
6777
+ {
6778
+ match: /(^|[/\\])cursor-agent-acp(\.[a-z]+)?$/i,
6779
+ profile: {
6780
+ enforcement: "config-isolated",
6781
+ note: "cursor-agent-acp bridges cursor-agent to ACP; cursor-agent loads MCP servers from ~/.cursor/mcp.json, so an isolated CURSOR_HOME scopes the session"
6782
+ }
6765
6783
  }
6766
6784
  ];
6767
6785
  function sessionToolScopeMeta(agentCommand) {
@@ -18026,6 +18044,7 @@ var DaemonApiClient = class {
18026
18044
  liveReconnectDelayMs = 1e3;
18027
18045
  liveRooms = /* @__PURE__ */ new Map();
18028
18046
  roomsChangedListener;
18047
+ configChangedListener;
18029
18048
  constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
18030
18049
  this.baseUrl = baseUrl;
18031
18050
  this.daemonToken = daemonToken;
@@ -18078,6 +18097,12 @@ var DaemonApiClient = class {
18078
18097
  setRoomsChangedListener(listener) {
18079
18098
  this.roomsChangedListener = listener;
18080
18099
  }
18100
+ /** Register the one listener invoked when the server reports the agent's
18101
+ * model/effort selection changed — the wake that hot-restarts every
18102
+ * retained session (agent-wide; the event names no single Room). */
18103
+ setConfigChangedListener(listener) {
18104
+ this.configChangedListener = listener;
18105
+ }
18081
18106
  updateLiveCursor(roomId, cursor3) {
18082
18107
  const room = this.liveRooms.get(roomId);
18083
18108
  if (room && cursor3)
@@ -18127,6 +18152,10 @@ var DaemonApiClient = class {
18127
18152
  this.roomsChangedListener?.();
18128
18153
  return;
18129
18154
  }
18155
+ if (event.type === "config-changed") {
18156
+ this.configChangedListener?.();
18157
+ return;
18158
+ }
18130
18159
  if (event.type === "subscribed" && typeof event.roomId === "string") {
18131
18160
  const capabilities = event.capabilities;
18132
18161
  this.liveRooms.get(event.roomId)?.onState?.(true, {
@@ -18510,6 +18539,10 @@ var HARNESS_HOME_STATE_DIRS = [
18510
18539
  {
18511
18540
  match: /(^|[/\\])grok(\.[a-z]+)?$/i,
18512
18541
  dirs: [".grok"]
18542
+ },
18543
+ {
18544
+ match: /(^|[/\\])cursor-agent-acp(\.[a-z]+)?$/i,
18545
+ dirs: [".cursor"]
18513
18546
  }
18514
18547
  ];
18515
18548
  function harnessHomeStateDirs(agentCommand, home = homedir3()) {
@@ -19381,6 +19414,7 @@ function isAgentPairingCode(value) {
19381
19414
 
19382
19415
  // apps/body/dist/beeline-skill.js
19383
19416
  var USING_BEELINE_SKILL_NAME = "using-beeline";
19417
+ var BEELINE_TRIAGE_SKILL_NAME = "beeline-triage";
19384
19418
  var BEELINE_REVIEW_SKILL_NAME = "beeline-review";
19385
19419
  function isConfiguredReviewer(agentHandle, reviewerHandle) {
19386
19420
  return Boolean(agentHandle && reviewerHandle && agentHandle.replace(/^@/, "") === reviewerHandle.replace(/^@/, ""));
@@ -19397,6 +19431,7 @@ var BEELINE_ROOM_CAPABILITIES = [
19397
19431
  `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.`,
19398
19432
  "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.",
19399
19433
  "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.",
19434
+ "Before emitting `Proposed corner:` or calling open_corner, consult the release-versioned beeline-triage skill and follow it. An unclear request requires a question; a warranted-work or desirability warning does not block the proposal or corner.",
19400
19435
  "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.",
19401
19436
  "Agreement is not action: never merely acknowledge an ask. Reply with a proposed corner, a question, or a line beginning `parked:` with the reason.",
19402
19437
  "When open_corner succeeds, the server posts the corner card: do not announce or restate the opening. End the turn with nothing more unless the person asked something else.",
@@ -19488,6 +19523,11 @@ Follow these steps in order. Do not skip or reorder them.
19488
19523
 
19489
19524
  ## 2. P0 - OBJECTIVE FULFILLED, DEMONSTRATED
19490
19525
 
19526
+ Before judging the implementation, independently repeat the two judgment legs from request triage:
19527
+
19528
+ - **Work warranted:** For a bug, reproduce the reported behavior on the target branch. For another request, establish the unmet user need from the request and current product. Search current code, history, issues, and open or recently merged pull requests for work that already resolves or supersedes it. Treat title similarity only as a candidate, not proof of duplication. FAIL confirmed duplicate or obsolete work.
19529
+ - **Desirable:** Check repository-owned goals, invariants, architecture, and established product behavior. Require a concrete user benefit, the smallest coherent solution, and no unapproved scope. FAIL a confirmed conflict or an unsupported product judgment; put merely plausible concerns below as non-blocking findings.
19530
+
19491
19531
  - Quote the corner objective verbatim.
19492
19532
  - Derive its end-user story in one sentence: \`a user who does X sees Y\`.
19493
19533
  - Make Y happen against the built PR head: run the app or affected service and perform X.
@@ -19531,6 +19571,8 @@ Follow these steps in order. Do not skip or reorder them.
19531
19571
 
19532
19572
  \`objective quoted:\`
19533
19573
  \`user story:\`
19574
+ \`work warranted evidence:\`
19575
+ \`desirability evidence:\`
19534
19576
  \`how Y was demonstrated (or FAIL):\`
19535
19577
  \`commands run + results:\`
19536
19578
  \`critical findings (block):\`
@@ -19545,6 +19587,49 @@ Then take exactly one action:
19545
19587
  - Approving is your last step as reviewer. The author merges it; you never do, and nothing merges it automatically.
19546
19588
  `;
19547
19589
  }
19590
+ function beelineTriageSkillMarkdown(releaseId) {
19591
+ return `---
19592
+ name: beeline-triage
19593
+ description: Clarify and assess a user request before proposing or opening a Beeline corner. Use immediately before emitting Proposed corner or calling open_corner.
19594
+ ---
19595
+
19596
+ <!-- beeline-release: ${releaseId} -->
19597
+
19598
+ # Beeline request triage
19599
+
19600
+ Run these checks before proposing or opening a corner.
19601
+
19602
+ ## 1. Is it clear?
19603
+
19604
+ - Rewrite the request as a concrete outcome and acceptance criteria.
19605
+ - State material exclusions needed to prevent unrequested work.
19606
+ - Keep the corner objective complete and within 24 words.
19607
+ - If an ambiguity could materially change the outcome, ask one focused question instead of proposing or opening the corner.
19608
+
19609
+ ## 2. Is work warranted?
19610
+
19611
+ - For a bug, try to reproduce the exact user-visible behavior and record the evidence.
19612
+ - For another request, identify the unmet user need and the current behavior that does not meet it.
19613
+ - Search current code, history, issues, and open or recently merged pull requests for released or unreleased work that resolves or supersedes the request.
19614
+ - Treat similar titles as candidates. Confirm behavior and scope before calling work duplicate.
19615
+ - If the need cannot be established, reproduction fails, or other work may obviate it, warn; do not block.
19616
+
19617
+ ## 3. Is it desirable?
19618
+
19619
+ - Compare the request with repository-owned goals, invariants, architecture, and established product behavior.
19620
+ - Look for concrete user benefit, the smallest coherent solution, and ongoing maintenance cost.
19621
+ - Do not invent product strategy. If the evidence is absent or conflicting, warn; do not block.
19622
+
19623
+ ## Output
19624
+
19625
+ When proposing work, emit the ordinary \`Proposed corner: <name> \u2014 <objective>\` line. Add only applicable warnings on following lines:
19626
+
19627
+ \`Triage warning \u2014 warranted: <evidence-backed reason>\`
19628
+ \`Triage warning \u2014 desirable: <evidence-backed reason>\`
19629
+
19630
+ Do not emit a warning merely because evidence is incomplete when the repository offers no practical way to obtain it. Never describe a warning as approval or rejection. Warnings inform the user and implementer; they do not block work.
19631
+ `;
19632
+ }
19548
19633
 
19549
19634
  // apps/body/dist/external-mcp-capabilities.js
19550
19635
  var SQUIRE_MCP_VERSION = "latest";
@@ -20139,11 +20224,15 @@ var SHARED_CREDENTIALS = [
20139
20224
  // Grok relocates ~/.grok via GROK_HOME; auth.json holds its login (same
20140
20225
  // shape as codex).
20141
20226
  { dir: "grok", source: ".grok/auth.json", target: "auth.json" },
20142
- { dir: "pi", source: ".pi/agent/auth.json", target: "auth.json" }
20227
+ { dir: "pi", source: ".pi/agent/auth.json", target: "auth.json" },
20228
+ // Cursor CLI stores auth state under ~/.cursor/; cursor-agent-acp
20229
+ // reads CURSOR_HOME to relocate the data directory.
20230
+ { dir: "cursor", source: ".cursor/agent-cli-state.json", target: "agent-cli-state.json" }
20143
20231
  ];
20144
20232
  var GOOSE_SHARED_CONFIG_FILES = ["config.yaml", "secrets.yaml"];
20145
20233
  var BEELINE_DEFAULT_SKILL_NAMES = [
20146
20234
  BEELINE_REVIEW_SKILL_NAME,
20235
+ BEELINE_TRIAGE_SKILL_NAME,
20147
20236
  USING_BEELINE_SKILL_NAME
20148
20237
  ];
20149
20238
  var OPERATOR_SKILL_SOURCE_DIRS = [
@@ -20152,7 +20241,7 @@ var OPERATOR_SKILL_SOURCE_DIRS = [
20152
20241
  ".codex/skills",
20153
20242
  ".pi/agent/skills"
20154
20243
  ];
20155
- var AGENT_SKILL_DIRS = ["claude", "codex", "grok", "pi"];
20244
+ var AGENT_SKILL_DIRS = ["claude", "codex", "grok", "pi", "cursor"];
20156
20245
  function agentSkillDir(kind) {
20157
20246
  return AGENT_SKILL_DIRS.includes(kind ?? "") ? kind : "codex";
20158
20247
  }
@@ -20180,6 +20269,7 @@ var HOME_SUBDIRS = [
20180
20269
  "goose",
20181
20270
  "grok",
20182
20271
  "pi",
20272
+ "cursor",
20183
20273
  "state",
20184
20274
  "cache",
20185
20275
  "tmp"
@@ -20225,6 +20315,7 @@ async function prepareRoomAgentHome(input) {
20225
20315
  async function provisionAgentSkillsAndMcp(root, operatorHome, skillReleaseId, failClosed, sharedSkills, skillDir, openRouterRouting, isReviewer) {
20226
20316
  const managedSkills = [
20227
20317
  { name: USING_BEELINE_SKILL_NAME, content: usingBeelineSkillMarkdown(skillReleaseId) },
20318
+ { name: BEELINE_TRIAGE_SKILL_NAME, content: beelineTriageSkillMarkdown(skillReleaseId) },
20228
20319
  ...isReviewer ? [{ name: BEELINE_REVIEW_SKILL_NAME, content: beelineReviewSkillMarkdown(skillReleaseId) }] : []
20229
20320
  ];
20230
20321
  const shared = await resolveSharedSkillSources(operatorHome, sharedSkills);
@@ -20634,6 +20725,7 @@ function roomAgentHomeEnv(root) {
20634
20725
  CODEX_HOME: resolve13(resolved, "codex"),
20635
20726
  GOOSE_PATH_ROOT: resolve13(resolved, "goose"),
20636
20727
  GROK_HOME: resolve13(resolved, "grok"),
20728
+ CURSOR_HOME: resolve13(resolved, "cursor"),
20637
20729
  PI_CODING_AGENT_DIR: resolve13(resolved, "pi"),
20638
20730
  XDG_STATE_HOME: resolve13(resolved, "state"),
20639
20731
  XDG_CACHE_HOME: resolve13(resolved, "cache"),
@@ -20645,6 +20737,7 @@ var HARNESS_STATE_ENV_VARS = [
20645
20737
  "CODEX_HOME",
20646
20738
  "GOOSE_PATH_ROOT",
20647
20739
  "GROK_HOME",
20740
+ "CURSOR_HOME",
20648
20741
  "PI_CODING_AGENT_DIR",
20649
20742
  "XDG_STATE_HOME",
20650
20743
  "XDG_CACHE_HOME",
@@ -24434,6 +24527,23 @@ var SessionScheduler = class {
24434
24527
  this.live.delete(key);
24435
24528
  await this.retire(session);
24436
24529
  }
24530
+ /**
24531
+ * Retire every RETAINED session — the warm processes holding no turn — in
24532
+ * one pass. This is the config-change hot restart: a phone-side model/effort
24533
+ * selection change must not wait for each Room's next hand-back currency
24534
+ * check to notice. A busy, draining, queued, or still-spawning session is
24535
+ * deliberately left alone: interrupting a live turn buys nothing, and its
24536
+ * next hand-back runs the ordinary `isCurrent` check, which cold-activates
24537
+ * against the new selection. Suspending a key that is no longer live is a
24538
+ * no-op, so overlapping wakes coalesce safely.
24539
+ */
24540
+ async suspendIdle() {
24541
+ for (const [key, session] of [...this.live.entries()]) {
24542
+ if (session.pending || this.busy.has(key) || this.drainingKeys.has(key) || (this.queues.get(key)?.length ?? 0) > 0)
24543
+ continue;
24544
+ await this.suspend(key);
24545
+ }
24546
+ }
24437
24547
  /**
24438
24548
  * Tear down a poisoned session even when its task is still marked busy.
24439
24549
  * The watchdog uses this only after a Room has stopped making progress; the
@@ -24867,6 +24977,9 @@ var RoomRuntimeCoordinator = class {
24867
24977
  this.options.daemonApi.setRoomsChangedListener?.(() => {
24868
24978
  this.discoveryWakes.wake();
24869
24979
  });
24980
+ this.options.daemonApi.setConfigChangedListener?.(() => {
24981
+ void this.scheduler.suspendIdle().catch((error) => console.error("[body] config-change session restart failed", error));
24982
+ });
24870
24983
  this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
24871
24984
  this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
24872
24985
  this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
@@ -25900,6 +26013,7 @@ Install one of these supported agents:
25900
26013
  goose https://block.github.io/goose/docs/getting-started/installation/
25901
26014
  pi npm install -g @mariozechner/pi-coding-agent pi-acp
25902
26015
  grok curl -fsSL https://x.ai/cli/install.sh | bash
26016
+ cursor npm install -g cursor-agent-acp
25903
26017
  Then retry, or explicitly use \`--agent reference\` with an LLM key.
25904
26018
  For another ACP server, use \`--agent custom --agent-command "<cmd> [args...]"\`.`;
25905
26019
  async function clackConfirmInstall(message) {
@@ -26138,7 +26252,8 @@ var DEFAULT_MODELS = {
26138
26252
  xai: "grok-4",
26139
26253
  codex: "gpt-5.4",
26140
26254
  claude: "claude-opus-4-1",
26141
- grok: "grok-4"
26255
+ grok: "grok-4",
26256
+ cursor: "claude-sonnet-4"
26142
26257
  };
26143
26258
  function connectHarnessNeedsProvider(harness) {
26144
26259
  return CONNECT_PROVIDER_HARNESSES.has(harness);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.110",
3
+ "version": "0.0.112",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {