cumora 0.1.42 → 0.1.44

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 +36 -2
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -292,6 +292,7 @@ var AgentRunner = class {
292
292
  busy = false;
293
293
  pendingRerun = false;
294
294
  stopped = false;
295
+ lastWakeConvo = null;
295
296
  adapter;
296
297
  async start() {
297
298
  await this.adapter.seedHome(this.home, { id: this.agent.id, name: this.agent.name, role: this.agent.role });
@@ -301,6 +302,14 @@ var AgentRunner = class {
301
302
  stop() {
302
303
  this.stopped = true;
303
304
  }
305
+ /** Does this runner's live config still match the latest server state? The
306
+ * engine + model + persona are captured at construction (the adapter is
307
+ * fixed, and seedHome runs once in start()), so when any of them changes in
308
+ * Cumora, sync() must tear this runner down and build a fresh one — otherwise
309
+ * e.g. a Claude→Codex switch wouldn't take effect until a daemon restart. */
310
+ configMatches(agent, engine) {
311
+ return this.adapter.id === engine && this.agent.name === agent.name && this.agent.role === agent.role && this.agent.model === agent.model && this.agent.fastModel === agent.fastModel;
312
+ }
304
313
  async ensureToken() {
305
314
  if (this.token && Date.now() < this.tokenExpiresAt - TOKEN_REFRESH_SKEW_MS) return this.token;
306
315
  const minted = await api(
@@ -348,7 +357,16 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
348
357
  this.pendingRerun = false;
349
358
  await this.ensureToken();
350
359
  const token = this.token;
360
+ const convo = this.lastWakeConvo;
351
361
  await runtimeBest(this.cfg.serverUrl, "/status", token, { status: "thinking" });
362
+ let typingTimer;
363
+ if (convo) {
364
+ const ping = () => {
365
+ void runtimeBest(this.cfg.serverUrl, "/typing", token, { conversationId: convo, done: false });
366
+ };
367
+ ping();
368
+ typingTimer = setInterval(ping, 6e3);
369
+ }
352
370
  const run = await runtimeBest(this.cfg.serverUrl, "/runs", token, {
353
371
  trigger: { source: "byoa", engine: this.adapter.id }
354
372
  });
@@ -368,6 +386,9 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
368
386
  } catch (err) {
369
387
  console.error(`[computer] ${this.agent.id} engine spawn failed:`, err instanceof Error ? err.message : err);
370
388
  exitCode = 1;
389
+ } finally {
390
+ if (typingTimer) clearInterval(typingTimer);
391
+ if (convo) await runtimeBest(this.cfg.serverUrl, "/typing", token, { conversationId: convo, done: true });
371
392
  }
372
393
  if (run?.runId) {
373
394
  await runtimeBest(this.cfg.serverUrl, `/runs/${run.runId}/finish`, token, {
@@ -397,7 +418,14 @@ If nothing genuinely needs you, it's fine to do nothing and stop. When finished,
397
418
  void this.runTurn();
398
419
  for await (const evt of parseSseStream(res.body)) {
399
420
  if (this.stopped) break;
400
- if (evt.event === "wake") void this.runTurn();
421
+ if (evt.event === "wake") {
422
+ try {
423
+ const convo = evt.data ? JSON.parse(evt.data).conversationId : null;
424
+ if (convo) this.lastWakeConvo = convo;
425
+ } catch {
426
+ }
427
+ void this.runTurn();
428
+ }
401
429
  }
402
430
  } catch (err) {
403
431
  if (this.stopped) break;
@@ -435,9 +463,15 @@ async function doRun(serverOverride) {
435
463
  return;
436
464
  }
437
465
  for (const agent of agents) {
438
- if (runners.has(agent.id)) continue;
439
466
  const engine = agent.engine && available.includes(agent.engine) ? agent.engine : available[0] ?? null;
440
467
  if (!engine) continue;
468
+ const existing = runners.get(agent.id);
469
+ if (existing) {
470
+ if (existing.configMatches(agent, engine)) continue;
471
+ console.log(`[computer] agent ${agent.name} (${agent.id}) config changed \u2192 restarting on ${engine}`);
472
+ existing.stop();
473
+ runners.delete(agent.id);
474
+ }
441
475
  const runner = new AgentRunner(cfg, agent, engine);
442
476
  runners.set(agent.id, runner);
443
477
  console.log(`[computer] hosting agent ${agent.name} (${agent.id}) on ${engine}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cumora",
3
- "version": "0.1.42",
3
+ "version": "0.1.44",
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": {