@nklisch/pi-enhanced 0.1.7 → 0.1.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.
Files changed (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-arm64-msvc.node +0 -0
  3. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
  4. package/node_modules/@nklisch/pi-model-modes/README.md +13 -4
  5. package/node_modules/@nklisch/pi-model-modes/package.json +1 -1
  6. package/node_modules/@nklisch/pi-model-modes/presets.json +7 -0
  7. package/node_modules/@nklisch/pi-model-modes/prompts/base/straight.md +9 -0
  8. package/node_modules/@nklisch/pi-model-modes/prompts/base.json +6 -1
  9. package/node_modules/@nklisch/pi-model-modes/prompts/styles/straight.md +9 -0
  10. package/node_modules/@nklisch/pi-model-modes/src/footer.ts +3 -2
  11. package/node_modules/@nklisch/pi-plugins/README.md +4 -4
  12. package/node_modules/@nklisch/pi-plugins/dist/application/plugin-lifecycle-service.js +19 -2
  13. package/node_modules/@nklisch/pi-plugins/dist/application/plugin-lifecycle-service.js.map +1 -1
  14. package/node_modules/@nklisch/pi-plugins/dist/application/ports/lifecycle-transition-store.d.ts +12 -0
  15. package/node_modules/@nklisch/pi-plugins/dist/application/ports/lifecycle-transition-store.js.map +1 -1
  16. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/create-node-recovery-adapters.js +2 -1
  17. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/create-node-recovery-adapters.js.map +1 -1
  18. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-transition-journal.d.ts +4 -0
  19. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-transition-journal.js +28 -1
  20. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/recovery/sqlite-transition-journal.js.map +1 -1
  21. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js +1 -1
  22. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js +1 -1
  23. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js +1 -1
  24. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/CHANGELOG.md +11 -0
  25. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/public.d.ts +3 -1
  26. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/FORK-MAINTENANCE.md +1 -1
  27. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +2 -1
  28. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +4 -4
  29. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/client-server-opportunities.md +4 -4
  30. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/comparison-with-upstream.md +1 -1
  31. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +1 -1
  32. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/index.ts +8 -8
  33. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/create-subagent-session.ts +1 -0
  34. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/parent-snapshot.ts +3 -0
  35. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-manager.ts +16 -1
  36. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-session.ts +12 -1
  37. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent.ts +18 -1
  38. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/composite-subagent-observer.ts +8 -0
  39. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/notification.ts +3 -0
  40. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/renderer.ts +2 -2
  41. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/runtime.ts +1 -0
  42. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service-adapter.ts +11 -3
  43. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/session-config.ts +7 -3
  44. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/thinking-level.ts +42 -0
  45. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/agent-tool.ts +3 -3
  46. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/background-spawner.ts +7 -2
  47. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/foreground-runner.ts +7 -5
  48. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-report.ts +4 -1
  49. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-tool.ts +1 -0
  50. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/helpers.ts +1 -1
  51. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/result-renderer.ts +7 -4
  52. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/spawn-config.ts +15 -2
  53. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/steer-tool.ts +3 -3
  54. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/types.ts +6 -2
  55. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/agent-widget.ts +117 -62
  56. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/display.ts +8 -1
  57. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigation.ts +7 -3
  58. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigator.ts +2 -2
  59. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/widget-renderer.ts +6 -4
  60. package/node_modules/@nklisch/pi-plugins/package.json +3 -3
  61. package/package.json +2 -2
@@ -8,9 +8,9 @@
8
8
 
9
9
  import { AgentTypeRegistry } from "#src/config/agent-types";
10
10
  import type { Subagent } from "#src/lifecycle/subagent";
11
- import type { SubagentManager, SubagentManagerObserver } from "#src/lifecycle/subagent-manager";
11
+ import type { SubagentManagerObserver } from "#src/lifecycle/subagent-manager";
12
12
  import type { CompactionInfo } from "#src/types";
13
- import { ERROR_STATUSES, formatMs, type Theme } from "#src/ui/display";
13
+ import { ERROR_STATUSES, formatModelThinking, formatMs, type Theme } from "#src/ui/display";
14
14
  import { renderWidgetLines, type WidgetAgent } from "#src/ui/widget-renderer";
15
15
 
16
16
  // ---- Types ----
@@ -54,7 +54,7 @@ export function assembleWidgetState(
54
54
  /** Render the aggregate status bar without hiding per-agent model/runtime details. */
55
55
  export function formatStatusBar(
56
56
  state: WidgetState,
57
- agents: readonly Pick<Subagent, "status" | "modelLabel" | "startedAt">[],
57
+ agents: readonly Pick<Subagent, "status" | "modelLabel" | "effectiveThinkingLevel" | "startedAt">[],
58
58
  now = Date.now(),
59
59
  ): string | undefined {
60
60
  if (!state.hasActive) return undefined;
@@ -65,8 +65,8 @@ export function formatStatusBar(
65
65
  const activeDetails = agents
66
66
  .filter((agent) => agent.status === "running" || agent.status === "queued")
67
67
  .map((agent) => agent.status === "running"
68
- ? `${agent.modelLabel} ${formatMs(now - agent.startedAt)}`
69
- : `${agent.modelLabel} queued`);
68
+ ? `${formatModelThinking(agent.modelLabel, agent.effectiveThinkingLevel)} ${formatMs(now - agent.startedAt)}`
69
+ : `${formatModelThinking(agent.modelLabel, agent.effectiveThinkingLevel)} queued`);
70
70
  return `${statusParts.join(", ")} agent${total === 1 ? "" : "s"} · ${activeDetails.join(", ")}`;
71
71
  }
72
72
 
@@ -85,10 +85,18 @@ export class AgentWidget implements SubagentManagerObserver {
85
85
  private uiCtx: UICtx | undefined;
86
86
  private widgetFrame = 0;
87
87
  private widgetInterval: ReturnType<typeof setInterval> | undefined;
88
- /** Tracks how many turns each finished agent has survived. Key: agent ID, Value: turns since finished. */
88
+ /**
89
+ * Bounded reactive read model: lifecycle callbacks add active background
90
+ * records and the small terminal linger set; render ticks never ask the
91
+ * manager to clone or sort its retained history.
92
+ */
93
+ private readonly backgroundAgents = new Map<string, Subagent>();
94
+ /** Tracks how many turns each finished agent has survived. */
89
95
  private finishedTurnAge = new Map<string, number>();
90
96
  /** How many extra turns errors/aborted agents linger (completed agents clear after 1 turn). */
91
97
  private static readonly ERROR_LINGER_TURNS = 2;
98
+ /** Pi renders the whole component tree for each requestRender call. */
99
+ private static readonly STATUS_REFRESH_INTERVAL_MS = 500;
92
100
 
93
101
  /** Whether the widget callback is currently registered with the TUI. */
94
102
  private widgetRegistered = false;
@@ -97,14 +105,12 @@ export class AgentWidget implements SubagentManagerObserver {
97
105
  /** Last status bar text, used to avoid redundant setStatus calls. */
98
106
  private lastStatusText: string | undefined;
99
107
 
100
- constructor(
101
- private manager: SubagentManager,
102
- private registry: AgentTypeRegistry,
103
- ) {}
108
+ constructor(private registry: AgentTypeRegistry) {}
104
109
 
105
110
  /** Set the UI context (grabbed from first tool execution). */
106
111
  setUICtx(ctx: UICtx) {
107
- if (ctx !== this.uiCtx) {
112
+ const contextChanged = ctx !== this.uiCtx;
113
+ if (contextChanged) {
108
114
  // UICtx changed — the widget registered on the old context is gone.
109
115
  // Force re-registration on next update().
110
116
  this.uiCtx = ctx;
@@ -112,6 +118,10 @@ export class AgentWidget implements SubagentManagerObserver {
112
118
  this.tui = undefined;
113
119
  this.lastStatusText = undefined;
114
120
  }
121
+ // Lifecycle events can arrive before the first tool gives us a UI context.
122
+ // Refresh when that context becomes available, but let update() decide
123
+ // whether any active record actually needs an animation timer.
124
+ if (contextChanged && this.uiCtx && this.backgroundAgents.size > 0) this.update();
115
125
  }
116
126
 
117
127
  /**
@@ -119,62 +129,118 @@ export class AgentWidget implements SubagentManagerObserver {
119
129
  * Ages finished agents and clears those that have lingered long enough.
120
130
  */
121
131
  onTurnStart() {
122
- // Age all finished agents
132
+ // Age terminal records and remove them before the next render. Completed
133
+ // records linger for one turn; errors and aborted records linger for two.
123
134
  for (const [id, age] of this.finishedTurnAge) {
124
- this.finishedTurnAge.set(id, age + 1);
135
+ const record = this.backgroundAgents.get(id);
136
+ const nextAge = age + 1;
137
+ if (!record || nextAge >= this.maxFinishedAge(record.status)) {
138
+ this.finishedTurnAge.delete(id);
139
+ this.backgroundAgents.delete(id);
140
+ } else {
141
+ this.finishedTurnAge.set(id, nextAge);
142
+ }
125
143
  }
126
- // Trigger a widget refresh (will filter out expired agents)
127
144
  this.update();
128
145
  }
129
146
 
130
147
  // ---- SubagentManagerObserver: react to lifecycle, self-drive the timer ----
131
148
 
132
149
  /** A subagent started running — ensure the update loop is live and render. */
133
- onSubagentStarted(_record: Subagent) {
150
+ onSubagentStarted(record: Subagent) {
151
+ if (!this.trackBackground(record)) return;
152
+ this.finishedTurnAge.delete(record.id);
134
153
  this.startLoop();
135
154
  }
136
155
 
137
156
  /** A background subagent was created (queued) — ensure the loop is live and render. */
138
- onSubagentCreated(_record: Subagent) {
157
+ onSubagentCreated(record: Subagent) {
158
+ if (!this.trackBackground(record)) return;
159
+ this.finishedTurnAge.delete(record.id);
160
+ this.startLoop();
161
+ }
162
+
163
+ /** A subagent completed — seed its bounded linger entry and render. */
164
+ onSubagentCompleted(record: Subagent) {
165
+ if (!this.trackBackground(record)) return;
166
+ this.finishedTurnAge.set(record.id, 0);
167
+ this.update();
168
+ }
169
+
170
+ /** A resumed subagent started — ensure the update loop is live and render. */
171
+ onSubagentResumedStarted(record: Subagent) {
172
+ if (!this.trackBackground(record)) return;
173
+ this.finishedTurnAge.delete(record.id);
139
174
  this.startLoop();
140
175
  }
141
176
 
142
- /** A subagent completedrender so the finished state is seeded and shown. */
143
- onSubagentCompleted(_record: Subagent) {
177
+ /** A resumed subagent settledseed its terminal linger entry. */
178
+ onSubagentResumed(record: Subagent) {
179
+ if (!this.trackBackground(record)) return;
180
+ this.finishedTurnAge.set(record.id, 0);
144
181
  this.update();
145
182
  }
146
183
 
147
184
  /** A subagent's session compacted — render to refresh the compaction count. */
148
- onSubagentCompacted(_record: Subagent, _info: CompactionInfo) {
185
+ onSubagentCompacted(record: Subagent, _info: CompactionInfo) {
186
+ if (this.trackBackground(record)) this.update();
187
+ }
188
+
189
+ /** Remove terminal state when the manager clears a parent session. */
190
+ onSubagentCleared(record: Subagent) {
191
+ if (!this.backgroundAgents.delete(record.id)) return;
192
+ this.finishedTurnAge.delete(record.id);
149
193
  this.update();
150
194
  }
151
195
 
152
- /** Start the update timer (if not already running) and render immediately. */
196
+ /** Refresh immediately; update() owns the timer lifecycle. */
153
197
  private startLoop() {
154
- this.ensureTimer();
198
+ if (!this.uiCtx) return;
155
199
  this.update();
156
200
  }
157
201
 
158
- /** Ensure the widget update timer is running. */
202
+ /** Ensure the widget update timer is running while active records animate. */
159
203
  private ensureTimer() {
160
- this.widgetInterval ??= setInterval(() => this.update(), 80);
204
+ this.widgetInterval ??= setInterval(
205
+ () => this.update(),
206
+ AgentWidget.STATUS_REFRESH_INTERVAL_MS,
207
+ );
208
+ }
209
+
210
+ /** Stop animated refreshes while leaving any static finished widget intact. */
211
+ private stopTimer() {
212
+ if (this.widgetInterval) {
213
+ clearInterval(this.widgetInterval);
214
+ this.widgetInterval = undefined;
215
+ }
161
216
  }
162
217
 
163
218
  /** Check if a finished agent should still be shown in the widget. */
164
219
  private shouldShowFinished(agentId: string, status: string): boolean {
165
220
  const age = this.finishedTurnAge.get(agentId) ?? 0;
166
- const maxAge = ERROR_STATUSES.has(status) ? AgentWidget.ERROR_LINGER_TURNS : 1;
167
- return age < maxAge;
221
+ return age < this.maxFinishedAge(status);
168
222
  }
169
223
 
170
- /**
171
- * Background agents only the widget's sole audience (ADR-0004 Decision A).
172
- * Foreground runs are rendered by the `subagent` tool's inline `onUpdate` stream,
173
- * so funneling both `listAgents()` call sites through this accessor applies the
174
- * background predicate exactly once at the source.
175
- */
176
- private listBackgroundAgents(): Subagent[] {
177
- return this.manager.listAgents().filter(record => record.invocation?.runInBackground === true);
224
+ private maxFinishedAge(status: string): number {
225
+ return ERROR_STATUSES.has(status) ? AgentWidget.ERROR_LINGER_TURNS : 1;
226
+ }
227
+
228
+ /** Add a record only when its immutable invocation snapshot marks it background. */
229
+ private trackBackground(record: Subagent): boolean {
230
+ if (record.invocation?.runInBackground !== true) return false;
231
+ this.backgroundAgents.set(record.id, record);
232
+ return true;
233
+ }
234
+
235
+ /** Drop terminal entries whose turn-based linger window has already elapsed. */
236
+ private pruneExpiredFinished(): void {
237
+ for (const [id, record] of this.backgroundAgents) {
238
+ if (record.isActive()) continue;
239
+ if (record.completedAt == null || !this.shouldShowFinished(id, record.status)) {
240
+ this.backgroundAgents.delete(id);
241
+ this.finishedTurnAge.delete(id);
242
+ }
243
+ }
178
244
  }
179
245
 
180
246
  /** Project a live Subagent record onto a pure-data WidgetAgent snapshot. */
@@ -185,6 +251,7 @@ export class AgentWidget implements SubagentManagerObserver {
185
251
  status: record.status,
186
252
  description: record.description,
187
253
  modelLabel: record.modelLabel,
254
+ thinkingLevel: record.effectiveThinkingLevel,
188
255
  toolUses: record.toolUses,
189
256
  startedAt: record.startedAt,
190
257
  completedAt: record.completedAt,
@@ -202,7 +269,7 @@ export class AgentWidget implements SubagentManagerObserver {
202
269
  /** Delegate rendering to the pure widget-renderer module. */
203
270
  private renderWidget(tui: any, theme: Theme): string[] {
204
271
  return renderWidgetLines({
205
- agents: this.listBackgroundAgents().map(r => this.toWidgetAgent(r)),
272
+ agents: [...this.backgroundAgents.values()].map(r => this.toWidgetAgent(r)),
206
273
  registry: this.registry,
207
274
  spinnerFrame: this.widgetFrame,
208
275
  terminalWidth: tui.terminal.columns,
@@ -213,10 +280,10 @@ export class AgentWidget implements SubagentManagerObserver {
213
280
 
214
281
  /**
215
282
  * Unregister the widget, clear the status bar, stop the interval timer, and
216
- * purge stale `finishedTurnAge` entries for agents no longer in `backgroundAgents`.
283
+ * release the bounded read model once no visible agents remain.
217
284
  * Called only from `update`'s idle path — not from `dispose`.
218
285
  */
219
- private clearWidget(backgroundAgents: readonly AgentSummary[]): void {
286
+ private clearWidget(): void {
220
287
  if (this.widgetRegistered) {
221
288
  this.uiCtx!.setWidget("agents", undefined);
222
289
  this.widgetRegistered = false;
@@ -226,10 +293,9 @@ export class AgentWidget implements SubagentManagerObserver {
226
293
  this.uiCtx!.setStatus("subagents", undefined);
227
294
  this.lastStatusText = undefined;
228
295
  }
229
- if (this.widgetInterval) { clearInterval(this.widgetInterval); this.widgetInterval = undefined; }
230
- for (const [id] of this.finishedTurnAge) {
231
- if (!backgroundAgents.some(a => a.id === id)) this.finishedTurnAge.delete(id);
232
- }
296
+ this.stopTimer();
297
+ this.backgroundAgents.clear();
298
+ this.finishedTurnAge.clear();
233
299
  }
234
300
 
235
301
  /**
@@ -244,34 +310,24 @@ export class AgentWidget implements SubagentManagerObserver {
244
310
  }
245
311
  }
246
312
 
247
- /**
248
- * Seed linger tracking for any newly-observed finished agent.
249
- * The widget owns detection of completions it observes via `listAgents()`,
250
- * so no external bookkeeping call is needed.
251
- * Idempotent — only seeds when an entry is absent, so repeated updates within
252
- * a turn neither reset nor advance the age.
253
- */
254
- private seedFinishedAgents(agents: readonly AgentSummary[]): void {
255
- for (const a of agents) {
256
- if (a.completedAt && !this.finishedTurnAge.has(a.id)) {
257
- this.finishedTurnAge.set(a.id, 0);
258
- }
259
- }
260
- }
261
-
262
313
  /** Force an immediate widget update. */
263
314
  update() {
264
315
  if (!this.uiCtx) return;
265
316
 
266
- const backgroundAgents = this.listBackgroundAgents();
267
- this.seedFinishedAgents(backgroundAgents);
317
+ this.pruneExpiredFinished();
318
+ const backgroundAgents = [...this.backgroundAgents.values()];
268
319
  const state = assembleWidgetState(backgroundAgents, (id, status) => this.shouldShowFinished(id, status));
269
320
 
270
321
  if (!state.hasActive && !state.hasFinished) {
271
- this.clearWidget(backgroundAgents);
322
+ this.clearWidget();
272
323
  return;
273
324
  }
274
325
 
326
+ // Finished records remain renderable for their turn-based linger window,
327
+ // but they have no changing state to animate. Stop the interval while the
328
+ // static completion widget remains registered.
329
+ if (state.hasActive) this.ensureTimer();
330
+ else this.stopTimer();
275
331
  this.updateStatusBar(state, backgroundAgents);
276
332
  this.widgetFrame++;
277
333
 
@@ -298,10 +354,7 @@ export class AgentWidget implements SubagentManagerObserver {
298
354
 
299
355
  // fallow-ignore-next-line unused-class-member
300
356
  dispose() {
301
- if (this.widgetInterval) {
302
- clearInterval(this.widgetInterval);
303
- this.widgetInterval = undefined;
304
- }
357
+ this.stopTimer();
305
358
  if (this.uiCtx) {
306
359
  this.uiCtx.setWidget("agents", undefined);
307
360
  this.uiCtx.setStatus("subagents", undefined);
@@ -309,5 +362,7 @@ export class AgentWidget implements SubagentManagerObserver {
309
362
  this.widgetRegistered = false;
310
363
  this.tui = undefined;
311
364
  this.lastStatusText = undefined;
365
+ this.backgroundAgents.clear();
366
+ this.finishedTurnAge.clear();
312
367
  }
313
368
  }
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type { AgentConfigLookup } from "#src/config/agent-types";
9
9
  import { GLYPHS } from "#src/ui/glyphs";
10
- import type { AgentInvocation, SubagentType } from "#src/types";
10
+ import type { AgentInvocation, SubagentType, ThinkingLevel } from "#src/types";
11
11
 
12
12
  // ---- Types ----
13
13
 
@@ -31,6 +31,8 @@ export interface AgentDetails {
31
31
  spinnerFrame?: number;
32
32
  /** Exact effective model label in `provider/id` form. */
33
33
  modelName?: string;
34
+ /** Exact effective thinking level used by the child session. */
35
+ thinkingLevel: ThinkingLevel;
34
36
  /** Notable config tags (e.g. ["thinking: high", "inherit context"]). */
35
37
  tags?: string[];
36
38
  /** Current turn count. */
@@ -106,6 +108,11 @@ export function formatTurns(turnCount: number, maxTurns?: number | null): string
106
108
  : `${GLYPHS.turns}${turnCount}`;
107
109
  }
108
110
 
111
+ /** Format an exact model identity and its effective thinking level together. */
112
+ export function formatModelThinking(modelLabel: string, thinkingLevel: ThinkingLevel): string {
113
+ return `${modelLabel} · thinking: ${thinkingLevel}`;
114
+ }
115
+
109
116
  /** Format milliseconds as human-readable duration. */
110
117
  export function formatMs(ms: number): string {
111
118
  return `${(ms / 1000).toFixed(1)}s`;
@@ -15,8 +15,8 @@
15
15
  import { buildSessionContext, parseSessionEntries, type SessionEntry, type ToolDefinition } from "@earendil-works/pi-coding-agent";
16
16
  import type { AgentConfigLookup } from "#src/config/agent-types";
17
17
  import type { SubagentStatus } from "#src/lifecycle/subagent-state";
18
- import type { AgentSessionEvent, SessionMessage, SubagentType } from "#src/types";
19
- import { formatDuration, getDisplayName } from "#src/ui/display";
18
+ import type { AgentSessionEvent, SessionMessage, SubagentType, ThinkingLevel } from "#src/types";
19
+ import { formatDuration, formatModelThinking, getDisplayName } from "#src/ui/display";
20
20
 
21
21
  // ─────────────────────────────────────────────────────────────────────────────
22
22
 
@@ -26,6 +26,7 @@ export interface NavigableSubagent {
26
26
  readonly type: SubagentType;
27
27
  readonly description: string;
28
28
  readonly modelLabel: string;
29
+ readonly effectiveThinkingLevel: ThinkingLevel;
29
30
  readonly status: SubagentStatus;
30
31
  readonly startedAt: number;
31
32
  readonly completedAt: number | undefined;
@@ -47,6 +48,7 @@ export interface NavigableSubagent {
47
48
  */
48
49
  export interface RunDisplayMetadata {
49
50
  readonly modelLabel: string;
51
+ readonly thinkingLevel: ThinkingLevel;
50
52
  readonly startedAt: number;
51
53
  readonly completedAt: () => number | undefined;
52
54
  }
@@ -60,6 +62,7 @@ interface LabelFields {
60
62
  readonly type: SubagentType;
61
63
  readonly description: string;
62
64
  readonly modelLabel: string;
65
+ readonly effectiveThinkingLevel: ThinkingLevel;
63
66
  readonly status: SubagentStatus;
64
67
  readonly startedAt: number;
65
68
  readonly completedAt: number | undefined;
@@ -95,6 +98,7 @@ export function listNavigableAgents(
95
98
  const live = agents.flatMap((record): NavigationEntry[] => {
96
99
  const run = {
97
100
  modelLabel: record.modelLabel,
101
+ thinkingLevel: record.effectiveThinkingLevel,
98
102
  startedAt: record.startedAt,
99
103
  completedAt: () => record.completedAt,
100
104
  };
@@ -150,5 +154,5 @@ function buildLabel(fields: LabelFields, registry: AgentConfigLookup, evicted =
150
154
  const name = getDisplayName(fields.type, registry);
151
155
  const duration = formatDuration(fields.startedAt, fields.completedAt);
152
156
  const marker = evicted ? " · released (snapshot)" : "";
153
- return `${name} (${fields.description}) · ${fields.modelLabel} · ${fields.toolUses} tools · ${fields.status} · ${duration}${marker}`;
157
+ return `${name} (${fields.description}) · ${formatModelThinking(fields.modelLabel, fields.effectiveThinkingLevel)} · ${fields.toolUses} tools · ${fields.status} · ${duration}${marker}`;
154
158
  }
@@ -39,7 +39,7 @@ import {
39
39
  } from "@earendil-works/pi-tui";
40
40
  import type { AgentConfigLookup } from "#src/config/agent-types";
41
41
  import type { SessionMessage } from "#src/types";
42
- import { describeActivity, formatDuration, type Theme } from "#src/ui/display";
42
+ import { describeActivity, formatDuration, formatModelThinking, type Theme } from "#src/ui/display";
43
43
  import { fileSnapshotSource, listNavigableAgents, liveSource, type NavigableSubagent, type RunDisplayMetadata, type TranscriptSource } from "#src/ui/session-navigation";
44
44
 
45
45
  // ─────────────────────────────────────────────────────────────────────────────
@@ -225,7 +225,7 @@ export class TranscriptOverlay implements Component {
225
225
 
226
226
  lines.push(hrTop);
227
227
  const runtime = formatDuration(this.run.startedAt, this.run.completedAt());
228
- lines.push(row(th.bold(`Subagent session · ${this.run.modelLabel} · ${runtime}`)));
228
+ lines.push(row(th.bold(`Subagent session · ${formatModelThinking(this.run.modelLabel, this.run.thinkingLevel)} · ${runtime}`)));
229
229
  lines.push(hrMid);
230
230
 
231
231
  const contentLines = this.buildContentLines(innerW);
@@ -9,9 +9,10 @@ import { truncateToWidth } from "@earendil-works/pi-tui";
9
9
  import type { AgentConfigLookup } from "#src/config/agent-types";
10
10
  import type { LifetimeUsage } from "#src/lifecycle/usage";
11
11
  import { getLifetimeTotal } from "#src/lifecycle/usage";
12
- import type { SubagentType } from "#src/types";
12
+ import type { SubagentType, ThinkingLevel } from "#src/types";
13
13
  import {
14
14
  describeActivity,
15
+ formatModelThinking,
15
16
  formatMs,
16
17
  formatSessionTokens,
17
18
  formatTurns,
@@ -30,6 +31,7 @@ export interface WidgetAgent {
30
31
  readonly status: string;
31
32
  readonly description: string;
32
33
  readonly modelLabel: string;
34
+ readonly thinkingLevel: ThinkingLevel;
33
35
  readonly toolUses: number;
34
36
  readonly startedAt: number;
35
37
  readonly completedAt?: number;
@@ -78,7 +80,7 @@ export function renderFinishedLine(
78
80
  statusText = theme.fg("warning", " aborted");
79
81
  }
80
82
 
81
- const parts: string[] = [agent.modelLabel];
83
+ const parts: string[] = [formatModelThinking(agent.modelLabel, agent.thinkingLevel)];
82
84
  parts.push(formatTurns(agent.turnCount, agent.maxTurns));
83
85
  if (agent.toolUses > 0) parts.push(`${agent.toolUses} tool use${agent.toolUses === 1 ? "" : "s"}`);
84
86
  parts.push(duration);
@@ -102,7 +104,7 @@ export function renderRunningLines(
102
104
  const tokens = getLifetimeTotal(agent.lifetimeUsage);
103
105
  const tokenText = tokens > 0 ? formatSessionTokens(tokens, agent.contextPercent, theme, agent.compactionCount) : "";
104
106
 
105
- const parts: string[] = [agent.modelLabel];
107
+ const parts: string[] = [formatModelThinking(agent.modelLabel, agent.thinkingLevel)];
106
108
  parts.push(formatTurns(agent.turnCount, agent.maxTurns));
107
109
  if (agent.toolUses > 0) parts.push(`${agent.toolUses} tool use${agent.toolUses === 1 ? "" : "s"}`);
108
110
  if (tokenText) parts.push(tokenText);
@@ -176,7 +178,7 @@ function buildSections(
176
178
  truncate(
177
179
  theme.fg("dim", "\u251C\u2500")
178
180
  + ` ${theme.fg("muted", "\u25E6")} ${theme.fg("dim", getDisplayName(agent.type, registry))}`
179
- + ` ${theme.fg("dim", "·")} ${theme.fg("dim", agent.modelLabel)}`
181
+ + ` ${theme.fg("dim", "·")} ${theme.fg("dim", formatModelThinking(agent.modelLabel, agent.thinkingLevel))}`
180
182
  + ` ${theme.fg("dim", "·")} ${theme.fg("dim", agent.description)}`
181
183
  + ` ${theme.fg("dim", "· queued")}`,
182
184
  ),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nklisch/pi-plugins",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "private": false,
5
5
  "description": "Native marketplace and plugin lifecycle management for Pi, with Claude Code and Codex compatibility.",
6
6
  "license": "MIT",
@@ -67,8 +67,8 @@
67
67
  "@nklisch/pi-subagents"
68
68
  ],
69
69
  "dependencies": {
70
- "@nklisch/pi-mcp-adapter": "2.20.1-nklisch.0",
71
- "@nklisch/pi-subagents": "18.1.0-nklisch.0",
70
+ "@nklisch/pi-mcp-adapter": "2.20.1-nklisch.1",
71
+ "@nklisch/pi-subagents": "18.1.0-nklisch.1",
72
72
  "jiti": "2.7.0",
73
73
  "semver": "7.8.1",
74
74
  "smol-toml": "^1.7.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nklisch/pi-enhanced",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Pi, enhanced — one install for nklisch's full harness: policy-gated command review, plugin marketplace, subagents, background tasks, research tools, search, model modes, and a curated UX set.",
5
5
  "author": {
6
6
  "name": "nklisch"
@@ -67,7 +67,7 @@
67
67
  "@nklisch/pi-clearance": "^0",
68
68
  "@nklisch/pi-conveniences": "^0",
69
69
  "@nklisch/pi-fff-compat": "^0",
70
- "@nklisch/pi-mcp-adapter": "2.20.1-nklisch.0",
70
+ "@nklisch/pi-mcp-adapter": "2.20.1-nklisch.1",
71
71
  "@nklisch/pi-model-modes": "^0",
72
72
  "@nklisch/pi-plugins": "^0",
73
73
  "@sinclair/typebox": "^0.34.49",