teapot-coding-agent 0.37.73 → 0.37.74

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.
@@ -1403,6 +1403,40 @@ export function buildApp(master) {
1403
1403
  };
1404
1404
  const ALLOWED_WIDTHS = [640, 1280, 1920, 2560, 3840];
1405
1405
  app.get("/api/assets/meta", (c) => c.json({ widths: ALLOWED_WIDTHS }));
1406
+ /**
1407
+ * Image bytes a session logged, served so the TIMELINE can show them.
1408
+ *
1409
+ * `read_file` and a prompt's attachment store `blobs/<sha256>.<ext>` beside `chat.jsonl`; the log is
1410
+ * read by a person, so the reference has to be resolvable from the browser. MEASURED before this
1411
+ * existed: `<img src="blobs/3220fc….png">` resolved against `/session/blobs/…`, 404'd, and every
1412
+ * attachment and tool image rendered broken (`naturalWidth: 0`).
1413
+ *
1414
+ * `:id` is an AGENT, not a path, so the directory is decided by what teapot already knows rather
1415
+ * than by the request — and the name is validated as a basename with a hash shape, so this cannot
1416
+ * read anything outside that session's `blobs/`.
1417
+ */
1418
+ app.get("/api/agents/:id/blobs/:name", async (c) => {
1419
+ const agent = master.agents.get(c.req.param("id"));
1420
+ if (!agent)
1421
+ return c.notFound();
1422
+ const name = c.req.param("name") ?? "";
1423
+ if (!/^[a-f0-9]{64}\.(png|jpg|jpeg|gif|webp)$/i.test(name))
1424
+ return c.notFound();
1425
+ const file = path.join(agent.snapshot().sessionDir, "blobs", name);
1426
+ try {
1427
+ const buf = await fs.readFile(file);
1428
+ const ext = name.split(".").pop().toLowerCase();
1429
+ const mime = ext === "png" ? "image/png" : ext === "gif" ? "image/gif" : ext === "webp" ? "image/webp" : "image/jpeg";
1430
+ // content-addressed, so the bytes for a name never change
1431
+ return c.body(buf, 200, {
1432
+ "content-type": mime,
1433
+ "cache-control": "public, max-age=31536000, immutable",
1434
+ });
1435
+ }
1436
+ catch {
1437
+ return c.notFound();
1438
+ }
1439
+ });
1406
1440
  app.get("/api/assets/:name", async (c) => {
1407
1441
  const name = assetName(c.req.param("name") ?? "");
1408
1442
  if (!name)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "teapot-coding-agent",
3
- "version": "0.37.73",
3
+ "version": "0.37.74",
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",