teapot-coding-agent 0.14.0 → 0.14.2

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.
@@ -109,6 +109,7 @@ export class Agent {
109
109
  progressMaxQuietTurns: 40,
110
110
  autoContinue: true,
111
111
  continueDelayMs: 15_000,
112
+ autoCompact: true,
112
113
  maxConsecutiveToolErrors: 5,
113
114
  contextTokenBudget: 96_000,
114
115
  contextWindowTokens: 0,
@@ -477,6 +478,7 @@ export class Agent {
477
478
  parent: this.opts.parent,
478
479
  awaiting: this.awaitingUser,
479
480
  autoContinue: this.opts.autoContinue,
481
+ autoCompact: this.opts.autoCompact,
480
482
  };
481
483
  }
482
484
  /**
@@ -1058,6 +1060,8 @@ export class Agent {
1058
1060
  const before = this.lastUsage?.input ?? this.estimateTokens();
1059
1061
  if (!force && before < this.opts.contextTokenBudget)
1060
1062
  return;
1063
+ if (!force && this.opts.autoCompact === false)
1064
+ return;
1061
1065
  // a forced pass on an already-tiny history would just summarize the
1062
1066
  // summary — report ran=false instead
1063
1067
  if (force && this.messages.length <= this.compactedAtLen)
package/dist/master.js CHANGED
@@ -634,6 +634,18 @@ export class Master {
634
634
  try {
635
635
  dirs = readdirSync(this.sessionsRoot())
636
636
  .filter((d) => d === agentId || d.startsWith(`${agentId}-`))
637
+ .filter((d) => {
638
+ // an EMPTY chat.jsonl means the session was never used (created via
639
+ // web, then abandoned). Reusing it would resurrect another agent's
640
+ // timeline under this id, and two incarnations could even land in
641
+ // one directory. Never treat empty logs as existing history.
642
+ try {
643
+ return statSync(path.join(this.sessionsRoot(), d, "chat.jsonl")).size > 0;
644
+ }
645
+ catch {
646
+ return false;
647
+ }
648
+ })
637
649
  .sort((a, b) => {
638
650
  // newest chat.jsonl wins
639
651
  const ma = this.sessionMtime(path.join(this.sessionsRoot(), a));
@@ -657,6 +669,13 @@ export class Master {
657
669
  resolveSessionDir(agentId, fresh) {
658
670
  const root = this.sessionsRoot();
659
671
  mkdirSync(root, { recursive: true });
672
+ // exact-restart match first: <agentId> itself (or a previously generated
673
+ // <agentId>-<suffix> dir when the bare name was taken). Prefix matching
674
+ // alone could hand agent "proj-2" agent "proj"'s directory.
675
+ const selfDir = path.join(root, agentId);
676
+ if (!fresh && statSync(path.join(selfDir, "chat.jsonl"), { throwIfNoEntry: false })?.size) {
677
+ return selfDir; // restart → continue where we left off
678
+ }
660
679
  // one-time migration from the ≤0.5.0 flat layout (<dataDir>/<id>.jsonl)
661
680
  if (!fresh) {
662
681
  const legacy = path.join(this.config.dataDir, `${agentId}.jsonl`);
@@ -209,7 +209,10 @@ export function buildApp(master) {
209
209
  id = `${base.slice(0, 38)}-${n++}`;
210
210
  try {
211
211
  const agent = await master.addAgent({ id, workspace: ws, provider: body.provider, model: body.model }, { persist: true, fresh: true });
212
- if (body.start !== false)
212
+ // NOTE: creation no longer auto-starts an LLM loop — the agent sits in
213
+ // "stopped" (a lazy, zero-cost session) until the operator sends the
214
+ // first prompt or presses ▶ start. The web UI relies on this.
215
+ if (body.start === true)
213
216
  agent.start("created via web");
214
217
  return c.json({ ok: true, agent: agent.snapshot() });
215
218
  }
@@ -550,6 +553,22 @@ export function buildApp(master) {
550
553
  bus.emit("update", { kind: "agent-update", agentId: c.req.param("id") });
551
554
  return c.json({ ok: true, value: body.value });
552
555
  });
556
+ // per-agent auto-compact toggle (auto-summarize when context exceeds budget)
557
+ app.post("/api/agents/:id/auto-compact", async (c) => {
558
+ const a = master.agents.get(c.req.param("id"));
559
+ if (!a)
560
+ return c.json({ error: "not found" }, 404);
561
+ const body = await c.req.json().catch(() => null);
562
+ if (!body || typeof body.value !== "boolean")
563
+ return c.json({ error: "boolean value required" }, 400);
564
+ const ac = master.config.agents.find((x) => x.id === c.req.param("id"));
565
+ if (ac)
566
+ ac.autoCompact = body.value;
567
+ a.opts.autoCompact = body.value;
568
+ master.saveConfig();
569
+ bus.emit("update", { kind: "agent-update", agentId: c.req.param("id") });
570
+ return c.json({ ok: true, value: body.value });
571
+ });
553
572
  // default sub-agent personas for @mentions and spawn_agent
554
573
  app.get("/api/personas", (c) => c.json({
555
574
  personas: Object.entries(SUB_PERSONAS).map(([key, p]) => ({ key, label: p.label, directive: p.directive })),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.14.0",
3
+ "version": "0.14.2",
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",