@nklisch/pi-enhanced 0.1.2 → 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.
Files changed (236) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/README.md +3 -2
  3. package/node_modules/@nklisch/pi-clearance/README.md +10 -5
  4. package/node_modules/@nklisch/pi-clearance/docs/ARCHITECTURE.md +12 -7
  5. package/node_modules/@nklisch/pi-clearance/docs/CONFIGURATION.md +25 -35
  6. package/node_modules/@nklisch/pi-clearance/docs/DEVELOPER_GUIDE.md +30 -30
  7. package/node_modules/@nklisch/pi-clearance/docs/PACK_AUTHORING.md +5 -4
  8. package/node_modules/@nklisch/pi-clearance/docs/PRINCIPLES.md +14 -10
  9. package/node_modules/@nklisch/pi-clearance/docs/REFERENCE_PATTERNS.md +1 -1
  10. package/node_modules/@nklisch/pi-clearance/docs/REVIEWER_PROMPTS.md +12 -8
  11. package/node_modules/@nklisch/pi-clearance/docs/RULE_PACKS.md +1 -1
  12. package/node_modules/@nklisch/pi-clearance/docs/SPEC.md +7 -6
  13. package/node_modules/@nklisch/pi-clearance/docs/TUNE.md +2 -2
  14. package/node_modules/@nklisch/pi-clearance/docs/USER_GUIDE.md +7 -3
  15. package/node_modules/@nklisch/pi-clearance/docs/VISION.md +3 -3
  16. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-arm64.node +0 -0
  17. package/node_modules/@nklisch/pi-clearance/native/clearance-core.darwin-x64.node +0 -0
  18. package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-arm64-gnu.node +0 -0
  19. package/node_modules/@nklisch/pi-clearance/native/clearance-core.linux-x64-gnu.node +0 -0
  20. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-arm64-msvc.node +0 -0
  21. package/node_modules/@nklisch/pi-clearance/native/clearance-core.win32-x64-msvc.node +0 -0
  22. package/node_modules/@nklisch/pi-clearance/package.json +9 -5
  23. package/node_modules/@nklisch/pi-clearance/src/config/config-command-plans.ts +79 -3
  24. package/node_modules/@nklisch/pi-clearance/src/config/config-command-writer.ts +7 -1
  25. package/node_modules/@nklisch/pi-clearance/src/config/defaults.ts +42 -0
  26. package/node_modules/@nklisch/pi-clearance/src/config/gated-tools.ts +7 -0
  27. package/node_modules/@nklisch/pi-clearance/src/config/loader.ts +7 -1
  28. package/node_modules/@nklisch/pi-clearance/src/config/pack-enablement-writer.ts +1 -0
  29. package/node_modules/@nklisch/pi-clearance/src/config/paths.ts +14 -3
  30. package/node_modules/@nklisch/pi-clearance/src/config/persistence.ts +135 -0
  31. package/node_modules/@nklisch/pi-clearance/src/config/schema.ts +74 -26
  32. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCase.ts +1 -1
  33. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseCategory.ts +1 -1
  34. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseExpectation.ts +1 -1
  35. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResult.ts +1 -1
  36. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseResultOutcome.ts +1 -1
  37. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialCaseSource.ts +1 -1
  38. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationReport.ts +1 -1
  39. package/node_modules/@nklisch/pi-clearance/src/contracts/AdversarialValidationStatus.ts +1 -1
  40. package/node_modules/@nklisch/pi-clearance/src/contracts/BashBlock.ts +1 -1
  41. package/node_modules/@nklisch/pi-clearance/src/contracts/BashCommandShape.ts +1 -1
  42. package/node_modules/@nklisch/pi-clearance/src/contracts/BashConditionalArm.ts +1 -1
  43. package/node_modules/@nklisch/pi-clearance/src/contracts/BashControlConstruct.ts +1 -1
  44. package/node_modules/@nklisch/pi-clearance/src/contracts/BashFlag.ts +1 -1
  45. package/node_modules/@nklisch/pi-clearance/src/contracts/BashForLoopKeywordSpans.ts +1 -1
  46. package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntry.ts +1 -1
  47. package/node_modules/@nklisch/pi-clearance/src/contracts/BashIteratorEntryKind.ts +1 -1
  48. package/node_modules/@nklisch/pi-clearance/src/contracts/BashListOperator.ts +1 -1
  49. package/node_modules/@nklisch/pi-clearance/src/contracts/BashLoopVariableReference.ts +1 -1
  50. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFact.ts +1 -1
  51. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactContext.ts +1 -1
  52. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenance.ts +1 -1
  53. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFactProvenanceEntry.ts +1 -1
  54. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPathFacts.ts +1 -1
  55. package/node_modules/@nklisch/pi-clearance/src/contracts/BashPipeline.ts +1 -1
  56. package/node_modules/@nklisch/pi-clearance/src/contracts/BashStage.ts +1 -1
  57. package/node_modules/@nklisch/pi-clearance/src/contracts/BashStageProgram.ts +1 -1
  58. package/node_modules/@nklisch/pi-clearance/src/contracts/BlockOperator.ts +1 -1
  59. package/node_modules/@nklisch/pi-clearance/src/contracts/CapturedOutcome.ts +1 -1
  60. package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilyKey.ts +1 -1
  61. package/node_modules/@nklisch/pi-clearance/src/contracts/CommandFamilySummary.ts +1 -1
  62. package/node_modules/@nklisch/pi-clearance/src/contracts/CompileError.ts +1 -1
  63. package/node_modules/@nklisch/pi-clearance/src/contracts/CompositionOperator.ts +1 -1
  64. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundBodyReason.ts +1 -1
  65. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundFeatureReason.ts +1 -1
  66. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundForm.ts +1 -1
  67. package/node_modules/@nklisch/pi-clearance/src/contracts/CompoundIteratorReason.ts +1 -1
  68. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusEntry.ts +1 -1
  69. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusExpectedLabel.ts +1 -1
  70. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusFidelity.ts +1 -1
  71. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQueryModel.ts +1 -1
  72. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummary.ts +1 -1
  73. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusQuerySummarySnapshot.ts +1 -1
  74. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusRecord.ts +1 -1
  75. package/node_modules/@nklisch/pi-clearance/src/contracts/CorpusSource.ts +1 -1
  76. package/node_modules/@nklisch/pi-clearance/src/contracts/CountByLabel.ts +1 -1
  77. package/node_modules/@nklisch/pi-clearance/src/contracts/Decision.ts +1 -1
  78. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionEffect.ts +1 -1
  79. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionProvenance.ts +1 -1
  80. package/node_modules/@nklisch/pi-clearance/src/contracts/DecisionSource.ts +1 -1
  81. package/node_modules/@nklisch/pi-clearance/src/contracts/DiagnosticSeverity.ts +1 -1
  82. package/node_modules/@nklisch/pi-clearance/src/contracts/EffectivePolicy.ts +1 -1
  83. package/node_modules/@nklisch/pi-clearance/src/contracts/EmbeddedShellProjection.ts +1 -1
  84. package/node_modules/@nklisch/pi-clearance/src/contracts/EnvironmentAssignment.ts +1 -1
  85. package/node_modules/@nklisch/pi-clearance/src/contracts/IteratorSourceKind.ts +1 -1
  86. package/node_modules/@nklisch/pi-clearance/src/contracts/LoopQuoteKind.ts +1 -1
  87. package/node_modules/@nklisch/pi-clearance/src/contracts/LoopVariableUnknownReason.ts +1 -1
  88. package/node_modules/@nklisch/pi-clearance/src/contracts/MatcherExpr.ts +1 -1
  89. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationShapeKind.ts +1 -1
  90. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryClassification.ts +1 -1
  91. package/node_modules/@nklisch/pi-clearance/src/contracts/MutationTrustBoundaryKind.ts +1 -1
  92. package/node_modules/@nklisch/pi-clearance/src/contracts/PackCompileResult.ts +1 -1
  93. package/node_modules/@nklisch/pi-clearance/src/contracts/PackWarningLevel.ts +1 -1
  94. package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedEvidence.ts +1 -1
  95. package/node_modules/@nklisch/pi-clearance/src/contracts/ParsedShapeSummary.ts +1 -1
  96. package/node_modules/@nklisch/pi-clearance/src/contracts/PathAccess.ts +1 -1
  97. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactProjectScope.ts +1 -1
  98. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsRequirement.ts +1 -1
  99. package/node_modules/@nklisch/pi-clearance/src/contracts/PathFactsResolvedConfig.ts +1 -1
  100. package/node_modules/@nklisch/pi-clearance/src/contracts/PathNormalization.ts +1 -1
  101. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScope.ts +1 -1
  102. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherExpr.ts +1 -1
  103. package/node_modules/@nklisch/pi-clearance/src/contracts/PathScopeMatcherMode.ts +1 -1
  104. package/node_modules/@nklisch/pi-clearance/src/contracts/PathUnknownReason.ts +1 -1
  105. package/node_modules/@nklisch/pi-clearance/src/contracts/PathUsageKind.ts +1 -1
  106. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolOperation.ts +1 -1
  107. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolPathInput.ts +1 -1
  108. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolShape.ts +1 -1
  109. package/node_modules/@nklisch/pi-clearance/src/contracts/PiBuiltinToolSpec.ts +1 -1
  110. package/node_modules/@nklisch/pi-clearance/src/contracts/PiFileMutationToolName.ts +1 -1
  111. package/node_modules/@nklisch/pi-clearance/src/contracts/PiToolMutationFacts.ts +1 -1
  112. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPack.ts +1 -1
  113. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackDocLink.ts +1 -1
  114. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackExample.ts +1 -1
  115. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackMetadata.ts +1 -1
  116. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyPackWarning.ts +1 -1
  117. package/node_modules/@nklisch/pi-clearance/src/contracts/PolicyRule.ts +1 -1
  118. package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunReason.ts +1 -1
  119. package/node_modules/@nklisch/pi-clearance/src/contracts/ProposalNotRunSeverity.ts +1 -1
  120. package/node_modules/@nklisch/pi-clearance/src/contracts/QuoteKind.ts +1 -1
  121. package/node_modules/@nklisch/pi-clearance/src/contracts/Redirect.ts +1 -1
  122. package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectStream.ts +1 -1
  123. package/node_modules/@nklisch/pi-clearance/src/contracts/RedirectTargetKind.ts +1 -1
  124. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayCorpus.ts +1 -1
  125. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDelta.ts +1 -1
  126. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaBlockedSummary.ts +1 -1
  127. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaChangedRecord.ts +1 -1
  128. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaFamily.ts +1 -1
  129. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaImprovement.ts +1 -1
  130. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaRegression.ts +1 -1
  131. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaStatus.ts +1 -1
  132. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayDeltaTransitionCount.ts +1 -1
  133. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayPathFacts.ts +1 -1
  134. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerMode.ts +1 -1
  135. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayReviewerOutcome.ts +1 -1
  136. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayStatus.ts +1 -1
  137. package/node_modules/@nklisch/pi-clearance/src/contracts/ReplayedDecision.ts +1 -1
  138. package/node_modules/@nklisch/pi-clearance/src/contracts/ShapeDiagnostic.ts +1 -1
  139. package/node_modules/@nklisch/pi-clearance/src/contracts/SourceFidelity.ts +1 -1
  140. package/node_modules/@nklisch/pi-clearance/src/contracts/SourceSpan.ts +1 -1
  141. package/node_modules/@nklisch/pi-clearance/src/contracts/Substitution.ts +1 -1
  142. package/node_modules/@nklisch/pi-clearance/src/contracts/SubstitutionKind.ts +1 -1
  143. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolCallIdentity.ts +1 -1
  144. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathAccess.ts +1 -1
  145. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFact.ts +1 -1
  146. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFactContext.ts +1 -1
  147. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathFacts.ts +1 -1
  148. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolPathUsage.ts +1 -1
  149. package/node_modules/@nklisch/pi-clearance/src/contracts/ToolShape.ts +1 -1
  150. package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownPathBehavior.ts +1 -1
  151. package/node_modules/@nklisch/pi-clearance/src/contracts/UnknownToolShape.ts +1 -1
  152. package/node_modules/@nklisch/pi-clearance/src/contracts/index.ts +1 -1
  153. package/node_modules/@nklisch/pi-clearance/src/index.ts +33 -2
  154. package/node_modules/@nklisch/pi-clearance/src/native/loader.ts +29 -42
  155. package/node_modules/@nklisch/pi-clearance/src/replay/reviewer-config-proposals.ts +7 -4
  156. package/node_modules/@nklisch/pi-clearance/src/runtime/allow-request-message.ts +21 -0
  157. package/node_modules/@nklisch/pi-clearance/src/runtime/auto-reviewer-read-models.ts +165 -8
  158. package/node_modules/@nklisch/pi-clearance/src/runtime/command-registry.ts +1 -1
  159. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/allow.ts +26 -2
  160. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/packs.ts +6 -6
  161. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/scope.ts +5 -5
  162. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/actions.ts +11 -0
  163. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/dispatcher.ts +274 -1
  164. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/model-options.ts +52 -0
  165. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/native-ui.ts +54 -67
  166. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/reviewer.ts +19 -3
  167. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels/scope.ts +2 -11
  168. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/panels.ts +10 -1
  169. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings/read-model.ts +47 -13
  170. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/settings.ts +32 -37
  171. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/status.ts +3 -0
  172. package/node_modules/@nklisch/pi-clearance/src/runtime/config-commands/types.ts +14 -5
  173. package/node_modules/@nklisch/pi-clearance/src/runtime/handler.ts +36 -0
  174. package/node_modules/@nklisch/pi-clearance/src/runtime/mode-prompt.ts +0 -1
  175. package/node_modules/@nklisch/pi-clearance/src/runtime/model-adapter.ts +5 -1
  176. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-context.ts +73 -10
  177. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer-prompts.ts +186 -175
  178. package/node_modules/@nklisch/pi-clearance/src/runtime/reviewer.ts +26 -6
  179. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.d.ts +23 -1
  180. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js +46 -50
  181. package/node_modules/@nklisch/pi-plugins/dist/infrastructure/state/local-lock-filesystem.js.map +1 -1
  182. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-package.js +2 -2
  183. package/node_modules/@nklisch/pi-plugins/dist/runtime/mcp/pi-mcp-adapter-runtime.js +2 -2
  184. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-lifecycle.js +1 -1
  185. package/node_modules/@nklisch/pi-plugins/dist/runtime/subagents/pi-subagents-package.js +2 -2
  186. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/README.md +41 -31
  187. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/dist/public.d.ts +2 -1
  188. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/FORK-MAINTENANCE.md +17 -11
  189. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/VISION.md +14 -7
  190. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/architecture/architecture.md +11 -19
  191. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/docs/comparison-with-upstream.md +58 -53
  192. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/package.json +2 -2
  193. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-type-resolution.ts +43 -0
  194. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/agent-types.ts +11 -6
  195. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/custom-agents.ts +13 -6
  196. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/default-agents.ts +0 -52
  197. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/config/thinking-levels.ts +12 -0
  198. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/interrupt.ts +5 -2
  199. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/handlers/lifecycle.ts +3 -3
  200. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/index.ts +7 -6
  201. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/create-subagent-session.ts +7 -2
  202. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/parent-snapshot.ts +11 -3
  203. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-manager.ts +26 -56
  204. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-session.ts +48 -9
  205. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent-state.ts +32 -0
  206. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/lifecycle/subagent.ts +105 -17
  207. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/composite-subagent-observer.ts +4 -0
  208. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/notification.ts +50 -46
  209. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/renderer.ts +1 -1
  210. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/observation/subagent-events-observer.ts +15 -0
  211. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/runtime.ts +2 -0
  212. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service-adapter.ts +35 -6
  213. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/service/service.ts +1 -0
  214. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/model-label.ts +19 -0
  215. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/prompts.ts +24 -1
  216. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/session/session-config.ts +11 -6
  217. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/settings.ts +70 -3
  218. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/agent-tool.ts +27 -10
  219. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/background-spawner.ts +6 -2
  220. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/foreground-runner.ts +12 -3
  221. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-report.ts +17 -3
  222. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/get-result-tool.ts +13 -20
  223. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/result-renderer.ts +6 -1
  224. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/spawn-config.ts +16 -16
  225. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/tools/steer-tool.ts +12 -4
  226. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/types.ts +2 -2
  227. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/agent-widget.ts +24 -11
  228. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/display.ts +10 -7
  229. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/glyphs.ts +7 -0
  230. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigation.ts +34 -25
  231. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/session-navigator.ts +30 -10
  232. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/subagents-settings.ts +58 -13
  233. package/node_modules/@nklisch/pi-plugins/node_modules/@nklisch/pi-subagents/src/ui/widget-renderer.ts +32 -19
  234. package/node_modules/@nklisch/pi-plugins/package.json +3 -3
  235. package/package.json +1 -1
  236. package/test/verify-bundle.mjs +28 -1
