pi-jar 0.1.3 → 0.1.4

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
@@ -22,10 +22,10 @@ The showcase is captured from a real pi-jar terminal session: the torch-style `
22
22
  | **Advisor** | A second-opinion model: the agent calls `jar_advisor` for risky decisions or when stuck, `/advisor [focus]` asks on demand, and automatic gates consult it when the agent repeats the same tool call or keeps failing. |
23
23
  | **Usage & context** | `/usage` shows session cost and tokens per model (advisor and commit calls included) and plan-limit bars with reset times; `/context` draws a grid of what fills the context window, with a per-file, per-skill and per-tool breakdown. |
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
- | **Tasks & questions** | A Claude-style `jar_todo` checklist the agent maintains itself, and `jar_ask` structured questions with options, multi-select and free-form answers. |
26
- | **Change review** | Every file the agent edits is remembered as it was; `/diff` (<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>D</kbd>) shows changed files next to a colored diff, with accept or revert per file or all at once. |
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
+ | **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
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. |
28
- | **Subagents** | `jar_delegate` fans out up to four read-only (or opt-in editing) subagents in parallel on your model roles. Each gets a live footer row (current tool, tool count, elapsed); click it to open its transcript and report, or stop it. |
28
+ | **Subagents** | `jar_delegate` fans out up to four read-only (or opt-in editing) subagents in parallel on your model roles. Each gets a live footer row; click it to open its checklist and an omp-style collapsible transcript, steer it with a message, or stop it. The parent gets a detailed report (tools, files, tasks, failures) from each. |
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. |
31
31
 
@@ -208,6 +208,26 @@ The advisor is a second model (the `advisor` role; the current model if unassign
208
208
  - **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.
209
209
  - 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.
210
210
 
211
+ ### Advisor fallback models
212
+
213
+ Configure an ordered fallback chain (the primary advisor remains configured with `/roles set advisor`):
214
+
215
+ ```text
216
+ /roles fallback advisor openai/gpt-5:high @smol
217
+ /roles fallback advisor
218
+ /roles fallback advisor clear
219
+ ```
220
+
221
+ Add `--project` when saving or clearing to override the global chain for this project. Configuration is stored in `pi-jar-roles.json` alongside `roles`:
222
+
223
+ ```json
224
+ "fallbacks": { "advisor": ["openai/gpt-5:high", "@smol"] }
225
+ ```
226
+
227
+ One-shot advisor calls (`jar_advisor`, `/advisor`, and stuck-work gates) try the primary first, then up to eight configured fallbacks on missing models, authentication/provider errors (including rate limits), or empty answers. Cancellation never starts another attempt. Aliases and per-model thinking levels are supported; duplicate model/effort pairs are skipped. Answers identify the successful model, returned attempts are counted in `/usage`, and exhaustion reports every failure. An empty project list disables inherited global fallbacks. No fallback is used unless explicitly configured; context is sent only to the models you choose. The same configuration works for other one-shot roles such as `commit`, but not interactive role activation or goal audit model switches.
228
+
229
+ This is a pi-jar enhancement: [pi-advisor](https://github.com/philipbrembeck/pi-advisor/) currently resolves one configured advisor model rather than a fallback chain.
230
+
211
231
  ## Usage and context
212
232
 
213
233
  `/usage` and `/context` open one tabbed panel (<kbd>Tab</kbd> switches, <kbd>Esc</kbd> closes):
@@ -217,12 +237,18 @@ The advisor is a second model (the `advisor` role; the current model if unassign
217
237
 
218
238
  ## Tasks, questions and history
219
239
 
220
- - **`jar_todo`** — a Claude-style task list the agent keeps for any multi-step request. It writes the full list at once; each task is `pending`, `in_progress` (exactly one at a time) or `completed`, with an `activeForm` ("Running tests") that replaces the working spinner text while it runs. The live checklist above the composer shows `✔` struck-through done tasks, a bold `◼` current task and `☐` pending ones; a finished list stays until your next prompt. `/jar tasks` is your view/editor: `a` add, <kbd>Space</kbd>/<kbd>Enter</kbd> check, `e` edit, `d` delete, `f` filter.
240
+ - **`jar_todo`** — a Claude/omp-style task list the agent keeps for any multi-step request. Prefer incremental `append` (new tasks only), `replace` (update one stable `id`), and `remove` (delete one task and its subtasks); IDs are returned for every task. Omitted replacement fields and subtasks stay intact. Legacy `write` (or `todos` without an action) still replaces the entire list, removing omitted tasks. Invalid updates leave the list unchanged. Each task is `pending`, `in_progress` (exactly one at a time) or `completed`, with an `activeForm` ("Running tests") that replaces the working spinner text while it runs. Tasks can have one level of **subtasks**: a parent's status follows its subtasks, its row shows `(done/total)`, and progress counts every subtask (and every task without subtasks). The live checklist above the composer shows `✔` struck-through done tasks, a bold `◼` current task and `☐` pending ones, with subtasks indented; a finished list stays until your next prompt. `/jar tasks` is your view/editor: `a` add, <kbd>Space</kbd>/<kbd>Enter</kbd> check, `e` edit, `d` delete, `f` filter.
241
+ ```json
242
+ { "action": "append", "todos": [{ "content": "Run tests", "status": "pending", "activeForm": "Running tests" }] }
243
+ { "action": "replace", "id": "<returned-id>", "status": "in_progress" }
244
+ { "action": "remove", "id": "<returned-id>" }
245
+ ```
246
+ Use `parent` with `append` to add subtasks; `start`, `done`, `open`, `edit`, and `list` remain available (`add`/`delete` also remain supported).
221
247
  - **`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.
