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 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.6` · **License:** MIT
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. Tested against **Pi 1.0.4**; the child launcher depends on that version's package-manager loading interfaces. Standalone compiled Pi binaries are not supported.
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
- - **Alt+W** closes the panel from any Watcher view. Press it again to reopen. **Esc** also returns from chat to the main view without stopping either agent. Conversation, draft, and scroll position are retained.
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
- npm run check
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
- Tests use an offline scripted provider; no paid model calls are needed. They 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.
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.7",
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-ai": "*",
43
- "@earendil-works/pi-agent-core": "*",
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-ai": "1.0.4",
49
- "@earendil-works/pi-agent-core": "1.0.4",
50
- "@earendil-works/pi-coding-agent": "1.0.4",
51
- "@earendil-works/pi-tui": "1.0.4",
52
- "@types/node": "^22.19.0",
53
- "jiti": "^2.6.1",
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} · Alt+W / Esc close (main agent untouched)`);
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