pi-jar 0.1.8 → 0.2.0

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-jar
2
2
 
3
- A warm, role-aware TUI and workflow extension for [Pi](https://github.com/earendil-works/pi). pi-jar turns the terminal into a more expressive agent workspace with a living pixel-fire welcome, an adaptive composer, first-class **plan**, **goal** and **role** workflows, tracked tasks, reviewable changes, background shells and parallel subagents.
3
+ A warm, role-aware TUI and workflow extension for [Pi](https://github.com/earendil-works/pi). pi-jar turns the terminal into a more expressive agent workspace with a living pixel-fire welcome, an adaptive composer, switchable **profiles**, first-class **plan**, **goal** and **role** workflows, tracked tasks, reviewable changes, background shells and parallel subagents.
4
4
 
5
5
  > Works with the current `@earendil-works/*` Pi API. Pi's core packages are peer dependencies (`"*"`), so pi-jar always runs against the Pi you have installed; it is tested against the latest release (0.87.x).
6
6
 
@@ -24,7 +24,7 @@ The showcase is captured from a real pi-jar terminal session: the torch-style `
24
24
  | **Commit** | `/jar commit [note]` drafts a message for the staged changes with the `commit` role, lets you edit it, then commits (never pushes). |
25
25
  | **Tasks & questions** | A Claude/omp-style `jar_todo` checklist with subtasks and per-task progress that the agent maintains itself, and `jar_ask` structured questions with options, multi-select and free-form answers. |
26
26
  | **Change review** | Every file the agent or its editing subagents change is remembered as it was; `/diff` (<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>D</kbd>) shows changed files (each counted once) next to a colored diff, with accept or revert per file or all at once. |
27
- | **Background shells** | `jar_shell` runs dev servers, watchers and long tests in the background; the agent is woken when a watch pattern matches or the process exits, so it never polls. Running shells get a footer row; click it (or <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd>) to tail or kill them. |
27
+ | **Background shells** | `jar_shell` runs dev servers, watchers and long tests in the background; watch/exit events wake an idle agent or reach it at the next safe turn boundary. Completion status appears immediately in the UI even during model reasoning. Running shells get a footer row; click it (or <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd>) to tail or kill them. |
28
28
  | **Subagents** | The main agent becomes a moderator over a configurable retained pool (2, 4, 6, 8 or 16; default 4). `jar_delegate` starts `scout`, `fork` or sandboxed `worktree` agents and returns immediately, so the main agent can keep responding to user steering; Pi RPC event-bus messages deliver initial and resumed turn completions without polling. `jar_subagent` peeks, steers, asks BTW questions, pauses, resumes and stops them. Workers can exchange bounded structured Q/A through `jar_discuss`; worktree changes stay isolated until stop, then safely enter `/diff`. |
29
29
  | **Sessions & history** | Recent sessions (with their goal and plan) on the welcome card, one click from resuming; <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>H</kbd> searches earlier prompts; pasted images show as chips under the composer. |
30
30
  | **Footer & themes** | Responsive footer (model, effort, session, cwd, context, RAM, cost, quota, goal, roles, branch, live subagents and shells) with unicode, [Nerd Font](https://www.nerdfonts.com) or ascii icons, and seven dark themes. |
@@ -61,6 +61,7 @@ Pi only delivers mouse events in its **fullscreen** TUI mode. In regular mode th
61
61
  | `/jar commit [note]` | Draft a commit message for the staged changes with the `commit` role, edit it, and commit. Offers `git add -A` when nothing is staged. |
62
62
  | `/jar` · `/jar settings` | Visual and workflow preferences. |
63
63
  | `/jar status` | One-line status summary. |
64
+ | `/jar perf` | Long-session diagnostics on demand: branch entries, context sampling, parent and child RSS, retained/hibernated subagents and recovery worktrees, render requests vs. frames, shell output, discussion and quota cache. |
64
65
  | `/jar tasks [list\|add\|done\|open\|edit\|delete]` | Human view/editor for the agent's checklist. |
65
66
  | `/jar history` | Read-only conversation timeline for the active branch. |
66
67
  | `/diff` | Review, accept or revert the files the agent changed. |
@@ -71,13 +72,14 @@ Pi only delivers mouse events in its **fullscreen** TUI mode. In regular mode th
71
72
  | `/jar ask [question]` | Answer a question in a dialog and insert the answer into the editor. |
72
73
  | `/jar accent [preset]` · `/jar footer` · `/jar icons [unicode\|nerd\|ascii]` | Switch a loaded accent theme; toggle footer fields; pick the icon set. |
73
74
  | `/jar composer on\|off` · `/jar animations on\|off` · `/jar ui on\|off` | Toggle the composer, motion, or all pi-jar UI. |
74
- | `/jar quota on\|off` | Session-only, read-only quota lookups for supported OAuth providers. |
75
+ | `/jar quota on\|off` | Session-only, read-only quota lookups for supported OAuth providers. Lookups start on session boundaries (startup, prompts, model changes), never from a render: at most one per provider every 5 minutes, and failures back off up to 30 minutes. |
75
76
  | `/jar hub` | Open an installed task or subagent manager command. |
76
77
  | `/jar demo` · `/jar reset` | Labeled sample roles in the footer / back to live data. |
77
78
 
78
79
  | Shortcut | Action |
79
80
  | --- | --- |
80
81
  | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>S</kbd> | Open pi-jar settings |
82
+ | <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>Tab</kbd> | Cycle profiles (theme and model roles) |
81
83
  | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd> | Subagents and background shells (activity view) |
82
84
  | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>R</kbd> | Refresh the welcome (new message and flame), or show it again |
83
85
  | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>P</kbd> | Toggle plan mode |
@@ -86,6 +88,14 @@ Pi only delivers mouse events in its **fullscreen** TUI mode. In regular mode th
86
88
  | <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>H</kbd> | Search earlier prompts into the composer |
87
89
  | <kbd>Tab</kbd> / <kbd>→</kbd> in an empty composer | Accept the dim suggestion into the input (it is not sent) |
88
90
 
91
+ ## Profiles
92
+
93
+ Profiles keep separate themes, appearance settings, and model-role configurations. **Default** preserves your existing setup.
94
+
95
+ Open `/jar settings` (or **Ctrl+Alt+S**) and choose **Profiles** to create or select a custom profile, or change its theme. The wizard lets you name it, choose its theme, configure roles, and review before saving. Cancelling leaves your existing setup unchanged.
96
+
97
+ Press **Ctrl+Shift+Tab** to cycle profiles **only in a fresh, idle session**. Once the first prompt is sent, the session's profile is locked—even after the agent finishes—and restored when you resume it. Open a new session to switch. Active agents, subagents, and plan/goal workflows also block switching. Configure the selected profile's roles through `/roles`; changing one profile does not change another. The selected profile is remembered across restarts and shown on the welcome screen and beside the composer mascot: `mascot · (Profile) session_name`. Some terminals require an extended keyboard protocol to distinguish Ctrl+Shift+Tab.
98
+
89
99
  ## Welcome screen
90
100
 
91
101
  The welcome stays until your first prompt, then dissolves (or hides immediately with motion off).
@@ -114,7 +124,7 @@ The welcome stays until your first prompt, then dissolves (or hides immediately
114
124
  | proud | `(★ᴗ★)` | a goal was completed |
115
125
  | poked | `(^o^)` | you clicked it |
116
126
 
117
- The title also shows Pi's session name (or a stable readable alias like `silver-lantern`). Toggle the mascot in settings; `/jar composer off` restores Pi's editor with your draft intact.
127
+ The title also shows the active profile in parentheses, then Pi's session name (or a stable readable alias like `silver-lantern`): `mascot · (Default) silver-lantern`. Toggle the mascot in settings; `/jar composer off` restores Pi's editor with your draft intact.
118
128
 
119
129
  ## Plan mode
120
130
 
@@ -212,7 +222,7 @@ Roles map a purpose to a model. They live in `~/.pi/agent/pi-jar-roles.json` (Pi
212
222
 
213
223
  The advisor is a second model (the `advisor` role; the current model if unassigned) that sees a fresh view of the recent conversation plus `git status` and a diff stat, but cannot run tools.
214
224
 
215
- - **`jar_advisor({ question?, draft? })`** — the agent is told to use it before risky or hard-to-reverse choices, after two failed attempts, and before declaring complex work done; it passes its own candidate as `draft`. Available in plan mode too.
225
+ - **`jar_advisor({ question?, draft? })`** — starts a background second opinion and immediately returns a request ID. Use `action: "status"`, `"get"`, bounded `"wait"` (at most 30 seconds), or `"cancel"` with that ID; a pending receipt is **not** reviewed approval. Advice arrives at a safe model boundary or wakes an idle agent, with immediate UI completion feedback. Requests have a 60-second deadline and bounded context; `/advisor` and automatic stuck-work gates are non-blocking too. Get completed advice before consequential decisions. Available in plan mode too.
216
226
  - **`/advisor [focus]`** — ask on demand; the answer is added to the conversation (visible) for the agent's next turn.
217
227
  - **Gates** — when the agent makes the same tool call three times within its last eight calls, the call is blocked and the advisor's review is returned instead; after three failing tool results in a row, the advice is steered into the running turn. At most two automatic consultations per prompt.
218
228
  - Settings → Pi toggles the advisor and the gates. Advisor calls are counted in `/usage`. The footer and welcome show it as a working teammate while it thinks.
@@ -255,16 +265,17 @@ This is a pi-jar enhancement: [pi-advisor](https://github.com/philipbrembeck/pi-
255
265
  Use `parent` with `append` to add a genuinely new subtask to active work; `start`, `done`, `open`, `edit`, `list`, `add` and `delete` remain available.
256
266
  - **`jar_ask`** — structured questions: numbered options with descriptions, single or multi-select, *Type your own answer* (multi-line, paste-friendly) and *Chat about this* to discuss before choosing.
257
267
  - **`/diff`** — review what the agent (and its editing subagents) changed since the first edit to each file: files with `+/−` counts on the left, a full-path header and numbered, colored diff on the right. Changed lines get a colored rail and tinted background, replaced lines highlight the changed words, code is syntax-highlighted for known languages, and collapsed regions are labeled `┄ N unchanged lines ┄`. `/` filters files; `t` switches unified and aligned side-by-side views (unified below 70 columns), `←`/`→` (or `[`/`]`) change the context from 0 lines to the whole file, `w` wraps long lines, and `n`/`p`, `Home`/`End` navigate. Large changes offer summary or explicit windowed review (`v`), with clearly labeled coarse fallback and existing safety limits—not arbitrary-file streaming. See [diff review](docs/DIFF-REVIEW.md). `a` accept (keep, stop tracking), `r` then `y` revert (restore the original, or remove a file it created), `A`/`R` for all. The footer shows `± N files · /diff` while anything is unreviewed; each file counts once no matter who edited it. Only `edit`/`write` changes inside the project are tracked; shell-made changes are not.
258
- - **`jar_shell`** — `start` (with optional `name`, `watch` regex, `notify` and task/service `purpose`), `list`, `peek`, bounded `wait`, `output`, `kill`. Output is ANSI-stripped and bounded (2000 lines). Watch/exit notifications are compact and coalesced; reads acknowledge observed events so obsolete completions do not replay. Relevant finite checks should be verified with `wait`/`peek` before claiming success; services should not block completion. Each running shell gets a footer row; the activity view (`/jar shells`, a click on the row) tails it live (`x` kill, `f` follow). All shells stop when the session ends.
259
- - **`jar_delegate`** — spawns **retained session-scoped** subagents up to **Max subagents** in `/jar settings` (2, 4, 6, 8 or 16; default 4). Idle/paused children and live legacy workers count toward the shared limit, not the parent; over-cap batches are rejected before launch. Lowering the limit does not terminate existing children, but blocks new launches until there is room. The parent is given moderator context and should coordinate rather than duplicate their work. `mode: "scout"` is fresh/read-only and defaults to the `scout` role; `fork` is a true Pi session fork, read-only, and defaults to `reviewer`; `worktree` forks context and defaults to `worker`, editing inside a path-guarded disposable Git worktree. A settled retained agent becomes `idle`, keeping its process, model context and workspace for reuse. Launch returns before startup or the first turn completes, including the old one-shot `write: true` shared-workspace mode retained for compatibility.
260
- - **`jar_subagent`** — moderator control plane: `peek` returns only task progress (done/total, the in-progress task, activity, the next open tasks), read from memory; `steer` redirects a running worker; `resume` wakes that same agent with an optional instruction. `ask`, `pause` and `stop` return **accepted operation receipts immediately**, never waiting for a child turn, Git reconciliation, or process shutdown; their answers and final handoffs arrive as `pi-jar.subagent` events. Acceptance does not mean a worker is already stopped or its files applied. Equivalent in-flight requests share an operation ID; conflicting controls are rejected, while `stop` can preempt an outstanding ask/pause. `pause` preserves context/workspace and `stop` reports progress, work directory, changed/applied files and what remains. When an initial or resumed turn ends, a visible `pi-jar.subagent` event-bus message wakes the moderator (or queues for its current run) with progress and the turn's report, so it can keep responding to user steering and never polls `peek`. Subagents use Pi's native `--mode rpc` JSONL protocol with streamed lifecycle events, not polling. Input queues and transcript buffers are bounded; shutdown signals the entire process group and drains inherited pipes for a bounded interval. Background control failures also produce a terminal event. A pause attempt settles or reports failure after 15 s, and a working child with no RPC activity for 10 minutes is stopped as hung.
268
+ - **Context diet (opt-in)** — `/jar settings` → **Pi** → **Context diet (prior-turn reasoning)**. Saved independently per profile; OFF by default. Removes eligible thinking blocks from completed earlier user turns only in outgoing context, never from stored history. Current reasoning signatures, user requests, tool arguments/results, and workflow state remain intact; ambiguous tool chains are left unchanged. Opaque signature character counts are diagnostics, not token savings. Existing SoL-Pi observation packing remains responsible for tool-result projection.
269
+ - **`jar_shell`** — `start` (optional `name`, `watch` regex, `notify`, task/service `purpose`), `list`, `peek`, bounded `wait`, `output`, `kill`. ANSI-stripped logs have per-job caps and a 4 MiB aggregate budget; surfaced finished jobs compact to a 32 KiB tail. Live jobs retain useful tails and are never killed to reclaim logs. Watch/exit notifications are compact and coalesced; reads acknowledge observed events. Active model responses cannot be interrupted by a completion event: the UI previews status without consuming it, and model delivery waits for a safe boundary. When no useful work remains, use bounded `wait` for the relevant finite jobs rather than waiting for an automatic wake-up. Each running shell gets a footer row and an activity view (`/jar shells`) with `x` kill and `f` follow. All shells stop when the session ends.
270
+ - **`jar_delegate`** — spawns retained, session-scoped subagents up to **Max subagents** in `/jar settings` (2, 4, 6, 8 or 16; default 4). Idle, paused and hibernated agents count toward this pool; over-cap batches are rejected. `scout` is fresh/read-only, `fork` inherits conversation read-only, and `worktree` forks context into a path-guarded Git worktree. Settled retained agents close their Pi process after 30 seconds of quiet while keeping their private session and workspace; `resume`/`ask` relaunch that same session. Launch returns before startup or the first turn completes, including legacy one-shot `write: true` mode.
271
+ - **`jar_subagent`** — `peek` reports progress, including the workspace path and changed files for recovery records; `steer` redirects a running worker; `resume` relaunches an idle/paused/hibernated agent. `ask`, `pause` and `stop` return accepted receipts immediately; answers and final handoffs arrive as `pi-jar.subagent` events. `stop` reconciles safe worktree changes; conflicts remain private and can be retried with `stop` after resolving parent drift. `discard` removes an unresolved recovery worktree without applying it. At most four active/recovery worktrees are admitted; resolve or discard one before starting another. Session shutdown reports paths of any preserved worktrees. Controls do not poll the child.
261
272
  - **Subagent capabilities** — each task can set `tools` or `inheritTools: true`; known-tool mode filters and child guards remain in force. See [tool capabilities](docs/SUBAGENT-TOOLS.md).
262
- - **Worktree finalization** — worktree edits do **not** touch the parent merely because a worker becomes idle. They remain private across pause/resume cycles. On `jar_subagent stop`, pi-jar verifies parent drift and reviewability, applies safe changes, and adds them to `/diff`; conflicts remain isolated and the workspace path is reported. Resolve parent drift and retry `stop` to reconcile; failed workspaces are retained rather than trimmed out of report history. Process closure is awaited before finalization. All replacements/rollback backups are prepared before parent mutation; failed multi-file application rolls back rather than silently leaving a partial result.
263
- - **`jar_democracy`** — **exceptional only**, for a persistent **super-complex** issue with several viable options and evidence of at least two distinct failed approaches. The moderator opens 2–8 evidence-backed options, resumes explicitly relevant idle/paused **fresh-context read-only scouts** or spawns fresh scouts within the same pool limit, and collects one private ballot per voter before publishing the tally. A **strict majority of the invited electorate** wins; ties, plurality without majority, and missing/invalid ballots require the user to choose among leading options, with evidence/tradeoffs rather than invented probabilities. TUI uses the structured question picker; RPC returns `needs-user` for the moderator to resolve with `jar_ask`. Voting returns a recommendation and **never implements it** or bypasses safety/approval. Prefer new scouts if past discussions could bias reused agents. Do not use democracy for ordinary complexity or routine decisions. A round is bounded: a 10-minute ballot deadline records unfinished voters as failures, rationales are capped at 600 characters, and scouts spawned for the round are retired when it ends.
264
- - **`jar_discuss`** — a bounded shared discussion paper for virtual agent-to-agent discussion. Questions and answers have stable IDs plus author/target metadata; entries are capped at 1600 characters, the newest 64 are retained, and serialized UTF-8 is capped at 64 KiB (older entries are evicted until it fits). `ask`/`answer` reply with the new id only (never the text) plus, when they fit, unseen answers to your questions and questions addressed to you. `list` returns only entries by others that this agent has not seen yet (capped at 4000 characters; one line when nothing changed); `since: "d0"` rereads everything and `questionId` reads one thread in full. Agents use it for narrow Q/A, not as another transcript.
273
+ - **Worktree finalization** — edits do not touch the parent merely because a worker settles or hibernates. On `jar_subagent stop`, pi-jar checks parent drift and reviewability, applies safe changes and adds them to `/diff`; conflicts remain isolated for retry or explicit discard. Failed worktree transcripts are compacted independently of finished history. Process closure is awaited before finalization, and failed multi-file application rolls back rather than leaving a partial result.
274
+ - **`jar_democracy`** — **exceptional only**, for a persistent super-complex issue with several evidence-backed options after at least two distinct failed approaches. The moderator opens 2–8 options, resumes relevant idle/paused/hibernated fresh-context read-only scouts or spawns new scouts within the pool, and collects one private ballot per voter. A strict majority of invited voters wins; ties, plurality or missing/invalid ballots require user choice. Voting never implements its recommendation. A round has a 10-minute deadline and bounded rationales.
275
+ - **`jar_discuss`** — session-local, parent-owned discussion broker over authenticated local IPC; no shared paper, lock or polling. `ask`/`answer` return stable IDs without echoing your text. `list` reads only relevant unread questions/answers and advances your cursor; `thread` (or `list` with `questionId`) reads one thread; `pending` shows counts without message bodies. `since: "d0"` rereads relevant mail. At most 128 messages, 1600 characters each and 128 KiB are retained; unread/unanswered questions block new posts instead of being silently lost. Messages do not wake idle agents; the next natural turn gets an unread notice.
265
276
  - **Activity view** — one split view (<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd>, `/jar activity`, or a click on a footer row) for subagents, background shells and other extensions' teammates: list on the left, live details on the right (auto-follows; scroll up to pause, `f` to follow again). For a subagent the details show its task, its own checklist with subtasks, and a transcript like omp's: each tool call and message is one collapsed line that expands on demand (`Tab` into it, `↑↓`, `Enter`, or click) to show arguments and output, while streaming text shows live. `s` opens a steering input: type a message and press `Enter` to redirect the running subagent. `x` stops just that one. Finished runs stay listed for review.
266
277
  - **`/jar history`** — separate, read-only timeline of the active branch (paging, search, expandable details). Pi's native transcript is untouched.
267
- - Pi's built-in read/shell/edit/write tool cards render compactly: collapsed, each is one padded, state-colored card with its call line and a one-line summary (edit included), and partially streamed arguments never flicker to a fallback; the full output or diff stays one click or <kbd>Ctrl</kbd>+<kbd>O</kbd> away. Background sources (subagent streams, shell output) repaint the footer at most every 250 ms and the activity view every 100 ms, because every requested frame re-renders the whole transcript. The sliding bar some terminals show in the tab while the agent works is Pi's own `OSC 9;4` progress (`terminal.showTerminalProgress` in Pi settings), not pi-jar.
278
+ - Pi's built-in read/shell/edit/write tool cards render compactly: collapsed, each is one padded, state-colored card with its call line and a one-line summary (edit included), and partially streamed arguments never flicker to a fallback; the full output or diff stays one click or <kbd>Ctrl</kbd>+<kbd>O</kbd> away. Every requested frame re-renders the whole transcript, so background sources (subagent streams, shell changes, context and cost updates) share one adaptive repaint budget: at most every 250 ms below 300 branch entries, 500 ms below 800, 1 s below 2,000, and only on visible state changes beyond that; a frame Pi draws on its own satisfies pending updates. User actions and state changes (a subagent or shell starting, settling or failing) still repaint at once. The open activity view repaints live updates every 100 ms, slowing to 500 ms in very large sessions. Context usage is cached: session start, tree navigation, compaction, model changes, renames and the end of a run refresh it at once, while message ends recompute it at most every 750 ms. `/jar perf` shows the counters. The sliding bar some terminals show in the tab while the agent works is Pi's own `OSC 9;4` progress (`terminal.showTerminalProgress` in Pi settings), not pi-jar.
268
279
 
269
280
  ## Settings
270
281
 
@@ -304,9 +315,11 @@ pi-jar/
304
315
  │ ├── delegate.ts retained subagents, moderator controls and RPC lifecycle
305
316
  │ ├── delegate-worktree.ts isolated Git worktrees, path guard and safe reconciliation
306
317
  │ ├── async-process.ts cancellable async subprocess runner with deadlines and tree cleanup
307
- │ ├── discussion.ts bounded structured cross-agent Q/A paper
318
+ │ ├── discussion.ts session-local discussion broker and tool
319
+ │ ├── discussion-hub.ts bounded in-memory discussion mailboxes
308
320
  │ ├── democracy.ts exceptional scout ballots, strict majority and user tie-break
309
321
  │ ├── activity-view.ts subagent/shell details view
322
+ │ ├── perf.ts long-session render budget, context sampling and /jar perf
310
323
  │ ├── icons.ts unicode / nerd / ascii glyph sets
311
324
  │ ├── attachments.ts image chips; prompt-search.ts is prompt history
312
325
  │ ├── session-gallery.ts recent sessions for the welcome
@@ -0,0 +1,333 @@
1
+ # Discussion Broker Improvement Plan
2
+
3
+ This is the detailed design for item 6 of the broader [Long-Session Stability and Performance Plan](./LONG_SESSION_PERFORMANCE_PLAN.md).
4
+
5
+ ## Status
6
+
7
+ Implemented in v0.1.9. This document preserves the design and acceptance criteria for the parent-owned in-memory broker and authenticated session-local IPC that replaced the file-backed paper. The “current” paper behavior and imperative implementation phases describe the pre-0.1.9 baseline and original proposal.
8
+
9
+ ## Problem
10
+
11
+ `jar_discuss` currently coordinates independent Pi processes through a shared temporary JSON paper. Every mutation must:
12
+
13
+ 1. acquire a filesystem lock directory,
14
+ 2. read and parse the whole paper,
15
+ 3. validate against that snapshot,
16
+ 4. mutate the in-memory copy,
17
+ 5. serialize and rewrite the whole paper,
18
+ 6. atomically rename the replacement, and
19
+ 7. release the lock.
20
+
21
+ The paper is bounded, so this is not an unbounded-data bug, but it is the wrong synchronization primitive for a parent process that already owns the subagent lifecycle. Concurrent agents can spend time retrying the same lock, stale-lock recovery adds failure modes, every write performs filesystem work, and the file cannot efficiently provide targeted delivery.
22
+
23
+ The desired model is agent coordination, not durable shared storage.
24
+
25
+ ## Goals
26
+
27
+ - Remove lock contention and filesystem polling from normal cross-agent communication.
28
+ - Keep the moderator as the authority for agent identity, lifecycle and routing.
29
+ - Preserve the useful `jar_discuss ask|answer|list` interaction model where practical.
30
+ - Deliver only relevant unread communication to each agent.
31
+ - Keep discussion state strictly bounded in memory.
32
+ - Avoid automatically waking LLM agents for ordinary messages.
33
+ - Make pending communication visible to the moderator and activity UI.
34
+ - Make cleanup deterministic on session replacement and shutdown.
35
+ - Keep protocol and transport simple enough to test without running model calls.
36
+
37
+ ## Non-goals
38
+
39
+ - A durable chat history.
40
+ - A general message queue.
41
+ - Direct child-to-child sockets.
42
+ - Automatic agent debates or unbounded LLM ping-pong.
43
+ - Persisting discussion across parent-process crashes.
44
+ - Replacing Pi RPC used for normal subagent lifecycle/control.
45
+
46
+ ## Proposed architecture
47
+
48
+ Pi Jar's parent process owns a session-local `DiscussionHub`. Child agents never mutate shared storage directly.
49
+
50
+ ```text
51
+ Pi Jar parent
52
+ ┌─────────────────┐
53
+ │ DiscussionHub │
54
+ │ │
55
+ │ bounded state │
56
+ │ routing │
57
+ │ sequence IDs │
58
+ │ unread cursors │
59
+ └───────┬─────────┘
60
+ │
61
+ ┌───────────┼───────────┐
62
+ │ │ │
63
+ scout-1 worker-2 reviewer-3
64
+ ```
65
+
66
+ The hub is owned beside `DelegateRegistry`, because the registry already knows which retained agents exist and when they start, settle, pause, resume, stop or disappear.
67
+
68
+ Suggested ownership:
69
+
70
+ ```ts
71
+ const subagents = new DelegateRegistry();
72
+ const discussions = new DiscussionHub();
73
+ ```
74
+
75
+ Lifecycle integration:
76
+
77
+ ```ts
78
+ discussions.register(record.key);
79
+ discussions.retire(record.key);
80
+ discussions.clear();
81
+ ```
82
+
83
+ ## Transport
84
+
85
+ Use a session-local IPC endpoint:
86
+
87
+ - Unix domain socket on macOS/Linux.
88
+ - Named pipe on Windows.
89
+ - Newline-delimited JSON request/response protocol.
90
+ - Children connect only when `jar_discuss` is invoked; no permanently open connection is required.
91
+ - No polling loop.
92
+ - No shared writable file.
93
+ - No lock file or lock directory.
94
+
95
+ The parent passes endpoint metadata to child processes through environment variables:
96
+
97
+ ```text
98
+ PI_JAR_DISCUSSION_ENDPOINT=...
99
+ PI_JAR_DISCUSSION_TOKEN=...
100
+ PI_JAR_SUBAGENT_KEY=delegate-2-1
101
+ ```
102
+
103
+ The child must not be allowed to choose an arbitrary sender identity. The parent authenticates the session token and maps the connection/request to a known subagent key.
104
+
105
+ On Unix, create the containing temporary directory with owner-only permissions. Treat the token as session-local capability data and never print it in tool output or logs.
106
+
107
+ ## Message model
108
+
109
+ Use monotonic numeric sequence IDs owned by the parent.
110
+
111
+ ```ts
112
+ interface DiscussionMessage {
113
+ id: number;
114
+ kind: "question" | "answer";
115
+ from: string;
116
+ to?: string;
117
+ replyTo?: number;
118
+ text: string;
119
+ createdAt: number;
120
+ }
121
+ ```
122
+
123
+ The parent is the only component allowed to:
124
+
125
+ - allocate IDs,
126
+ - validate `from`,
127
+ - validate target agent keys,
128
+ - validate answer references,
129
+ - mutate retention state, and
130
+ - advance per-agent read cursors.
131
+
132
+ This gives deterministic ordering without a filesystem lock.
133
+
134
+ ## Mailboxes instead of a shared transcript
135
+
136
+ Treat the discussion as bounded per-agent mailboxes over one shared message store.
137
+
138
+ Each agent keeps a logical `lastSeenId`. A normal unread query returns only messages after that cursor that are relevant to the caller:
139
+
140
+ - questions addressed to that agent,
141
+ - broadcast questions when broadcasts are supported,
142
+ - answers to questions created by that agent.
143
+
144
+ Do not make every agent ingest the full discussion history.
145
+
146
+ Tool behavior:
147
+
148
+ - `jar_discuss ask`: append one question and return its ID.
149
+ - `jar_discuss answer`: append one answer to a known question and return its ID.
150
+ - `jar_discuss list`: return unread relevant messages and advance the caller cursor.
151
+ - `jar_discuss thread <id>`: return one question and its bounded answers without advancing unrelated unread state.
152
+ - `jar_discuss pending`: compact unanswered/unread summary for moderator diagnostics.
153
+
154
+ The existing `since` compatibility behavior can remain during migration, but the normal path should be cursor-based.
155
+
156
+ ## Delivery policy
157
+
158
+ Communication should be event-driven but not model-trigger-happy.
159
+
160
+ When a new message is routed:
161
+
162
+ 1. update the target mailbox state,
163
+ 2. update a cheap pending-count signal in the parent,
164
+ 3. request only the necessary Pi Jar UI/status update, and
165
+ 4. do not automatically wake an idle model.
166
+
167
+ At the target agent's next natural turn, Pi Jar may inject a tiny notice such as:
168
+
169
+ ```text
170
+ You have 1 unread discussion message. Use jar_discuss list when relevant.
171
+ ```
172
+
173
+ The moderator can explicitly resume/steer a retained agent when a question is urgent. Ordinary agent-generated messages must not be able to create an automatic wake/answer/wake loop.
174
+
175
+ ## Retention
176
+
177
+ Discussion state remains bounded.
178
+
179
+ Initial limits:
180
+
181
+ - at most 128 messages,
182
+ - at most 1,600 characters per message,
183
+ - at most 128 KiB total encoded message text/metadata,
184
+ - at most a small fixed mailbox/cursor record per registered agent.
185
+
186
+ Eviction priority:
187
+
188
+ 1. answered question/answer groups already acknowledged by relevant agents,
189
+ 2. acknowledged broadcast messages,
190
+ 3. oldest acknowledged messages,
191
+ 4. never silently evict an unanswered targeted question while disposable acknowledged entries exist.
192
+
193
+ If the hard bound cannot be maintained without removing an unresolved question, surface a compact capacity error and require progress rather than growing memory indefinitely.
194
+
195
+ ## Context discipline
196
+
197
+ The broker stores coordination state. The model receives only the slice it needs.
198
+
199
+ Normal tool results should not echo:
200
+
201
+ - the caller's own submitted text,
202
+ - old acknowledged messages,
203
+ - unrelated agent chatter,
204
+ - the full mailbox,
205
+ - the full discussion graph.
206
+
207
+ This protects both response latency and model context in long sessions.
208
+
209
+ ## Failure behavior
210
+
211
+ The broker is ephemeral by design.
212
+
213
+ If the parent process exits, discussion state is lost. That is acceptable because:
214
+
215
+ - the parent owns the child processes,
216
+ - discussion is coordination state rather than user-authored durable data,
217
+ - durable task/plan/session artifacts already live elsewhere.
218
+
219
+ Do not add per-message snapshots merely to reconstruct transient chatter after a crash. If later evidence shows recovery is valuable, add an optional atomic snapshot at coarse lifecycle boundaries, not a lock-based primary transport.
220
+
221
+ If the IPC endpoint is unavailable, `jar_discuss` should return a clear broker-unavailable error. Do not silently fall back to the old shared-file implementation, because maintaining two synchronization models would preserve the complexity this change is intended to remove.
222
+
223
+ ## Observability
224
+
225
+ Expose lightweight broker information to Pi Jar diagnostics/activity state:
226
+
227
+ - registered agents,
228
+ - unread count per agent,
229
+ - unanswered question count,
230
+ - retained message count/bytes,
231
+ - oldest/newest sequence ID,
232
+ - IPC endpoint status,
233
+ - request/error counters.
234
+
235
+ Do not expose message bodies in generic telemetry.
236
+
237
+ ## Implementation phases
238
+
239
+ ### Phase 1: DiscussionHub
240
+
241
+ Add a pure in-memory `DiscussionHub` with no sockets and unit-test:
242
+
243
+ - registration/retirement,
244
+ - monotonic IDs,
245
+ - ask/answer validation,
246
+ - targeted routing,
247
+ - cursor semantics,
248
+ - thread reads,
249
+ - retention/eviction,
250
+ - cleanup.
251
+
252
+ ### Phase 2: local IPC
253
+
254
+ Add the local server/client transport and authenticate children using session-local capability data.
255
+
256
+ Test:
257
+
258
+ - concurrent asks receive unique ordered IDs,
259
+ - concurrent answers do not require retries,
260
+ - invalid sender/target/reply IDs are rejected,
261
+ - malformed requests cannot crash the parent,
262
+ - endpoint cleanup happens on shutdown.
263
+
264
+ ### Phase 3: jar_discuss adapter
265
+
266
+ Keep the public tool concise while switching its storage implementation to the broker.
267
+
268
+ Preserve useful response limits from the current implementation, but remove:
269
+
270
+ - `paper.json`,
271
+ - `paper.json.lock/`,
272
+ - lock retries,
273
+ - stale lock recovery,
274
+ - whole-paper reads and rewrites.
275
+
276
+ ### Phase 4: DelegateRegistry/activity integration
277
+
278
+ Show pending counts in moderator/activity state and add a small unread notice at natural agent boundaries.
279
+
280
+ Do not auto-resume agents for normal messages.
281
+
282
+ ### Phase 5: delete compatibility code
283
+
284
+ After the broker tests cover existing semantics:
285
+
286
+ - remove `createDiscussionPaper`,
287
+ - remove `disposeDiscussionPaper`,
288
+ - remove file-path environment variables,
289
+ - remove filesystem discussion tests,
290
+ - remove lock-specific code and stale-lock handling.
291
+
292
+ ## Acceptance criteria
293
+
294
+ The implementation is complete when:
295
+
296
+ - two or more child agents can ask/answer concurrently with no filesystem lock,
297
+ - discussion operations perform no shared-paper read/write in the steady state,
298
+ - no polling timer is required for message delivery,
299
+ - ordering is deterministic,
300
+ - each agent sees only relevant unread messages by default,
301
+ - memory remains bounded under a stress test,
302
+ - idle agents are not automatically woken by normal discussion traffic,
303
+ - session replacement/shutdown removes IPC resources and in-memory state,
304
+ - existing subagent pause/resume/stop behavior remains unchanged,
305
+ - tests cover concurrency and lifecycle cleanup.
306
+
307
+ ## Performance validation
308
+
309
+ Add a synthetic test/benchmark that compares the current file-backed design with the broker for concurrent local operations. The target is not an arbitrary microbenchmark trophy; it is removal of contention and variance.
310
+
311
+ Measure:
312
+
313
+ - median and p95 ask/answer latency,
314
+ - number of filesystem operations,
315
+ - number of retry waits,
316
+ - parent CPU time,
317
+ - retained heap after a fixed message workload.
318
+
319
+ Expected steady-state broker behavior is zero discussion filesystem operations and zero lock retries.
320
+
321
+ ## Related cleanup: legacy footer settings
322
+
323
+ Pi Jar previously supported `$AGENT_DIR/pi-jar-footer.json` and migrated it into `pi-jar-settings.json`.
324
+
325
+ That compatibility path is retired alongside this plan PR:
326
+
327
+ - `pi-jar-settings.json` is the sole visual/footer settings source,
328
+ - Pi Jar no longer reads, writes or migrates `pi-jar-footer.json`,
329
+ - an existing legacy file is ignored and left untouched,
330
+ - footer field definitions remain reusable code, but have no independent persistence layer,
331
+ - regression tests verify the legacy file cannot influence current settings.
332
+
333
+ This keeps configuration ownership singular before more parent-owned coordination state is introduced.
@@ -39,7 +39,7 @@ ctx.ui.setStatus("pi-jar.quota.openai-codex", JSON.stringify({
39
39
  - `used` is a percentage from 0 to 100. Either window may be omitted, and each can carry an optional `resetsAt` (epoch milliseconds).
40
40
  - `expiresAt` is required and may be at most five minutes ahead. A valid published status always wins.
41
41
  - `/jar quota on` (the default each session) lets pi-jar resolve the current Anthropic or OpenAI Codex OAuth credentials through Pi's model registry and make a **read-only** request to the provider's quota endpoint when nothing is published.
42
- - Requests have a 5 s timeout and results (including failures) are cached for 5 minutes. `/jar quota off` cancels pending requests and clears the cache.
42
+ - Requests have a 5 s timeout; successful results are cached for 5 minutes, while consecutive failures back off for 5, 10, 20, then 30 minutes. `/jar quota off` cancels pending requests and clears the cache. Renders only read cached values.
43
43
  - These provider endpoints are not stable public APIs.
44
44
 
45
45
  ## Session entries owned by pi-jar