pi-ui-extend 1.0.40 → 1.0.42

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 (186) hide show
  1. package/README.md +10 -2
  2. package/dist/app/app.d.ts +3 -0
  3. package/dist/app/app.js +26 -0
  4. package/dist/app/commands/command-host.d.ts +1 -0
  5. package/dist/app/commands/command-model-actions.js +6 -1
  6. package/dist/app/commands/command-registry.js +3 -3
  7. package/dist/app/commands/command-session-actions.d.ts +0 -1
  8. package/dist/app/commands/command-session-actions.js +30 -15
  9. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  10. package/dist/app/commands/reload-context-inventory.js +59 -0
  11. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  12. package/dist/app/extensions/extension-actions-controller.js +6 -0
  13. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  14. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  15. package/dist/app/icons.d.ts +14 -0
  16. package/dist/app/icons.js +33 -0
  17. package/dist/app/rendering/conversation-tool-renderer.js +2 -2
  18. package/dist/app/rendering/dcp-stats.d.ts +6 -1
  19. package/dist/app/rendering/dcp-stats.js +214 -46
  20. package/dist/app/rendering/editor-panels.js +17 -8
  21. package/dist/app/session/lazy-session-manager.js +12 -1
  22. package/dist/app/session/session-lifecycle-controller.d.ts +1 -0
  23. package/dist/app/session/session-lifecycle-controller.js +6 -0
  24. package/dist/app/session/tabs-controller.d.ts +3 -5
  25. package/dist/app/session/tabs-controller.js +31 -22
  26. package/dist/app/subagents/subagents-files.js +60 -1
  27. package/dist/app/subagents/subagents-model.d.ts +15 -1
  28. package/dist/app/subagents/subagents-model.js +52 -17
  29. package/dist/app/types.d.ts +7 -0
  30. package/dist/bundled-extensions/session-title/config.js +1 -1
  31. package/dist/default-pix-config.js +10 -0
  32. package/dist/markdown-format.js +27 -9
  33. package/dist/schemas/index.d.ts +1 -0
  34. package/dist/schemas/index.js +1 -0
  35. package/dist/schemas/pi-tools-suite-schema.d.ts +106 -71
  36. package/dist/schemas/pi-tools-suite-schema.js +68 -88
  37. package/dist/schemas/pix-schema.d.ts +7 -0
  38. package/dist/schemas/pix-schema.js +14 -0
  39. package/dist/schemas/tasks-schema.d.ts +18 -0
  40. package/dist/schemas/tasks-schema.js +41 -0
  41. package/docs/concurrency.md +6 -0
  42. package/docs/desktop-mvp.md +12 -1
  43. package/docs/desktop-task-manager.md +93 -82
  44. package/external/pi-tools-suite/README.md +192 -72
  45. package/external/pi-tools-suite/docs/browser-qa-subagent.md +50 -21
  46. package/external/pi-tools-suite/docs/context-gateway-p00-adr.md +216 -0
  47. package/external/pi-tools-suite/docs/context-gateway-p01n-gate-review.md +122 -0
  48. package/external/pi-tools-suite/docs/context-gateway-p01n-measurement.md +133 -0
  49. package/external/pi-tools-suite/docs/context-gateway-p01r-ra-evidence.md +111 -0
  50. package/external/pi-tools-suite/docs/context-gateway-p01r-rb-evidence.md +100 -0
  51. package/external/pi-tools-suite/docs/context-gateway-p01r-rc-evidence.md +69 -0
  52. package/external/pi-tools-suite/docs/context-gateway-p01r-rd-evidence.md +100 -0
  53. package/external/pi-tools-suite/docs/context-gateway-p01r-re-evidence.md +74 -0
  54. package/external/pi-tools-suite/docs/context-gateway-p01r-rf-evidence.md +153 -0
  55. package/external/pi-tools-suite/docs/context-gateway-p01r-rg-evidence.md +235 -0
  56. package/external/pi-tools-suite/docs/evals.md +1 -1
  57. package/external/pi-tools-suite/docs/session-recovery.md +40 -14
  58. package/external/pi-tools-suite/docs/subagent-model-pools.md +120 -0
  59. package/external/pi-tools-suite/package.json +3 -0
  60. package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/scripts/browser-qa-runner.mjs +82 -1
  61. package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/SKILL.md → agents/browser-qa.md} +261 -12
  62. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  63. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +20 -0
  64. package/external/pi-tools-suite/src/async-subagents/agents/oracle.md +16 -0
  65. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  66. package/external/pi-tools-suite/src/async-subagents/agents/research.md +20 -0
  67. package/external/pi-tools-suite/src/async-subagents/agents/verify.md +18 -0
  68. package/external/pi-tools-suite/src/async-subagents/commands.ts +20 -72
  69. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  70. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +42 -0
  71. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +13 -93
  72. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +499 -0
  73. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +9 -0
  74. package/external/pi-tools-suite/src/async-subagents/core/config.ts +284 -417
  75. package/external/pi-tools-suite/src/async-subagents/core/model-fallback.ts +1 -1
  76. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +54 -0
  77. package/external/pi-tools-suite/src/async-subagents/core/prompt.ts +7 -6
  78. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +65 -45
  79. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +18 -6
  80. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  81. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  82. package/external/pi-tools-suite/src/async-subagents/index.ts +77 -3
  83. package/external/pi-tools-suite/src/async-subagents/lib.ts +12 -9
  84. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +54 -22
  85. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +6 -4
  86. package/external/pi-tools-suite/src/async-subagents/types.ts +3 -0
  87. package/external/pi-tools-suite/src/config.ts +42 -2
  88. package/external/pi-tools-suite/src/context-gateway/accounting.ts +151 -0
  89. package/external/pi-tools-suite/src/context-gateway/config.ts +120 -0
  90. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +102 -0
  91. package/external/pi-tools-suite/src/context-gateway/index.ts +214 -0
  92. package/external/pi-tools-suite/src/context-gateway/metadata-normalization.ts +88 -0
  93. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +89 -0
  94. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +502 -0
  95. package/external/pi-tools-suite/src/context-gateway/test-output-parser.ts +326 -0
  96. package/external/pi-tools-suite/src/context-gateway/types.ts +158 -0
  97. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  98. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +106 -0
  99. package/external/pi-tools-suite/src/dcp/auto-compress.ts +892 -136
  100. package/external/pi-tools-suite/src/dcp/commands.ts +82 -139
  101. package/external/pi-tools-suite/src/dcp/compress-tool.ts +533 -74
  102. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +488 -94
  103. package/external/pi-tools-suite/src/dcp/compression-preview.ts +122 -0
  104. package/external/pi-tools-suite/src/dcp/compression-progress.ts +70 -0
  105. package/external/pi-tools-suite/src/dcp/config.ts +86 -68
  106. package/external/pi-tools-suite/src/dcp/conversation-index.ts +421 -0
  107. package/external/pi-tools-suite/src/dcp/debug-log.ts +7 -5
  108. package/external/pi-tools-suite/src/dcp/index.ts +617 -203
  109. package/external/pi-tools-suite/src/dcp/journal.ts +566 -0
  110. package/external/pi-tools-suite/src/dcp/progress-controller.ts +244 -0
  111. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -189
  112. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  113. package/external/pi-tools-suite/src/dcp/provider-tool-results.ts +189 -0
  114. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +299 -79
  115. package/external/pi-tools-suite/src/dcp/pruner-compression-blocks.ts +173 -281
  116. package/external/pi-tools-suite/src/dcp/pruner-emergency.ts +2 -4
  117. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +23 -15
  118. package/external/pi-tools-suite/src/dcp/pruner-metadata.ts +11 -1
  119. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +30 -82
  120. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +28 -136
  121. package/external/pi-tools-suite/src/dcp/pruner.ts +18 -33
  122. package/external/pi-tools-suite/src/dcp/recovery.ts +129 -0
  123. package/external/pi-tools-suite/src/dcp/shadow-plan.ts +127 -0
  124. package/external/pi-tools-suite/src/dcp/state-transaction.ts +102 -0
  125. package/external/pi-tools-suite/src/dcp/state.ts +158 -580
  126. package/external/pi-tools-suite/src/dcp/ui.ts +1 -0
  127. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +36 -214
  128. package/external/pi-tools-suite/src/index.ts +16 -1
  129. package/external/pi-tools-suite/src/model-tools/index.ts +76 -42
  130. package/external/pi-tools-suite/src/repo-discovery/index.ts +84 -18
  131. package/external/pi-tools-suite/src/repo-discovery/native-compact.ts +458 -0
  132. package/external/pi-tools-suite/src/resource-registry/index.ts +2290 -0
  133. package/external/pi-tools-suite/src/session-recovery/index.ts +189 -43
  134. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  135. package/external/pi-tools-suite/src/tool-descriptions.ts +41 -38
  136. package/external/pi-tools-suite/src/truncation-metadata-normalizer/index.ts +17 -0
  137. package/package.json +6 -3
  138. package/schemas/pi-tools-suite.json +540 -312
  139. package/schemas/pix.json +24 -0
  140. package/schemas/tasks.json +132 -0
  141. package/skills/spec-wiki/SKILL.md +55 -0
  142. package/skills/spec-wiki/evals/evals.json +132 -0
  143. package/skills/spec-wiki/evals/files/bootstrap/architecture/rfc-007.md +14 -0
  144. package/skills/spec-wiki/evals/files/bootstrap/docs/how-to-pay.md +4 -0
  145. package/skills/spec-wiki/evals/files/bootstrap/docs/overview.md +9 -0
  146. package/skills/spec-wiki/evals/files/bootstrap/requirements/payment-behavior.md +17 -0
  147. package/skills/spec-wiki/evals/files/bootstrap/src/payments.ts +12 -0
  148. package/skills/spec-wiki/evals/files/bootstrap/tests/payments.test.ts +11 -0
  149. package/skills/spec-wiki/evals/files/focused/src/session.ts +5 -0
  150. package/skills/spec-wiki/evals/files/focused/tests/session.test.ts +17 -0
  151. package/skills/spec-wiki/evals/files/maintenance/low-signal/incoming/capture.txt +2 -0
  152. package/skills/spec-wiki/evals/files/maintenance/low-signal/src/capture.ts +3 -0
  153. package/skills/spec-wiki/evals/files/maintenance/move/docs/old-job-contract.md +18 -0
  154. package/skills/spec-wiki/evals/files/maintenance/move/src/job.ts +5 -0
  155. package/skills/spec-wiki/evals/files/maintenance/new-relation/docs/session-refresh.md +24 -0
  156. package/skills/spec-wiki/evals/files/maintenance/new-relation/incoming/refresh-worker.ts +7 -0
  157. package/skills/spec-wiki/evals/files/maintenance/new-relation/src/session.ts +7 -0
  158. package/skills/spec-wiki/evals/files/maintenance/no-impact/docs/auth-contract.md +17 -0
  159. package/skills/spec-wiki/evals/files/maintenance/no-impact/incoming/telemetry-format.ts +3 -0
  160. package/skills/spec-wiki/evals/files/maintenance/no-impact/src/auth.ts +3 -0
  161. package/skills/spec-wiki/evals/files/query/docs/how-to-run-jobs.md +4 -0
  162. package/skills/spec-wiki/evals/files/query/docs/spec-overview.md +9 -0
  163. package/skills/spec-wiki/evals/files/query/features/jobs/cancellation-contract.md +15 -0
  164. package/skills/spec-wiki/evals/files/query/features/jobs/retry-spec.md +6 -0
  165. package/skills/spec-wiki/evals/maintenance_regression.py +311 -0
  166. package/skills/spec-wiki/evals/retrieval-eval.md +37 -0
  167. package/skills/spec-wiki/evals/retrieval-evals.json +37 -0
  168. package/skills/spec-wiki/evals/trigger-evals.json +94 -0
  169. package/skills/spec-wiki/references/bootstrap.md +100 -0
  170. package/skills/spec-wiki/references/focused-spec.md +117 -0
  171. package/skills/spec-wiki/references/index-schema.md +330 -0
  172. package/skills/spec-wiki/references/maintenance.md +134 -0
  173. package/skills/spec-wiki/references/query.md +74 -0
  174. package/skills/spec-wiki/scripts/spec_wiki.py +2076 -0
  175. package/docs/desktop-markdown-media.md +0 -77
  176. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  177. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -270
  178. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/auth-scaffold-spec.md +0 -78
  179. package/external/pi-tools-suite/src/async-subagents/private-skills/browser-qa/references/qa-design.md +0 -223
  180. package/external/pi-tools-suite/src/dcp/state-persistence.ts +0 -195
  181. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  182. package/skills/spec-lite/SKILL.md +0 -140
  183. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-auth.example.jsonc +0 -0
  184. /package/external/pi-tools-suite/src/async-subagents/{private-skills/browser-qa/references → agents/browser-qa/examples}/qa-flow.example.jsonc +0 -0
  185. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.LICENSE +0 -0
  186. /package/external/pi-tools-suite/src/async-subagents/{private-skills → agents}/browser-qa/vendor/fflate.mjs +0 -0
