usebeeline 0.0.109 → 0.0.110

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18025,6 +18025,7 @@ var DaemonApiClient = class {
18025
18025
  liveReconnect;
18026
18026
  liveReconnectDelayMs = 1e3;
18027
18027
  liveRooms = /* @__PURE__ */ new Map();
18028
+ roomsChangedListener;
18028
18029
  constructor(baseUrl, daemonToken, agentId, fetchImpl = fetch, webSocketFactory = (url, protocols) => new wrapper_default(url, protocols)) {
18029
18030
  this.baseUrl = baseUrl;
18030
18031
  this.daemonToken = daemonToken;
@@ -18071,6 +18072,12 @@ var DaemonApiClient = class {
18071
18072
  }
18072
18073
  };
18073
18074
  }
18075
+ /** Register the one listener invoked when the server reports this agent's
18076
+ * Room/corner memberships changed — the wake that discovers a freshly
18077
+ * created Room without waiting for the reconciliation heartbeat. */
18078
+ setRoomsChangedListener(listener) {
18079
+ this.roomsChangedListener = listener;
18080
+ }
18074
18081
  updateLiveCursor(roomId, cursor3) {
18075
18082
  const room = this.liveRooms.get(roomId);
18076
18083
  if (room && cursor3)
@@ -18104,6 +18111,7 @@ var DaemonApiClient = class {
18104
18111
  this.liveReconnectDelayMs = 1e3;
18105
18112
  for (const roomId of this.liveRooms.keys())
18106
18113
  this.sendLiveSubscription(roomId);
18114
+ this.roomsChangedListener?.();
18107
18115
  };
18108
18116
  socket.onmessage = (message) => {
18109
18117
  let value;
@@ -18115,6 +18123,10 @@ var DaemonApiClient = class {
18115
18123
  if (!value || typeof value !== "object")
18116
18124
  return;
18117
18125
  const event = value;
18126
+ if (event.type === "rooms-changed") {
18127
+ this.roomsChangedListener?.();
18128
+ return;
18129
+ }
18118
18130
  if (event.type === "subscribed" && typeof event.roomId === "string") {
18119
18131
  const capabilities = event.capabilities;
18120
18132
  this.liveRooms.get(event.roomId)?.onState?.(true, {
@@ -19382,7 +19394,7 @@ var BEELINE_ROOM_CAPABILITIES = [
19382
19394
  "Files and photos people share are downloaded for you: read them at the local path named in the prompt (photos may also arrive inline); never fetch the reference URL.",
19383
19395
  "To create a file (this Room has no other way to write one), call beeline-agent write_scratch_file with a relative path and content - text by default, or base64 for bytes you computed; it returns a path in your writable session home. To send a file, call beeline-agent post_artifact with a path inside your checkout or anywhere in your writable session home (wherever a file you or your harness generated actually landed, including one you just wrote), or with html/bytes content directly; it is uploaded and attached to your reply, and title and mime default from the file when you post by path. write_scratch_file produces the file, not a picture - turning it into a raster image needs a converter, which needs shell, which this Room does not have.",
19384
19396
  "To run something later or repeatedly, call beeline-agent create_schedule (interval in minutes or a 5-field cron, optional maxRuns); list_schedules / delete_schedule manage them.",
19385
- `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
19397
+ `To react to things that HAPPEN in this Room rather than only to what is said to you, call beeline-agent subscribe_events with the kinds you want (${SERVER_EVENT_KINDS.join(", ")}); each one then wakes you for a turn. Subscriptions are per Room and cover every way the event lands: joining a Room you subscribed to wakes you, and so does a person arriving in the Workspace when that arrival projects into this Room - subscribe to joined in an onboarding Room and every newcomer wakes you, exactly like a greeter. It replaces your list, so send every kind you want - list_event_subscriptions shows the current one. You do this yourself: nobody has to configure it for you. grant-decided carries the grant id and status and resumes the turn that asked for the grant.`,
19386
19398
  "To state something that happened so the Room and other agents can act on it, call beeline-agent emit_event with your own agent:<slug> kind, one sentence, and optionally the agent members to wake. Chains of events are bounded and a refused emit posts nothing.",
19387
19399
  "When repository work is needed, you MUST call beeline-agent open_corner with a name of at most three words - it titles the corner everywhere - and a complete objective of no more than 24 words. The host-governed call is the only way to start write work.",
19388
19400
  "Never open a corner from your own reading of a person's ask. For every ask, first reply on one line `Proposed corner: <name> \u2014 <objective>`, using the exact title and objective you would pass to open_corner, then stop and wait. If one message contains several asks, list one numbered `Proposed corner:` line per ask; `go on 1 and 3` opens exactly those objectives and leaves the others proposed. A person's `go`, `yes`, or equivalent opens exactly the proposed corner; if they edit it, their edited text is the objective. Skip this ceremony only when the message itself explicitly commands a corner and states its scope, such as `open a corner and do X` or `go build X in a corner`; open that scope immediately.",
@@ -24632,6 +24644,25 @@ var DEFAULT_ROOM_WATCHDOG_STALE_MS = 9e4;
24632
24644
  var DEFAULT_RECONCILE_HEARTBEAT_MS = 6e4;
24633
24645
  var DEFAULT_DRAIN_DEADLINE_MS = 30 * 6e4;
24634
24646
  var CORNER_BRANCH_DELETE_ATTEMPTS = 3;
24647
+ var DiscoveryWakes = class {
24648
+ arrived = 0;
24649
+ served = 0;
24650
+ /** Called for every agent-directed `rooms-changed` wake. */
24651
+ wake() {
24652
+ this.arrived += 1;
24653
+ }
24654
+ needsFastReconcile() {
24655
+ return this.arrived !== this.served;
24656
+ }
24657
+ /** Snapshot at reconcile entry: the wake count its reads will cover. */
24658
+ beginReconcile() {
24659
+ return this.arrived;
24660
+ }
24661
+ /** Clear only wakes the completed start pass actually served. */
24662
+ completeReconcile(covered) {
24663
+ this.served = Math.max(this.served, covered);
24664
+ }
24665
+ };
24635
24666
  function shouldPostInitialCornerWorkingState(restore, isOpener = true) {
24636
24667
  return isOpener && !restore.featureBranch && !restore.lifecycle?.branch && !restore.lifecycle?.pr;
24637
24668
  }
@@ -24808,6 +24839,11 @@ var RoomRuntimeCoordinator = class {
24808
24839
  workspaceRemovalConfirmations = 0;
24809
24840
  roomRemovalConfirmations = /* @__PURE__ */ new Map();
24810
24841
  confirmationPending = false;
24842
+ /** Agent-directed discovery wakes (#1369), counted instead of flagged: a
24843
+ * wake that arrives while a reconcile is already running must survive its
24844
+ * start-pass clearing, or a corner opened mid-reconcile waits a heartbeat
24845
+ * for a wake the daemon already received. */
24846
+ discoveryWakes = new DiscoveryWakes();
24811
24847
  /** One command-grant runner per daemon; Rooms and corners register their checkouts on it. */
24812
24848
  grantRunner;
24813
24849
  grantRunnerServer;
@@ -24828,6 +24864,9 @@ var RoomRuntimeCoordinator = class {
24828
24864
  });
24829
24865
  this.grantRunnerServer = new GrantRunnerServer(this.grantRunner);
24830
24866
  this.connectorUsage = new ConnectorUsageRecorder();
24867
+ this.options.daemonApi.setRoomsChangedListener?.(() => {
24868
+ this.discoveryWakes.wake();
24869
+ });
24831
24870
  this.watchdogStaleMs = options.watchdogStaleMs ?? DEFAULT_ROOM_WATCHDOG_STALE_MS;
24832
24871
  this.reconcileHeartbeatMs = options.reconcileHeartbeatMs ?? DEFAULT_RECONCILE_HEARTBEAT_MS;
24833
24872
  this.drainDeadlineMs = options.drainDeadlineMs ?? DEFAULT_DRAIN_DEADLINE_MS;
@@ -24858,7 +24897,7 @@ var RoomRuntimeCoordinator = class {
24858
24897
  return this.scheduler.snapshot();
24859
24898
  }
24860
24899
  needsFastReconcile() {
24861
- return this.confirmationPending;
24900
+ return this.confirmationPending || this.discoveryWakes.needsFastReconcile();
24862
24901
  }
24863
24902
  reconcileHeartbeatIntervalMs() {
24864
24903
  return this.reconcileHeartbeatMs;
@@ -24894,6 +24933,7 @@ var RoomRuntimeCoordinator = class {
24894
24933
  }
24895
24934
  async reconcile() {
24896
24935
  this.confirmationPending = false;
24936
+ const coveredWakes = this.discoveryWakes.beginReconcile();
24897
24937
  const bootstrap = await this.options.daemonApi.execute("getDaemonBootstrap", {
24898
24938
  agentId: this.agent.publicKey
24899
24939
  });
@@ -24961,8 +25001,13 @@ var RoomRuntimeCoordinator = class {
24961
25001
  }
24962
25002
  }
24963
25003
  await mapWithConcurrency(desiredTopRooms, ROOM_JOIN_CONCURRENCY, async (roomId) => {
24964
- if (!this.running.has(roomId))
25004
+ if (this.running.has(roomId))
25005
+ return;
25006
+ try {
24965
25007
  await this.startRoom(roomId);
25008
+ } catch (error) {
25009
+ console.error(`[thin-core] failed to start Room ${roomId}:`, error);
25010
+ }
24966
25011
  });
24967
25012
  await mapWithConcurrency([...desiredCorners.values()], ROOM_JOIN_CONCURRENCY, async (corner) => {
24968
25013
  if (!this.running.has(corner.cornerId))
@@ -24970,6 +25015,7 @@ var RoomRuntimeCoordinator = class {
24970
25015
  });
24971
25016
  for (const running of this.running.values())
24972
25017
  running.body.requestReconciliation();
25018
+ this.discoveryWakes.completeReconcile(coveredWakes);
24973
25019
  return "member";
24974
25020
  }
24975
25021
  roomRecord(roomId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usebeeline",
3
- "version": "0.0.109",
3
+ "version": "0.0.110",
4
4
  "description": "Connect an AI coding agent to Beeline with one command.",
5
5
  "homepage": "https://usebeeline.app",
6
6
  "bugs": {