teapot-coding-agent 0.37.49 → 0.37.51

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.
@@ -74,13 +74,6 @@ export const PROGRESS_REQUEST =
74
74
  // and every requested report appeared TWICE on the timeline
75
75
  "Record it with the report_progress TOOL (doing, goalStatus, recent, problems, next) — " +
76
76
  "call that tool; do NOT answer in prose, which would just duplicate this request.";
77
- /**
78
- * SYSTEM_TEMPLATE must stay byte-identical across every request of a session:
79
- * provider prefix caches key on it, so changing it (or injecting per-turn
80
- * state) re-prices the whole context. That is why session state lives behind
81
- * meta tools instead. The cache rationale itself stays HERE in a comment —
82
- * the model does not need our cost-engineering notes every turn.
83
- */
84
77
  /**
85
78
  * Recent context kept VERBATIM through a compaction, in tokens.
86
79
  *
@@ -235,6 +228,13 @@ const SUMMARY_CLOSE = "\n</conversation_checkpoint>";
235
228
  * claim, and must not be able to change what it is judging.
236
229
  */
237
230
  const AUDIT_READ_ONLY_TOOLS = new Set(["read_file", "list_dir", "grep", "bash"]);
231
+ /**
232
+ * SYSTEM_TEMPLATE must stay byte-identical across every request of a session:
233
+ * provider prefix caches key on it, so changing it (or injecting per-turn
234
+ * state) re-prices the whole context. That is why session state lives behind
235
+ * meta tools instead. The cache rationale itself stays HERE in a comment —
236
+ * the model does not need our cost-engineering notes every turn.
237
+ */
238
238
  const SYSTEM_TEMPLATE = `You are a coding agent working autonomously inside a workspace.
239
239
 
240
240
  Most session state is not injected into prompts — fetch it with tools instead:
@@ -665,31 +665,14 @@ export class Agent {
665
665
  * are exempt — those are not "keep trying", they are "the operator/provider asked".
666
666
  */
667
667
  consecutiveCompactFailures = 0;
668
- /**
669
- * Rescan skill roots — but only when something actually changed.
670
- * This used to run a readdir per root plus a read of every SKILL.md at the
671
- * top of EVERY turn; the cache is only ever read by list_skills, so that
672
- * was pure I/O on the hot path. A cheap stat fingerprint of the roots and
673
- * their SKILL.md files gates the real scan, so a turn only pays when a
674
- * skill was added, edited or removed (save_skill writes one, so a
675
- * self-created skill still shows up on the very next turn).
676
- */
677
- /**
678
- * Render the skill catalogue for the system prompt.
679
- *
680
- * Name + description only, one line each — the full SKILL.md is what
681
- * load_skill() is for, and this block goes into EVERY request of the session,
682
- * so it has to stay small. The description is what the model matches its task
683
- * against, so a skill with a poor description is effectively undiscoverable;
684
- * that is worth knowing when authoring one.
685
- */
686
668
  /**
687
669
  * A bounded DIGEST of memory.md: every heading, plus its first line.
688
670
  *
689
- * they are genuinely long — 2.6KB is the median, and the largest hold full project briefs. A
690
- * model that never sees them cannot decide to consult them: that is the whole finding of #48
691
- * (182 writes, 13 reads after a write). A model that sees the WHOLE file pays for it on every
692
- * request and breaks the prefix cache.
671
+ * Memory notes are worth this much because they are what a later session reads instead of
672
+ * re-learning the workspace: they are genuinely long — 2.6KB is the median, and the largest hold
673
+ * full project briefs. A model that never sees them cannot decide to consult them: that is the
674
+ * whole finding of #48 (182 writes, 13 reads after a write). A model that sees the WHOLE file
675
+ * pays for it on every request and breaks the prefix cache.
693
676
  *
694
677
  * Codex solves this with a `MEMORY_SUMMARY` in the prompt plus a searchable registry on disk.
695
678
  * This is the same shape: the digest says what memory HOLDS, `read_memory(query)` fetches the
@@ -950,14 +933,6 @@ export class Agent {
950
933
  // `needs_operator` snapshot reported `live: false` until this was added.
951
934
  this.status === "needs_operator");
952
935
  }
953
- /**
954
- * Is this agent doing in-flight work that a WAITER (wait_children) should
955
- * hold for? Deliberately narrower than isLive(): queued prompts are DORMANT
956
- * work (a stopped/idle child touches them only after it is started), so
957
- * they must not keep a parent parked on a child that is visibly idle —
958
- * that wait would hang. isLive() answers "stop vs start" (#59/#133); this
959
- * answers "is work in flight".
960
- */
961
936
  /**
962
937
  * The text of every prompt still waiting for a turn boundary.
963
938
  *
@@ -968,6 +943,14 @@ export class Agent {
968
943
  pendingPromptTexts() {
969
944
  return this.pendingPrompts.map((p) => p.text);
970
945
  }
946
+ /**
947
+ * Is this agent doing in-flight work that a WAITER (wait_children) should
948
+ * hold for? Deliberately narrower than isLive(): queued prompts are DORMANT
949
+ * work (a stopped/idle child touches them only after it is started), so
950
+ * they must not keep a parent parked on a child that is visibly idle —
951
+ * that wait would hang. isLive() answers "stop vs start" (#59/#133); this
952
+ * answers "is work in flight".
953
+ */
971
954
  isBusy() {
972
955
  if (this.stopRequested)
973
956
  return false;
@@ -977,6 +960,11 @@ export class Agent {
977
960
  this.parkedByTool ||
978
961
  this.compactPhase !== "");
979
962
  }
963
+ /** recently read workspace files (most recent first) — post-compact restore */
964
+ recentReads = [];
965
+ trackFileRead(p) {
966
+ this.recentReads = [p, ...this.recentReads.filter((x) => x !== p)].slice(0, 5);
967
+ }
980
968
  /**
981
969
  * A background shell (bash background=true) finished. Two channels:
982
970
  * 1. a system_note row in the timeline — the operator sees the outcome
@@ -985,11 +973,6 @@ export class Agent {
985
973
  * agent learns exit code + output tail WITHOUT having to poll
986
974
  * bash_output blindly. Failed jobs say so loudly so the model reacts.
987
975
  */
988
- /** recently read workspace files (most recent first) — post-compact restore */
989
- recentReads = [];
990
- trackFileRead(p) {
991
- this.recentReads = [p, ...this.recentReads.filter((x) => x !== p)].slice(0, 5);
992
- }
993
976
  onBackgroundJobExit(info) {
994
977
  const ok = info.code === 0;
995
978
  const one = info.cmd.replace(/\s+/g, " ").slice(0, 80);
@@ -1097,11 +1080,6 @@ export class Agent {
1097
1080
  bus.emit("update", { kind: "agent-update", agentId: this.opts.id });
1098
1081
  }
1099
1082
  }
1100
- /**
1101
- * Restore the conversation from the JSONL log exactly once, on demand.
1102
- * Everything that touches history (prompts, start, fork, UI selection)
1103
- * funnels through here; boot stays cheap no matter how many sessions exist.
1104
- */
1105
1083
  /**
1106
1084
  * Resolve the session/workspace this agent operates on.
1107
1085
  *
@@ -1272,16 +1250,6 @@ export class Agent {
1272
1250
  /* keep the session copy if the agent-wide write failed — losing notes is worse */
1273
1251
  }
1274
1252
  }
1275
- /**
1276
- * Rebuild the in-memory conversation from the JSONL event log so a restart
1277
- * continues where the agent left off instead of starting blank.
1278
- *
1279
- * Strategy: find the most recent event overall (its branch is where the
1280
- * agent was), walk the `parent` chain backwards (crossing fork points),
1281
- * then replay events forward into ChatMessages. Meta-tool calls
1282
- * (finish / report_progress) never produced logged tool results, so we
1283
- * synthesize their responses to keep the message sequence valid.
1284
- */
1285
1253
  /**
1286
1254
  * Resolve a leading sub_fork header: load the parent session's file (and
1287
1255
  * recurse if THAT one is itself a sub_fork), take its lineage up to the
@@ -1323,6 +1291,17 @@ export class Agent {
1323
1291
  prefix.shift();
1324
1292
  return [...prefix, ...own.filter((e) => e !== header)];
1325
1293
  }
1294
+ /**
1295
+ * Restore the conversation from the JSONL log exactly once, on demand.
1296
+ * Everything that touches history (prompts, start, fork, UI selection)
1297
+ * funnels through here; boot stays cheap no matter how many sessions exist.
1298
+ *
1299
+ * Strategy: find the most recent event overall (its branch is where the
1300
+ * agent was), walk the `parent` chain backwards (crossing fork points),
1301
+ * then replay events forward into ChatMessages. Meta-tool calls
1302
+ * (finish / report_progress) never produced logged tool results, so we
1303
+ * synthesize their responses to keep the message sequence valid.
1304
+ */
1326
1305
  async restoreFromLog() {
1327
1306
  const own = await readEvents(this.log.filePath);
1328
1307
  if (own.length === 0)
@@ -1637,14 +1616,6 @@ export class Agent {
1637
1616
  await fs.writeFile(this.todoFile, text, "utf8");
1638
1617
  await this.log.append("todo", this.currentSession, this.currentBranch, { event: "set", by });
1639
1618
  }
1640
- /**
1641
- * Update SPECIFIC checkbox items without rewriting the whole list — the
1642
- * full-replacement path made agents lazy (a mid-task progress tick meant
1643
- * re-emitting the entire markdown, so they skipped updates) and risky (one
1644
- * sloppy regeneration mangled unrelated sections). Items are matched by
1645
- * normalized text; unknown items are reported back instead of silently
1646
- * dropped. Returns a short human-readable summary for the tool result.
1647
- */
1648
1619
  /**
1649
1620
  * Apply surgical changes to todo.md.
1650
1621
  *
@@ -4592,7 +4563,6 @@ export class Agent {
4592
4563
  bus.emit("update", { kind: "compaction-progress", agentId: this.opts.id, phase: "done" });
4593
4564
  }
4594
4565
  }
4595
- /** Ask the model for dense continuation notes over the compacted range. */
4596
4566
  /**
4597
4567
  * Summarize `old` into continuation notes.
4598
4568
  *
package/dist/master.js CHANGED
@@ -19,7 +19,6 @@ import { bus } from "./bus.js";
19
19
  import { mcp } from "./agent/mcp-registry.js";
20
20
  import { loadPlugins } from "./agent/plugins.js";
21
21
  import { fetchModelList, peekModelList, contextLengthFor, isReasoningEffort, } from "./model-meta.js";
22
- /** how deep sub-agent spawning may nest (parent=0, its subs=1, …) */
23
22
  /**
24
23
  * Did the effective data dir come from an ENV OVERRIDE that disagrees with the
25
24
  * config file's own `dataDir`?
@@ -282,7 +281,6 @@ export function loadConfig(configPath) {
282
281
  providers: Object.fromEntries(Object.entries(user.providers ?? {}).map(([name, p]) => [name, migrateProviderKeys(p)])),
283
282
  };
284
283
  }
285
- /** Raw parsed user config (for lossless persistence of web edits). */
286
284
  /**
287
285
  * Can this path be replaced by an atomic rename?
288
286
  *
@@ -587,7 +585,6 @@ export class Master {
587
585
  this.configDirty = false;
588
586
  this.saveConfig();
589
587
  }
590
- /** write-and-rename, so config.json is never observed empty or half-written */
591
588
  /**
592
589
  * The shared pool for a provider, created on first use.
593
590
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.37.49",
3
+ "version": "0.37.51",
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",
@@ -37,6 +37,8 @@
37
37
  "typecheck-web": "tsc -p tsconfig.frontend.json",
38
38
  "typecheck:scripts": "tsc -p tsconfig.scripts.json",
39
39
  "lint": "oxlint src frontend test scripts",
40
+ "lint:comments": "node scripts/find-stacked-comments.ts",
41
+ "lint:comments:strict": "node scripts/find-stacked-comments.ts --strict",
40
42
  "start": "node dist/index.js",
41
43
  "dev": "node src/index.ts",
42
44
  "dev-web": "vite",