package/README.md CHANGED
@@ -95,7 +95,7 @@ On startup, Pix tries to link the bundled suite into the active Pi agent directo
95
95
  | Context control | `dcp` | Explicit compression and context pruning for long sessions, including sidecar state. |
96
96
  | Local web access | `web-search` | Web search and page extraction through a local Ollama instance. |
97
97
  | Providers and quotas | `antigravity-auth`, `opencode-import`, `usage` | Antigravity OAuth/account failover, OpenCode credential import, and multi-provider usage views. |
98
- | Reusable workflows | `prompt-commands`, `skill-installer`, `session-name` | Prompt-command CRUD, skill installation/export, and session naming. |
98
+ | Reusable workflows | `prompt-commands`, `resource-registry`, `session-name` | Prompt-command CRUD, private Git-backed skill/agent install/update/push/remote-remove/local-uninstall with reload-on-change, project-scoped `.pi/tasks.jsonc` + `.pi/plans/` + `.pi/TODO.md` sync, a Desktop Registry sidebar for status/actions/configuration, and session naming. |
99
99
  | Model compatibility | `coding-discipline`, `model-tools`, `codex-reasoning-fix` | Model-specific discipline and vision lookup, compatibility aliases, and a Codex reasoning payload workaround. |
100
100
 
101
101
  Every module can be disabled. Optional integrations only activate when their requirements are available — for example, repository tools require an index, web tools require local Ollama web search, and LSP servers must be configured and trusted.
