teapot-coding-agent 0.17.5 → 0.17.7

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/agent/llm.js CHANGED
@@ -181,6 +181,9 @@ export async function chatStream(cfg, messages, tools, signal, onDelta) {
181
181
  messages: sanitize(messages),
182
182
  ...(tools.length ? { tools } : {}),
183
183
  stream: true,
184
+ // ask providers to include the final usage chunk in streaming mode
185
+ // (OpenAI-compatible; harmless where unsupported)
186
+ stream_options: { include_usage: true },
184
187
  }, { signal });
185
188
  const calls = {};
186
189
  let finishReason;
@@ -555,10 +555,21 @@ export const TOOLS = [
555
555
  required: ["path", "content"],
556
556
  },
557
557
  async run(args, ctx) {
558
+ // content must be a real string: a non-string (object/array from bad
559
+ // model output) coerced to "[object Object]" or silently truncated —
560
+ // worse, an undefined slipped past required and WIPED the file
561
+ if (typeof args.content !== "string") {
562
+ return {
563
+ ok: false,
564
+ result: "content must be a string (got " +
565
+ (args.content === null ? "null" : typeof args.content) +
566
+ ") — nothing was written",
567
+ };
568
+ }
558
569
  const p = safeJoin(ctx.cwd, str(args.path));
559
570
  await fs.mkdir(path.dirname(p), { recursive: true });
560
- await fs.writeFile(p, str(args.content), "utf8");
561
- return { ok: true, result: `wrote ${p} (${String(args.content).length} bytes)` };
571
+ await fs.writeFile(p, args.content, "utf8");
572
+ return { ok: true, result: `wrote ${p} (${args.content.length} bytes)` };
562
573
  },
563
574
  },
564
575
  {
@@ -12,6 +12,13 @@ const BOUNDS = [
12
12
  ];
13
13
  function parseField(part, min, max) {
14
14
  const out = new Set();
15
+ const clampOrThrow = (v) => {
16
+ // out-of-range values used to slip through silently and produce a set
17
+ // that NEVER matched (e.g. "99 * * * *" ran never) — reject loudly
18
+ if (!Number.isInteger(v) || v < min || v > max)
19
+ throw new Error(`bad cron field: ${part} (${min}-${max} expected)`);
20
+ return v;
21
+ };
15
22
  for (const piece of part.split(",")) {
16
23
  const [range, stepStr] = piece.split("/");
17
24
  const step = Math.max(1, Number(stepStr ?? 1) || 1);
@@ -21,15 +28,15 @@ function parseField(part, min, max) {
21
28
  }
22
29
  else if (range.includes("-")) {
23
30
  const [a, b] = range.split("-").map(Number);
24
- if (Number.isNaN(a) || Number.isNaN(b))
25
- throw new Error(`bad cron field: ${part}`);
31
+ clampOrThrow(a);
32
+ clampOrThrow(b);
33
+ if (a > b)
34
+ throw new Error(`bad cron field: ${part} (inverted range)`);
26
35
  for (let i = a; i <= b; i += step)
27
36
  out.add(i);
28
37
  }
29
38
  else {
30
- const v = Number(range);
31
- if (Number.isNaN(v))
32
- throw new Error(`bad cron field: ${part}`);
39
+ const v = clampOrThrow(Number(range));
33
40
  if (!stepStr || step === 1) {
34
41
  out.add(v);
35
42
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.17.5",
3
+ "version": "0.17.7",
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",