teapot-coding-agent 0.37.98 → 0.37.100

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.
package/dist/master.js CHANGED
@@ -350,6 +350,27 @@ export class Master {
350
350
  this.keyPools.set(name, pool);
351
351
  return pool;
352
352
  }
353
+ /**
354
+ * The llm an agent should be using, derived from the CURRENT config.
355
+ *
356
+ * One owner on purpose: a running agent holds its own `opts.llm`, so a provider edit (a rotated token, a
357
+ * new baseUrl, a changed default provider) only reaches it if something re-derives from
358
+ * `config.providers`. `addAgent` and `updateConfig` both need that derivation, and two copies would drift
359
+ * the same way every other duplicated fact in this codebase has.
360
+ */
361
+ llmFor(ac) {
362
+ const provName = ac.provider ?? this.config.defaultProvider ?? "openrouter";
363
+ const prov = this.config.providers?.[provName];
364
+ const inlineKey = typeof ac.apiKey === "string" && ac.apiKey.trim() ? ac.apiKey.trim() : undefined;
365
+ const pool = inlineKey ? undefined : this.keyPoolFor(provName, prov);
366
+ return {
367
+ baseUrl: ac.baseUrl ?? prov?.baseUrl ?? this.config.llm.baseUrl,
368
+ apiKey: inlineKey ?? currentKey(pool) ?? prov?.apiKey ?? this.config.llm.apiKey,
369
+ model: ac.model ?? prov?.model ?? this.config.llm.model,
370
+ timeoutMs: 120_000,
371
+ ...(pool && pool.keys.length > 1 ? { keyPool: pool } : {}),
372
+ };
373
+ }
353
374
  keyPoolReport() {
354
375
  return [...this.keyPools.entries()].map(([provider, pool]) => ({
355
376
  provider,
@@ -456,6 +477,43 @@ export class Master {
456
477
  lastRunMin: t.lastRunMin ?? -1,
457
478
  }));
458
479
  }
480
+ /**
481
+ * A provider edit has to reach agents that are already running.
482
+ *
483
+ * Each agent holds its own `opts.llm`, built when it was created, so writing the new key into
484
+ * `config.providers` alone left every live agent sending the OLD token — a rotated key looked like it had
485
+ * not been saved, and the next request 401'd. Re-derive for live agents; agents with their own inline
486
+ * `apiKey`/`baseUrl` keep them, because that is what those fields mean.
487
+ */
488
+ if (patch.providers || patch.defaultProvider !== undefined) {
489
+ for (const [agentId, agent] of this.agents) {
490
+ // the config entry is the source of an agent's provider/model overrides, but it only exists for
491
+ // persisted agents — fall back to what the live agent reports about itself
492
+ const ac = this.config.agents.find((x) => x.id === agentId) ?? {
493
+ id: agentId,
494
+ workspace: "",
495
+ provider: agent.providerName || undefined,
496
+ model: agent.llm.model || undefined,
497
+ };
498
+ const next = this.llmFor(ac);
499
+ const merged = {
500
+ ...agent.llm,
501
+ baseUrl: next.baseUrl || agent.llm.baseUrl,
502
+ // exact, not `||`: clearing a token means clearing it, and an empty baseUrl/model would break the
503
+ // agent, so those two keep the working value
504
+ apiKey: next.apiKey,
505
+ model: next.model || agent.llm.model,
506
+ };
507
+ if (next.keyPool)
508
+ merged.keyPool = next.keyPool;
509
+ else
510
+ delete merged.keyPool;
511
+ agent.setLlmConfig(merged);
512
+ // the name shown in the UI has to match the llm actually in use
513
+ agent.opts.provider =
514
+ ac.provider ?? this.config.defaultProvider ?? "";
515
+ }
516
+ }
459
517
  this.saveConfig();
460
518
  }
461
519
  async loadPluginsAndMcp() {
@@ -615,18 +673,7 @@ export class Master {
615
673
  if (!prov && !ac.baseUrl && !this.config.llm.baseUrl) {
616
674
  throw new Error(`agent ${ac.id}: unknown provider "${provName}" and no fallback`);
617
675
  }
618
- const inlineKey = typeof ac.apiKey === "string" && ac.apiKey.trim() ? ac.apiKey.trim() : undefined;
619
- const pool = inlineKey ? undefined : this.keyPoolFor(provName, prov);
620
- const llm = {
621
- baseUrl: ac.baseUrl ?? prov?.baseUrl ?? this.config.llm.baseUrl,
622
- apiKey: inlineKey ??
623
- currentKey(pool) ??
624
- prov?.apiKey ??
625
- this.config.llm.apiKey,
626
- model: ac.model ?? prov?.model ?? this.config.llm.model,
627
- timeoutMs: 120_000,
628
- ...(pool && pool.keys.length > 1 ? { keyPool: pool } : {}),
629
- };
676
+ const llm = this.llmFor(ac);
630
677
  if (!llm.model)
631
678
  throw new Error(`agent ${ac.id}: no model configured (set model on the agent or on its provider)`);
632
679
  const myDepth = ac.parent ? this.depthOf(ac.parent) + 1 : 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.37.98",
3
+ "version": "0.37.100",
4
4
  "description": "A lightweight, always-on multi-agent harness for AI coding agents",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0-or-later",