222
- - **`/diff`** — review what the agent changed since its first edit to each file: files with `+/−` counts on the left, a numbered, colored diff on the right. `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. Only `edit`/`write` changes inside the project are tracked; shell-made changes are not.
248
+ - **`/diff`** — review what the agent (and its editing subagents) changed since the first edit to each file: files with `+/−` counts on the left, a numbered, colored diff on the right. `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.
223
249
  - **`jar_shell`** — `start` (with optional `name`, `watch` regex and `notify`), `list`, `output`, `kill`. Output is ANSI-stripped and bounded (2000 lines). When a watch matches or the process ends, a visible message wakes the agent (queued if it is busy). 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.
224
- - **`jar_delegate`** — up to four subagents run in parallel as separate one-shot Pi processes with a fresh context, on a role (default `task`, then `default`, then the current model). They are read-only (`read`, `grep`, `find`, `ls`) unless `write: true`; subagents cannot delegate further; aborting the turn stops them. Each has a live footer row; the activity view (<kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>A</kbd>, `/jar activity`, a click on the row) shows its task, live transcript and report, and `x` stops just that one. The tool result lists every report.
225
- - **Activity view** — one split view 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). Finished runs stay listed for review.
250
+ - **`jar_delegate`** — up to four subagents run in parallel as separate Pi processes (RPC mode) with a fresh context, on a role (default `task`, then `default`, then the current model). They are read-only (`read`, `grep`, `find`, `ls`, plus `jar_todo`) unless `write: true`; subagents cannot delegate further; aborting the turn stops them. Each has a live footer row. When they finish, the parent gets a detailed report per subagent: outcome, duration, tool calls by name, files read and edited, its task checklist, failed calls, then its own Summary / Details / Verification / Open issues.
251
+ - **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.
226
252
  - **`/jar history`** — separate, read-only timeline of the active branch (paging, search, expandable details). Pi's native transcript is untouched.
227
253
  - Pi's built-in read/shell/edit/write tool cards render compactly; the full output or diff stays one click or <kbd>Ctrl</kbd>+<kbd>O</kbd> away.
228
254
 
@@ -58,7 +58,7 @@ Then check:
58
58
  - the composer: typing `/pl` shows autocomplete below the frame; long drafts grow before scrolling; Ember changes mood while dialogs are open;
59
59
  - `/jar settings`: Tab through Appearance → Footer → Pi; cycle Unicode/Nerd Font/ASCII icons and toggle mouse mode in the temporary agent dir;
60
60
  - `/jar sessions`: search, move between rows, inspect the selected prompt/goal/plan, then resume; the footer session name or `sessions` indicator opens the same picker;
61
- - `/jar activity`: select a subagent or background shell, scroll its live transcript/output, pause and resume follow, and stop/kill only the selected run;
61
+ - `/jar activity`: select a subagent or background shell, scroll its live transcript/output, pause and resume follow, `Tab` into a subagent's transcript and expand/collapse entries, steer it with `s`, and stop/kill only the selected run;
62
62
  - `/roles`: the split manager, `n` new role, `a` alias, `s` scope;
