oira666_pi-watcher 0.0.7 → 0.0.9
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/EVERGREEN.md +61 -0
- package/README.md +14 -7
- package/package.json +14 -12
- package/src/index.ts +0 -14
- package/src/ui.ts +1 -7
package/EVERGREEN.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
- Watcher is a persistent, independent conversation for observing and steering a running main agent.
|
|
3
|
+
- Opening, closing, copying, and picking a model must not prompt or stop either agent.
|
|
4
|
+
- Public touchpoints: extension commands/events/UI, session metadata, tool registration, and Pi RPC.
|
|
5
|
+
- The child wraps exported package-manager implementation methods before extensions load.
|
|
6
|
+
- Main history is read-only input; steering requires human approval and is refused during compaction.
|
|
7
|
+
- Main risks are CLI/resource-loading drift, session identity, lifecycle races, and overlay focus.
|
|
8
|
+
|
|
9
|
+
## Intent
|
|
10
|
+
Give the human a side conversation without interrupting or duplicating the main agent's work.
|
|
11
|
+
One side session belongs to each main session; reopening and restarting preserve its history/model.
|
|
12
|
+
Observe the supplied main JSONL path and bounded live-tool previews, not source or verification work
|
|
13
|
+
unless explicitly requested. This is a prompt policy, not a sandbox. Never change main model defaults
|
|
14
|
+
or its status bar. Closing the panel leaves the side conversation running.
|
|
15
|
+
|
|
16
|
+
## Pi integration (public APIs)
|
|
17
|
+
`src/index.ts` registers `/watcher`, session/agent/tool/compaction handlers, and approved
|
|
18
|
+
`sendUserMessage(..., { deliverAs })`. It reads session ID/file/leaf, idle state, and project trust.
|
|
19
|
+
The overlay uses `ctx.ui.custom`, native dialogs, clipboard helpers, themes, and pi-tui components.
|
|
20
|
+
The child registers `live_main_tools`/`steer_main`, adds prompt sections in `before_agent_start`,
|
|
21
|
+
and persists its main-session link using `appendEntry`/session entries.
|
|
22
|
+
RPC supplies prompts, state/history/models, queue clearing, abort, extension dialogs, and settlement.
|
|
23
|
+
The package manifest exposes `src/index.ts`; credentials/settings/resources follow normal Pi loading.
|
|
24
|
+
|
|
25
|
+
## Internal Pi APIs
|
|
26
|
+
Internal/undocumented coupling is present: `runtime-child.mjs` calls exported CLI `main()` and
|
|
27
|
+
patches `DefaultPackageManager.prototype.resolve` and `resolveExtensionSources`, relying on the
|
|
28
|
+
resolved extension-resource shape (`enabled`, path, source metadata) before imports occur.
|
|
29
|
+
`runtime-args.ts` discovers npm package layouts (`dist/bundle/index.js`, `dist/index.js`, CLI entry).
|
|
30
|
+
There are no direct private Pi module imports or private Pi instance-field accesses in runtime code.
|
|
31
|
+
The `__PI_WATCHER_CHILD__` global and Node IPC protocol belong to Watcher, not Pi.
|
|
32
|
+
Standalone binaries alone cannot supply the importable child runtime.
|
|
33
|
+
|
|
34
|
+
## Weak points
|
|
35
|
+
Resource/CLI changes can break exclusions, inherited flags, or nested subagent launches.
|
|
36
|
+
Session replacement, reload, startup cancellation, approval, and child exit must release locks/listeners.
|
|
37
|
+
RPC message framing, event names, model persistence, and compaction delivery boundaries matter.
|
|
38
|
+
Inherited extensions can start work/processes; exclusion is essential and permissions remain inherited.
|
|
39
|
+
UI tests simulate terminal components; e2e does not drive an actual interactive PTY or clipboard.
|
|
40
|
+
|
|
41
|
+
## Tests
|
|
42
|
+
`npm run test:evergreen` runs `npm test` (latest Pi dependencies, TypeScript check, unit/integration),
|
|
43
|
+
then `test/e2e/watcher.test.ts`. No lint script is configured.
|
|
44
|
+
Unit tests cover bounded live state, prompt identity/policy, CLI filtering, JSONL transport, and UI.
|
|
45
|
+
`runtime-integration.test.ts` uses real RPC children and an offline provider to protect prompt-free
|
|
46
|
+
startup, inheritance/exclusions, nested Pi launches, isolated models, resume, approval refusal,
|
|
47
|
+
session guards, locks, shutdown, and recovery. `extension.test.cjs` exercises command → overlay →
|
|
48
|
+
RPC → approval → main delivery, including compaction refusal and shutdown cancellation.
|
|
49
|
+
It also verifies that Watcher registers no keyboard shortcuts and that `/watcher` opens and reopens
|
|
50
|
+
the panel without prompting or losing the side conversation.
|
|
51
|
+
E2E launches `$EVERGREEN_PI_BIN` (or `pi` on PATH) with stdin ignored and uses
|
|
52
|
+
`$EVERGREEN_E2E_MODEL` (required) for real main/side model calls. It verifies reading actual main
|
|
53
|
+
history, live-tool access, unchanged main history, and exact side-history restoration after restart.
|
|
54
|
+
It copies configured auth/models into a temporary agent directory; credentials are never committed.
|
|
55
|
+
The child uses the latest modular npm Pi from devDependencies, even with a standalone global host.
|
|
56
|
+
|
|
57
|
+
## Relevant changelog topics
|
|
58
|
+
Watch extension loading/package-manager resources, CLI entry/layout/flags, trust and built-in MCP;
|
|
59
|
+
RPC framing/dialogs/settlement; session JSONL/resume/tools and model persistence; steering/compaction;
|
|
60
|
+
TUI key parsing, overlay focus/disposal, and clipboard support. Pi 1.4.0 includes relevant fixes for
|
|
61
|
+
concurrent dialogs and resumed extension-tool activation. Tested against Pi 1.4.0.
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
A persistent side-agent conversation for your running [Pi](https://pi.dev) agent.
|
|
4
4
|
|
|
5
|
-
**Package:** `oira666_pi-watcher` · **Version:** `0.0.
|
|
5
|
+
**Package:** `oira666_pi-watcher` · **Version:** `0.0.9` · **License:** MIT
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -10,7 +10,9 @@ A persistent side-agent conversation for your running [Pi](https://pi.dev) agent
|
|
|
10
10
|
pi install npm:oira666_pi-watcher
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
Then restart Pi or run `/reload`. Requires Node.js **22.19+** and the modular Pi installation.
|
|
13
|
+
Then restart Pi or run `/reload`. Requires Node.js **22.19+** and the modular Pi installation. The child launcher uses Pi's package-manager loading interfaces. Standalone compiled Pi binaries are not supported.
|
|
14
|
+
|
|
15
|
+
Tested against Pi 1.4.0.
|
|
14
16
|
|
|
15
17
|
For local development:
|
|
16
18
|
|
|
@@ -23,7 +25,6 @@ pi -e ./src/index.ts
|
|
|
23
25
|
|
|
24
26
|
| Command | Action |
|
|
25
27
|
| --- | --- |
|
|
26
|
-
| **Alt+W** | Toggle the Watcher panel without sending a prompt or stopping either agent. |
|
|
27
28
|
| `/watcher` | Create/resume the side agent and show its conversation. **Sends no prompt.** |
|
|
28
29
|
| `/watcher Why is it retrying?` | Open and send exactly that question. |
|
|
29
30
|
| `/watcher model` | Open the side agent's model picker. Does not prompt either agent. |
|
|
@@ -36,7 +37,7 @@ The panel opens immediately while the side process initializes its inherited ext
|
|
|
36
37
|
Inside the overlay:
|
|
37
38
|
|
|
38
39
|
- Type a question and press **Enter**. Use **Shift+Enter** for a newline.
|
|
39
|
-
- **
|
|
40
|
+
- **Esc** closes the conversation view without stopping either agent. In the model picker or an editor, it returns to the previous view. Use `/watcher` to reopen. Conversation, draft, and scroll position are retained.
|
|
40
41
|
- `/model` opens a searchable provider/model picker. Its selection affects **only the side agent** and is persisted in that side conversation. You can change models while the side agent is running, just like in Pi; switching does not abort the current reply.
|
|
41
42
|
- `/stop` cancels only the side agent and clears its queued messages.
|
|
42
43
|
- `/copy` copies to the system clipboard, because terminal mouse selection spans the full terminal width. Messages are numbered in the transcript (`You #n`, `Watcher #n`):
|
|
@@ -131,11 +132,17 @@ Closing only the overlay leaves the side agent alive, including any inherited MC
|
|
|
131
132
|
|
|
132
133
|
```sh
|
|
133
134
|
npm ci
|
|
134
|
-
|
|
135
|
-
npm test
|
|
135
|
+
mkdir -p tmp
|
|
136
|
+
TMPDIR="$PWD/tmp" npm test
|
|
136
137
|
npm pack --dry-run --ignore-scripts
|
|
137
138
|
```
|
|
138
139
|
|
|
139
|
-
|
|
140
|
+
`npm test` installs the direct Pi development dependencies at `@latest` with install scripts disabled, then runs `npm run check` and the tests. Tests target only the latest npm Pi release. Before each release, update the tested version above and the Pi peer dependency baseline after all checks pass. No lint command is configured.
|
|
141
|
+
|
|
142
|
+
`npm test` uses an offline scripted provider; no paid model calls are needed. These tests exercise the real RPC child, extension inheritance/exclusion, session resume, models, steering, main-extension integration, transport limits, live-tool cleanup, and overlay keyboard/rendering behavior.
|
|
143
|
+
|
|
144
|
+
For Evergreen, set `EVERGREEN_E2E_MODEL` to a configured `provider/model` and run `npm run test:evergreen`. It runs the suite above plus a real-model e2e test using `EVERGREEN_PI_BIN` (default: `pi` on PATH), with stdin ignored. The e2e host is the global Pi; its Watcher child uses the latest modular npm Pi development dependency. This test makes model calls, requires configured credentials, and checks main-history observation, live-tool access, read-only main history, and side-session restoration. See [EVERGREEN.md](EVERGREEN.md) for compatibility risks and coverage.
|
|
145
|
+
|
|
146
|
+
These are automated tests, not a manual terminal smoke test. README snippets are not separately executed.
|
|
140
147
|
|
|
141
148
|
The implementation is in `src/index.ts` (integration), `src/runtime*` (isolated child and RPC/IPC bridge), `src/live-tools.ts`, `src/config.ts`, and `src/ui*` (overlay).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oira666_pi-watcher",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "A persistent side-agent conversation for observing and steering your running Pi agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"files": [
|
|
26
26
|
"src/",
|
|
27
27
|
"README.md",
|
|
28
|
+
"EVERGREEN.md",
|
|
28
29
|
"LICENSE",
|
|
29
30
|
"watcher-settings.example.json"
|
|
30
31
|
],
|
|
@@ -35,22 +36,23 @@
|
|
|
35
36
|
},
|
|
36
37
|
"scripts": {
|
|
37
38
|
"check": "tsc --noEmit",
|
|
39
|
+
"pretest": "npm install @earendil-works/pi-ai@latest @earendil-works/pi-agent-core@latest @earendil-works/pi-coding-agent@latest @earendil-works/pi-tui@latest --save-dev --ignore-scripts && npm run check",
|
|
38
40
|
"test": "node --test test/*.test.ts test/*.test.cjs",
|
|
41
|
+
"test:evergreen": "node test/evergreen.mjs",
|
|
39
42
|
"prepack": "npm run check && npm test"
|
|
40
43
|
},
|
|
41
44
|
"peerDependencies": {
|
|
42
|
-
"@earendil-works/pi-
|
|
43
|
-
"@earendil-works/pi-
|
|
44
|
-
"@earendil-works/pi-coding-agent": "
|
|
45
|
-
"@earendil-works/pi-tui": "
|
|
45
|
+
"@earendil-works/pi-agent-core": ">=1.4.0",
|
|
46
|
+
"@earendil-works/pi-ai": ">=1.4.0",
|
|
47
|
+
"@earendil-works/pi-coding-agent": ">=1.4.0",
|
|
48
|
+
"@earendil-works/pi-tui": ">=1.4.0"
|
|
46
49
|
},
|
|
47
50
|
"devDependencies": {
|
|
48
|
-
"@earendil-works/pi-
|
|
49
|
-
"@earendil-works/pi-
|
|
50
|
-
"@earendil-works/pi-coding-agent": "1.0
|
|
51
|
-
"@earendil-works/pi-tui": "1.0
|
|
52
|
-
"@types/node": "^
|
|
53
|
-
"
|
|
54
|
-
"typescript": "^5.9.3"
|
|
51
|
+
"@earendil-works/pi-agent-core": "^1.4.0",
|
|
52
|
+
"@earendil-works/pi-ai": "^1.4.0",
|
|
53
|
+
"@earendil-works/pi-coding-agent": "^1.4.0",
|
|
54
|
+
"@earendil-works/pi-tui": "^1.4.0",
|
|
55
|
+
"@types/node": "^26.6.5",
|
|
56
|
+
"typescript": "^7.0.2"
|
|
55
57
|
}
|
|
56
58
|
}
|
package/src/index.ts
CHANGED
|
@@ -270,20 +270,6 @@ export default function watcher(pi: ExtensionAPI): void {
|
|
|
270
270
|
pi.on("session_compact", () => { compacting = false; mainStatus(); });
|
|
271
271
|
pi.on("session_compact_failed", () => { compacting = false; mainStatus(); });
|
|
272
272
|
|
|
273
|
-
pi.registerShortcut("alt+w", {
|
|
274
|
-
description: "Toggle the Watcher panel",
|
|
275
|
-
handler: async (ctx) => {
|
|
276
|
-
if (ctx.mode !== "tui" || !ctx.hasUI) return;
|
|
277
|
-
try {
|
|
278
|
-
if (side && isCurrent(side) && side.store.state.open) {
|
|
279
|
-
side.overlay?.abort();
|
|
280
|
-
return;
|
|
281
|
-
}
|
|
282
|
-
await show(await ensure(ctx));
|
|
283
|
-
} catch (error) { ctx.ui.notify(`Watcher: ${errorText(error)}`, "error"); }
|
|
284
|
-
},
|
|
285
|
-
});
|
|
286
|
-
|
|
287
273
|
/** Copy `pi --session <side session file>` (opens it from any directory) and echo it dimmed. */
|
|
288
274
|
async function copySessionCommand(ctx: ExtensionContext): Promise<void> {
|
|
289
275
|
try {
|
package/src/ui.ts
CHANGED
|
@@ -313,11 +313,6 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
313
313
|
|
|
314
314
|
handleInput(data: string): void {
|
|
315
315
|
if (this.closed) return;
|
|
316
|
-
// Focused overlays own input, so the main editor's shortcut cannot close us.
|
|
317
|
-
if (matchesKey(data, "alt+w")) {
|
|
318
|
-
this.close("closed");
|
|
319
|
-
return;
|
|
320
|
-
}
|
|
321
316
|
if (this.notice) {
|
|
322
317
|
this.notice = undefined;
|
|
323
318
|
this.requestRender();
|
|
@@ -397,7 +392,7 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
397
392
|
if (command === "model") this.openPicker(args.trim());
|
|
398
393
|
else if (command === "stop") this.doStop();
|
|
399
394
|
else if (command === "copy") this.doCopy(args);
|
|
400
|
-
else this.setNotice(`Enter send · Shift+Enter newline · PgUp/PgDn scroll · /model [filter] · /stop · ${COPY_USAGE} ·
|
|
395
|
+
else this.setNotice(`Enter send · Shift+Enter newline · PgUp/PgDn scroll · /model [filter] · /stop · ${COPY_USAGE} · Esc close (main agent untouched)`);
|
|
401
396
|
}
|
|
402
397
|
|
|
403
398
|
/** Allow local commands while an approval owns the normal input controls. */
|
|
@@ -745,7 +740,6 @@ export class WatcherOverlay implements Component, Focusable {
|
|
|
745
740
|
else if (inner >= 76) txt = "Enter send · ⇧Enter newline · PgUp/PgDn scroll · /model · /copy · /stop · Esc close";
|
|
746
741
|
else if (inner >= 50) txt = "Enter send · PgUp/PgDn · /model · /copy · /stop · Esc";
|
|
747
742
|
else txt = "Enter send · Esc close";
|
|
748
|
-
txt = `Alt+W close · ${txt}`;
|
|
749
743
|
return th.fg("dim", ` ${txt}`);
|
|
750
744
|
}
|
|
751
745
|
|