@@ -4,6 +4,7 @@ import { defineTool } from "@earendil-works/pi-coding-agent";
4
4
  import { Text } from "@earendil-works/pi-tui";
5
5
  import { Type } from "@sinclair/typebox";
6
6
  import { AgentTypeRegistry } from "#src/config/agent-types";
7
+ import { THINKING_LEVELS_DESCRIPTION } from "#src/config/thinking-levels";
7
8
  import type { ParentSnapshot } from "#src/lifecycle/parent-snapshot";
8
9
  import type { AgentSpawnConfig } from "#src/lifecycle/subagent-manager";
9
10
  import { spawnBackground } from "#src/tools/background-spawner";
@@ -12,7 +13,7 @@ import { buildAgentGuidelines, buildDetails, buildTypeListText, textResult } fro
12
13
  import { renderAgentResult } from "#src/tools/result-renderer";
13
14
  import { type ModelInfo, resolveSpawnConfig } from "#src/tools/spawn-config";
14
15
  import type { ParentSessionInfo, Subagent } from "#src/types";
15
- import { type AgentDetails, getDisplayName } from "#src/ui/display";
16
+ import { type AgentDetails, formatDuration, getDisplayName } from "#src/ui/display";
16
17
 
17
18
  // ---- Deps interfaces ----