@@ -317,7 +317,7 @@ Type `/` for the live, searchable list. Extensions can add more commands than th
317
317
  | Status | `/usage` |
318
318
  | Process | `/quit`, `/exit` |
319
319
 
320
- Useful suite commands include `/todos`, `/todos-persist`, `/todos-scope`, `/sub-status`, `/sub-stop`, `/ultrawork`, `/hyperplan`, `/subagent-preset`, `/usage`, `/dcp`, `/idx-init`, `/idx-update`, `/opencode-import`, `/antigravity-add-account`, `/install-skill`, and `/export-skill`.
320
+ Useful suite commands include `/todos`, `/todos-persist`, `/todos-scope`, `/sub-status`, `/sub-stop`, `/ultrawork`, `/hyperplan`, `/subagent-preset`, `/usage`, `/dcp`, `/idx-init`, `/idx-update`, `/opencode-import`, `/antigravity-add-account`, and `/registry`.
321
321
 
322
322
  ### Keyboard and mouse
323
323
 
@@ -500,8 +500,16 @@ npm run test:tools-suite
500
500
 
501
501
  # Build the publishable renderer
502
502
  npm run build:pix
503
+
504
+ # Watch Pix, ACP, the bundled suite, and Desktop
505
+ npm run watch:all
503
506
  ```
504
507
 
508
+ `watch:all` rebuilds only the affected project parts. It keeps the last working
509
+ Desktop process alive while changes compile, then replaces it only after the
510
+ entire queued build succeeds. A failed build is reported without replacing the
511
+ running Desktop.
512
+
505
513
  Regenerate the README screenshots without accounts or live model traffic:
506
514
 
507
515
  ```bash
