teapot-coding-agent 0.37.68 → 0.37.69
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/agent.js +20 -1
- package/dist/agent/tools.js +31 -0
- package/package.json +1 -1
- package/public/assets/{index-CFlcSO24.js → index-BlOQv88N.js} +4 -4
- package/public/assets/{index-CFlcSO24.js.map → index-BlOQv88N.js.map} +1 -1
- package/public/assets/{shiki-Dk2Or2eZ.js → shiki-frNMxtMU.js} +2 -2
- package/public/assets/{shiki-Dk2Or2eZ.js.map → shiki-frNMxtMU.js.map} +1 -1
- package/public/index.html +1 -1
package/dist/agent/agent.js
CHANGED
|
@@ -1850,7 +1850,26 @@ export class Agent {
|
|
|
1850
1850
|
durationMs: Date.now() - t0,
|
|
1851
1851
|
result: capped,
|
|
1852
1852
|
});
|
|
1853
|
-
|
|
1853
|
+
/**
|
|
1854
|
+
* A tool may hand the model an IMAGE (read_file on a picture).
|
|
1855
|
+
*
|
|
1856
|
+
* It rides on the tool message as `content_parts`, which `sanitize()` already knows how to send
|
|
1857
|
+
* to a vision model. The text stays in `content` so the log, the timeline and a non-vision model
|
|
1858
|
+
* all still see something meaningful.
|
|
1859
|
+
*/
|
|
1860
|
+
this.messages.push({
|
|
1861
|
+
role: "tool",
|
|
1862
|
+
tool_call_id: call.id,
|
|
1863
|
+
content: capped,
|
|
1864
|
+
...(result.image
|
|
1865
|
+
? {
|
|
1866
|
+
content_parts: [
|
|
1867
|
+
{ type: "text", text: capped },
|
|
1868
|
+
{ type: "image_url", image_url: { url: result.image.url } },
|
|
1869
|
+
],
|
|
1870
|
+
}
|
|
1871
|
+
: {}),
|
|
1872
|
+
});
|
|
1854
1873
|
this.consecutiveToolErrors = result.ok ? 0 : this.consecutiveToolErrors + 1;
|
|
1855
1874
|
if (this.consecutiveToolErrors >= this.opts.maxConsecutiveToolErrors) {
|
|
1856
1875
|
await this.log.append("system_note", this.currentSession, this.currentBranch, {
|
package/dist/agent/tools.js
CHANGED
|
@@ -32,6 +32,13 @@ export async function looksBinary(p) {
|
|
|
32
32
|
await fh.close();
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
|
+
/** the `looksBinary()` kinds that are pictures a vision model can actually receive */
|
|
36
|
+
const IMAGE_MIME = {
|
|
37
|
+
"PNG image": "image/png",
|
|
38
|
+
"JPEG image": "image/jpeg",
|
|
39
|
+
"GIF image": "image/gif",
|
|
40
|
+
"WEBP image": "image/webp",
|
|
41
|
+
};
|
|
35
42
|
function sniff(head) {
|
|
36
43
|
const hex = (n) => head.subarray(0, n).toString("hex");
|
|
37
44
|
if (hex(8).startsWith("89504e47"))
|
|
@@ -40,6 +47,9 @@ function sniff(head) {
|
|
|
40
47
|
return "JPEG image";
|
|
41
48
|
if (head.subarray(0, 4).toString("latin1") === "GIF8")
|
|
42
49
|
return "GIF image";
|
|
50
|
+
// RIFF....WEBP — the four bytes after "RIFF" are the size, so "WEBP" sits at offset 8
|
|
51
|
+
if (head.subarray(0, 4).toString("latin1") === "RIFF" && head.subarray(8, 12).toString("latin1") === "WEBP")
|
|
52
|
+
return "WEBP image";
|
|
43
53
|
if (head.subarray(0, 4).toString("latin1") === "PK\x03\x04")
|
|
44
54
|
return "zip archive";
|
|
45
55
|
if (hex(4) === "25504446")
|
|
@@ -1045,6 +1055,27 @@ export const TOOLS = [
|
|
|
1045
1055
|
try {
|
|
1046
1056
|
const probe = await looksBinary(p);
|
|
1047
1057
|
if (probe.binary) {
|
|
1058
|
+
/**
|
|
1059
|
+
* An IMAGE is handed to the model as an image; anything else is still refused.
|
|
1060
|
+
*
|
|
1061
|
+
* Refusing images was never a feature — it was the fix for returning raw bytes as mangled
|
|
1062
|
+
* text (`d907af3`). But the operator's point is the asymmetry: a user can attach a picture
|
|
1063
|
+
* and the agent cannot, so the agent is asked to work on files it is not allowed to see.
|
|
1064
|
+
* MEASURED: `read_file` had never been able to return an image at all; the multimodal commit
|
|
1065
|
+
* (`257333a`) was the USER path (composer → `/prompt`), not a tool.
|
|
1066
|
+
*/
|
|
1067
|
+
const mime = IMAGE_MIME[probe.kind];
|
|
1068
|
+
if (mime) {
|
|
1069
|
+
const buf = await fs.readFile(p);
|
|
1070
|
+
const name = path.basename(p);
|
|
1071
|
+
return {
|
|
1072
|
+
ok: true,
|
|
1073
|
+
result: `${str(args.path)} is a ${probe.kind} (${probe.bytes} bytes) — the image itself is ` +
|
|
1074
|
+
`attached to this message, so you are looking at it. Describe or reason about it ` +
|
|
1075
|
+
`directly; there is no need to decode bytes.`,
|
|
1076
|
+
image: { url: `data:${mime};base64,${buf.toString("base64")}`, name },
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1048
1079
|
return {
|
|
1049
1080
|
ok: false,
|
|
1050
1081
|
result: `${str(args.path)} is a ${probe.kind} (${probe.bytes} bytes), not text — read_file returns ` +
|