63
63
  - `/plan`: the status shows `◆ PLAN · read-only` and a directory appears under `$TMPDIR/pi-jar/plans/`.
64
64
 
package/docs/WORKFLOWS.md CHANGED
@@ -114,6 +114,7 @@ Manual choices win: a model or effort you select yourself (model picker, cycling
114
114
  - `/diff` (or <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>D</kbd>) compares the baseline with the file on disk using a line diff with 3 lines of context. Files that are back to their baseline drop out.
115
115
  - **Accept** forgets the baseline. **Revert** writes the baseline back (or removes a file the agent created) after a second key press to confirm. Errors are reported and the file stays listed.
116
116
  - Baselines live in memory for the session; changes made through `bash` or by you are not tracked.
117
+ - **Subagent edits** join the same review. An editing `jar_delegate` batch gets a private temporary directory (`PI_JAR_BASELINE_DIR`); pi-jar inside each subagent writes a file's baseline there right before the subagent's first edit to it. After each successful subagent `edit`/`write` the parent adopts that baseline, unless it already tracks the file (the older baseline wins), so every file is counted once in the footer and `/diff`. The directory is removed when the batch ends. A subagent without pi-jar loaded cannot record baselines, so its edits are listed in its report but not in `/diff`.
117
118
 
118
119
  ## Background shells
119
120
 
@@ -129,11 +130,13 @@ watch matches or process exits → pi-jar.shell message → agent wakes (o
129
130
 
130
131
  ## Subagents
131
132
 
132
- - `jar_delegate` takes 1–4 tasks. Each runs `pi --mode json -p --no-session --model <role model> [--thinking <effort>] [--tools read,grep,find,ls] "<task>"` in the project directory, with `PI_JAR_CHILD=1`.
133
+ - `jar_delegate` takes 1–4 tasks. Each runs `pi --mode rpc --no-session [parent's -e/--extension/-ne flags] --model <role model> [--thinking <effort>] [--tools read,grep,find,ls,jar_todo]` in the project directory, with `PI_JAR_CHILD=1`, and receives the task as an RPC `prompt` (with instructions to track work in `jar_todo` and to report in Summary / Details / Verification / Open issues sections). Once the child emits `agent_settled`, stdin is closed and Pi exits in order. Dialogs a child opens are answered as cancelled so it never blocks.
133
134
  - The model comes from the task's role (default `task`), falling back to `default` and then the current model.
134
- - Progress (tools used, turns, cost) streams into the tool card; each running subagent is published as a teammate for the welcome TEAM row and footer, and cleared when it ends.
135
- - The activity view reads a live transcript per subagent (each tool call with its command, path or pattern, then the assistant's text; last 200 lines). It lives only in memory with the 8 most recent finished runs; the tool result saved in the session carries the reports, not the transcript.
136
- - Subagents time out after 20 minutes and stop when the turn is aborted; `x` in the activity view stops one without touching the rest of its batch (its report reads `failed: stopped`). The result lists every report with its role, model and outcome. In goal mode, `write: true` needs an open task.
135
+ - Progress (current tool, streaming text, tools used, turns, cost) streams into the tool card; each running subagent is published as a teammate for the welcome TEAM row and footer, and cleared when it ends.
136
+ - The activity view keeps a structured transcript per subagent: every tool call (arguments, streamed and final output, duration, success) and each assistant message, plus steering messages; the newest 200 entries, each clipped to 4000 characters, and the text still streaming. Entries are collapsed to one line and rendered only when expanded (`Tab` into the transcript, `↑↓`, `Enter`, or a click on the entry). The subagent's own `jar_todo` checklist (with subtasks) is mirrored from its tool results. It all lives only in memory with the 8 most recent finished runs; the tool result saved in the session carries the reports, not the transcript.
137
+ - **Steering:** `s` in the activity view (or a click on the input row) opens an input; `Enter` sends the text as an RPC `steer` message, delivered after the subagent's current tool calls. Steering works while the subagent is running.
138
+ - **Report back:** for each subagent the parent reads its outcome and duration, turns, tool calls by name, files read and edited, its checklist with per-task progress, failed tool calls, and then the subagent's own report.
139
+ - Subagents time out after 20 minutes and stop when the turn is aborted; `x` in the activity view stops one without touching the rest of its batch (its report reads `failed: stopped`). In goal mode, `write: true` needs an open task.
137
140
 
138
141
  ## Sessions and prompt history
139
142
 
@@ -30,7 +30,7 @@ import { createDemoRoles } from "../src/roles.ts";
30
30
  import { ACTIVE_STATES, cleanText, collectStatuses, type JarRole } from "../src/status.ts";
31
31
  import { manageTasks } from "../src/tasks-ui.ts";
32
32
  import { registerTaskTool, todoRow } from "../src/task-tool.ts";
33
- import { TASK_ENTRY, TodoStore } from "../src/tasks.ts";
33
+ import { TASK_ENTRY, TodoStore, todoProgress, todoTotals } from "../src/tasks.ts";
34
34
  import { formatCost, sessionCost } from "../src/usage.ts";
35
35
  import { WorkingState } from "../src/working.ts";
36
36
  import { ChangeTracker } from "../src/changes.ts";
@@ -62,6 +62,17 @@ export default function piJar(pi: ExtensionAPI): void {
62
62
  let enabled = visualSettings.ui;
63
63
  let disposeFooter: (() => void) | undefined;
64
64
  let footerTui: { requestRender(): void } | undefined;
65
+ /** Footer facts that walk the session to compute; refreshed on the events that change them, never per frame. */
66
+ let footerContext = "ctx ?";
67
+ let footerSessionName: string | undefined;
68
+ const sampleFooter = (ctx: ExtensionContext) => {
69
+ try {
70
+ const usage = ctx.getContextUsage();
71
+ footerContext = usage?.percent == null || !Number.isFinite(usage.percent) ? "ctx ?" : `ctx ${Math.round(usage.percent)}%`;
72
+ } catch { footerContext = "ctx ?"; }
73
+ try { footerSessionName = ctx.sessionManager?.getSessionName?.(); } catch { footerSessionName = undefined; }
74
+ footerTui?.requestRender();
75
+ };
65
76
  let quotaCache: QuotaCache | undefined;
66
77
  let cost = 0;
67
78
  let welcomeInterval: ReturnType<typeof setInterval> | undefined;
@@ -93,27 +104,37 @@ export default function piJar(pi: ExtensionAPI): void {
93
104
  if (open.length) todosAcknowledged = false;
94
105
  const visible = enabled && items.length > 0 && (open.length > 0 || !todosAcknowledged);
95
106
  try {
96
- ctx.ui.setWidget("pi-jar.todos", !visible ? undefined : (_tui, theme) => ({
97
- invalidate() {},
98
- render(width: number) {
99
- const colors = ctx.ui.theme ?? theme;
100
- const all = todos?.all() ?? [];
101
- const done = all.filter((item) => item.done).length;
102
- // Show a window of up to 8 rows that keeps the running (or next open) task in view.
103
- const focus = Math.max(0, all.findIndex((item) => item.status === "in_progress") >= 0
104
- ? all.findIndex((item) => item.status === "in_progress") : all.findIndex((item) => !item.done));
105
- const start = Math.max(0, Math.min(focus - 2, all.length - 8));
106
- const shown = all.slice(start, start + 8);
107
- const rows = [
108
- colors.fg("accent", "Tasks") + colors.fg("dim", ` · ${done}/${all.length} done` + (done === all.length ? " · all complete" : "") + " · /jar tasks"),
109
- ...(start > 0 ? [colors.fg("dim", ` … ${start} earlier`)] : []),
110
- ...shown.map((item) => todoRow(item, (color, text) => colors.fg(color, text), (text) => colors.bold(text))),
111
- ...(start + shown.length < all.length ? [colors.fg("dim", ` … +${all.length - start - shown.length} more`)] : [])
112
- ];
113
- // Pi inserts a spacer before widgets, but not between widgets and the composer.
114
- return [...rows.map((line) => truncateToWidth(line, Math.max(0, width))), truncateToWidth(" ", Math.max(0, width))];
115
- }
116
- }));
107
+ // Rows are rebuilt only when the list changes (every change reinstalls the widget), the width or the theme.
108
+ ctx.ui.setWidget("pi-jar.todos", !visible ? undefined : (_tui, theme) => {
109
+ let memo: { width: number; sample: string; lines: string[] } | undefined;
110
+ return {
111
+ invalidate() { memo = undefined; },
112
+ render(width: number) {
113
+ const colors = ctx.ui.theme ?? theme;
114
+ const sample = colors.fg("accent", "·") + colors.fg("dim", "·");
115
+ if (memo && memo.width === width && memo.sample === sample) return memo.lines.slice();
116
+ const all = todos?.all() ?? [];
117
+ const { done, total } = todoTotals(all);
118
+ const parents = new Set<string>();
119
+ for (const item of all) if (item.parentId) parents.add(item.parentId);
120
+ // Show a window of up to 8 rows that keeps the running leaf (or next open one) in view.
121
+ const running = all.findIndex((item) => item.status === "in_progress" && !parents.has(item.id));
122
+ const focus = Math.max(0, running >= 0 ? running : all.findIndex((item) => !item.done && !parents.has(item.id)));
123
+ const start = Math.max(0, Math.min(focus - 2, all.length - 8));
124
+ const shown = all.slice(start, start + 8);
125
+ const rows = [
126
+ colors.fg("accent", "Tasks") + colors.fg("dim", ` · ${done}/${total} done` + (done === total ? " · all complete" : "") + " · /jar tasks"),
127
+ ...(start > 0 ? [colors.fg("dim", ` … ${start} earlier`)] : []),
128
+ ...shown.map((item) => todoRow(item, (color, text) => colors.fg(color, text), (text) => colors.bold(text), parents.has(item.id) ? todoProgress(all, item.id) : undefined)),
129
+ ...(start + shown.length < all.length ? [colors.fg("dim", ` … +${all.length - start - shown.length} more`)] : [])
130
+ ];
131
+ // Pi inserts a spacer before widgets, but not between widgets and the composer.
132
+ const lines = [...rows.map((line) => truncateToWidth(line, Math.max(0, width))), truncateToWidth(" ", Math.max(0, width))];
133
+ memo = { width, sample, lines };
134
+ return lines.slice();
135
+ }
136
+ };
137
+ });
117
138
  } catch { /* Optional widget; task data remains available via /jar tasks and jar_todo. */ }
118
139
  };
119
140
  registerTaskTool(pi, () => todos, (ctx) => updateTaskWidget(ctx));
@@ -207,7 +228,7 @@ export default function piJar(pi: ExtensionAPI): void {
207
228
  registerInfoPanels(pi, { side: sideUsage, quotaEnabled: () => quotaCache?.enabled ?? false,
208
229
  quota: (ctx) => quotaCache?.get(ctx.model?.provider, welcomeStatuses(), Date.now()) });
209
230
  modelRoles.register((ctx) => openRolesUi(ctx, modelRoles));
210
- registerDelegate(pi, modelRoles, subagents);
231
+ registerDelegate(pi, modelRoles, subagents, { changes: () => changes, changed: refreshChanges });
211
232
  const planMode = new PlanMode(pi, () => todos, modelRoles, updateTaskWidget);
212
233
  planMode.register();
213
234
  const goalLoop = new GoalLoop(pi, {
@@ -420,8 +441,6 @@ export default function piJar(pi: ExtensionAPI): void {
420
441
  let expiryAt: number | undefined;
421
442
  let memory = `ram ${Math.round(process.memoryUsage.rss() / 1048576)} MiB`;
422
443
  let memorySampledAt = 0;
423
- let context = "ctx ?";
424
- let contextSampledAt = 0;
425
444
  let disposed = false;
426
445
  let hits: FooterHit[] = [];
427
446
  let pressed: { target: FooterTarget; at: number } | undefined;
@@ -473,13 +492,8 @@ export default function piJar(pi: ExtensionAPI): void {
473
492
  // Do not schedule cosmetic footer repaints. Role state still updates on Pi's native
474
493
  // stream/tool renders, which keeps long transcripts responsive.
475
494
  const active = animations && footerSettings.roles && roles.some((role) => ACTIVE_STATES.has(role.state));
476
- if (active) frame++;
477
- if (now - contextSampledAt >= 1000) {
478
- const usage = ctx.getContextUsage();
479
- context = usage?.percent == null || !Number.isFinite(usage.percent)
480
- ? "ctx ?" : `ctx ${Math.round(usage.percent)}%`;
481
- contextSampledAt = now;
482
- }
495
+ // Role glyphs advance with time, not with the render rate, so the footer memo still hits between steps.
496
+ if (active) frame = Math.floor(now / 250);
483
497
  if (now - memorySampledAt >= 5000) {
484
498
  memory = `ram ${Math.round(process.memoryUsage.rss() / 1048576)} MiB`;
485
499
  memorySampledAt = now;
@@ -487,9 +501,9 @@ export default function piJar(pi: ExtensionAPI): void {
487
501
  const quota = footerSettings.quota ? quotaCache?.get(ctx.model?.provider, statuses, now) : undefined;
488
502
  const layout = renderFooterLayout({
489
503
  model: ctx.model?.id ?? "no-model", effort: ctx.model?.reasoning === false ? "off" : (pi.getThinkingLevel?.() ?? "off"),
490
- sessionName: ctx.sessionManager?.getSessionName?.(),
504
+ sessionName: footerSessionName,
491
505
  cwd: ctx.cwd, settings: footerSettings, branch: footerData.getGitBranch(),
492
- context, goal: goalLoop.progress(), chips: footerChips(), activity: footerActivity(now),
506
+ context: footerContext, goal: goalLoop.progress(), chips: footerChips(), activity: footerActivity(now),
493
507
  memory, cost: formatCost(cost), quota, roles, extras: live.extras,
494
508
  demo, animations, frame, motionBudget: ctx.isIdle() ? 2 : 1
495
509
  }, width, ctx.ui.theme ?? theme);
@@ -609,6 +623,7 @@ export default function piJar(pi: ExtensionAPI): void {
609
623
  quotaDisabledByUser = false;
610
624
  updateCost(ctx);
611
625
  installUi(ctx);
626
+ sampleFooter(ctx);
612
627
  composer.setMascot(visualSettings.mascot);
613
628
  if (visualSettings.composer && enabled) composer.enable(ctx);
614
629
  suggest.sync();
@@ -649,6 +664,8 @@ export default function piJar(pi: ExtensionAPI): void {
649
664
  pi.on("agent_start", (_event, ctx) => { dismissWelcome(ctx); working.start(); applyWorking(ctx); });
650
665
  pi.on("turn_start", (_event, ctx) => { working.start(); applyWorking(ctx); });
651
666
  pi.on("message_end", (event, ctx) => {
667
+ // Every message (user, assistant, tool result) moves the context window.
668
+ sampleFooter(ctx);
652
669
  if (event.message.role === "assistant") {
653
670
  working.reportOutputTokens(event.message.usage?.output ?? 0);
654
671
  addMessageCost(event.message);
@@ -671,11 +688,12 @@ export default function piJar(pi: ExtensionAPI): void {
671
688
  restoreGoal(ctx, branch);
672
689
  updateCost(ctx);
673
690
  composer.refreshSession(ctx);
691
+ sampleFooter(ctx);
674
692
  });
675
- pi.on("session_compact", (_event, ctx) => { restoreTodos(ctx); restoreGoal(ctx); updateCost(ctx); });
676
- pi.on("model_select", (_event, _ctx) => footerTui?.requestRender());
693
+ pi.on("session_compact", (_event, ctx) => { restoreTodos(ctx); restoreGoal(ctx); updateCost(ctx); sampleFooter(ctx); });
694
+ pi.on("model_select", (_event, ctx) => sampleFooter(ctx));
677
695
  pi.on("thinking_level_select", (_event, _ctx) => footerTui?.requestRender());
678
- pi.on("session_info_changed", (_event, ctx) => { composer.refreshSession(ctx); footerTui?.requestRender(); });
696
+ pi.on("session_info_changed", (_event, ctx) => { composer.refreshSession(ctx); sampleFooter(ctx); });
679
697
  pi.on("session_shutdown", (_event, ctx) => {
680
698
  clearSessionBranchCache(ctx);
681
699
  stopWelcome(ctx);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-jar",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "A warm, role-aware Pi terminal workspace with animated UI, plan and goal workflows, tasks, change review, model roles, background shells and subagents.",
5
5
  "type": "module",
6
6
  "license": "MIT",