cumora 0.1.47 → 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 +42 -1
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -181,7 +181,11 @@ var ClaudeAdapter = class {
181
181
  const flags = extraArgs("CUMORA_CLAUDE_ARGS");
182
182
  const model = args.model ? ["--model", args.model] : [];
183
183
  const argv = flags.length ? [...flags, "-p", args.prompt] : ["-p", args.prompt, ...model, "--output-format", "stream-json", "--verbose", "--dangerously-skip-permissions"];
184
- const env = args.fastModel ? { ...args.env, ANTHROPIC_SMALL_FAST_MODEL: args.fastModel } : args.env;
184
+ const env = {
185
+ ...args.env,
186
+ MAX_THINKING_TOKENS: args.env.MAX_THINKING_TOKENS ?? "0"
187
+ };
188
+ if (args.fastModel) env.ANTHROPIC_SMALL_FAST_MODEL = args.fastModel;
185
189
  return spawnEngine(this.bin, argv, { ...args, env });
186
190
  }
187
191
  };
@@ -222,6 +226,7 @@ var DEFAULT_SERVER = process.env.CUMORA_SERVER_URL || "https://api.cumora.ai";
222
226
  var TOKEN_REFRESH_SKEW_MS = 5 * 60 * 1e3;
223
227
  var AGENT_POLL_MS = 6e4;
224
228
  var HEARTBEAT_MS = 3e4;
229
+ var INBOX_POLL_MS = 2e4;
225
230
  var MAX_VISIBLE_ERROR_CHARS = 900;
226
231
  function parseArgs(argv) {
227
232
  const out = {};
@@ -379,14 +384,22 @@ var AgentRunner = class {
379
384
  pendingRerun = false;
380
385
  stopped = false;
381
386
  lastWakeConvo = null;
387
+ pollTimer;
382
388
  adapter;
383
389
  async start() {
384
390
  await this.adapter.seedHome(this.home, { id: this.agent.id, name: this.agent.name, role: this.agent.role });
385
391
  await writeShim(this.binDir);
386
392
  void this.streamLoop();
393
+ this.pollTimer = setInterval(() => {
394
+ if (!this.busy && !this.stopped) void this.runTurn();
395
+ }, INBOX_POLL_MS);
387
396
  }
388
397
  stop() {
389
398
  this.stopped = true;
399
+ if (this.pollTimer) {
400
+ clearInterval(this.pollTimer);
401
+ this.pollTimer = void 0;
402
+ }
390
403
  }
391
404
  /** Does this runner's live config still match the latest server state? The
392
405
  * engine + model + persona are captured at construction (the adapter is
@@ -473,6 +486,31 @@ ${hint}`,
473
486
  async inboxTriage(token) {
474
487
  return runtimeGet(this.cfg.serverUrl, "/inbox-triage", token);
475
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
+ }
476
514
  formatTriageNote(triage) {
477
515
  if (!triage) return "";
478
516
  const note = typeof triage?.promptNote === "string" ? triage.promptNote.trim() : "";
@@ -530,10 +568,12 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
530
568
  const token = this.token;
531
569
  const convo = this.lastWakeConvo;
532
570
  this.lastWakeConvo = null;
571
+ const seen = await this.snapshotUnread(token);
533
572
  const triage = await this.inboxTriage(token);
534
573
  if (triage?.actionable === false) {
535
574
  const reason = typeof triage.reason === "string" ? triage.reason : "not relevant";
536
575
  console.log(`[computer] ${this.agent.id} skipped by small-brain inbox triage: ${reason}`);
576
+ await this.ackSeen(token, seen);
537
577
  await runtimeBest(this.cfg.serverUrl, "/status", token, { status: "avail" });
538
578
  continue;
539
579
  }
@@ -602,6 +642,7 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
602
642
  error: engineError
603
643
  });
604
644
  }
645
+ if (!engineError) await this.ackSeen(token, seen);
605
646
  await runtimeBest(this.cfg.serverUrl, "/status", token, { status: "avail" });
606
647
  } while (this.pendingRerun && !this.stopped);
607
648
  } finally {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cumora",
3
- "version": "0.1.47",
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": {