@selesai/code 0.8.12 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/core/agent-session.js +41 -29
  3. package/dist/core/resource-loader.d.ts +1 -1
  4. package/dist/core/resource-loader.js +2 -2
  5. package/dist/defaults/models.json +15 -3
  6. package/dist/defaults/settings.json +10 -8
  7. package/dist/extensions/model-prompt-injector/config.json +6 -0
  8. package/dist/extensions/package.json +2 -1
  9. package/dist/extensions/pi-subagents/README.md +4 -4
  10. package/dist/extensions/pi-subagents/docs/configuration.md +3 -54
  11. package/dist/extensions/pi-subagents/docs/extension-api.md +4 -15
  12. package/dist/extensions/pi-subagents/docs/missions.md +2 -39
  13. package/dist/extensions/pi-subagents/docs/observability.md +2 -42
  14. package/dist/extensions/pi-subagents/docs/tool-reference.md +1 -2
  15. package/dist/extensions/pi-subagents/docs/watchdog.md +0 -57
  16. package/dist/extensions/pi-subagents/docs/workflows.md +2 -0
  17. package/dist/extensions/pi-subagents/skills/pi-subagents/references/execution-controls.md +4 -49
  18. package/dist/extensions/pi-subagents/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  19. package/dist/extensions/pi-subagents/skills/pi-subagents/references/prompting-and-roles.md +1 -2
  20. package/dist/extensions/pi-subagents/src/extension/config.ts +3 -15
  21. package/dist/extensions/pi-subagents/src/extension/index.ts +1 -53
  22. package/dist/extensions/pi-subagents/src/extension/public-execution.ts +4 -10
  23. package/dist/extensions/pi-subagents/src/extension/rpc.ts +1 -2
  24. package/dist/extensions/pi-subagents/src/extension/schemas.ts +1 -12
  25. package/dist/extensions/pi-subagents/src/extension/tool-description.ts +1 -2
  26. package/dist/extensions/pi-subagents/src/policy/authority.ts +0 -2
  27. package/dist/extensions/pi-subagents/src/runs/foreground/subagent-executor.ts +33 -102
  28. package/dist/extensions/pi-subagents/src/runs/shared/subagent-prompt-runtime.ts +0 -2
  29. package/dist/extensions/pi-subagents/src/shared/types.ts +1 -1
  30. package/dist/extensions/pi-subagents/src/slash/slash-commands.ts +4 -58
  31. package/dist/extensions/pi-subagents/src/watchdog/register-main.ts +2 -8
  32. package/dist/extensions/pi-subagents/src/watchdog/tool-actions.ts +1 -2
  33. package/dist/extensions/pi-subagents/src/workflows/workflow-auto-relaunch.ts +16 -3
  34. package/dist/extensions/pi-subagents/test/integration/async-execution.test.ts +0 -95
  35. package/dist/extensions/pi-subagents/test/integration/async-job-tracker.test.ts +0 -15
  36. package/dist/extensions/pi-subagents/test/integration/result-watcher.test.ts +0 -59
  37. package/dist/extensions/pi-subagents/test/integration/single-execution.test.ts +0 -59
  38. package/dist/extensions/pi-subagents/test/integration/slash-commands.test.ts +0 -47
  39. package/dist/extensions/pi-subagents/test/unit/authority-policy.test.ts +0 -1
  40. package/dist/extensions/pi-subagents/test/unit/compaction-resume.test.ts +2 -2
  41. package/dist/extensions/pi-subagents/test/unit/index-child-registration.test.ts +5 -5
  42. package/dist/extensions/pi-subagents/test/unit/public-execution.test.ts +0 -8
  43. package/dist/extensions/pi-subagents/test/unit/rpc.test.ts +2 -2
  44. package/dist/extensions/pi-subagents/test/unit/subagent-action-recovery.test.ts +2 -2
  45. package/dist/extensions/pi-subagents/test/unit/subagent-prompt-runtime.test.ts +4 -4
  46. package/dist/extensions/pi-subagents/test/unit/workflow-auto-relaunch.test.ts +21 -1
  47. package/dist/modes/interactive/components/model-selector.d.ts +10 -1
  48. package/dist/modes/interactive/components/model-selector.js +10 -6
  49. package/dist/modes/interactive/components/settings-selector.d.ts +2 -0
  50. package/dist/modes/interactive/components/settings-selector.js +3 -0
  51. package/dist/modes/interactive/interactive-mode.js +7 -0
  52. package/dist/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  53. package/dist/skills/pi-subagents/references/execution-controls.md +2 -32
  54. package/dist/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
  55. package/dist/skills/pi-subagents/references/prompting-and-roles.md +3 -3
  56. package/docs/custom-provider.md +1 -0
  57. package/package.json +3 -3
  58. package/dist/extensions/pi-subagents/src/runs/background/scheduled-runs.ts +0 -803
  59. package/dist/extensions/pi-subagents/src/tui/fleet-status.ts +0 -691
  60. package/dist/extensions/pi-subagents/src/tui/fleet.ts +0 -1314
  61. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/cli.d.ts +0 -3
  62. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/register-bedrock.d.ts +0 -2
  63. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/bun/restore-sandbox-env.d.ts +0 -17
  64. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/args.d.ts +0 -57
  65. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/config-selector.d.ts +0 -16
  66. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/file-processor.d.ts +0 -15
  67. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/initial-message.d.ts +0 -18
  68. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/list-models.d.ts +0 -9
  69. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/project-trust.d.ts +0 -10
  70. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/session-picker.d.ts +0 -10
  71. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli/startup-ui.d.ts +0 -20
  72. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/cli.d.ts +0 -3
  73. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/config.d.ts +0 -96
  74. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-runtime.d.ts +0 -119
  75. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session-services.d.ts +0 -85
  76. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/agent-session.d.ts +0 -620
  77. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-guidance.d.ts +0 -5
  78. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/auth-storage.d.ts +0 -56
  79. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/bash-executor.d.ts +0 -32
  80. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/cache-stats.d.ts +0 -49
  81. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/branch-summarization.d.ts +0 -93
  82. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/compaction.d.ts +0 -128
  83. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/index.d.ts +0 -7
  84. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/compaction/utils.d.ts +0 -38
  85. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/defaults.d.ts +0 -3
  86. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/diagnostics.d.ts +0 -15
  87. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/event-bus.d.ts +0 -9
  88. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/exec.d.ts +0 -29
  89. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/experimental.d.ts +0 -2
  90. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/ansi-to-html.d.ts +0 -22
  91. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/index.d.ts +0 -37
  92. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/export-html/tool-renderer.d.ts +0 -34
  93. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/index.d.ts +0 -12
  94. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/loader.d.ts +0 -23
  95. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/runner.d.ts +0 -171
  96. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/types.d.ts +0 -1263
  97. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/extensions/wrapper.d.ts +0 -20
  98. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/footer-data-provider.d.ts +0 -54
  99. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/http-dispatcher.d.ts +0 -22
  100. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/index.d.ts +0 -13
  101. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/keybindings.d.ts +0 -358
  102. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/messages.d.ts +0 -77
  103. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-config.d.ts +0 -512
  104. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-registry.d.ts +0 -43
  105. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-resolver.d.ts +0 -121
  106. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/model-runtime.d.ts +0 -82
  107. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/models-store.d.ts +0 -17
  108. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/output-guard.d.ts +0 -7
  109. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/package-manager.d.ts +0 -210
  110. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/project-trust.d.ts +0 -15
  111. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/prompt-templates.d.ts +0 -54
  112. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-attribution.d.ts +0 -4
  113. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/provider-composer.d.ts +0 -55
  114. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/radius.d.ts +0 -2
  115. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/remote-catalog-provider.d.ts +0 -5
  116. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resolve-config-value.d.ts +0 -30
  117. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/resource-loader.d.ts +0 -206
  118. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/runtime-credentials.d.ts +0 -15
  119. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/sdk.d.ts +0 -106
  120. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-cwd.d.ts +0 -19
  121. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/session-manager.d.ts +0 -356
  122. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/settings-manager.d.ts +0 -297
  123. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/skills.d.ts +0 -60
  124. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/slash-commands.d.ts +0 -15
  125. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/source-info.d.ts +0 -18
  126. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/system-prompt.d.ts +0 -28
  127. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/telemetry.d.ts +0 -3
  128. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/timings.d.ts +0 -10
  129. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/bash.d.ts +0 -68
  130. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit-diff.d.ts +0 -106
  131. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/edit.d.ts +0 -51
  132. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/file-mutation-queue.d.ts +0 -6
  133. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/find.d.ts +0 -35
  134. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/grep.d.ts +0 -37
  135. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/index.d.ts +0 -40
  136. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/ls.d.ts +0 -37
  137. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/output-accumulator.d.ts +0 -52
  138. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/path-utils.d.ts +0 -10
  139. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/read.d.ts +0 -35
  140. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/render-utils.d.ts +0 -24
  141. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/tool-definition-wrapper.d.ts +0 -14
  142. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/truncate.d.ts +0 -70
  143. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/tools/write.d.ts +0 -26
  144. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/trust-manager.d.ts +0 -36
  145. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/core/usage-totals.d.ts +0 -19
  146. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/index.d.ts +0 -3
  147. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/client.d.ts +0 -61
  148. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/huggingface.d.ts +0 -23
  149. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/index.d.ts +0 -3
  150. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/provider.d.ts +0 -10
  151. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/extensions/llama/ui.d.ts +0 -42
  152. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/index.d.ts +0 -35
  153. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/main.d.ts +0 -12
  154. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/migrations.d.ts +0 -33
  155. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/index.d.ts +0 -9
  156. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/armin.d.ts +0 -34
  157. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/assistant-message.d.ts +0 -22
  158. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bash-execution.d.ts +0 -34
  159. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/bordered-loader.d.ts +0 -16
  160. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/branch-summary-message.d.ts +0 -16
  161. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/compaction-summary-message.d.ts +0 -16
  162. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/config-selector.d.ts +0 -102
  163. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/countdown-timer.d.ts +0 -14
  164. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-editor.d.ts +0 -21
  165. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-entry.d.ts +0 -19
  166. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/custom-message.d.ts +0 -20
  167. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/daxnuts.d.ts +0 -23
  168. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/diff.d.ts +0 -12
  169. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/dynamic-border.d.ts +0 -15
  170. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/earendil-announcement.d.ts +0 -5
  171. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-editor.d.ts +0 -22
  172. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-input.d.ts +0 -23
  173. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/extension-selector.d.ts +0 -26
  174. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/first-time-setup.d.ts +0 -25
  175. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/footer.d.ts +0 -32
  176. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/index.d.ts +0 -34
  177. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/keybinding-hints.d.ts +0 -13
  178. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/login-dialog.d.ts +0 -52
  179. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/model-selector.d.ts +0 -54
  180. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/oauth-selector.d.ts +0 -33
  181. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/scoped-models-selector.d.ts +0 -42
  182. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector-search.d.ts +0 -23
  183. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/session-selector.d.ts +0 -95
  184. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/settings-selector.d.ts +0 -77
  185. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/show-images-selector.d.ts +0 -10
  186. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/skill-invocation-message.d.ts +0 -17
  187. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/status-indicator.d.ts +0 -28
  188. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/theme-selector.d.ts +0 -11
  189. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/thinking-selector.d.ts +0 -11
  190. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tool-execution.d.ts +0 -63
  191. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/tree-selector.d.ts +0 -94
  192. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/trust-selector.d.ts +0 -23
  193. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message-selector.d.ts +0 -30
  194. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/user-message.d.ts +0 -14
  195. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/components/visual-truncate.d.ts +0 -24
  196. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/interactive-mode.d.ts +0 -394
  197. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/model-search.d.ts +0 -12
  198. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme-controller.d.ts +0 -29
  199. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/interactive/theme/theme.d.ts +0 -120
  200. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/print-mode.d.ts +0 -28
  201. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/jsonl.d.ts +0 -17
  202. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-client.d.ts +0 -246
  203. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-mode.d.ts +0 -20
  204. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/modes/rpc/rpc-types.d.ts +0 -457
  205. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/package-manager-cli.d.ts +0 -8
  206. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/rpc-entry.d.ts +0 -3
  207. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/ansi.d.ts +0 -2
  208. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/changelog.d.ts +0 -22
  209. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/child-process.d.ts +0 -18
  210. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-image.d.ts +0 -11
  211. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard-native.d.ts +0 -11
  212. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/clipboard.d.ts +0 -4
  213. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/deprecation.d.ts +0 -4
  214. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/exif-orientation.d.ts +0 -5
  215. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/frontmatter.d.ts +0 -8
  216. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/fs-watch.d.ts +0 -5
  217. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/git.d.ts +0 -26
  218. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/html.d.ts +0 -7
  219. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-convert.d.ts +0 -10
  220. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-process.d.ts +0 -18
  221. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-core.d.ts +0 -30
  222. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize-worker.d.ts +0 -2
  223. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/image-resize.d.ts +0 -16
  224. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/json.d.ts +0 -3
  225. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/mime.d.ts +0 -3
  226. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/open-browser.d.ts +0 -9
  227. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/paths.d.ts +0 -31
  228. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/photon.d.ts +0 -21
  229. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/pi-user-agent.d.ts +0 -2
  230. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/shell.d.ts +0 -31
  231. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/sleep.d.ts +0 -5
  232. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/syntax-highlight.d.ts +0 -12
  233. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/tools-manager.d.ts +0 -3
  234. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/version-check.d.ts +0 -15
  235. package/dist/extensions/pi-subagents/test/fixtures/pi-coding-agent-shim/dist/utils/windows-self-update.d.ts +0 -3
  236. package/dist/extensions/pi-subagents/test/unit/fleet-status.test.ts +0 -1004
  237. package/dist/extensions/pi-subagents/test/unit/fleet.test.ts +0 -1511
  238. package/dist/extensions/pi-subagents/test/unit/scheduled-runs.test.ts +0 -634
  239. package/dist/extensions/pi-subagents/test/unit/scheduled-store-root.test.ts +0 -67
