agent-repl 0.1.0 → 0.1.2
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 +20 -0
- package/README.md +7 -2
- package/dist/pi-repl.js +6 -5
- package/dist/shared/repl-session-stop.js +12 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,26 @@ All notable changes to `agent-repl` are documented here.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.1.2] — 2026-10-19
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
- Generated from pi-repl 0.8.3.
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- agent-repl works when the locale is not UTF-8, for example with `LANG` unset. Commands and tools failed with "no panes found", because tmux printed the tabs in its pane listings as `_`.
|
|
14
|
+
|
|
15
|
+
## [0.1.1] — 2026-10-19
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- Generated from pi-repl 0.8.2.
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
- `agent-repl stop` no longer refuses to run on macOS when a process of `nobody` (uid -2) is running.
|
|
22
|
+
- IPython shows an expression's value once, as `Out[N]: …`. It used to be printed again as a bare `repr`.
|
|
23
|
+
|
|
24
|
+
### Documentation
|
|
25
|
+
- Set `PI_REPL_AGENT_LABEL` in the OpenCode configuration example: OpenCode 2 identifies itself to MCP servers only as `cli`.
|
|
26
|
+
|
|
7
27
|
## [0.1.0] — 2026-09-25
|
|
8
28
|
|
|
9
29
|
First public release.
|
package/README.md
CHANGED
|
@@ -73,7 +73,12 @@ codex mcp add agent-repl -- agent-repl mcp
|
|
|
73
73
|
```json
|
|
74
74
|
{
|
|
75
75
|
"mcp": {
|
|
76
|
-
"agent-repl": {
|
|
76
|
+
"agent-repl": {
|
|
77
|
+
"type": "local",
|
|
78
|
+
"command": ["agent-repl", "mcp"],
|
|
79
|
+
"environment": { "PI_REPL_AGENT_LABEL": "OpenCode" },
|
|
80
|
+
"enabled": true
|
|
81
|
+
}
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
```
|
|
@@ -125,7 +130,7 @@ An agent can also pass `echoMode` for a single `repl_send`. Full mode keeps long
|
|
|
125
130
|
|
|
126
131
|
## Agent names in the record
|
|
127
132
|
|
|
128
|
-
Entries in the clean record, and in `agent-repl export`, are labelled with the name the agent gives when it connects: `Claude Code
|
|
133
|
+
Entries in the clean record, and in `agent-repl export`, are labelled with the name the agent gives when it connects: `Claude Code` and `Codex` name themselves, and other agents are labelled with whatever name they send. To choose the label yourself, set `PI_REPL_AGENT_LABEL` in the server's environment, as for the echo setting. OpenCode 2 identifies itself only as `cli`, so the OpenCode configuration above sets the label. Submissions from Pi are labelled `Pi`.
|
|
129
134
|
|
|
130
135
|
## Privacy and safety
|
|
131
136
|
|
package/dist/pi-repl.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// GENERATED by scripts/sync-from-pi-repl.mjs from pi-repl 0.8.
|
|
1
|
+
// GENERATED by scripts/sync-from-pi-repl.mjs from pi-repl 0.8.3 (45b5042). Do not edit by hand:
|
|
2
2
|
// change pi-repl and re-sync. Identical to pi-repl's index.ts except that its Pi
|
|
3
3
|
// imports come from ./pi-compat/index.js.
|
|
4
4
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, truncateTail, } from "./pi-compat/index.js";
|
|
@@ -459,7 +459,9 @@ async function commandExists(pi, command, cwd) {
|
|
|
459
459
|
}
|
|
460
460
|
}
|
|
461
461
|
async function execTmux(pi, args, cwd, timeout = 5_000) {
|
|
462
|
-
|
|
462
|
+
// Unless the locale is UTF-8 (LANG is unset in some terminals), tmux prints
|
|
463
|
+
// the tabs in -F output as `_`; -u keeps them, so pane listings parse.
|
|
464
|
+
const result = await pi.exec("tmux", ["-u", ...args], { cwd, timeout });
|
|
463
465
|
return {
|
|
464
466
|
code: result.code ?? 1,
|
|
465
467
|
stdout: result.stdout ?? "",
|
|
@@ -898,9 +900,8 @@ function buildPythonControlSource(runtime, code, doneFile, display) {
|
|
|
898
900
|
" __pi_repl_ip = get_ipython()",
|
|
899
901
|
" if __pi_repl_ip is None:",
|
|
900
902
|
" raise RuntimeError('Expected IPython session, but get_ipython() returned None.')",
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
" print(repr(__pi_repl_result.result))",
|
|
903
|
+
// run_cell shows the value itself (Out[N]: ...), so nothing more is printed here.
|
|
904
|
+
` __pi_repl_ip.run_cell(${JSON.stringify(code)}, store_history=False)`,
|
|
904
905
|
"except Exception:",
|
|
905
906
|
" __pi_repl_traceback.print_exc()",
|
|
906
907
|
"finally:",
|
|
@@ -12,7 +12,8 @@ const paneFormat = "#{pid}\t#{session_id}\t#{session_name}\t#{session_created}\t
|
|
|
12
12
|
export function parseStopProcessTable(text) {
|
|
13
13
|
return text.split("\n").filter((line) => line.trim()).map((line) => {
|
|
14
14
|
const f = line.trim().split(/\s+/);
|
|
15
|
-
|
|
15
|
+
// macOS lists processes of `nobody` with uid -2; it never matches ours.
|
|
16
|
+
if (f.length !== 11 || !f.slice(0, 4).every((v, i) => (i === 3 ? /^-?\d+$/ : /^\d+$/).test(v) && Number.isSafeInteger(Number(v)))) throw new Error("Could not parse process ownership data; refusing unsafe shutdown.");
|
|
16
17
|
return { pid: Number(f[0]), ppid: Number(f[1]), pgid: Number(f[2]), uid: Number(f[3]), tty: f[4], startedAt: f.slice(5, 10).join(" "), state: f[10] };
|
|
17
18
|
});
|
|
18
19
|
}
|
|
@@ -152,9 +153,16 @@ export class SessionStopProcesses {
|
|
|
152
153
|
}
|
|
153
154
|
}
|
|
154
155
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
export const PI_REPL_STOP_SESSION_NAME = /^pi-repl-(python|julia|r|ghci|clojure|ruby|java|octave|matlab|gnuplot|cpp)$/;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Explicit stop only. Never used by send, startup, reload, or shutdown hooks.
|
|
160
|
+
* sessionNamePattern names the sessions this host owns; pi-studio passes its own.
|
|
161
|
+
*/
|
|
162
|
+
export async function stopVerifiedReplSession({ tmux, sessionName, sessionNamePattern = PI_REPL_STOP_SESSION_NAME, snapshot = readStopProcessTable, signal = (pid, value) => process.kill(pid, value), currentPid = process.pid, uid = process.getuid?.(), graceMs = 500, termMs = 1000, killMs = 2000, findMarked = findOwnedGnuplotProcesses, snapshotSockets = snapshotGnuplotSockets, cleanupSockets = cleanupGnuplotSockets }) {
|
|
163
|
+
// A global or sticky pattern keeps state between tests; refuse it rather than misjudge a name.
|
|
164
|
+
if (!(sessionNamePattern instanceof RegExp) || sessionNamePattern.global || sessionNamePattern.sticky) throw new Error("Verified REPL shutdown needs a plain session-name pattern.");
|
|
165
|
+
if (typeof uid !== "number" || typeof sessionName !== "string" || !sessionNamePattern.test(sessionName)) throw new Error("Verified REPL shutdown requires a supported local Unix session.");
|
|
158
166
|
const panes = await readPanes(tmux);
|
|
159
167
|
const selected = panes.filter((p) => p.sessionName === sessionName);
|
|
160
168
|
if (!selected.length) throw new Error("The selected session ended or changed before shutdown; no processes were signalled.");
|
package/package.json
CHANGED