pi-roundtable-drawing 0.9.2 → 0.9.5

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/CHANGELOG.md CHANGED
@@ -5,6 +5,18 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.9.5] - 2026-10-10
9
+
10
+ 0.9.4 was tagged but never published (its release checks failed on the recorded prompt texts); everything listed under it is released in 0.9.5.
11
+
12
+ ### Added
13
+
14
+ - Every image tool also saves its picture as `<scratchDir>/drawings/<tool>-<timestamp>.png`, or under the workspace without a scratch dir, and names the path in its result, so the model can send it on with `attach_file` or a `discord_send_message` file `path`. It still attaches the picture to the reply, and a session with neither root (with pi-roundtable before 0.9.5, any session) only attaches it.
15
+
16
+ ## [0.9.3] - 2026-10-10
17
+
18
+ - Release in lockstep with pi-roundtable 0.9.3; no package-specific behavior changes.
19
+
8
20
  ## [0.9.2] - 2026-10-10
9
21
 
10
22
  - Release in lockstep with pi-roundtable 0.9.2; no package-specific behavior changes.
package/README.md CHANGED
@@ -106,6 +106,8 @@ Each tool draws one PNG and attaches it to the agent's reply with `turn.attachFi
106
106
  The model reads a short text back (the file name, and for card draws the cards), and a request it can fix comes back as a refusal with a plain message, such as `An edge references the unknown node "Ghost". Add it to nodes or fix the edge.`
107
107
  Nothing is attached for a refused call.
108
108
 
109
+ In a session with a workspace (an agent's), the tool also saves the picture as `<scratchDir>/drawings/<tool>-<timestamp>.png`, or under the workspace when there is no scratch dir, and its result names that path, so the model can send the picture elsewhere with `attach_file` or a `discord_send_message` file `path` (pi-roundtable 0.9.5 or later). The picture's bytes never return to the model. A session with neither, such as a sandbox or an owner conversation, only attaches it.
110
+
109
111
  The tool does not send a message of its own: the host delivers the picture with the reply of the turn, after the agent's text, under the same name and avatar as the text.
110
112
  On Discord each picture goes out as one file in its own message.
111
113
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-roundtable-drawing",
3
- "version": "0.9.2",
3
+ "version": "0.9.5",
4
4
  "description": "Relationship maps, magic circles, sigils, sacred geometry, and card spreads drawn locally for pi-roundtable",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  "devDependencies": {
49
49
  "@biomejs/biome": "2.5.15",
50
50
  "@types/bun": "1.4.2",
51
- "pi-roundtable": "0.9.2",
51
+ "pi-roundtable": "0.9.5",
52
52
  "typescript": "7.0.2"
53
53
  }
54
54
  }
@@ -1,3 +1,5 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { join } from "node:path";
1
3
  import {
2
4
  defineTool,
3
5
  REPLY_FILE_LIMITS,
@@ -63,7 +65,31 @@ function checkArguments<Schema extends TObject>(
63
65
  }
64
66
 
65
67
  /**
66
- * A tool that draws one image and attaches it to the agent's reply as a file. A request the renderer
68
+ * Saves a picture under the session's scratch dir, or its workspace without one, so the model can
69
+ * send it on by path; says where, or why not. A session with neither saves nothing.
70
+ */
71
+ async function saveDrawing(
72
+ tool: string,
73
+ image: Uint8Array,
74
+ turn: ToolTurn,
75
+ ): Promise<string | undefined> {
76
+ const root = turn.workspace?.scratchDir ?? turn.workspace?.workspace;
77
+ if (root === undefined) return undefined;
78
+ const dir = join(root, "drawings");
79
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
80
+ const path = join(dir, `${tool}-${stamp}.png`);
81
+ try {
82
+ await mkdir(dir, { recursive: true });
83
+ await writeFile(path, image, { flag: "wx" });
84
+ } catch (error) {
85
+ return `It was not saved to a file (${error instanceof Error ? error.message : String(error)}).`;
86
+ }
87
+ return `It is also saved at ${path}; to send it elsewhere, give this path to discord_send_message or attach_file rather than reading it.`;
88
+ }
89
+
90
+ /**
91
+ * A tool that draws one image, attaches it to the agent's reply as a file, and saves it in the
92
+ * session's scratch dir or workspace when it has one. A request the renderer
67
93
  * cannot honour comes back to the model as a refusal it can correct.
68
94
  */
69
95
  export function imageTool<Schema extends TObject>(
@@ -91,7 +117,9 @@ export function imageTool<Schema extends TObject>(
91
117
  `The picture is ${(drawn.image.byteLength / 2 ** 20).toFixed(1)} MiB, over the ${REPLY_FILE_LIMITS.maxFileBytes / 2 ** 20} MiB a reply may carry. Ask for a smaller picture, such as fewer cards or a smaller size.`,
92
118
  );
93
119
  turn.attachFile({ name, data: drawn.image });
94
- return drawn.text.replace("{file}", name);
120
+ const text = drawn.text.replace("{file}", name);
121
+ const saved = await saveDrawing(spec.name, drawn.image, turn);
122
+ return saved ? `${text}\n\n${saved}` : text;
95
123
  },
96
124
  });
97
125
  }