pi-roundtable 0.19.0 → 0.19.1

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,13 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.19.1] - 2026-10-18
9
+
10
+ ### Fixed
11
+
12
+ - The core's tests no longer depend on the host's `mktemp` or on a free-port probe. The check that the agents' `bash` writes into the scratch dir ran a bare `mktemp`, which macOS's BSD `mktemp` sends to the user temp dir whatever `TMPDIR` says; it now runs `mktemp -p "$TMPDIR"`, which both implementations honour, and the bare `mktemp` is held to the scratch dir only where GNU `mktemp` is the default. The listener tests found a free port by opening one and closing it, and another process could take it before the listener bound it (`EADDRINUSE`); they now let the listener bind port 0 and ask which port it got.
13
+ - The docs say what the scratch dir promises: the agents' shell runs with `TMPDIR` pointing to it, so tools that read `TMPDIR` and GNU `mktemp` write there, while macOS's BSD `mktemp` ignores `TMPDIR` for a bare `mktemp` and needs `mktemp -p "$TMPDIR"`. The agents' prompt is unchanged: it already sends temporary files to `$TMPDIR` and never asks for a bare `mktemp`.
14
+
8
15
  ## [0.19.0] - 2026-10-17
9
16
 
10
17
  ### Added
package/docs/plugins.md CHANGED
@@ -918,7 +918,7 @@ defineTool({
918
918
 
919
919
  `shellHoldRule` in `pi-roundtable/kit` judges the agents' `bash`, `write` and `edit` calls; the agent server links it, and a session without a workspace has no shell.
920
920
  Its scratch roots are the shared workspace (`HoldContext.workspace`) and the scratch dir (`HoldContext.scratchDir`).
921
- The scratch dir is the config's `scratchDir`, by default `<os temp dir>/<discord.rootCommand>-scratch` (such as `/tmp/roundtable-scratch`); the agent server creates it with mode 0700 at startup and refuses one that is a symlink or another user's, and the agents' `bash` runs with `TMPDIR` pointing to it, so `mktemp` and tools write there.
921
+ The scratch dir is the config's `scratchDir`, by default `<os temp dir>/<discord.rootCommand>-scratch` (such as `/tmp/roundtable-scratch`); the agent server creates it with mode 0700 at startup and refuses one that is a symlink or another user's, and the agents' `bash` runs with `TMPDIR` pointing to it, so tools that read `TMPDIR` and GNU `mktemp` write there. macOS's BSD `mktemp` ignores `TMPDIR` for a bare `mktemp`; `mktemp -p "$TMPDIR"` works with both.
922
922
  `AgentSessions.scratchDir` carries it to a runtime, and the agents' prompt tells them to write temporary files there, and to show a file with `attach_file` or a `discord_send_message` file `path` rather than reading an image or base64-encoding it.
923
923
 
924
924
  - A `>`, `>>` or `tee` target, and a `write` or `edit` path, inside a scratch root run; anything else, the rest of `/tmp` included, is held.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-roundtable",
3
- "version": "0.19.0",
3
+ "version": "0.19.1",
4
4
  "description": "A plugin-driven Pi agent server for one owner, reached through Discord or the web",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -225,6 +225,7 @@ export class HttpListeners {
225
225
  readonly #sockets: SocketSet;
226
226
  readonly #closeTimeoutMs: number;
227
227
  #servers: Pick<Server<unknown>, "stop">[] = [];
228
+ readonly #ports = new Map<string, number>();
228
229
 
229
230
  /** Validates every route before any socket opens; `closeTimeoutMs` bounds how long a stop waits on WebSocket handlers. */
230
231
  constructor(
@@ -271,9 +272,15 @@ export class HttpListeners {
271
272
  );
272
273
  chmodSync(listener.socketPath, listener.mode ?? 0o660);
273
274
  } else {
274
- this.#servers.push(
275
- serveTcp(listener.port, listener.hostname, handle, websocket),
275
+ const server = serveTcp(
276
+ listener.port,
277
+ listener.hostname,
278
+ handle,
279
+ websocket,
276
280
  );
281
+ this.#servers.push(server);
282
+ if (server.port !== undefined)
283
+ this.#ports.set(listener.id, server.port);
277
284
  }
278
285
  }
279
286
  } catch (error) {
@@ -282,6 +289,11 @@ export class HttpListeners {
282
289
  }
283
290
  }
284
291
 
292
+ /** The port an open TCP listener is bound to, which differs from the configured one when that was 0. */
293
+ portOf(listener: string): number | undefined {
294
+ return this.#ports.get(listener);
295
+ }
296
+
285
297
  /** The listener's WebSocket handler, sized for its largest route limits; none without WebSocket routes. */
286
298
  #websocketHandler(
287
299
  listener: string,
@@ -314,6 +326,7 @@ export class HttpListeners {
314
326
  this.#sockets.closeAll();
315
327
  for (const server of this.#servers) void server.stop(true);
316
328
  this.#servers = [];
329
+ this.#ports.clear();
317
330
  await this.#sockets.drain(this.#closeTimeoutMs);
318
331
  }
319
332
  }
@@ -472,7 +472,7 @@ export function imageTools(
472
472
  return images ? { generateImage: generateImageExtension(images, roots) } : {};
473
473
  }
474
474
 
475
- /** Pi's bash tool with TMPDIR set to `scratchDir`, so `mktemp` and tools write there. */
475
+ /** Pi's bash tool with TMPDIR set to `scratchDir`, so tools that read TMPDIR and GNU `mktemp` write there (BSD `mktemp` needs `-p "$TMPDIR"`). */
476
476
  export function scratchBash(cwd: string, scratchDir: string): ToolDefinition {
477
477
  // SAFETY: Pi's bash definition is a ToolDefinition typed by its own parameter schema, which the session takes as any custom tool.
478
478
  return createBashToolDefinition(cwd, {