@@ -1,1004 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { describe, it } from "node:test";
3
- import { Editor, type EditorComponent, visibleWidth } from "@earendil-works/pi-tui";
4
- import type { ExtensionContext } from "@selesai/code";
5
- import type { SubagentState } from "../../src/shared/types.ts";
6
- import { EXTERNAL_RUN_REGISTRY_KEY, EXTERNAL_RUN_REGISTRY_VERSION, registerExternalRun } from "../../src/api/external-runs.ts";
7
- import { collectFleetSnapshot } from "../../src/tui/fleet.ts";
8
- import {
9
- FLEET_STATUS_WIDGET_KEY,
10
- SubagentFleetStatus,
11
- collectFleetStatusEntries,
12
- formatFleetElapsed,
13
- formatFleetTokens,
14
- resolveFleetViewPlacement,
15
- } from "../../src/tui/fleet-status.ts";
16
-
17
- function clearExternalRuns(): void {
18
- delete (globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)];
19
- }
20
-
21
- function stateForTest(): SubagentState {
22
- return {
23
- baseCwd: process.cwd(),
24
- currentSessionId: "session-current",
25
- asyncJobs: new Map(),
26
- fleetJobs: new Map(),
27
- foregroundRuns: new Map(),
28
- foregroundControls: new Map(),
29
- lastForegroundControlId: null,
30
- cleanupTimers: new Map(),
31
- lastUiContext: null,
32
- poller: null,
33
- completionSeen: new Map(),
34
- watcher: null,
35
- watcherRestartTimer: null,
36
- resultFileCoalescer: { schedule: () => false, clear: () => {} },
37
- };
38
- }
39
-
40
- const theme = {
41
- fg: (_name: string, text: string) => text,
42
- bg: (_name: string, text: string) => text,
43
- bold: (text: string) => text,
44
- };
45
-
46
- describe("below-editor subagent FleetView", () => {
47
- it("formats elapsed time and token counts like the Claude Code fleet", () => {
48
- assert.equal(formatFleetElapsed(10_600), "11s");
49
- assert.equal(formatFleetTokens(999), "↓ 999 tokens");
50
- assert.equal(formatFleetTokens(13_100), "↓ 13.1k tokens");
51
- assert.equal(formatFleetTokens(1_250_000), "↓ 1.3M tokens");
52
- });
53
-
54
- it("renders cached external jobs with an external marker and elapsed time", () => {
55
- clearExternalRuns();
56
- registerExternalRun({
57
- id: "external-review",
58
- sessionId: "session-current",
59
- source: "interactive-shell",
60
- label: "Dependency review",
61
- state: "running",
62
- startedAt: Date.now() - 11_000,
63
- currentAction: "Inspecting package metadata",
64
- });
65
- const state = stateForTest();
66
- state.activeAsyncCapacity = { used: 0, limit: 0 };
67
- assert.deepEqual(collectFleetStatusEntries(state).map((entry) => ({ key: entry.key, agent: entry.agent, description: entry.description })), [{
68
- key: "external:external-review",
69
- agent: "external · Dependency review",
70
- description: "Inspecting package metadata",
71
- }]);
72
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
73
- const ctx = {
74
- hasUI: true,
75
- ui: {
76
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
77
- onTerminalInput() { return () => {}; },
78
- getEditorText() { return ""; },
79
- requestRender() {},
80
- notify() {},
81
- theme,
82
- },
83
- } as unknown as ExtensionContext;
84
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
85
- try {
86
- fleet.setContext(ctx);
87
- const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
88
- const component = widgetFactory!(tui, theme);
89
- const compact = component.render(80)[0]!;
90
- assert.match(compact, /1 active job/);
91
- assert.doesNotMatch(compact, /Async runs|tokens/);
92
- fleet.handleKey("\x1b[B");
93
- const expanded = component.render(80).join("\n");
94
- assert.match(expanded, /external · Dependency review · running/);
95
- assert.match(expanded, /11s/);
96
- assert.doesNotMatch(expanded.split("\n").find((line) => line.includes("Dependency review"))!, /tokens/);
97
- } finally {
98
- fleet.dispose();
99
- clearExternalRuns();
100
- }
101
- });
102
-
103
- it("warns when compact FleetView cannot inspect external jobs", () => {
104
- clearExternalRuns();
105
- const warnings: string[] = [];
106
- const originalWarn = console.warn;
107
- console.warn = (message?: unknown) => warnings.push(String(message));
108
- (globalThis as Record<PropertyKey, unknown>)[Symbol.for(EXTERNAL_RUN_REGISTRY_KEY)] = {
109
- version: EXTERNAL_RUN_REGISTRY_VERSION + 1,
110
- runs: new Map(),
111
- };
112
- try {
113
- assert.deepEqual(collectFleetStatusEntries(stateForTest()), []);
114
- assert.match(warnings[0]!, /Failed to inspect external jobs/);
115
- } finally {
116
- console.warn = originalWarn;
117
- clearExternalRuns();
118
- }
119
- });
120
-
121
- it("resolves configured FleetView placement with a below-editor fallback", () => {
122
- assert.equal(resolveFleetViewPlacement(undefined), "belowEditor");
123
- assert.equal(resolveFleetViewPlacement("belowEditor"), "belowEditor");
124
- assert.equal(resolveFleetViewPlacement("aboveEditor"), "aboveEditor");
125
- assert.equal(resolveFleetViewPlacement("side"), "belowEditor");
126
- });
127
-
128
- it("renders main plus active children below the editor and bounds every line", () => {
129
- const state = stateForTest();
130
- state.activeAsyncCapacity = { used: 2, limit: 4 };
131
- const now = Date.now();
132
- for (let index = 0; index < 7; index++) {
133
- state.foregroundControls.set(`run-${index}`, {
134
- runId: `run-${index}`,
135
- mode: "single",
136
- startedAt: now - 11_000 + index,
137
- updatedAt: now,
138
- currentAgent: `worker-${index}`,
139
- description: index === 0 ? "Inspect\nmodule 0" : `Inspect module ${index}`,
140
- ...(index === 0 ? { model: "anthropic/fable-5", thinking: "low" } : {}),
141
- tokens: index === 0 ? 13_100 : 100,
142
- });
143
- }
144
-
145
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
146
- const ctx = {
147
- hasUI: true,
148
- ui: {
149
- setWidget(key: string, content: typeof widgetFactory | undefined, options?: { placement?: string }) {
150
- assert.equal(key, FLEET_STATUS_WIDGET_KEY);
151
- if (content) {
152
- assert.equal(options?.placement, "belowEditor");
153
- widgetFactory = content;
154
- }
155
- },
156
- onTerminalInput() { return () => {}; },
157
- getEditorText() { return ""; },
158
- requestRender() {},
159
- notify() {},
160
- theme,
161
- },
162
- } as unknown as ExtensionContext;
163
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
164
- try {
165
- fleet.setContext(ctx);
166
- assert.ok(widgetFactory);
167
- const tui = {
168
- requestRender() {},
169
- focusedComponent: Object.create(Editor.prototype) as Editor,
170
- };
171
- const component = widgetFactory!(tui, theme);
172
- const compactLines = component.render(80);
173
- assert.equal(compactLines.length, 1);
174
- assert.ok(compactLines[0]!.includes("7 active agents · Async runs 2/4"));
175
- assert.ok(compactLines[0]!.includes("↓ 13.7k tokens"));
176
- assert.ok(compactLines[0]!.includes("↓/← to inspect"));
177
- assert.ok(visibleWidth(compactLines[0]!) <= 80);
178
-
179
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
180
- const expandedLines = component.render(80);
181
- assert.ok(expandedLines.some((line) => line.includes("> main")));
182
- assert.ok(expandedLines.some((line) => line.includes(" worker-0")), "unselected agents use blank focus space");
183
- assert.ok(expandedLines.every((line) => !/[⏺◯]/u.test(line)), "selection avoids terminal-ambiguous circle glyphs");
184
- assert.ok(expandedLines.some((line) => line.includes("worker-0 (fable-5 · thinking low)")));
185
- assert.ok(expandedLines.some((line) => line.includes("11s · ↓ 13.1k tokens")));
186
- assert.ok(expandedLines.some((line) => line.includes("↓ 1 more")));
187
- for (const line of expandedLines) assert.ok(visibleWidth(line) <= 80, `line exceeded width: ${line}`);
188
-
189
- assert.deepEqual(fleet.handleKey("\x1b"), { consume: true });
190
- assert.equal(component.render(80).length, 1);
191
- assert.deepEqual(fleet.handleKey("\x1b[D"), { consume: true });
192
- assert.ok(component.render(80).length > 1, "Left should also expand the roster");
193
- } finally {
194
- fleet.dispose();
195
- }
196
- });
197
-
198
- it("keeps occupied capacity visible without active child rows", () => {
199
- const state = stateForTest();
200
- state.activeAsyncCapacity = { used: 1, limit: 2 };
201
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
202
- const ctx = {
203
- hasUI: true,
204
- ui: {
205
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
206
- onTerminalInput() { return () => {}; },
207
- getEditorText() { return ""; },
208
- requestRender() {},
209
- notify() {},
210
- theme,
211
- },
212
- } as unknown as ExtensionContext;
213
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
214
- try {
215
- fleet.setContext(ctx);
216
- assert.ok(widgetFactory);
217
- assert.deepEqual(widgetFactory!({ requestRender() {} }, theme).render(80), [" Async runs 1/2 · ↓ 0 tokens · ↓/← to inspect"]);
218
- } finally {
219
- fleet.dispose();
220
- }
221
- });
222
-
223
- it("keeps one queued agent visible in the compact summary", () => {
224
- const state = stateForTest();
225
- state.asyncJobs.set("run-worker", {
226
- asyncId: "run-worker",
227
- asyncDir: "/tmp/run-worker",
228
- status: "queued",
229
- mode: "single",
230
- startedAt: 10,
231
- updatedAt: 20,
232
- totalTokens: { input: 40, output: 2, total: 42 },
233
- });
234
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
235
- const ctx = {
236
- hasUI: true,
237
- ui: {
238
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
239
- onTerminalInput() { return () => {}; },
240
- getEditorText() { return ""; },
241
- requestRender() {},
242
- notify() {},
243
- theme,
244
- },
245
- } as unknown as ExtensionContext;
246
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
247
- try {
248
- fleet.setContext(ctx);
249
- const lines = widgetFactory!({ requestRender() {} }, theme).render(50);
250
- assert.equal(lines.length, 1);
251
- assert.ok(lines[0]!.includes("1 active agent"));
252
- assert.ok(lines[0]!.includes("↓ 42 tokens"));
253
- assert.ok(visibleWidth(lines[0]!) <= 50);
254
- } finally {
255
- fleet.dispose();
256
- }
257
- });
258
-
259
- it("registers above the editor when configured", () => {
260
- const state = stateForTest();
261
- state.foregroundControls.set("run-worker", {
262
- runId: "run-worker",
263
- mode: "single",
264
- startedAt: 10,
265
- updatedAt: 20,
266
- currentAgent: "worker",
267
- });
268
- let placement: string | undefined;
269
- const ctx = {
270
- hasUI: true,
271
- ui: {
272
- setWidget(_key: string, content: unknown, options?: { placement?: string }) {
273
- if (content) placement = options?.placement;
274
- },
275
- onTerminalInput() { return () => {}; },
276
- getEditorText() { return ""; },
277
- notify() {},
278
- theme,
279
- },
280
- } as unknown as ExtensionContext;
281
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000, placement: "aboveEditor" });
282
- try {
283
- fleet.setContext(ctx);
284
- assert.equal(placement, "aboveEditor");
285
- } finally {
286
- fleet.dispose();
287
- }
288
- });
289
-
290
- it("stops refreshing when the captured extension context becomes stale", () => {
291
- const state = stateForTest();
292
- state.foregroundControls.set("run-worker", {
293
- runId: "run-worker",
294
- mode: "single",
295
- startedAt: 10,
296
- updatedAt: 20,
297
- currentAgent: "worker",
298
- });
299
- let stale = false;
300
- let contextReads = 0;
301
- let inputUnsubscribes = 0;
302
- let widgetRemovals = 0;
303
- const ctx = {
304
- get hasUI() {
305
- contextReads++;
306
- if (stale) {
307
- throw new Error("This extension ctx is stale after session replacement or reload.");
308
- }
309
- return true;
310
- },
311
- ui: {
312
- setWidget(_key: string, content: unknown) {
313
- if (content === undefined) widgetRemovals++;
314
- },
315
- onTerminalInput() { return () => { inputUnsubscribes++; }; },
316
- getEditorText() { return ""; },
317
- notify() {},
318
- theme,
319
- },
320
- } as unknown as ExtensionContext;
321
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
322
- try {
323
- fleet.setContext(ctx);
324
- stale = true;
325
- assert.doesNotThrow(() => fleet.refresh());
326
- assert.equal(inputUnsubscribes, 1);
327
- assert.equal(widgetRemovals, 1);
328
- assert.equal((fleet as unknown as { timer?: unknown }).timer, undefined);
329
- const readsAfterStaleRefresh = contextReads;
330
- fleet.refresh();
331
- assert.equal(contextReads, readsAfterStaleRefresh, "later refreshes must not reuse the stale context");
332
- } finally {
333
- fleet.dispose();
334
- }
335
- });
336
-
337
- it("does not swallow unrelated widget cleanup errors", () => {
338
- const state = stateForTest();
339
- state.foregroundControls.set("run-worker", {
340
- runId: "run-worker",
341
- mode: "single",
342
- startedAt: 10,
343
- updatedAt: 20,
344
- currentAgent: "worker",
345
- });
346
- const ctx = {
347
- hasUI: true,
348
- ui: {
349
- setWidget(_key: string, content: unknown) {
350
- if (content === undefined) throw new Error("widget cleanup failed");
351
- },
352
- onTerminalInput() { return () => {}; },
353
- getEditorText() { return ""; },
354
- notify() {},
355
- theme,
356
- },
357
- } as unknown as ExtensionContext;
358
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
359
- fleet.setContext(ctx);
360
- assert.throws(() => fleet.dispose(), /widget cleanup failed/);
361
- });
362
-
363
- it("preserves multiple unrelated UI cleanup errors", () => {
364
- const state = stateForTest();
365
- state.foregroundControls.set("run-worker", {
366
- runId: "run-worker",
367
- mode: "single",
368
- startedAt: 10,
369
- updatedAt: 20,
370
- currentAgent: "worker",
371
- });
372
- const ctx = {
373
- hasUI: true,
374
- ui: {
375
- setWidget(_key: string, content: unknown) {
376
- if (content === undefined) throw new Error("widget cleanup failed");
377
- },
378
- onTerminalInput() {
379
- return () => { throw new Error("input cleanup failed"); };
380
- },
381
- getEditorText() { return ""; },
382
- notify() {},
383
- theme,
384
- },
385
- } as unknown as ExtensionContext;
386
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
387
- fleet.setContext(ctx);
388
- assert.throws(
389
- () => fleet.dispose(),
390
- (error: unknown) => error instanceof AggregateError
391
- && error.errors.map(String).join("\n").includes("input cleanup failed")
392
- && error.errors.map(String).join("\n").includes("widget cleanup failed"),
393
- );
394
- });
395
-
396
- it("keeps widget ownership through invalidation so an empty refresh removes it", () => {
397
- const state = stateForTest();
398
- state.foregroundControls.set("run-worker", {
399
- runId: "run-worker",
400
- mode: "single",
401
- startedAt: 10,
402
- updatedAt: 20,
403
- currentAgent: "worker",
404
- });
405
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[]; invalidate(): void }) | undefined;
406
- let removals = 0;
407
- const ctx = {
408
- hasUI: true,
409
- ui: {
410
- setWidget(_key: string, content: typeof widgetFactory | undefined) {
411
- if (content) widgetFactory = content;
412
- else removals++;
413
- },
414
- onTerminalInput() { return () => {}; },
415
- getEditorText() { return ""; },
416
- requestRender() {},
417
- notify() {},
418
- theme,
419
- },
420
- } as unknown as ExtensionContext;
421
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
422
- try {
423
- fleet.setContext(ctx);
424
- const component = widgetFactory!({ requestRender() {} }, theme);
425
- component.invalidate();
426
- state.foregroundControls.clear();
427
- fleet.refresh();
428
- assert.equal(removals, 1);
429
- } finally {
430
- fleet.dispose();
431
- }
432
- });
433
-
434
- it("removes the dynamic widget while the fleet inspector owns the viewport", () => {
435
- const state = stateForTest();
436
- state.foregroundControls.set("run-worker", {
437
- runId: "run-worker",
438
- mode: "single",
439
- startedAt: 10,
440
- updatedAt: 20,
441
- currentAgent: "worker",
442
- });
443
- const registrations: string[] = [];
444
- const ctx = {
445
- hasUI: true,
446
- ui: {
447
- setWidget(_key: string, content: unknown) {
448
- registrations.push(content ? "shown" : "hidden");
449
- },
450
- onTerminalInput() { return () => {}; },
451
- getEditorText() { return ""; },
452
- requestRender() {},
453
- notify() {},
454
- theme,
455
- },
456
- } as unknown as ExtensionContext;
457
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
458
- try {
459
- fleet.setContext(ctx);
460
- state.fleetInspectorOpen = true;
461
- fleet.refresh();
462
- state.fleetInspectorOpen = false;
463
- fleet.refresh();
464
- assert.deepEqual(registrations, ["shown", "hidden", "shown"]);
465
- } finally {
466
- fleet.dispose();
467
- }
468
- });
469
-
470
- it("renders retained nested terminal siblings under an active owner with bounded leaves", () => {
471
- const state = stateForTest();
472
- state.asyncJobs.set("supervisor", {
473
- asyncId: "supervisor",
474
- asyncDir: "/tmp/supervisor",
475
- status: "running",
476
- mode: "single",
477
- startedAt: 10,
478
- updatedAt: 20,
479
- steps: [{ agent: "supervisor", index: 0, status: "running" }],
480
- nestedChildren: [0, 1, 2, 3, 4].map((index) => ({
481
- id: `nested-${index}`,
482
- parentRunId: "supervisor",
483
- parentStepIndex: 0,
484
- depth: 1,
485
- path: [{ runId: "supervisor", stepIndex: 0 }],
486
- state: index === 0 ? "complete" as const : "running" as const,
487
- agent: `leaf-${index}`,
488
- model: index === 0 ? "provider/gpt-5.6-luna:medium" : "provider/gpt-5.6-luna",
489
- thinking: "medium",
490
- startedAt: 10,
491
- lastUpdate: 20,
492
- ...(index === 4 ? {
493
- children: [{
494
- id: "nested-hidden-child",
495
- parentRunId: "nested-4",
496
- depth: 2,
497
- path: [{ runId: "supervisor", stepIndex: 0 }, { runId: "nested-4" }],
498
- state: "running" as const,
499
- agent: "hidden-child",
500
- }],
501
- } : {}),
502
- })),
503
- });
504
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
505
- const ctx = {
506
- hasUI: true,
507
- ui: {
508
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
509
- onTerminalInput() { return () => {}; },
510
- getEditorText() { return ""; },
511
- requestRender() {},
512
- notify() {},
513
- theme,
514
- },
515
- } as unknown as ExtensionContext;
516
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
517
- try {
518
- fleet.setContext(ctx);
519
- const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
520
- const component = widgetFactory!(tui, theme);
521
- assert.equal(component.render(120).length, 1, "nested activity should stay compact until navigation activates the roster");
522
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
523
- const lines = component.render(120).join("\n");
524
- assert.match(lines, /supervisor/);
525
- for (const [index, state] of ["complete", "running", "running", "running"].entries()) {
526
- const line = lines.split("\n").find((candidate) => candidate.includes(`leaf-${index}`));
527
- assert.ok(line);
528
- assert.match(line!, /gpt-5.6-luna/);
529
- assert.match(line!, /thinking medium/);
530
- assert.match(line!, new RegExp(state));
531
- }
532
- assert.doesNotMatch(lines, /leaf-4.*running/);
533
- assert.doesNotMatch(lines, /hidden-child/);
534
- assert.match(lines, /\+2 nested leaves/);
535
- } finally {
536
- fleet.dispose();
537
- }
538
- });
539
-
540
- it("counts hidden nested leaves across multiple parallel children", () => {
541
- const state = stateForTest();
542
- state.asyncJobs.set("supervisor", {
543
- asyncId: "supervisor",
544
- asyncDir: "/tmp/supervisor",
545
- status: "running",
546
- mode: "single",
547
- startedAt: 10,
548
- updatedAt: 20,
549
- steps: [{ agent: "supervisor", index: 0, status: "running" }],
550
- nestedChildren: [
551
- {
552
- id: "nested-a",
553
- parentRunId: "supervisor",
554
- parentStepIndex: 0,
555
- depth: 1,
556
- path: [{ runId: "supervisor", stepIndex: 0 }],
557
- state: "running",
558
- mode: "parallel",
559
- steps: [0, 1, 2, 3].map((index) => ({ agent: `child-a-${index}`, index, status: "running" as const })),
560
- },
561
- {
562
- id: "nested-b",
563
- parentRunId: "supervisor",
564
- parentStepIndex: 0,
565
- depth: 1,
566
- path: [{ runId: "supervisor", stepIndex: 0 }],
567
- state: "running",
568
- mode: "parallel",
569
- steps: [0, 1].map((index) => ({ agent: `child-b-${index}`, index, status: "running" as const })),
570
- },
571
- ],
572
- });
573
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
574
- const ctx = {
575
- hasUI: true,
576
- ui: {
577
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
578
- onTerminalInput() { return () => {}; },
579
- getEditorText() { return ""; },
580
- requestRender() {},
581
- notify() {},
582
- theme,
583
- },
584
- } as unknown as ExtensionContext;
585
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
586
- try {
587
- fleet.setContext(ctx);
588
- const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
589
- const component = widgetFactory!(tui, theme);
590
- assert.equal(component.render(120).length, 1, "parallel nested activity should stay compact until activated");
591
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
592
- const lines = component.render(120).join("\n");
593
- for (const index of [0, 1, 2, 3]) assert.match(lines, new RegExp(`child-a-${index}`));
594
- assert.doesNotMatch(lines, /child-b-[01]/);
595
- assert.match(lines, /\+2 nested leaves/);
596
- } finally {
597
- fleet.dispose();
598
- }
599
- });
600
-
601
- it("shows only the current sequential chain step while retaining active parallel siblings", () => {
602
- const state = stateForTest();
603
- state.asyncJobs.set("sequential", {
604
- asyncId: "sequential",
605
- asyncDir: "/tmp/sequential",
606
- status: "running",
607
- mode: "chain",
608
- currentStep: 1,
609
- startedAt: 50,
610
- updatedAt: 200,
611
- steps: [
612
- { agent: "scout", index: 0, status: "complete" },
613
- { agent: "worker", index: 1, status: "running" },
614
- { agent: "reviewer", index: 2, status: "pending" },
615
- ],
616
- });
617
- state.asyncJobs.set("parallel-group", {
618
- asyncId: "parallel-group",
619
- asyncDir: "/tmp/parallel-group",
620
- status: "running",
621
- mode: "chain",
622
- currentStep: 3,
623
- activeParallelGroup: true,
624
- startedAt: 100,
625
- updatedAt: 200,
626
- steps: [
627
- { agent: "reviewer", index: 3, status: "running" },
628
- { agent: "tester", index: 4, status: "pending" },
629
- ],
630
- });
631
- assert.deepEqual(collectFleetStatusEntries(state).map((entry) => entry.key), [
632
- "async:sequential:1",
633
- "async:parallel-group:3",
634
- "async:parallel-group:4",
635
- ]);
636
- });
637
-
638
- it("shows every active foreground parallel child", () => {
639
- const state = stateForTest();
640
- state.foregroundControls.set("parallel", {
641
- runId: "parallel",
642
- mode: "parallel",
643
- startedAt: 10,
644
- updatedAt: 30,
645
- activeChildren: new Map([
646
- [0, { index: 0, agent: "reviewer", description: "Review correctness", startedAt: 11, updatedAt: 21, tokens: 100 }],
647
- [1, { index: 1, agent: "reviewer", description: "Review quality", startedAt: 12, updatedAt: 22, tokens: 200 }],
648
- [2, { index: 2, agent: "reviewer", description: "Review tests", startedAt: 13, updatedAt: 23, tokens: 300 }],
649
- ]),
650
- });
651
-
652
- const entries = collectFleetStatusEntries(state);
653
- assert.deepEqual(entries.map((entry) => entry.key), [
654
- "foreground-active:parallel:0",
655
- "foreground-active:parallel:1",
656
- "foreground-active:parallel:2",
657
- ]);
658
- assert.deepEqual(entries.map((entry) => entry.description), ["Review correctness", "Review quality", "Review tests"]);
659
- assert.deepEqual(collectFleetSnapshot(state).items.map((item) => item.key), entries.map((entry) => entry.key));
660
- });
661
-
662
- it("labels workflow-owned foreground children", () => {
663
- const state = stateForTest();
664
- state.foregroundControls.set("child-1", {
665
- runId: "child-1",
666
- parentWorkflowRunId: "workflow-1",
667
- workflowKey: "review",
668
- mode: "single",
669
- startedAt: 10,
670
- updatedAt: 20,
671
- activeChildren: new Map([[0, { index: 0, agent: "reviewer", description: "Review the change", startedAt: 10, updatedAt: 20 }]]),
672
- });
673
-
674
- assert.deepEqual(collectFleetStatusEntries(state).map((entry) => ({ agent: entry.agent, description: entry.description })), [{
675
- agent: "reviewer",
676
- description: "workflow child: workflow-1 (review) · Review the change",
677
- }]);
678
- });
679
-
680
- it("renders a workflow-owned foreground child under its workflow parent", () => {
681
- const state = stateForTest();
682
- state.asyncJobs.set("workflow-1", {
683
- asyncId: "workflow-1",
684
- asyncDir: "/tmp/workflow-1",
685
- status: "running",
686
- mode: "workflow",
687
- startedAt: 10,
688
- updatedAt: 20,
689
- nestedChildren: [{
690
- id: "owner-nested",
691
- parentRunId: "workflow-1",
692
- depth: 1,
693
- path: [{ runId: "workflow-1" }],
694
- state: "running",
695
- agent: "owner-nested",
696
- }],
697
- });
698
- state.foregroundControls.set("child-1", {
699
- runId: "child-1",
700
- parentWorkflowRunId: "workflow-1",
701
- workflowKey: "review",
702
- mode: "single",
703
- startedAt: 11,
704
- updatedAt: 20,
705
- activeChildren: new Map([[0, { index: 0, agent: "reviewer", startedAt: 11, updatedAt: 20 }]]),
706
- nestedChildren: [{
707
- id: "nested-review",
708
- parentRunId: "child-1",
709
- parentStepIndex: 0,
710
- depth: 1,
711
- path: [{ runId: "child-1", stepIndex: 0 }],
712
- state: "running",
713
- agent: "nested-reviewer",
714
- }],
715
- });
716
- state.foregroundControls.set("child-2", {
717
- runId: "child-2",
718
- parentWorkflowRunId: "workflow-1",
719
- workflowKey: "test",
720
- mode: "single",
721
- startedAt: 12,
722
- updatedAt: 20,
723
- activeChildren: new Map([[0, { index: 0, agent: "tester", startedAt: 12, updatedAt: 20 }]]),
724
- nestedChildren: [{
725
- id: "nested-test",
726
- parentRunId: "child-2",
727
- parentStepIndex: 0,
728
- depth: 1,
729
- path: [{ runId: "child-2", stepIndex: 0 }],
730
- state: "running",
731
- agent: "nested-tester",
732
- }],
733
- });
734
- const entries = collectFleetStatusEntries(state);
735
- assert.deepEqual(entries.map((entry) => [entry.key, entry.parentKey]), [
736
- ["async:workflow-1", undefined],
737
- ["foreground-active:child-1:0", "async:workflow-1"],
738
- ["foreground-active:child-2:0", "async:workflow-1"],
739
- ]);
740
-
741
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
742
- const ctx = {
743
- hasUI: true,
744
- ui: {
745
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
746
- onTerminalInput() { return () => {}; },
747
- getEditorText() { return ""; },
748
- requestRender() {},
749
- notify() {},
750
- theme,
751
- },
752
- } as unknown as ExtensionContext;
753
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
754
- try {
755
- fleet.setContext(ctx);
756
- const component = widgetFactory!({ requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor }, theme);
757
- const compact = component.render(120);
758
- assert.match(compact[0]!, /2 active agents/, "workflow wrappers must not be counted as leaf agents");
759
- assert.doesNotMatch(compact[0]!, /3 active agents/);
760
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
761
- const lines = component.render(120);
762
- const workflowIndex = lines.findIndex((line) => line.includes("workflow · running"));
763
- const childIndex = lines.findIndex((line) => line.includes("reviewer · running"));
764
- const nestedIndex = lines.findIndex((line) => line.includes("nested-reviewer"));
765
- const secondChildIndex = lines.findIndex((line) => line.includes("tester · running"));
766
- const secondNestedIndex = lines.findIndex((line) => line.includes("nested-tester"));
767
- const ownerNestedIndex = lines.findIndex((line) => line.includes("owner-nested"));
768
- assert.ok(workflowIndex >= 0 && childIndex > workflowIndex && nestedIndex > childIndex);
769
- assert.ok(secondChildIndex > nestedIndex && secondNestedIndex > secondChildIndex && ownerNestedIndex > secondNestedIndex);
770
- assert.match(lines[childIndex]!, /├─.*reviewer/);
771
- assert.match(lines[nestedIndex]!, /├─.*nested-reviewer/);
772
- assert.match(lines[secondNestedIndex]!, /├─.*nested-tester/);
773
- assert.match(lines[ownerNestedIndex]!, /└─.*owner-nested/);
774
- } finally {
775
- fleet.dispose();
776
- }
777
- });
778
-
779
- it("renders recursive nested runs and steps within the existing row budget", () => {
780
- const state = stateForTest();
781
- state.asyncJobs.set("supervisor", {
782
- asyncId: "supervisor",
783
- asyncDir: "/tmp/supervisor",
784
- status: "running",
785
- mode: "single",
786
- startedAt: 10,
787
- updatedAt: 20,
788
- steps: [{ agent: "supervisor", index: 0, status: "running" }],
789
- nestedChildren: [{
790
- id: "level-one",
791
- parentRunId: "supervisor",
792
- parentStepIndex: 0,
793
- depth: 1,
794
- path: [{ runId: "supervisor", stepIndex: 0 }],
795
- state: "running",
796
- agent: "level-one",
797
- children: [{
798
- id: "level-two",
799
- parentRunId: "level-one",
800
- depth: 2,
801
- path: [{ runId: "supervisor", stepIndex: 0 }, { runId: "level-one" }],
802
- state: "running",
803
- mode: "parallel",
804
- steps: [{
805
- agent: "level-two",
806
- status: "running",
807
- children: [0, 1, 2, 3].map((index) => ({
808
- id: `leaf-${index}`,
809
- parentRunId: "level-two",
810
- depth: 3,
811
- path: [{ runId: "level-two" }],
812
- state: "running" as const,
813
- agent: `leaf-${index}`,
814
- })),
815
- }],
816
- }],
817
- }],
818
- });
819
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
820
- const ctx = {
821
- hasUI: true,
822
- ui: {
823
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
824
- onTerminalInput() { return () => {}; },
825
- getEditorText() { return ""; },
826
- requestRender() {},
827
- notify() {},
828
- theme,
829
- },
830
- } as unknown as ExtensionContext;
831
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
832
- try {
833
- fleet.setContext(ctx);
834
- const component = widgetFactory!({ requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor }, theme);
835
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
836
- const lines = component.render(140).join("\n");
837
- assert.match(lines, /level-one/);
838
- assert.match(lines, /level-two/);
839
- assert.match(lines, /leaf-0/);
840
- assert.match(lines, /leaf-1/);
841
- assert.doesNotMatch(lines, /leaf-[23]/);
842
- assert.match(lines, /\+2 nested leaves/);
843
- } finally {
844
- fleet.dispose();
845
- }
846
- });
847
-
848
- it("uses the same item keys as the full inspector", () => {
849
- const state = stateForTest();
850
- state.foregroundControls.set("foreground", {
851
- runId: "foreground",
852
- mode: "single",
853
- startedAt: 10,
854
- updatedAt: 20,
855
- currentAgent: "worker",
856
- currentIndex: 2,
857
- });
858
- const asyncJob = {
859
- asyncId: "background",
860
- asyncDir: "/tmp/background",
861
- sessionId: "session-current",
862
- status: "running" as const,
863
- mode: "single" as const,
864
- startedAt: 10,
865
- updatedAt: 20,
866
- steps: [{ agent: "reviewer", index: 0, status: "running" as const }],
867
- };
868
- state.asyncJobs.set(asyncJob.asyncId, asyncJob);
869
- state.fleetJobs!.set(asyncJob.asyncId, asyncJob);
870
-
871
- const statusKeys = collectFleetStatusEntries(state).map((entry) => entry.key).sort();
872
- const inspectorKeys = collectFleetSnapshot(state).items.map((item) => item.key).sort();
873
- assert.deepEqual(statusKeys, inspectorKeys);
874
- });
875
-
876
- it("hides tracked async task descriptions and shows per-child token totals", () => {
877
- const state = stateForTest();
878
- state.asyncJobs.set("async-run", {
879
- asyncId: "async-run",
880
- asyncDir: "/tmp/async-run",
881
- status: "running",
882
- mode: "parallel",
883
- description: "Review the authentication changes",
884
- startedAt: 100,
885
- updatedAt: 200,
886
- steps: [
887
- { agent: "reviewer", index: 0, status: "running", description: "Review only authentication", startedAt: 120, model: "openai/gpt-5", thinking: "medium", tokens: { input: 4_000, output: 200, total: 4_200 } },
888
- { agent: "worker", index: 1, status: "running", description: "Implement only billing", startedAt: 121, tokens: { input: 100, output: 20, total: 120 } },
889
- ],
890
- });
891
- const fleet = new SubagentFleetStatus(state, () => {}, { refreshMs: 60_000 });
892
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
893
- const ctx = {
894
- hasUI: true,
895
- ui: {
896
- setWidget(_key: string, content: typeof widgetFactory | undefined) { if (content) widgetFactory = content; },
897
- onTerminalInput() { return () => {}; },
898
- getEditorText() { return ""; },
899
- requestRender() {},
900
- notify() {},
901
- theme,
902
- },
903
- } as unknown as ExtensionContext;
904
- try {
905
- fleet.setContext(ctx);
906
- const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
907
- const component = widgetFactory!(tui, theme);
908
- assert.deepEqual(fleet.handleKey("\x1b[B"), { consume: true });
909
- const lines = component.render(180);
910
- assert.ok(lines.some((line) => line.includes("reviewer (gpt-5 · thinking medium)")));
911
- assert.ok(lines.some((line) => line.includes("worker")));
912
- assert.ok(lines.every((line) => !line.includes("Review only authentication") && !line.includes("Implement only billing") && !line.includes("Review the authentication changes")));
913
- assert.ok(lines.some((line) => line.includes("↓ 4.2k tokens")));
914
- } finally {
915
- fleet.dispose();
916
- }
917
- });
918
-
919
- it("only captures navigation at an empty editor and opens the selected child", async () => {
920
- const state = stateForTest();
921
- state.foregroundControls.set("run-worker", {
922
- runId: "run-worker",
923
- mode: "single",
924
- startedAt: Date.now() - 1_000,
925
- updatedAt: Date.now(),
926
- currentAgent: "worker",
927
- description: "Implement FleetView",
928
- });
929
- let editorText = "draft";
930
- let inputHandler: ((data: string) => { consume?: boolean } | undefined) | undefined;
931
- let widgetFactory: ((tui: unknown, theme: typeof theme) => { render(width: number): string[] }) | undefined;
932
- const opened: string[] = [];
933
- let closeInspector: (() => void) | undefined;
934
- const ctx = {
935
- hasUI: true,
936
- ui: {
937
- setWidget(_key: string, content: typeof widgetFactory | undefined) { widgetFactory = content; },
938
- onTerminalInput(handler: typeof inputHandler) { inputHandler = handler; return () => { inputHandler = undefined; }; },
939
- getEditorText() { return editorText; },
940
- requestRender() {},
941
- notify() {},
942
- theme,
943
- },
944
- } as unknown as ExtensionContext;
945
- const fleet = new SubagentFleetStatus(state, (key) => {
946
- opened.push(key);
947
- return new Promise<void>((resolve) => { closeInspector = resolve; });
948
- }, { refreshMs: 60_000 });
949
- try {
950
- fleet.setContext(ctx);
951
- assert.ok(inputHandler);
952
- assert.ok(widgetFactory);
953
- const tui = { requestRender() {}, focusedComponent: Object.create(Editor.prototype) as Editor };
954
- const component = widgetFactory!(tui, theme);
955
-
956
- assert.equal(inputHandler!("\x1b[B"), undefined, "non-empty editor should retain Down");
957
- editorText = "";
958
- tui.focusedComponent = {
959
- render() { return []; },
960
- invalidate() {},
961
- handleInput() {},
962
- } as unknown as Editor;
963
- assert.equal(inputHandler!("\x1b[B"), undefined, "non-editor focus should retain Down");
964
-
965
- const crossModuleCustomEditor = {
966
- render() { return []; },
967
- invalidate() {},
968
- handleInput() {},
969
- getText() { return ""; },
970
- setText() {},
971
- } satisfies EditorComponent;
972
- assert.equal(crossModuleCustomEditor instanceof Editor, false, "regression setup must cross the instanceof boundary");
973
- tui.focusedComponent = crossModuleCustomEditor as unknown as Editor;
974
- assert.equal(inputHandler!("j"), undefined, "inactive FleetView should retain printable navigation keys");
975
- assert.equal(inputHandler!("k"), undefined, "inactive FleetView should retain printable navigation keys");
976
- assert.equal(component.render(100).length, 1, "inactive FleetView should stay compact");
977
- assert.deepEqual(inputHandler!("\x1b[B"), { consume: true }, "custom editors should activate FleetView across jiti boundaries");
978
- assert.ok(component.render(100).length > 1, "keyboard activation should expand the roster");
979
- assert.deepEqual(inputHandler!("j"), { consume: true }, "active FleetView should navigate down with j");
980
- assert.ok(component.render(100).some((line) => line.includes("> worker")));
981
- assert.deepEqual(inputHandler!("k"), { consume: true }, "active FleetView should navigate up with k");
982
- assert.ok(component.render(100).some((line) => line.includes("> main")));
983
-
984
- tui.focusedComponent = Object.create(Editor.prototype) as Editor;
985
- assert.deepEqual(inputHandler!("\x1b[B"), { consume: true });
986
- assert.ok(component.render(100).some((line) => line.includes("> worker")));
987
- assert.deepEqual(inputHandler!("\r"), { consume: true });
988
- await Promise.resolve();
989
- assert.deepEqual(opened, ["foreground-active:run-worker:0"]);
990
- assert.equal(widgetFactory, undefined, "the widget should unregister while the inspector owns the viewport");
991
-
992
- closeInspector!();
993
- await new Promise<void>((resolve) => setImmediate(resolve));
994
- assert.ok(widgetFactory, "closing should restore the FleetView widget");
995
- assert.notEqual(widgetFactory, component, "restoration should install a new component factory");
996
- const restoredComponent = widgetFactory!(tui, theme);
997
- assert.ok(restoredComponent.render(100).some((line) => line.includes("> worker")), "closing should restore the prior selected roster row");
998
- assert.deepEqual(inputHandler!("\x1b"), { consume: true });
999
- assert.equal(restoredComponent.render(100).length, 1, "Escape should return to the compact summary");
1000
- } finally {
1001
- fleet.dispose();
1002
- }
1003
- });
1004
- });