@botbuddy/cli 1.32.3 → 1.32.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/wait.mjs +24 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botbuddy/cli",
3
- "version": "1.32.3",
3
+ "version": "1.32.4",
4
4
  "description": "BotBuddy — Swarm coordination CLI for multi-agent workflows",
5
5
  "type": "module",
6
6
  "bin": {
package/src/wait.mjs CHANGED
@@ -55,7 +55,13 @@ CONDITIONS (TYPE:key=val,key=val — repeat for several; --any wakes on the fir
55
55
  timer:duration=<seconds> wake after N seconds (DB-clocked server-side)
56
56
  timer:deadline=<ISO-8601> wake at an absolute time
57
57
  chat:channel=<id>|* new Direct Chat message (or * = any channel you are in;
58
- you are never woken by your own messages)
58
+ you are never woken by your own messages). Level-
59
+ triggered at arm: an unread reply from another
60
+ participant that already exists when you arm wakes
61
+ the wait immediately (payload.reason=unread_at_arm),
62
+ so send-then-arm never misses a fast reply. Respects
63
+ your read marker (mark_direct_chat_read) and your own
64
+ last message; a sibling session's reply still wakes.
59
65
  lock:subtype=<t>,host=<h>[,slot=<s>][,claim=true]
60
66
  a resource lock becomes available
61
67
  subtype ∈ playwright_lane|vite_port|backend_port|supabase_local
@@ -273,6 +279,12 @@ async function registerWait(opts, conditions, deadlineIso) {
273
279
  conditions,
274
280
  deadline: deadlineIso,
275
281
  mode: opts.mode,
282
+ // BOT-1753: always sent so the relay knows this client reports its replay
283
+ // boundary (a pre-1.32.3 client omits the key and the relay then skips the chat
284
+ // arm snapshot to avoid a chained-resume duplicate). "-1" = a fresh arm; a
285
+ // resume sends its `--since` cursor. The relay only arm-grants messages this
286
+ // wait will NOT replay itself.
287
+ since: String(opts.since ?? -1),
276
288
  ...(opts.localWaitId ? { local_wait_id: opts.localWaitId } : {}),
277
289
  ...(opts.expectedAgentId ? {
278
290
  expected_agent_id: opts.expectedAgentId,
@@ -422,6 +434,7 @@ async function registerWait(opts, conditions, deadlineIso) {
422
434
  return {
423
435
  waitSessionId: body.wait_session_id ?? null,
424
436
  cursorStart: body.cursor_start ?? null,
437
+ chatReplayFloor: body.chat_replay_floor ?? null,
425
438
  // BOT-1184: the server canonicalizes lock-condition hosts on register and
426
439
  // echoes them here so the client matches the canonical subject_key.
427
440
  conditions: Array.isArray(body.conditions) ? body.conditions : null,
@@ -1059,6 +1072,7 @@ async function resolveRecoveryWaitSession(checkpoint, { reserveMissing = false }
1059
1072
  registration: {
1060
1073
  waitSessionId: body.wait_session_id,
1061
1074
  cursorStart: body.cursor_start ?? null,
1075
+ chatReplayFloor: body.chat_replay_floor ?? null,
1062
1076
  sessionTenant: checkpoint.tenant_id ?? null,
1063
1077
  agentId: checkpoint.agent_id ?? null,
1064
1078
  sessionId: checkpoint.arming_session_id ?? null,
@@ -1749,6 +1763,15 @@ export async function runWait(argv, { recoveryLocalWaitId = null } = {}) {
1749
1763
  // Arm from the registration high-water mark unless the caller pinned an
1750
1764
  // explicit --since (resume). cursor_start replays the register→connect gap.
1751
1765
  if (opts.since == null && reg.cursorStart != null) effectiveSince = String(reg.cursorStart);
1766
+ // BOT-1753 (Codex R7 F2): a still-unread chat arm grant a dead/sibling wait left
1767
+ // below our replay boundary. Replay from just before it so it isn't stranded —
1768
+ // for a fresh arm AND a --since resume. The relay only reports a floor for a
1769
+ // message that is genuinely still unread, so this never re-delivers a read reply.
1770
+ if (reg.chatReplayFloor != null && /^\d+$/.test(String(reg.chatReplayFloor))) {
1771
+ if (effectiveSince == null || BigInt(reg.chatReplayFloor) < BigInt(effectiveSince)) {
1772
+ effectiveSince = String(reg.chatReplayFloor);
1773
+ }
1774
+ }
1752
1775
  checkpoint = await store.update(checkpoint.local_wait_id, {
1753
1776
  status: "armed",
1754
1777
  cloud_wait_session_id: waitSessionId,