cumora 0.1.48 → 0.1.49

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/dist/cli.js +28 -0
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -486,6 +486,31 @@ ${hint}`,
486
486
  async inboxTriage(token) {
487
487
  return runtimeGet(this.cfg.serverUrl, "/inbox-triage", token);
488
488
  }
489
+ /** Snapshot the unread inbox as {conversationId → latest unread message id}.
490
+ * Captured BEFORE the engine runs so we can later ack exactly what THIS
491
+ * turn saw (`ackSeen`) without swallowing messages that arrive mid-turn —
492
+ * those keep a higher id, stay unread, and drive the coalesced rerun. */
493
+ async snapshotUnread(token) {
494
+ const inbox = await runtimeGet(this.cfg.serverUrl, "/inbox", token);
495
+ const seen = /* @__PURE__ */ new Map();
496
+ for (const row of inbox?.rows ?? []) {
497
+ if (typeof row.conversation_id === "string" && row.conversation_id && typeof row.id === "string" && row.id) {
498
+ seen.set(row.conversation_id, row.id);
499
+ }
500
+ }
501
+ return seen;
502
+ }
503
+ /** Advance this agent's read cursor over the conversations it just saw, so a
504
+ * wake that ends WITHOUT a reply (small-brain said "skip", or the engine
505
+ * read the room and chose silence) does not re-trigger on the same messages
506
+ * every INBOX_POLL_MS. markConversationRead is monotonic, so this never
507
+ * regresses a cursor the engine already advanced further via `cumora reply`. */
508
+ async ackSeen(token, seen) {
509
+ if (seen.size === 0) return;
510
+ await Promise.all([...seen].map(
511
+ ([conversationId, upToMessageId]) => runtimeBest(this.cfg.serverUrl, "/conversation/mark-read", token, { conversationId, upToMessageId })
512
+ ));
513
+ }
489
514
  formatTriageNote(triage) {
490
515
  if (!triage) return "";
491
516
  const note = typeof triage?.promptNote === "string" ? triage.promptNote.trim() : "";
@@ -543,10 +568,12 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
543
568
  const token = this.token;
544
569
  const convo = this.lastWakeConvo;
545
570
  this.lastWakeConvo = null;
571
+ const seen = await this.snapshotUnread(token);
546
572
  const triage = await this.inboxTriage(token);
547
573
  if (triage?.actionable === false) {
548
574
  const reason = typeof triage.reason === "string" ? triage.reason : "not relevant";
549
575
  console.log(`[computer] ${this.agent.id} skipped by small-brain inbox triage: ${reason}`);
576
+ await this.ackSeen(token, seen);
550
577
  await runtimeBest(this.cfg.serverUrl, "/status", token, { status: "avail" });
551
578
  continue;
552
579
  }
@@ -615,6 +642,7 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
615
642
  error: engineError
616
643
  });
617
644
  }
645
+ if (!engineError) await this.ackSeen(token, seen);
618
646
  await runtimeBest(this.cfg.serverUrl, "/status", token, { status: "avail" });
619
647
  } while (this.pendingRerun && !this.stopped);
620
648
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cumora",
3
- "version": "0.1.48",
3
+ "version": "0.1.49",
4
4
  "description": "Run your Cumora agents on your own machine or VPS, powered by your local Claude Code or Codex CLI (BYOA).",
5
5
  "type": "module",
6
6
  "bin": {