18
19
 
@@ -35,6 +36,7 @@ export interface AgentToolRuntime {
35
36
  export type AgentToolSettings = {
36
37
  readonly defaultMaxTurns: number | undefined;
37
38
  readonly maxConcurrent: number;
39
+ readonly fallbackSubagent?: string | false;
38
40
  };
39
41
 
40
42
  // ---- Class ----
@@ -90,7 +92,9 @@ export class AgentTool {
90
92
  }
91
93
  if (!existing.isSessionReady()) {
92
94
  return textResult(
93
- `Agent "${params.resume}" has no active session to resume.`,
95
+ existing.sessionReleased
96
+ ? `Agent "${params.resume}" retained its result and transcript, but its live session was released after the retention window. Start a new subagent for fresh context.`
97
+ : `Agent "${params.resume}" has no active session to resume.`,
94
98
  );
95
99
  }
96
100
  const record = await this.manager.resume(
@@ -101,9 +105,19 @@ export class AgentTool {
101
105
  if (!record) {
102
106
  return textResult(`Failed to resume agent "${params.resume}".`);
103
107
  }
108
+ record.markConsumed();
109
+ const partial = record.result?.trim();
110
+ const resultText = record.status === "error"
111
+ ? partial
112
+ ? `Error: ${record.error}\n\nPartial output before the failure:\n${partial}`
113
+ : `Error: ${record.error}`
114
+ : partial ?? "No output.";
104
115
  return textResult(
105
- record.result?.trim() ?? record.error?.trim() ?? "No output.",
106
- buildDetails(config.presentation.detailBase, record),
116
+ `Model: ${record.modelLabel}\nRuntime: ${formatDuration(record.startedAt, record.completedAt)}\n\n${resultText}`,
117
+ buildDetails(
118
+ { ...config.presentation.detailBase, modelName: record.modelLabel },
119
+ record,
120
+ ),
107
121
  );
108
122
  }
109
123
 
@@ -135,9 +149,11 @@ export class AgentTool {
135
149
  ...this.agentGuidelines,
136
150
  "- Provide clear, detailed prompts so the agent can work autonomously.",
137
151
  "- Subagent results are returned as text — summarize them for the user.",
138
- "- Use run_in_background for work you don't need immediately. You will be notified when it completes.",
139
- "- Use resume with an agent ID to continue a previous agent's work.",
140
- "- Use steer_subagent to send mid-run messages to a running background agent.",
152
+ "- Background completion automatically wakes you with a result preview. Continue other work instead of polling.",
153
+ "- Use get_subagent_result only for full output, verbose conversation, an explicit status check or synchronization point, or recovery after a missed notification.",
154
+ "- Use steer_subagent to redirect an agent that is still running.",
155
+ "- Use resume only after an agent finishes when it should continue with the same retained conversation history.",
156
+ "- Launch a new subagent without resume when prior conversation history is unnecessary.",
141
157
  '- Use model to specify a different model (as "provider/modelId", or fuzzy e.g. "haiku", "sonnet").',
142
158
  "- Use thinking to control extended thinking level.",
143
159
  "- Use inherit_context if the agent needs the parent conversation history.",
@@ -176,7 +192,7 @@ ${guidelines}
176
192
  thinking: Type.Optional(
177
193
  Type.String({
178
194
  description:
179
- "Thinking level: off, minimal, low, medium, high, xhigh. Overrides agent default.",
195
+ `Thinking level: ${THINKING_LEVELS_DESCRIPTION}. Overrides agent default. Availability depends on the selected model and host Pi version.`,
180
196
  }),
181
197
  ),
182
198
  max_turns: Type.Optional(
@@ -189,12 +205,13 @@ ${guidelines}
189
205
  run_in_background: Type.Optional(
190
206
  Type.Boolean({
191
207
  description:
192
- "Set to true to run in background. Returns agent ID immediately. You will be notified when it completes.",
208
+ "Set to true to run in background. Returns an agent ID immediately; completion automatically wakes you with a result preview.",
193
209
  }),
194
210
  ),
195
211
  resume: Type.Optional(
196
212
  Type.String({
197
- description: "Optional agent ID to resume from. Continues from previous context.",
213
+ description:
214
+ "Agent ID of a retained, finished session to continue with the same conversation history. Omit to start a new context.",
198
215
  }),
199
216
  ),
200
217
  inherit_context: Type.Optional(
@@ -52,13 +52,17 @@ export function spawnBackground(
52
52
  `Agent ${isQueued ? "queued" : "started"} in background.\n` +
53
53
  `Agent ID: ${id}\n` +
54
54
  `Type: ${identity.displayName}\n` +
55
+ `Model: ${presentation.modelName}\n` +
56
+ `Runtime: 0.0s\n` +
55
57
  `Description: ${execution.description}\n` +
58
+ (identity.fellBack ? `Fallback: requested ${identity.rawType}; using ${identity.subagentType}\n` : "") +
56
59
  (record?.outputFile ? `Output file: ${record.outputFile}\n` : "") +
57
60
  (isQueued
58
61
  ? `Position: queued (max ${params.settings.maxConcurrent} concurrent)\n`
59
62
  : "") +
60
- `\nYou will be notified when this agent completes.\n` +
61
- `Use get_subagent_result to retrieve full results, or steer_subagent to send it messages.\n` +
63
+ `\nCompletion will automatically notify and wake you with a result preview. Continue other work; do not poll.\n` +
64
+ `Use steer_subagent to redirect this agent while it runs.\n` +
65
+ `Use get_subagent_result only for full output, verbose conversation, an explicit status check or synchronization point, or recovery after a missed notification.\n` +
62
66
  `Do not duplicate this agent's work.`,
63
67
  {
64
68
  ...presentation.detailBase,
@@ -111,22 +111,31 @@ export async function runForeground(
111
111
 
112
112
  clearInterval(spinnerInterval);
113
113
 
114
+ // Foreground delivery returns the complete outcome inline, so retention can
115
+ // use the shorter consumed-session window.
116
+ record.markConsumed();
117
+
114
118
  const tokenText = formatLifetimeTokens(record);
115
119
  const details = buildDetails(presentation.detailBase, record, { tokens: tokenText });
116
120
 
117
121
  const fallbackNote = identity.fellBack
118
- ? `Note: Unknown agent type "${identity.rawType}" — using general-purpose.\n\n`
122
+ ? `Note: Unknown agent type "${identity.rawType}" — using ${identity.subagentType}.\n\n`
119
123
  : "";
120
124
 
121
125
  if (record.status === "error") {
122
- return textResult(`${fallbackNote}Agent failed: ${record.error}`, details);
126
+ const partial = record.result?.trim();
127
+ return textResult(
128
+ `${fallbackNote}Model: ${record.modelLabel}\nRuntime: ${formatMs(details.durationMs)}\nAgent failed: ${record.error}` +
129
+ (partial ? `\n\nPartial output before the failure:\n${partial}` : ""),
130
+ details,
131
+ );
123
132
  }
124
133
 
125
134
  const durationMs = (record.completedAt ?? Date.now()) - record.startedAt;
126
135
  const statsParts = [`${record.toolUses} tool uses`];
127
136
  if (tokenText) statsParts.push(tokenText);
128
137
  return textResult(
129
- `${fallbackNote}Agent completed in ${formatMs(durationMs)} (${statsParts.join(", ")})${getStatusNote(record.status)}.\n\n` +
138
+ `${fallbackNote}Model: ${record.modelLabel}\nRuntime: ${formatMs(durationMs)}\nAgent completed (${statsParts.join(", ")})${getStatusNote(record.status)}.\n\n` +
130
139
  (record.result?.trim() ?? "No output."),
131
140
  details,
132
141
  );
@@ -13,6 +13,7 @@ import type { SubagentStatus } from "#src/lifecycle/subagent";
13
13
  export interface AgentReport {
14
14
  id: string;
15
15
  displayName: string;
16
+ modelLabel: string;
16
17
  status: SubagentStatus;
17
18
  toolUses: number;
18
19
  /** Pre-formatted lifetime token total; "" when zero. */
@@ -24,8 +25,11 @@ export interface AgentReport {
24
25
  description: string;
25
26
  result: string | undefined;
26
27
  error: string | undefined;
28
+ stoppedWhileQueued: boolean;
27
29
  /** Present only when verbose was requested and a conversation is available. */
28
30
  conversation?: string;
31
+ /** Persisted transcript remains readable after the live session is released. */
32
+ transcriptPath?: string;
29
33
  }
30
34
 
31
35
  /** Assemble the stats parts: Tool uses / tokens? / Context? / Compactions? / Duration. */
@@ -40,9 +44,18 @@ export function renderStatsParts(report: AgentReport): string[] {
40
44
 
41
45
  /** Select the per-status body: running note, error line, or trimmed result. */
42
46
  export function renderReportBody(report: AgentReport): string {
47
+ if (report.status === "queued")
48
+ return "Agent is queued and has not started. Use wait: true to synchronize, or rely on the automatic completion notification.";
43
49
  if (report.status === "running")
44
- return "Agent is still running. Use wait: true or check back later.";
45
- if (report.status === "error") return `Error: ${report.error}`;
50
+ return "Agent is still running. Use wait: true to synchronize, or rely on the automatic completion notification.";
51
+ if (report.stoppedWhileQueued)
52
+ return "Agent was stopped while queued and never started. No work was performed.";
53
+ if (report.status === "error") {
54
+ const partial = report.result?.trim();
55
+ return partial
56
+ ? `Error: ${report.error}\n\nPartial output before the failure:\n${partial}`
57
+ : `Error: ${report.error}`;
58
+ }
46
59
  return report.result?.trim() ?? "No output.";
47
60
  }
48
61
 
@@ -50,11 +63,12 @@ export function renderReportBody(report: AgentReport): string {
50
63
  export function formatAgentReport(report: AgentReport): string {
51
64
  let output =
52
65
  `Agent: ${report.id}\n` +
53
- `Type: ${report.displayName} | Status: ${report.status} | ${renderStatsParts(report).join(" | ")}\n` +
66
+ `Type: ${report.displayName} | Model: ${report.modelLabel} | Status: ${report.status} | ${renderStatsParts(report).join(" | ")}\n` +
54
67
  `Description: ${report.description}\n\n`;
55
68
  output += renderReportBody(report);
56
69
  if (report.conversation) {
57
70
  output += `\n\n--- Agent Conversation ---\n${report.conversation}`;
58
71
  }
72
+ if (report.transcriptPath) output += `\n\nFull transcript: ${report.transcriptPath}`;
59
73
  return output;
60
74
  }
@@ -12,23 +12,18 @@ export interface GetResultToolManager {
12
12
  getRecord(id: string): Subagent | undefined;
13
13
  }
14
14
 
15
- export interface GetResultToolNotifications {
16
- consume(id: string): void;
17
- }
18
-
19
15
  // ---- Class ----
20
16
 
21
17
  export class GetResultTool {
22
18
  constructor(
23
19
  private readonly manager: GetResultToolManager,
24
- private readonly notifications: GetResultToolNotifications,
25
20
  private readonly registry: AgentConfigLookup,
26
21
  ) {}
27
22
 
28
23
  async execute(
29
24
  _toolCallId: string,
30
25
  params: { agent_id: string; wait?: boolean; verbose?: boolean },
31
- _signal: AbortSignal,
26
+ signal: AbortSignal,
32
27
  _onUpdate: unknown,
33
28
  _ctx: unknown,
34
29
  ) {
@@ -37,19 +32,12 @@ export class GetResultTool {
37
32
  return textResult(`Agent not found: "${params.agent_id}". It may have been cleaned up.`);
38
33
  }
39
34
 
40
- // Wait for completion if requested.
41
- // Consume BEFORE awaiting: onComplete fires inside .then() (attached
42
- // earlier at spawn time) and always runs before this await resumes.
43
- // Consuming here prevents a redundant follow-up notification.
44
- if (params.wait && record.status === "running" && record.promise) {
45
- this.notifications.consume(params.agent_id);
46
- await record.promise;
47
- }
35
+ // A queued record is awaitable from spawn, and a resumed record republishes
36
+ // its live promise. Interrupting this tool stops only the wait.
37
+ if (params.wait) await record.waitUntilSettled(signal);
48
38
 
49
- // Consume the settled result suppresses the completion notification.
50
- if (record.status !== "running" && record.status !== "queued") {
51
- this.notifications.consume(params.agent_id);
52
- }
39
+ // Pull delivery: only a terminal result was actually collected.
40
+ if (!record.isActive()) record.markConsumed();
53
41
 
54
42
  return textResult(formatAgentReport(this.buildReport(record, params.verbose)));
55
43
  }
@@ -58,6 +46,7 @@ export class GetResultTool {
58
46
  return {
59
47
  id: record.id,
60
48
  displayName: getDisplayName(record.type, this.registry),
49
+ modelLabel: record.modelLabel,
61
50
  status: record.status,
62
51
  toolUses: record.toolUses,
63
52
  tokens: formatLifetimeTokens(record),
@@ -67,7 +56,9 @@ export class GetResultTool {
67
56
  description: record.description,
68
57
  result: record.result,
69
58
  error: record.error,
59
+ stoppedWhileQueued: record.stoppedWhileQueued,
70
60
  conversation: verbose ? record.getConversation() : undefined,
61
+ transcriptPath: record.outputFile,
71
62
  };
72
63
  }
73
64
 
@@ -76,9 +67,11 @@ export class GetResultTool {
76
67
  name: "get_subagent_result" as const,
77
68
  label: "Get Agent Result",
78
69
  promptSnippet:
79
- "get_subagent_result: Check status and retrieve results from a background agent.",
70
+ "get_subagent_result: Inspect status or retrieve full results from a background agent.",
80
71
  description:
81
- "Check status and retrieve results from a background agent. Use the agent ID returned by Agent with run_in_background.",
72
+ "Background completion automatically wakes you with a result preview, so do not poll. " +
73
+ "Use this tool only for full output, verbose conversation, an explicit status check or synchronization point, " +
74
+ "or recovery after a missed notification.",
82
75
  parameters: Type.Object({
83
76
  agent_id: Type.String({
84
77
  description: "The agent ID to check.",
@@ -33,14 +33,17 @@ export function renderAgentResult(
33
33
  export function renderRunning(details: AgentDetails, theme: Theme): string {
34
34
  const frame = SPINNER[details.spinnerFrame ?? 0];
35
35
  const s = renderStats(details, theme);
36
+ const duration = theme.fg("dim", formatMs(details.durationMs));
36
37
  let line = theme.fg("accent", frame) + (s ? " " + s : "");
38
+ line += `${s ? " " + theme.fg("dim", "·") + " " : " "}${duration}`;
37
39
  line += "\n" + theme.fg("dim", ` ⎿ ${details.activity ?? "thinking\u2026"}`);
38
40
  return line;
39
41
  }
40
42
 
41
43
  /** Render background launch status. */
42
44
  export function renderBackground(details: AgentDetails, theme: Theme): string {
43
- return theme.fg("dim", ` \u23BF Running in background (ID: ${details.agentId})`);
45
+ const identity = [details.modelName, formatMs(details.durationMs)].filter(Boolean).join(" · ");
46
+ return theme.fg("dim", ` \u23BF Running in background (ID: ${details.agentId}) · ${identity}`);
44
47
  }
45
48
 
46
49
  /** Render completed or steered status with optional expanded result text. */
@@ -83,6 +86,7 @@ export function renderCompleted(
83
86
  export function renderStopped(details: AgentDetails, theme: Theme): string {
84
87
  const s = renderStats(details, theme);
85
88
  let line = theme.fg("dim", "\u25A0") + (s ? " " + s : "");
89
+ line += ` ${theme.fg("dim", "·")} ${theme.fg("dim", formatMs(details.durationMs))}`;
86
90
  line += "\n" + theme.fg("dim", " \u23BF Stopped");
87
91
  return line;
88
92
  }
@@ -91,6 +95,7 @@ export function renderStopped(details: AgentDetails, theme: Theme): string {
91
95
  export function renderFailed(details: AgentDetails, theme: Theme): string {
92
96
  const s = renderStats(details, theme);
93
97
  let line = theme.fg("error", "\u2717") + (s ? " " + s : "");
98
+ line += ` ${theme.fg("dim", "·")} ${theme.fg("dim", formatMs(details.durationMs))}`;
94
99
 
95
100
  if (details.status === "error") {
96
101
  line += "\n" + theme.fg("error", ` \u23BF Error: ${details.error ?? "unknown"}`);
@@ -7,10 +7,12 @@
7
7
  */
8
8
 
9
9
  import type { Model } from "@earendil-works/pi-ai";
10
+ import { resolveDispatchAgentType } from "#src/config/agent-type-resolution";
10
11
  import type { AgentTypeRegistry } from "#src/config/agent-types";
11
12
  import { resolveAgentInvocationConfig } from "#src/config/invocation-config";
12
13
  import { normalizeMaxTurns } from "#src/lifecycle/turn-limits";
13
14
  import type { ModelRegistry } from "#src/session/model-resolver";
15
+ import { formatModelLabel } from "#src/session/model-label";
14
16
  import { resolveInvocationModel } from "#src/session/model-resolver";
15
17
  import type { AgentInvocation, SubagentType, ThinkingLevel } from "#src/types";
16
18
  import {
@@ -75,18 +77,21 @@ export function resolveSpawnConfig(
75
77
  params: Record<string, unknown>,
76
78
  registry: AgentTypeRegistry,
77
79
  modelInfo: ModelInfo,
78
- settings: { readonly defaultMaxTurns: number | undefined },
80
+ settings: {
81
+ readonly defaultMaxTurns: number | undefined;
82
+ readonly fallbackSubagent?: string | false;
83
+ },
79
84
  ): ResolvedSpawnConfig | SpawnConfigError {
80
85
  const rawType = params.subagent_type as SubagentType;
81
- const resolved = registry.resolveType(rawType);
82
-
83
- // A known-but-disabled type is an explicit error, not a silent unknown-type fallback.
84
- if (resolved !== undefined && !registry.isValidType(resolved)) {
85
- return { error: `Agent type "${resolved}" is disabled` };
86
- }
86
+ const typeResolution = resolveDispatchAgentType(
87
+ rawType,
88
+ registry,
89
+ settings.fallbackSubagent,
90
+ );
91
+ if ("error" in typeResolution) return typeResolution;
87
92
 
88
- const subagentType = resolved ?? "general-purpose";
89
- const fellBack = resolved === undefined;
93
+ const subagentType = typeResolution.type;
94
+ const fellBack = typeResolution.fellBack;
90
95
 
91
96
  const displayName = getDisplayName(subagentType, registry);
92
97
 
@@ -108,13 +113,8 @@ export function resolveSpawnConfig(
108
113
  const inheritContext = resolvedConfig.inheritContext;
109
114
  const runInBackground = resolvedConfig.runInBackground;
110
115
 
111
- // Compute display model name (only shown when different from parent)
112
- const parentModelId = modelInfo.parentModel?.id;
113
- const effectiveModelId = model?.id;
114
- const modelName =
115
- effectiveModelId && effectiveModelId !== parentModelId
116
- ? model.name.replace(/^Claude\s+/i, "").toLowerCase()
117
- : undefined;
116
+ // Every status surface shows the exact effective model, including inheritance.
117
+ const modelName = formatModelLabel(model);
118
118
 
119
119
  const effectiveMaxTurns = normalizeMaxTurns(
120
120
  resolvedConfig.maxTurns ?? settings.defaultMaxTurns,
@@ -2,6 +2,7 @@ import { defineTool } from "@earendil-works/pi-coding-agent";
2
2
  import { Type } from "@sinclair/typebox";
3
3
  import { formatLifetimeTokens, textResult } from "#src/tools/helpers";
4
4
  import type { SteerOutcome, Subagent } from "#src/types";
5
+ import { formatDuration } from "#src/ui/display";
5
6
 
6
7
  // ---- Deps interfaces ----
7
8
 
@@ -40,19 +41,22 @@ export class SteerTool {
40
41
  outcome = await record.steer(params.message);
41
42
  } catch (err) {
42
43
  return textResult(
43
- `Failed to steer agent: ${err instanceof Error ? err.message : String(err)}`,
44
+ `Failed to steer agent: ${err instanceof Error ? err.message : String(err)}\n`
45
+ + this.renderRunIdentity(record),
44
46
  );
45
47
  }
46
48
 
47
49
  switch (outcome.kind) {
48
50
  case "rejected":
49
51
  return textResult(
50
- `Agent "${params.agent_id}" is not running (status: ${outcome.status}). Cannot steer a non-running agent.`,
52
+ `Agent "${params.agent_id}" is not running (status: ${outcome.status}). Cannot steer a non-running agent.\n`
53
+ + this.renderRunIdentity(record),
51
54
  );
52
55
  case "buffered":
53
56
  this.events.emit("subagents:steered", { id: record.id, message: params.message });
54
57
  return textResult(
55
- `Steering message queued for agent ${record.id}. It will be delivered once the session initializes.`,
58
+ `Steering message queued for agent ${record.id}. It will be delivered once the session initializes.\n`
59
+ + this.renderRunIdentity(record),
56
60
  );
57
61
  case "delivered":
58
62
  this.events.emit("subagents:steered", { id: record.id, message: params.message });
@@ -64,7 +68,7 @@ export class SteerTool {
64
68
  private renderDelivered(record: Subagent) {
65
69
  const tokens = formatLifetimeTokens(record);
66
70
  const contextPercent = record.getContextPercent();
67
- const stateParts: string[] = [];
71
+ const stateParts: string[] = [record.modelLabel, formatDuration(record.startedAt, record.completedAt)];
68
72
  if (tokens) stateParts.push(tokens);
69
73
  stateParts.push(`${record.toolUses} tool ${record.toolUses === 1 ? "use" : "uses"}`);
70
74
  if (contextPercent !== null)
@@ -79,6 +83,10 @@ export class SteerTool {
79
83
  );
80
84
  }
81
85
 
86
+ private renderRunIdentity(record: Subagent): string {
87
+ return `Model: ${record.modelLabel}\nRuntime: ${formatDuration(record.startedAt, record.completedAt)}`;
88
+ }
89
+
82
90
  toToolDefinition() {
83
91
  return defineTool({
84
92
  name: "steer_subagent" as const,
@@ -63,11 +63,11 @@ export interface AgentConfig extends AgentIdentity, AgentPromptConfig {
63
63
  /** false = agent is hidden from the registry */
64
64
  enabled?: boolean;
65
65
  /** Where this agent was loaded from */
66
- source?: "default" | "project" | "global";
66
+ source?: "default" | "project" | "shared-project" | "global";
67
67
  }
68
68
 
69
69
  export interface AgentInvocation {
70
- /** Short display name, e.g. "haiku" only set when different from parent. */
70
+ /** Exact effective model label in `provider/id` form. */
71
71
  modelName?: string;
72
72
  thinking?: ThinkingLevel;
73
73
  maxTurns?: number;
@@ -10,7 +10,7 @@ import { AgentTypeRegistry } from "#src/config/agent-types";
10
10
  import type { Subagent } from "#src/lifecycle/subagent";
11
11
  import type { SubagentManager, SubagentManagerObserver } from "#src/lifecycle/subagent-manager";
12
12
  import type { CompactionInfo } from "#src/types";
13
- import { ERROR_STATUSES, type Theme } from "#src/ui/display";
13
+ import { ERROR_STATUSES, formatMs, type Theme } from "#src/ui/display";
14
14
  import { renderWidgetLines, type WidgetAgent } from "#src/ui/widget-renderer";
15
15
 
16
16
  // ---- Types ----
@@ -51,6 +51,25 @@ export function assembleWidgetState(
51
51
  return { runningCount, queuedCount, hasFinished, hasActive };
52
52
  }
53
53
 
54
+ /** Render the aggregate status bar without hiding per-agent model/runtime details. */
55
+ export function formatStatusBar(
56
+ state: WidgetState,
57
+ agents: readonly Pick<Subagent, "status" | "modelLabel" | "startedAt">[],
58
+ now = Date.now(),
59
+ ): string | undefined {
60
+ if (!state.hasActive) return undefined;
61
+ const statusParts: string[] = [];
62
+ if (state.runningCount > 0) statusParts.push(`${state.runningCount} running`);
63
+ if (state.queuedCount > 0) statusParts.push(`${state.queuedCount} queued`);
64
+ const total = state.runningCount + state.queuedCount;
65
+ const activeDetails = agents
66
+ .filter((agent) => agent.status === "running" || agent.status === "queued")
67
+ .map((agent) => agent.status === "running"
68
+ ? `${agent.modelLabel} ${formatMs(now - agent.startedAt)}`
69
+ : `${agent.modelLabel} queued`);
70
+ return `${statusParts.join(", ")} agent${total === 1 ? "" : "s"} · ${activeDetails.join(", ")}`;
71
+ }
72
+
54
73
  export type UICtx = {
55
74
  setStatus(key: string, text: string | undefined): void;
56
75
  setWidget(
@@ -165,6 +184,7 @@ export class AgentWidget implements SubagentManagerObserver {
165
184
  type: record.type,
166
185
  status: record.status,
167
186
  description: record.description,
187
+ modelLabel: record.modelLabel,
168
188
  toolUses: record.toolUses,
169
189
  startedAt: record.startedAt,
170
190
  completedAt: record.completedAt,
@@ -216,15 +236,8 @@ export class AgentWidget implements SubagentManagerObserver {
216
236
  * Compute the status bar text from the current widget state and call
217
237
  * `setStatus` only when it differs from the last cached value.
218
238
  */
219
- private updateStatusBar(state: WidgetState): void {
220
- let newStatusText: string | undefined;
221
- if (state.hasActive) {
222
- const statusParts: string[] = [];
223
- if (state.runningCount > 0) statusParts.push(`${state.runningCount} running`);
224
- if (state.queuedCount > 0) statusParts.push(`${state.queuedCount} queued`);
225
- const total = state.runningCount + state.queuedCount;
226
- newStatusText = `${statusParts.join(", ")} agent${total === 1 ? "" : "s"}`;
227
- }
239
+ private updateStatusBar(state: WidgetState, agents: readonly Subagent[]): void {
240
+ const newStatusText = formatStatusBar(state, agents);
228
241
  if (newStatusText !== this.lastStatusText) {
229
242
  this.uiCtx!.setStatus("subagents", newStatusText);
230
243
  this.lastStatusText = newStatusText;
@@ -259,7 +272,7 @@ export class AgentWidget implements SubagentManagerObserver {
259
272
  return;
260
273
  }
261
274
 
262
- this.updateStatusBar(state);
275
+ this.updateStatusBar(state, backgroundAgents);
263
276
  this.widgetFrame++;
264
277
 
265
278
  // Register widget callback once; subsequent updates use requestRender()
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { AgentConfigLookup } from "#src/config/agent-types";
9
+ import { GLYPHS } from "#src/ui/glyphs";
9
10
  import type { AgentInvocation, SubagentType } from "#src/types";
10
11
 
11
12
  // ---- Types ----
@@ -28,7 +29,7 @@ export interface AgentDetails {
28
29
  activity?: string;
29
30
  /** Current spinner frame index (for animated running indicator). */
30
31
  spinnerFrame?: number;
31
- /** Short model name if different from parent (e.g. "haiku", "sonnet"). */
32
+ /** Exact effective model label in `provider/id` form. */
32
33
  modelName?: string;
33
34
  /** Notable config tags (e.g. ["thinking: high", "inherit context"]). */
34
35
  tags?: string[];
@@ -71,12 +72,12 @@ export function formatTokens(count: number): string {
71
72
  /**
72
73
  * Token count with optional context-fill % and compaction-count annotations.
73
74
  * Thresholds for percent: <70% dim, 70–85% warning, ≥85% error.
74
- * Compaction count rendered as `↻N` in dim.
75
+ * Compaction count rendered as `⇊N` in dim.
75
76
  *
76
77
  * "12.3k token" — no annotations
77
78
  * "12.3k token (45%)" — percent only
78
- * "12.3k token (2)" — compactions only (e.g. right after compact)
79
- * "12.3k token (45% · 2)" — both
79
+ * "12.3k token (2)" — compactions only (e.g. right after compact)
80
+ * "12.3k token (45% · 2)" — both
80
81
  */
81
82
  export function formatSessionTokens(
82
83
  tokens: number,
@@ -91,16 +92,18 @@ export function formatSessionTokens(
91
92
  annot.push(theme.fg(color, `${Math.round(percent)}%`));
92
93
  }
93
94
  if (compactions > 0) {
94
- annot.push(theme.fg("dim", `↻${compactions}`));
95
+ annot.push(theme.fg("dim", `${GLYPHS.compactions}${compactions}`));
95
96
  }
96
97
  if (annot.length === 0) return tokenStr;
97
98
  const sep = theme.fg("dim", " · ");
98
99
  return `${tokenStr} ${theme.fg("dim", "(")}${annot.join(sep)}${theme.fg("dim", ")")}`;
99
100
  }
100
101
 
101
- /** Format turn count with optional max limit: "5≤30" or "5". */
102
+ /** Format turn count with optional max limit: "5≤30" or "5". */
102
103
  export function formatTurns(turnCount: number, maxTurns?: number | null): string {
103
- return maxTurns != null ? `⟳${turnCount}≤${maxTurns}` : `⟳${turnCount}`;
104
+ return maxTurns != null
105
+ ? `${GLYPHS.turns}${turnCount}≤${maxTurns}`
106
+ : `${GLYPHS.turns}${turnCount}`;
104
107
  }
105
108
 
106
109
  /** Format milliseconds as human-readable duration. */
@@ -0,0 +1,7 @@
1
+ /** Terminal-safe semantic glyphs shared by status renderers. */
2
+ export const GLYPHS = {
3
+ /** Turn count, as `↻5≤30`. */
4
+ turns: "↻",
5
+ /** Session compaction count. */
6
+ compactions: "⇊",
7
+ } as const;