package/dist/app/app.d.ts CHANGED
@@ -51,6 +51,7 @@ export declare class PiUiExtendApp {
51
51
  * for one tab never reaches another tab's extensions.
52
52
  */
53
53
  private readonly extensionEventBusByRuntime;
54
+ private readonly subagentCatalogBySessionId;
54
55
  private readonly inputEditor;
55
56
  private lastInputEditorContentVersion;
56
57
  private readonly requestHistory;
@@ -78,6 +79,8 @@ export declare class PiUiExtendApp {
78
79
  private awaitCurrentSessionExtensions;
79
80
  private activateRuntime;
80
81
  private createExtensionEventBus;
82
+ private subagentTypesForRuntime;
83
+ private contextInventoryText;
81
84
  private handleTerminalBellAttention;
82
85
  private afterSessionReplacement;
83
86
  private requireRuntime;
package/dist/app/app.js CHANGED
@@ -47,6 +47,8 @@ import { checkPiUpdate, checkPixUpdate, formatPixStartupUpdateDialog, formatPiSt
47
47
  import { checkAndUpdateIdxOnStartup, formatIdxStartupUpdateNotice } from "./cli/startup-checks.js";
48
48
  import { AppVoiceController } from "./input/voice-controller.js";
49
49
  import { createIsolatedExtensionEventBus } from "./extensions/extension-event-bus.js";
50
+ import { parseSubagentCatalogState, SUBAGENTS_CATALOG_STATE_EVENT, } from "./extensions/subagent-catalog-state.js";
51
+ import { createReloadContextInventory, formatReloadContextInventory } from "./commands/reload-context-inventory.js";
50
52
  import { setAppIconTheme } from "./icons.js";
51
53
  const TERMINAL_BELL_ATTENTION_EVENT = "pix:terminal-bell:attention";
52
54
  function normalizeJumpTargetText(text) {
@@ -123,6 +125,7 @@ export class PiUiExtendApp {
123
125
  * for one tab never reaches another tab's extensions.
124
126
  */
125
127
  extensionEventBusByRuntime = new WeakMap();
128
+ subagentCatalogBySessionId = new Map();
126
129
  inputEditor = new InputEditor();
127
130
  lastInputEditorContentVersion = this.inputEditor.contentVersion;
128
131
  requestHistory;
@@ -221,6 +224,7 @@ export class PiUiExtendApp {
221
224
  restoreAutoUserMessages: (messages) => this.queuedMessages.restoreAutoUserMessages(messages),
222
225
  captureDeferredUserMessages: () => this.queuedMessages.captureDeferredUserMessages(),
223
226
  restoreDeferredUserMessages: (messages) => this.queuedMessages.restoreDeferredUserMessages(messages),
227
+ contextInventoryText: (runtime, heading) => this.contextInventoryText(runtime, heading),
224
228
  addEntry: (entry) => this.addEntry(entry),
225
229
  showToast: (message, kind) => this.showToast(message, kind),
226
230
  render: () => this.render(),
@@ -358,6 +362,7 @@ export class PiUiExtendApp {
358
362
  this.extensionActions = new AppExtensionActionsController({
359
363
  isRunning: () => this.running,
360
364
  runtime: () => this.runtime,
365
+ subagentTypes: (runtime) => this.subagentTypesForRuntime(runtime),
361
366
  getInput: () => this.input,
362
367
  setInput: (value) => this.setInput(value),
363
368
  awaitCurrentSessionExtensions: (runtime) => this.awaitCurrentSessionExtensions(runtime),
@@ -508,6 +513,7 @@ export class PiUiExtendApp {
508
513
  this.commandController = new AppCommandController({
509
514
  options: this.options,
510
515
  runtime: () => this.runtime,
516
+ subagentTypes: (runtime) => this.subagentTypesForRuntime(runtime),
511
517
  requestHistory: () => this.requestHistory,
512
518
  getInput: () => this.input,
513
519
  setInput: (value) => this.setInput(value),
@@ -800,6 +806,7 @@ export class PiUiExtendApp {
800
806
  handleExtensionError: (error) => this.extensionActions.handleExtensionError(error),
801
807
  handleSessionEvent: (event) => this.handleSessionEvent(event),
802
808
  bindAgentPause: (session) => this.agentPauseController.bind(session),
809
+ contextInventoryText: (runtime, heading) => this.contextInventoryText(runtime, heading),
803
810
  addEntry: (entry) => this.addEntry(entry),
804
811
  setStatus: (status) => this.setStatus(status),
805
812
  showToast: (message, kind) => this.showToast(message, kind),
@@ -945,10 +952,29 @@ export class PiUiExtendApp {
945
952
  this.handleTerminalBellAttention(data);
946
953
  if (channel === SUBAGENTS_LIVE_STATE_EVENT)
947
954
  this.subagentsWidgetController.observeLiveState(data);
955
+ if (channel === SUBAGENTS_CATALOG_STATE_EVENT) {
956
+ const catalog = parseSubagentCatalogState(data);
957
+ if (catalog?.sessionId)
958
+ this.subagentCatalogBySessionId.set(catalog.sessionId, catalog);
959
+ }
948
960
  if (channel === TODO_STATE_EVENT)
949
961
  this.todoWidgetController.observeLiveState(data);
950
962
  });
951
963
  }
964
+ subagentTypesForRuntime(runtime) {
965
+ const sessionId = runtime.session.sessionManager.getSessionId();
966
+ const catalog = this.subagentCatalogBySessionId.get(sessionId);
967
+ if (!catalog)
968
+ return undefined;
969
+ const model = runtime.session.model;
970
+ const currentModelRef = model ? `${model.provider}/${model.id}` : undefined;
971
+ if (catalog.model && currentModelRef && catalog.model !== currentModelRef)
972
+ return undefined;
973
+ return catalog.types;
974
+ }
975
+ contextInventoryText(runtime, heading) {
976
+ return formatReloadContextInventory(createReloadContextInventory(runtime, this.subagentTypesForRuntime(runtime)), heading);
977
+ }
952
978
  handleTerminalBellAttention(data) {
953
979
  const sessionFile = isRecord(data) && typeof data.sessionFile === "string" ? data.sessionFile : undefined;
954
980
  this.tabsController.markTerminalBellAttention(sessionFile);
@@ -7,6 +7,7 @@ export type DirectPopupMenu = Exclude<ActivePopupMenu, "slash">;
7
7
  export type CommandControllerHost = {
8
8
  readonly options: AppOptions;
9
9
  runtime(): AgentSessionRuntime | undefined;
10
+ subagentTypes?(runtime: AgentSessionRuntime): readonly string[] | undefined;
10
11
  awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
11
12
  requestHistory(): AppRequestHistory;
12
13
  getInput(): string;
@@ -4,6 +4,7 @@ import { getProjectPixConfigPath, savePixAutocompleteModel, savePixDefaultModel,
4
4
  import { createId } from "../id.js";
5
5
  import { isThinkingLevel, parseScopedModelRef } from "../model/model-ref.js";
6
6
  import { appendPixSystemDisplayEntry } from "../session/pix-system-message.js";
7
+ import { createReloadContextInventory, formatReloadContextInventory } from "./reload-context-inventory.js";
7
8
  export class ModelCommandActions {
8
9
  host;
9
10
  constructor(host) {
@@ -333,10 +334,14 @@ export class ModelCommandActions {
333
334
  await session.reload();
334
335
  if (!isCommandScopeActive(this.host, scope))
335
336
  return;
337
+ const runtime = scope.runtime;
338
+ const text = runtime
339
+ ? formatReloadContextInventory(createReloadContextInventory(runtime, this.host.subagentTypes?.(runtime)), `Reloaded resources after model change to ${ref}`)
340
+ : `Reloaded resources after model change to ${ref}`;
336
341
  this.host.addEntry({
337
342
  id: createId("system"),
338
343
  kind: "system",
339
- text: `Reloaded resources after model change to ${ref}`,
344
+ text,
340
345
  });
341
346
  this.host.toast.success("Model changed and resources reloaded");
342
347
  }
@@ -217,7 +217,7 @@ export function createSlashCommands(actions, host) {
217
217
  },
218
218
  {
219
219
  name: "reload",
220
- description: "Reload keybindings, extensions, skills, prompts, and themes",
220
+ description: "Reload resources and show model-available skills, tools, and agents",
221
221
  kind: "builtin",
222
222
  keywords: ["restart", "refresh", "resources", "extensions", "skills", "prompts"],
223
223
  run: () => actions.runReloadCommand(),
@@ -249,9 +249,9 @@ export function createSlashCommands(actions, host) {
249
249
  },
250
250
  {
251
251
  name: "delete",
252
- description: "Delete the current (or specified) session file plus its sidecar DCP state",
252
+ description: "Delete the current (or specified) session file",
253
253
  kind: "builtin",
254
- keywords: ["remove", "destroy", "purge", "session", "sidecar", "dcp"],
254
+ keywords: ["remove", "destroy", "purge", "session", "dcp"],
255
255
  allowArguments: true,
256
256
  run: (argumentsText) => actions.runDeleteCommand(argumentsText),
257
257
  },
@@ -15,7 +15,6 @@ export declare class SessionCommandActions {
15
15
  runHotkeysCommand(): Promise<void>;
16
16
  runReloadCommand(): Promise<void>;
17
17
  runDeleteCommand(argumentsText: string): Promise<void>;
18
- private removeDcpSidecarState;
19
18
  runNewSessionCommand(): Promise<void>;
20
19
  runCompactCommand(customInstructions?: string): Promise<void>;
21
20
  private piPackageRoot;
@@ -11,6 +11,7 @@ import { copyTextToClipboard } from "../screen/clipboard.js";
11
11
  import { formatAccountUsageReport, queryAccountUsageReport } from "../model/model-usage-status.js";
12
12
  import { checkGlobalPiInstall, checkPixUpdate, formatGlobalPiCheck, formatPixUpdateCheck, parsePixUpdateArgs, pixUpdateUsage, } from "../cli/update.js";
13
13
  import { createStartupInfoMessage } from "../cli/startup-info.js";
14
+ import { createReloadContextInventory, formatReloadContextInventory } from "./reload-context-inventory.js";
14
15
  import { getCompleteSessionStats } from "../session/session-stats.js";
15
16
  import { loadSessionTitleConfig } from "../../bundled-extensions/session-title/config.js";
16
17
  import { fallbackSessionTitleFromInput, firstUserMessageText, generateSessionTitleWithRuntime, sessionTitleModelRefs, } from "../../bundled-extensions/session-title/title-generation.js";
@@ -309,7 +310,8 @@ export class SessionCommandActions {
309
310
  if (!isCommandScopeActive(this.host, scope))
310
311
  return;
311
312
  this.host.setSessionStatus(runtime.session);
312
- this.host.addEntry({ id: createId("system"), kind: "system", text: "Reloaded keybindings, extensions, skills, prompts, themes" });
313
+ const inventory = createReloadContextInventory(runtime, this.host.subagentTypes?.(runtime));
314
+ this.host.addEntry({ id: createId("system"), kind: "system", text: formatReloadContextInventory(inventory) });
313
315
  this.host.toast.success("Reloaded resources");
314
316
  }
315
317
  catch (error) {
@@ -342,7 +344,7 @@ export class SessionCommandActions {
342
344
  {
343
345
  value: true,
344
346
  label: `Yes, delete ${isCurrent ? "the current session" : basename(targetSessionFile)}`,
345
- description: "This permanently removes the session file and any sidecar data. This cannot be undone.",
347
+ description: "This permanently removes the session file. This cannot be undone.",
346
348
  variant: "error",
347
349
  },
348
350
  { value: false, label: "Cancel" },
@@ -356,11 +358,28 @@ export class SessionCommandActions {
356
358
  }
357
359
  this.host.setStatus("deleting session");
358
360
  this.host.render();
359
- const sidecarRemoved = await this.removeDcpSidecarState(dirname(targetSessionFile), targetSessionId).catch(() => false);
360
- await rm(targetSessionFile, { force: true }).catch(() => undefined);
361
361
  const deleteCurrent = isCurrent || targetSessionFile === currentSessionFile;
362
362
  if (deleteCurrent) {
363
+ // Do not race a final extension persistence operation with destructive
364
+ // session cleanup.
363
365
  await this.host.awaitCurrentSessionExtensions(runtime);
366
+ if (!isCommandRuntimeActive(this.host, runtime))
367
+ return;
368
+ }
369
+ try {
370
+ await rm(targetSessionFile, { force: true });
371
+ }
372
+ catch (error) {
373
+ this.host.addEntry({
374
+ id: createId("system"),
375
+ kind: "system",
376
+ text: `Could not delete session file ${targetSessionFile}: ${error instanceof Error ? error.message : String(error)}`,
377
+ });
378
+ this.host.toast.error("Session deletion failed");
379
+ this.host.setSessionStatus(runtime.session);
380
+ return;
381
+ }
382
+ if (deleteCurrent) {
364
383
  const result = await runtime.newSession();
365
384
  if (!isCommandRuntimeActive(this.host, runtime))
366
385
  return;
@@ -374,7 +393,7 @@ export class SessionCommandActions {
374
393
  this.host.addEntry({
375
394
  id: createId("system"),
376
395
  kind: "system",
377
- text: `Deleted session ${targetSessionId}. ${sidecarRemoved ? "Sidecar DCP state removed. " : ""}Started a new session. cwd=${runtime.cwd}`,
396
+ text: formatReloadContextInventory(createReloadContextInventory(runtime, this.host.subagentTypes?.(runtime)), `Deleted session ${targetSessionId}. Started a new session. cwd=${runtime.cwd}`),
378
397
  });
379
398
  if (runtime.modelFallbackMessage)
380
399
  this.host.addEntry({ id: createId("system"), kind: "system", text: runtime.modelFallbackMessage });
@@ -386,20 +405,12 @@ export class SessionCommandActions {
386
405
  this.host.addEntry({
387
406
  id: createId("system"),
388
407
  kind: "system",
389
- text: `Deleted session file ${targetSessionFile}${sidecarRemoved ? " and its sidecar DCP state" : ""}.`,
408
+ text: `Deleted session file ${targetSessionFile}.`,
390
409
  });
391
410
  this.host.setSessionStatus(runtime.session);
392
411
  }
393
412
  this.host.toast.success("Session deleted");
394
413
  }
395
- async removeDcpSidecarState(sessionDir, sessionId) {
396
- if (!sessionId)
397
- return false;
398
- const safeName = `${sessionId.replace(/[^a-zA-Z0-9._-]/g, "_")}.json`;
399
- const statePath = join(sessionDir, "dcp-state", safeName);
400
- await rm(statePath, { force: true });
401
- return true;
402
- }
403
414
  async runNewSessionCommand() {
404
415
  const runtime = getIdleRuntime(this.host, "new");
405
416
  if (!runtime)
@@ -418,7 +429,11 @@ export class SessionCommandActions {
418
429
  return;
419
430
  }
420
431
  this.host.resetSessionView();
421
- this.host.addEntry({ id: createId("system"), kind: "system", text: `Started a new session. cwd=${runtime.cwd}` });
432
+ this.host.addEntry({
433
+ id: createId("system"),
434
+ kind: "system",
435
+ text: formatReloadContextInventory(createReloadContextInventory(runtime, this.host.subagentTypes?.(runtime)), `Started a new session. cwd=${runtime.cwd}`),
436
+ });
422
437
  if (runtime.modelFallbackMessage)
423
438
  this.host.addEntry({ id: createId("system"), kind: "system", text: runtime.modelFallbackMessage });
424
439
  for (const diag of runtime.diagnostics ?? []) {
@@ -0,0 +1,11 @@
1
+ import type { AgentSessionRuntime } from "@earendil-works/pi-coding-agent";
2
+ export interface ReloadContextInventory {
3
+ readonly model: string;
4
+ readonly skills: readonly string[];
5
+ readonly tools: readonly string[];
6
+ readonly agents: readonly string[] | undefined;
7
+ readonly skillsReadable: boolean;
8
+ readonly subagentsActive: boolean;
9
+ }
10
+ export declare function createReloadContextInventory(runtime: AgentSessionRuntime, agentTypes: readonly string[] | undefined): ReloadContextInventory;
11
+ export declare function formatReloadContextInventory(inventory: ReloadContextInventory, heading?: string): string;
@@ -0,0 +1,59 @@
1
+ export function createReloadContextInventory(runtime, agentTypes) {
2
+ const session = runtime.session;
3
+ const tools = unique(session.getActiveToolNames());
4
+ const skillsReadable = tools.includes("read") || tools.includes("bash");
5
+ const subagentsActive = tools.includes("subagents");
6
+ const skills = skillsReadable
7
+ ? unique(session.resourceLoader.getSkills().skills.map((skill) => skill.name)).sort(compareText)
8
+ : [];
9
+ const agents = subagentsActive && agentTypes !== undefined
10
+ ? unique(agentTypes).sort(compareText)
11
+ : subagentsActive
12
+ ? undefined
13
+ : [];
14
+ return {
15
+ model: currentModelLabel(runtime),
16
+ skills,
17
+ tools,
18
+ agents,
19
+ skillsReadable,
20
+ subagentsActive,
21
+ };
22
+ }
23
+ export function formatReloadContextInventory(inventory, heading = "Reloaded keybindings, extensions, skills, prompts, themes") {
24
+ return [
25
+ heading,
26
+ "",
27
+ `Model: ${inventory.model}`,
28
+ "",
29
+ `Skills (in context): ${inventory.skillsReadable ? listOrNone(inventory.skills) : "(none; read/bash inactive)"}`,
30
+ "",
31
+ `Tools (active): ${listOrNone(inventory.tools)}`,
32
+ "",
33
+ `Agents (available): ${formatAgents(inventory)}`,
34
+ ].join("\n");
35
+ }
36
+ function currentModelLabel(runtime) {
37
+ const model = runtime.session.model;
38
+ if (!model)
39
+ return "unavailable";
40
+ const thinking = runtime.session.thinkingLevel;
41
+ const modelRef = `${model.provider}/${model.id}`;
42
+ return !thinking || thinking === "off" ? modelRef : `${modelRef}:${thinking}`;
43
+ }
44
+ function formatAgents(inventory) {
45
+ if (!inventory.subagentsActive)
46
+ return "(none; subagents tool inactive)";
47
+ if (inventory.agents === undefined)
48
+ return "(catalog unavailable)";
49
+ return listOrNone(inventory.agents);
50
+ }
51
+ function listOrNone(values) {
52
+ return values.length > 0 ? values.join(", ") : "(none)";
53
+ }
54
+ function unique(values) {
55
+ return [...new Set(values.map((value) => value.trim()).filter(Boolean))];
56
+ }
57
+ function compareText(left, right) {
58
+ return left.localeCompare(right);
59
+ }
@@ -4,6 +4,7 @@ import type { Entry } from "../types.js";
4
4
  export type AppExtensionActionsHost = {
5
5
  isRunning(): boolean;
6
6
  runtime(): AgentSessionRuntime | undefined;
7
+ subagentTypes?(runtime: AgentSessionRuntime): readonly string[] | undefined;
7
8
  getInput(): string;
8
9
  setInput(value: string): void;
9
10
  awaitCurrentSessionExtensions(runtime?: AgentSessionRuntime): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  import { basename } from "node:path";
2
2
  import { createId } from "../id.js";
3
3
  import { logPixEvent } from "../logger.js";
4
+ import { createReloadContextInventory, formatReloadContextInventory } from "../commands/reload-context-inventory.js";
4
5
  export class AppExtensionActionsController {
5
6
  host;
6
7
  logExtensionError;
@@ -60,6 +61,11 @@ export class AppExtensionActionsController {
60
61
  if (!this.isSessionActive(runtime, session))
61
62
  return;
62
63
  this.host.setSessionStatus(session);
64
+ this.host.addEntry({
65
+ id: createId("system"),
66
+ kind: "system",
67
+ text: formatReloadContextInventory(createReloadContextInventory(runtime, this.host.subagentTypes?.(runtime)), "Reloaded resources"),
68
+ });
63
69
  this.host.showToast("Reloaded resources", "success");
64
70
  this.host.render();
65
71
  },
@@ -0,0 +1,9 @@
1
+ export declare const SUBAGENTS_CATALOG_STATE_EVENT = "pi-tools-suite:async-subagents:catalog";
2
+ export interface SubagentCatalogState {
3
+ readonly version: 1;
4
+ readonly sessionId?: string;
5
+ readonly sessionFile?: string;
6
+ readonly model?: string;
7
+ readonly types: readonly string[];
8
+ }
9
+ export declare function parseSubagentCatalogState(value: unknown): SubagentCatalogState | undefined;
@@ -0,0 +1,23 @@
1
+ export const SUBAGENTS_CATALOG_STATE_EVENT = "pi-tools-suite:async-subagents:catalog";
2
+ export function parseSubagentCatalogState(value) {
3
+ if (!isRecord(value) || value.version !== 1 || !Array.isArray(value.types))
4
+ return undefined;
5
+ if (!value.types.every((type) => typeof type === "string" && type.trim().length > 0))
6
+ return undefined;
7
+ if (value.sessionId !== undefined && typeof value.sessionId !== "string")
8
+ return undefined;
9
+ if (value.sessionFile !== undefined && typeof value.sessionFile !== "string")
10
+ return undefined;
11
+ if (value.model !== undefined && typeof value.model !== "string")
12
+ return undefined;
13
+ return {
14
+ version: 1,
15
+ ...(value.sessionId ? { sessionId: value.sessionId } : {}),
16
+ ...(value.sessionFile ? { sessionFile: value.sessionFile } : {}),
17
+ ...(value.model ? { model: value.model } : {}),
18
+ types: [...new Set(value.types.map((type) => type.trim()))],
19
+ };
20
+ }
21
+ function isRecord(value) {
22
+ return typeof value === "object" && value !== null && !Array.isArray(value);
23
+ }
@@ -33,6 +33,20 @@ declare const NERD_FONT_ICONS: {
33
33
  readonly toolPreviewTruncated: "⊞";
34
34
  readonly up: "↑";
35
35
  readonly down: "↓";
36
+ readonly agent: "󰚩";
37
+ readonly search: "󰍉";
38
+ readonly code: "󰅴";
39
+ readonly flask: "󰂓";
40
+ readonly globe: "󰇧";
41
+ readonly sparkles: "󰫢";
42
+ readonly brain: "󰧑";
43
+ readonly wrench: "󰖷";
44
+ readonly terminal: "󰆍";
45
+ readonly bug: "󰃤";
46
+ readonly book: "󰂺";
47
+ readonly eye: "󰈈";
48
+ readonly zap: "󰉁";
49
+ readonly rocket: "󰑣";
36
50
  };
37
51
  export type AppIconName = keyof typeof NERD_FONT_ICONS;
38
52
  export type AppIconMap = Record<AppIconName, string>;
package/dist/app/icons.js CHANGED
@@ -44,6 +44,22 @@ const NERD_FONT_ICONS = {
44
44
  toolPreviewTruncated: "⊞",
45
45
  up: "↑",
46
46
  down: "↓",
47
+ // Sub-agent icons (agent .md frontmatter `icon:` names). Values are shared
48
+ // with the Pix Desktop lucide mapping in desktop/src/lib/agent-icons.ts.
49
+ agent: "\u{f06a9}", // nf-md-robot
50
+ search: "\u{f0349}", // nf-md-magnify
51
+ code: "\u{f0174}", // nf-md-code_tags
52
+ flask: "\u{f0093}", // nf-md-flask
53
+ globe: "\u{f01e7}", // nf-md-earth
54
+ sparkles: "\u{f0ae2}", // nf-md-star_four_points
55
+ brain: "\u{f09d1}", // nf-md-brain
56
+ wrench: "\u{f05b7}", // nf-md-wrench
57
+ terminal: "\u{f018d}", // nf-md-console
58
+ bug: "\u{f00e4}", // nf-md-bug
59
+ book: "\u{f00ba}", // nf-md-book
60
+ eye: "\u{f0208}", // nf-md-eye
61
+ zap: "\u{f0241}", // nf-md-flash
62
+ rocket: "\u{f0463}", // nf-md-rocket
47
63
  };
48
64
  const FALLBACK_ICONS = {
49
65
  alert: "⚠",
@@ -78,6 +94,23 @@ const FALLBACK_ICONS = {
78
94
  toolPreviewTruncated: "+",
79
95
  up: "↑",
80
96
  down: "↓",
97
+ // Sub-agent icon fallbacks (no icon-font codepoints; readable when the
98
+ // terminal font lacks Nerd Font glyphs). Emoji follow the existing fallback
99
+ // precedent (lightbulb, timerSand).
100
+ agent: "◇",
101
+ search: "⌕",
102
+ code: "{}",
103
+ flask: "∆",
104
+ globe: "⊕",
105
+ sparkles: "✦",
106
+ brain: "∞",
107
+ wrench: "⚙",
108
+ terminal: ">_",
109
+ bug: "🐛",
110
+ book: "▤",
111
+ eye: "◉",
112
+ zap: "↯",
113
+ rocket: "🚀",
81
114
  };
82
115
  export const APP_ICON_THEMES = {
83
116
  nerdFont: NERD_FONT_ICONS,
@@ -4,7 +4,7 @@ import { renderToolDisplay } from "../../tool-renderers/index.js";
4
4
  import { SUBAGENT_STATUSES, THINKING_TOOL_NAME, TODO_TOOL_NAME } from "../constants.js";
5
5
  import { attachImageClickTargets } from "../screen/image-click-targets.js";
6
6
  import { formatStructuredText } from "./message-content.js";
7
- import { formatSubagentTimestamp, isSubagentRunRenderDetails, isSubagentsToolName, subagentRunName, subagentStatusIcon, taskPreviewMap, } from "../subagents/subagents-model.js";
7
+ import { formatSubagentTimestamp, isSubagentRunRenderDetails, isSubagentsToolName, subagentIcon, subagentRunName, taskPreviewMap, } from "../subagents/subagents-model.js";
8
8
  import { formatTodoTaskLine, isTodoDetails, visibleTodoTasks } from "../todo/todo-model.js";
9
9
  import { renderToolBlock } from "./tool-block-renderer.js";
10
10
  export function renderConversationToolEntry(entry, width, options) {
@@ -202,7 +202,7 @@ function subagentsDetailsText(details) {
202
202
  return lines.join("\n");
203
203
  }
204
204
  function formatSubagentToolLine(agent, preview) {
205
- const parts = [subagentStatusIcon(agent.status), agent.status, agent.id];
205
+ const parts = [subagentIcon(preview), agent.status, agent.id];
206
206
  if (preview?.model)
207
207
  parts.push(`model:${preview.model}`);
208
208
  if (preview?.task)
@@ -1,2 +1,7 @@
1
1
  import type { AgentSession } from "@earendil-works/pi-coding-agent";
2
- export declare function formatDcpStatsToast(session: AgentSession): string;
2
+ type FormatDcpStatsOptions = {
3
+ manualModeBaseline?: boolean;
4
+ configPath?: string;
5
+ };
6
+ export declare function formatDcpStatsToast(session: AgentSession, options?: FormatDcpStatsOptions): string;
7
+ export {};