@pellux/goodvibes-agent 0.1.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 (398) hide show
  1. package/.goodvibes/GOODVIBES.md +35 -0
  2. package/.goodvibes/agents/reviewer.md +48 -0
  3. package/.goodvibes/skills/add-provider/SKILL.md +199 -0
  4. package/CHANGELOG.md +25 -0
  5. package/README.md +74 -0
  6. package/bin/goodvibes-agent.ts +2 -0
  7. package/docs/README.md +23 -0
  8. package/docs/deployment-and-services.md +57 -0
  9. package/docs/getting-started.md +53 -0
  10. package/docs/release-and-publishing.md +46 -0
  11. package/package.json +134 -0
  12. package/scripts/check-bun.sh +20 -0
  13. package/src/audio/player.ts +156 -0
  14. package/src/audio/spoken-turn-controller.ts +203 -0
  15. package/src/audio/spoken-turn-model-routing.ts +117 -0
  16. package/src/audio/spoken-turn-wiring.ts +44 -0
  17. package/src/audio/text-chunker.ts +110 -0
  18. package/src/cli/bundle-command.ts +227 -0
  19. package/src/cli/completion.ts +90 -0
  20. package/src/cli/config-overrides.ts +159 -0
  21. package/src/cli/endpoints.ts +63 -0
  22. package/src/cli/entrypoint.ts +172 -0
  23. package/src/cli/help.ts +299 -0
  24. package/src/cli/index.ts +11 -0
  25. package/src/cli/management-commands.ts +426 -0
  26. package/src/cli/management.ts +744 -0
  27. package/src/cli/network-posture.ts +46 -0
  28. package/src/cli/package-verification.ts +123 -0
  29. package/src/cli/parser.ts +369 -0
  30. package/src/cli/provider-auth-routes.ts +22 -0
  31. package/src/cli/provider-classification.ts +107 -0
  32. package/src/cli/redaction.ts +105 -0
  33. package/src/cli/service-command.ts +26 -0
  34. package/src/cli/service-posture.ts +482 -0
  35. package/src/cli/status.ts +383 -0
  36. package/src/cli/surface-command.ts +247 -0
  37. package/src/cli/tui-startup.ts +32 -0
  38. package/src/cli/types.ts +69 -0
  39. package/src/cli-flags.ts +21 -0
  40. package/src/config/goodvibes-home-audit.ts +465 -0
  41. package/src/config/index.ts +57 -0
  42. package/src/config/provider-model.ts +23 -0
  43. package/src/config/secret-config.ts +119 -0
  44. package/src/config/secrets.ts +71 -0
  45. package/src/config/surface.ts +1 -0
  46. package/src/core/composer-state.ts +61 -0
  47. package/src/core/conversation-rendering.ts +359 -0
  48. package/src/core/conversation.ts +551 -0
  49. package/src/core/history.ts +45 -0
  50. package/src/core/orchestrator.ts +7 -0
  51. package/src/core/system-message-router.ts +171 -0
  52. package/src/daemon/cli.ts +55 -0
  53. package/src/daemon/safe-serve.ts +61 -0
  54. package/src/input/agent-workspace.ts +428 -0
  55. package/src/input/autocomplete.ts +96 -0
  56. package/src/input/bookmark-modal.ts +115 -0
  57. package/src/input/command-args-hint.ts +36 -0
  58. package/src/input/command-registry.ts +329 -0
  59. package/src/input/commands/agent-externalized-tui.ts +73 -0
  60. package/src/input/commands/agent-workspace-runtime.ts +17 -0
  61. package/src/input/commands/branch-runtime.ts +72 -0
  62. package/src/input/commands/cloudflare-runtime.ts +370 -0
  63. package/src/input/commands/config.ts +18 -0
  64. package/src/input/commands/control-room-runtime.ts +255 -0
  65. package/src/input/commands/conversation-runtime.ts +207 -0
  66. package/src/input/commands/discovery-runtime.ts +52 -0
  67. package/src/input/commands/eval.ts +204 -0
  68. package/src/input/commands/experience-runtime.ts +278 -0
  69. package/src/input/commands/guidance-runtime.ts +106 -0
  70. package/src/input/commands/health-runtime.ts +434 -0
  71. package/src/input/commands/hooks-runtime.ts +148 -0
  72. package/src/input/commands/incident-runtime.ts +95 -0
  73. package/src/input/commands/integration-runtime.ts +394 -0
  74. package/src/input/commands/intelligence-runtime.ts +223 -0
  75. package/src/input/commands/knowledge.ts +531 -0
  76. package/src/input/commands/local-auth-runtime.ts +105 -0
  77. package/src/input/commands/local-provider-runtime.ts +170 -0
  78. package/src/input/commands/local-runtime.ts +392 -0
  79. package/src/input/commands/local-setup-review.ts +199 -0
  80. package/src/input/commands/local-setup-transfer.ts +135 -0
  81. package/src/input/commands/local-setup.ts +282 -0
  82. package/src/input/commands/managed-runtime.ts +209 -0
  83. package/src/input/commands/marketplace-runtime.ts +290 -0
  84. package/src/input/commands/mcp-runtime.ts +432 -0
  85. package/src/input/commands/memory-product-runtime.ts +111 -0
  86. package/src/input/commands/memory.ts +151 -0
  87. package/src/input/commands/notify-runtime.ts +83 -0
  88. package/src/input/commands/onboarding-runtime.ts +14 -0
  89. package/src/input/commands/operator-panel-runtime.ts +146 -0
  90. package/src/input/commands/operator-runtime.ts +392 -0
  91. package/src/input/commands/planning-runtime.ts +205 -0
  92. package/src/input/commands/platform-access-runtime.ts +422 -0
  93. package/src/input/commands/platform-services-runtime.ts +246 -0
  94. package/src/input/commands/policy-dispatch.ts +339 -0
  95. package/src/input/commands/policy.ts +17 -0
  96. package/src/input/commands/product-runtime.ts +351 -0
  97. package/src/input/commands/profile-sync-runtime.ts +99 -0
  98. package/src/input/commands/provider-accounts-runtime.ts +113 -0
  99. package/src/input/commands/provider.ts +363 -0
  100. package/src/input/commands/qrcode-runtime.ts +20 -0
  101. package/src/input/commands/quit-shared.ts +162 -0
  102. package/src/input/commands/recall-bundle.ts +132 -0
  103. package/src/input/commands/recall-capture.ts +152 -0
  104. package/src/input/commands/recall-query.ts +229 -0
  105. package/src/input/commands/recall-review.ts +98 -0
  106. package/src/input/commands/recall-shared.ts +22 -0
  107. package/src/input/commands/remote-runtime-pool.ts +106 -0
  108. package/src/input/commands/remote-runtime-setup.ts +199 -0
  109. package/src/input/commands/remote-runtime.ts +431 -0
  110. package/src/input/commands/replay-runtime.ts +18 -0
  111. package/src/input/commands/runtime-services.ts +291 -0
  112. package/src/input/commands/schedule-runtime.ts +91 -0
  113. package/src/input/commands/services-runtime.ts +209 -0
  114. package/src/input/commands/session-content.ts +408 -0
  115. package/src/input/commands/session-workflow.ts +464 -0
  116. package/src/input/commands/session.ts +375 -0
  117. package/src/input/commands/settings-sync-runtime.ts +174 -0
  118. package/src/input/commands/share-runtime.ts +119 -0
  119. package/src/input/commands/shell-core.ts +307 -0
  120. package/src/input/commands/skills-runtime.ts +221 -0
  121. package/src/input/commands/subscription-runtime.ts +434 -0
  122. package/src/input/commands/tasks-runtime.ts +230 -0
  123. package/src/input/commands/teamwork-runtime.ts +339 -0
  124. package/src/input/commands/teleport-runtime.ts +57 -0
  125. package/src/input/commands/tts-runtime.ts +29 -0
  126. package/src/input/commands/work-plan-runtime.ts +169 -0
  127. package/src/input/commands.ts +131 -0
  128. package/src/input/feed-context-factory.ts +254 -0
  129. package/src/input/file-picker.ts +192 -0
  130. package/src/input/handler-command-route.ts +180 -0
  131. package/src/input/handler-content-actions.ts +497 -0
  132. package/src/input/handler-feed-routes.ts +648 -0
  133. package/src/input/handler-feed.ts +452 -0
  134. package/src/input/handler-interactions.ts +281 -0
  135. package/src/input/handler-modal-routes.ts +418 -0
  136. package/src/input/handler-modal-stack.ts +263 -0
  137. package/src/input/handler-modal-token-routes.ts +329 -0
  138. package/src/input/handler-onboarding-cloudflare.ts +391 -0
  139. package/src/input/handler-onboarding.ts +620 -0
  140. package/src/input/handler-picker-routes.ts +472 -0
  141. package/src/input/handler-prompt-buffer.ts +320 -0
  142. package/src/input/handler-shortcuts.ts +213 -0
  143. package/src/input/handler-ui-state.ts +372 -0
  144. package/src/input/handler.ts +729 -0
  145. package/src/input/input-history.ts +297 -0
  146. package/src/input/keybindings.ts +292 -0
  147. package/src/input/mcp-workspace.ts +554 -0
  148. package/src/input/model-picker-provider-filter.ts +28 -0
  149. package/src/input/model-picker-types.ts +137 -0
  150. package/src/input/model-picker.ts +797 -0
  151. package/src/input/onboarding/handler-onboarding-routes.ts +125 -0
  152. package/src/input/onboarding/onboarding-runtime-status.ts +87 -0
  153. package/src/input/onboarding/onboarding-wizard-apply.ts +277 -0
  154. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +494 -0
  155. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +204 -0
  156. package/src/input/onboarding/onboarding-wizard-constants.ts +158 -0
  157. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +130 -0
  158. package/src/input/onboarding/onboarding-wizard-external-surfaces.ts +762 -0
  159. package/src/input/onboarding/onboarding-wizard-helpers.ts +167 -0
  160. package/src/input/onboarding/onboarding-wizard-rules.ts +256 -0
  161. package/src/input/onboarding/onboarding-wizard-state.ts +365 -0
  162. package/src/input/onboarding/onboarding-wizard-steps.ts +798 -0
  163. package/src/input/onboarding/onboarding-wizard-types.ts +195 -0
  164. package/src/input/onboarding/onboarding-wizard.ts +711 -0
  165. package/src/input/panel-integration-actions.ts +78 -0
  166. package/src/input/profile-picker-modal.ts +222 -0
  167. package/src/input/search.ts +100 -0
  168. package/src/input/selection-modal.ts +163 -0
  169. package/src/input/selection.ts +135 -0
  170. package/src/input/session-picker-modal.ts +136 -0
  171. package/src/input/settings-modal-behavior.ts +37 -0
  172. package/src/input/settings-modal-secrets.ts +41 -0
  173. package/src/input/settings-modal-subscriptions.ts +95 -0
  174. package/src/input/settings-modal-types.ts +91 -0
  175. package/src/input/settings-modal.ts +793 -0
  176. package/src/input/submission-intent.ts +17 -0
  177. package/src/input/submission-router.ts +59 -0
  178. package/src/input/tts-settings-actions.ts +100 -0
  179. package/src/main.ts +792 -0
  180. package/src/mcp/runtime-reload.ts +81 -0
  181. package/src/panels/agent-inspector-panel.ts +521 -0
  182. package/src/panels/agent-inspector-shared.ts +94 -0
  183. package/src/panels/agent-logs-panel.ts +559 -0
  184. package/src/panels/agent-logs-shared.ts +129 -0
  185. package/src/panels/approval-panel.ts +150 -0
  186. package/src/panels/automation-control-panel.ts +212 -0
  187. package/src/panels/base-panel.ts +254 -0
  188. package/src/panels/builtin/agent.ts +117 -0
  189. package/src/panels/builtin/development.ts +31 -0
  190. package/src/panels/builtin/knowledge.ts +26 -0
  191. package/src/panels/builtin/operations.ts +349 -0
  192. package/src/panels/builtin/session.ts +129 -0
  193. package/src/panels/builtin/shared.ts +274 -0
  194. package/src/panels/builtin-panels.ts +23 -0
  195. package/src/panels/cockpit-panel.ts +183 -0
  196. package/src/panels/communication-panel.ts +153 -0
  197. package/src/panels/confirm-state.ts +61 -0
  198. package/src/panels/context-visualizer-panel.ts +204 -0
  199. package/src/panels/control-plane-panel.ts +211 -0
  200. package/src/panels/cost-tracker-panel.ts +444 -0
  201. package/src/panels/debug-panel.ts +432 -0
  202. package/src/panels/diff-panel.ts +520 -0
  203. package/src/panels/docs-panel.ts +283 -0
  204. package/src/panels/eval-panel.ts +399 -0
  205. package/src/panels/file-explorer-panel.ts +584 -0
  206. package/src/panels/file-preview-panel.ts +434 -0
  207. package/src/panels/forensics-panel.ts +364 -0
  208. package/src/panels/git-panel.ts +638 -0
  209. package/src/panels/hooks-panel.ts +239 -0
  210. package/src/panels/incident-review-panel.ts +197 -0
  211. package/src/panels/index.ts +46 -0
  212. package/src/panels/intelligence-panel.ts +176 -0
  213. package/src/panels/knowledge-panel.ts +345 -0
  214. package/src/panels/local-auth-panel.ts +130 -0
  215. package/src/panels/marketplace-panel.ts +212 -0
  216. package/src/panels/memory-panel.ts +225 -0
  217. package/src/panels/ops-control-panel.ts +150 -0
  218. package/src/panels/ops-strategy-panel.ts +235 -0
  219. package/src/panels/orchestration-panel.ts +273 -0
  220. package/src/panels/panel-list-panel.ts +509 -0
  221. package/src/panels/panel-manager.ts +570 -0
  222. package/src/panels/panel-picker.ts +106 -0
  223. package/src/panels/plan-dashboard-panel.ts +274 -0
  224. package/src/panels/plugins-panel.ts +178 -0
  225. package/src/panels/policy-panel.ts +308 -0
  226. package/src/panels/polish.ts +717 -0
  227. package/src/panels/project-planning-panel.ts +711 -0
  228. package/src/panels/provider-account-snapshot.ts +259 -0
  229. package/src/panels/provider-accounts-panel.ts +218 -0
  230. package/src/panels/provider-health-domains.ts +215 -0
  231. package/src/panels/provider-health-panel.ts +727 -0
  232. package/src/panels/provider-health-tracker.ts +115 -0
  233. package/src/panels/provider-stats-panel.ts +366 -0
  234. package/src/panels/qr-panel.ts +182 -0
  235. package/src/panels/remote-panel.ts +449 -0
  236. package/src/panels/routes-panel.ts +178 -0
  237. package/src/panels/sandbox-panel.ts +283 -0
  238. package/src/panels/schedule-panel.ts +329 -0
  239. package/src/panels/scrollable-list-panel.ts +491 -0
  240. package/src/panels/search-focus.ts +32 -0
  241. package/src/panels/security-panel.ts +295 -0
  242. package/src/panels/services-panel.ts +231 -0
  243. package/src/panels/session-browser-panel.ts +400 -0
  244. package/src/panels/session-maintenance.ts +125 -0
  245. package/src/panels/settings-sync-panel.ts +120 -0
  246. package/src/panels/skills-panel.ts +431 -0
  247. package/src/panels/subscription-panel.ts +263 -0
  248. package/src/panels/symbol-outline-panel.ts +486 -0
  249. package/src/panels/system-messages-panel.ts +230 -0
  250. package/src/panels/tasks-panel.ts +399 -0
  251. package/src/panels/thinking-panel.ts +304 -0
  252. package/src/panels/token-budget-panel.ts +475 -0
  253. package/src/panels/tool-inspector-panel.ts +429 -0
  254. package/src/panels/types.ts +54 -0
  255. package/src/panels/watchers-panel.ts +193 -0
  256. package/src/panels/work-plan-panel.ts +175 -0
  257. package/src/panels/worktree-panel.ts +182 -0
  258. package/src/panels/wrfc-panel.ts +609 -0
  259. package/src/permissions/prompt.ts +165 -0
  260. package/src/planning/project-planning-coordinator.ts +543 -0
  261. package/src/plugins/loader.ts +15 -0
  262. package/src/renderer/agent-detail-modal.ts +331 -0
  263. package/src/renderer/agent-workspace.ts +238 -0
  264. package/src/renderer/ansi-sanitize.ts +76 -0
  265. package/src/renderer/autocomplete-overlay.ts +154 -0
  266. package/src/renderer/block-actions.ts +76 -0
  267. package/src/renderer/bookmark-modal.ts +101 -0
  268. package/src/renderer/bottom-bar.ts +58 -0
  269. package/src/renderer/buffer.ts +113 -0
  270. package/src/renderer/code-block.ts +373 -0
  271. package/src/renderer/compositor.ts +283 -0
  272. package/src/renderer/context-inspector.ts +219 -0
  273. package/src/renderer/conversation-layout.ts +67 -0
  274. package/src/renderer/conversation-overlays.ts +140 -0
  275. package/src/renderer/conversation-surface.ts +260 -0
  276. package/src/renderer/diff-view.ts +132 -0
  277. package/src/renderer/diff.ts +130 -0
  278. package/src/renderer/file-picker-overlay.ts +101 -0
  279. package/src/renderer/file-tree.ts +153 -0
  280. package/src/renderer/fullscreen-primitives.ts +130 -0
  281. package/src/renderer/fullscreen-workspace.ts +199 -0
  282. package/src/renderer/git-status.ts +89 -0
  283. package/src/renderer/help-overlay.ts +267 -0
  284. package/src/renderer/history-search-overlay.ts +73 -0
  285. package/src/renderer/layout-engine.ts +97 -0
  286. package/src/renderer/layout.ts +32 -0
  287. package/src/renderer/live-tail-modal.ts +156 -0
  288. package/src/renderer/markdown.ts +635 -0
  289. package/src/renderer/mcp-workspace.ts +237 -0
  290. package/src/renderer/modal-factory.ts +467 -0
  291. package/src/renderer/modal-utils.ts +24 -0
  292. package/src/renderer/model-picker-overlay.ts +473 -0
  293. package/src/renderer/model-workspace.ts +488 -0
  294. package/src/renderer/onboarding/onboarding-wizard.ts +615 -0
  295. package/src/renderer/overlay-box.ts +146 -0
  296. package/src/renderer/overlay-viewport.ts +104 -0
  297. package/src/renderer/panel-composite.ts +158 -0
  298. package/src/renderer/panel-picker-overlay.ts +202 -0
  299. package/src/renderer/panel-tab-bar.ts +69 -0
  300. package/src/renderer/panel-workspace-bar.ts +42 -0
  301. package/src/renderer/process-indicator.ts +96 -0
  302. package/src/renderer/process-modal.ts +656 -0
  303. package/src/renderer/process-summary.ts +67 -0
  304. package/src/renderer/profile-picker-modal.ts +129 -0
  305. package/src/renderer/progress.ts +98 -0
  306. package/src/renderer/qr-renderer.ts +120 -0
  307. package/src/renderer/search-overlay.ts +54 -0
  308. package/src/renderer/selection-modal-overlay.ts +214 -0
  309. package/src/renderer/semantic-diff.ts +369 -0
  310. package/src/renderer/session-picker-modal.ts +127 -0
  311. package/src/renderer/settings-modal-helpers.ts +193 -0
  312. package/src/renderer/settings-modal.ts +537 -0
  313. package/src/renderer/shell-surface.ts +88 -0
  314. package/src/renderer/status-glyphs.ts +21 -0
  315. package/src/renderer/status-token.ts +67 -0
  316. package/src/renderer/surface-layout.ts +101 -0
  317. package/src/renderer/syntax-highlighter.ts +542 -0
  318. package/src/renderer/system-message.ts +83 -0
  319. package/src/renderer/tab-strip.ts +108 -0
  320. package/src/renderer/text-layout.ts +31 -0
  321. package/src/renderer/thinking.ts +17 -0
  322. package/src/renderer/tool-call.ts +234 -0
  323. package/src/renderer/ui-factory.ts +524 -0
  324. package/src/renderer/ui-primitives.ts +96 -0
  325. package/src/runtime/bootstrap-command-context.ts +278 -0
  326. package/src/runtime/bootstrap-command-parts.ts +386 -0
  327. package/src/runtime/bootstrap-core.ts +540 -0
  328. package/src/runtime/bootstrap-hook-bridge.ts +112 -0
  329. package/src/runtime/bootstrap-shell.ts +283 -0
  330. package/src/runtime/bootstrap.ts +575 -0
  331. package/src/runtime/cloudflare-control-plane.ts +349 -0
  332. package/src/runtime/context.ts +142 -0
  333. package/src/runtime/diagnostics/panels/index.ts +24 -0
  334. package/src/runtime/diagnostics/panels/ops.ts +156 -0
  335. package/src/runtime/diagnostics/panels/panel-resources.ts +118 -0
  336. package/src/runtime/diagnostics/panels/policy.ts +177 -0
  337. package/src/runtime/index.ts +662 -0
  338. package/src/runtime/onboarding/apply.ts +642 -0
  339. package/src/runtime/onboarding/derivation.ts +534 -0
  340. package/src/runtime/onboarding/index.ts +7 -0
  341. package/src/runtime/onboarding/markers.ts +148 -0
  342. package/src/runtime/onboarding/snapshot.ts +406 -0
  343. package/src/runtime/onboarding/state.ts +141 -0
  344. package/src/runtime/onboarding/types.ts +404 -0
  345. package/src/runtime/onboarding/verify.ts +171 -0
  346. package/src/runtime/operator-token-cleanup.ts +27 -0
  347. package/src/runtime/perf/panel-contracts.ts +32 -0
  348. package/src/runtime/perf/panel-health-monitor.ts +18 -0
  349. package/src/runtime/sandbox-public-gaps.ts +358 -0
  350. package/src/runtime/services.ts +670 -0
  351. package/src/runtime/store/domains/domain-read-matrix.ts +15 -0
  352. package/src/runtime/store/domains/index.ts +222 -0
  353. package/src/runtime/store/domains/panels.ts +117 -0
  354. package/src/runtime/store/domains/ui-perf.ts +103 -0
  355. package/src/runtime/store/index.ts +305 -0
  356. package/src/runtime/store/selectors/index.ts +359 -0
  357. package/src/runtime/store/state.ts +145 -0
  358. package/src/runtime/surface-feature-flags.ts +65 -0
  359. package/src/runtime/terminal-output-guard.ts +228 -0
  360. package/src/runtime/ui/index.ts +39 -0
  361. package/src/runtime/ui/model-picker/data-provider.ts +182 -0
  362. package/src/runtime/ui/model-picker/health-enrichment.ts +228 -0
  363. package/src/runtime/ui/model-picker/index.ts +59 -0
  364. package/src/runtime/ui/model-picker/types.ts +149 -0
  365. package/src/runtime/ui/provider-health/data-provider.ts +244 -0
  366. package/src/runtime/ui/provider-health/fallback-visualizer.ts +71 -0
  367. package/src/runtime/ui/provider-health/index.ts +46 -0
  368. package/src/runtime/ui/provider-health/types.ts +146 -0
  369. package/src/runtime/ui-events.ts +1 -0
  370. package/src/runtime/ui-read-model-helpers.ts +1 -0
  371. package/src/runtime/ui-read-models-observability-maintenance.ts +1 -0
  372. package/src/runtime/ui-read-models-observability-options.ts +1 -0
  373. package/src/runtime/ui-read-models-observability-remote.ts +1 -0
  374. package/src/runtime/ui-read-models-observability-security.ts +1 -0
  375. package/src/runtime/ui-read-models-observability-system.ts +1 -0
  376. package/src/runtime/ui-read-models-observability.ts +1 -0
  377. package/src/runtime/ui-read-models.ts +61 -0
  378. package/src/runtime/ui-service-queries.ts +1 -0
  379. package/src/runtime/ui-services.ts +190 -0
  380. package/src/scripts/process-messages.ts +42 -0
  381. package/src/shell/blocking-input.ts +98 -0
  382. package/src/shell/service-settings-sync.ts +273 -0
  383. package/src/shell/ui-openers.ts +352 -0
  384. package/src/tools/index.ts +1 -0
  385. package/src/tools/wrfc-agent-guard.ts +49 -0
  386. package/src/types/grid.ts +48 -0
  387. package/src/types/sql-js.d.ts +15 -0
  388. package/src/utils/clipboard.ts +22 -0
  389. package/src/utils/splash-lines.ts +46 -0
  390. package/src/utils/terminal-width.ts +185 -0
  391. package/src/verification/live-verifier.ts +430 -0
  392. package/src/verification/verification-ledger.ts +242 -0
  393. package/src/version.ts +17 -0
  394. package/src/widget/index.ts +2 -0
  395. package/src/widget/types.ts +9 -0
  396. package/src/widget/widget.ts +8 -0
  397. package/src/work-plans/work-plan-store.ts +374 -0
  398. package/tsconfig.json +18 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * MemoryPanel — project memory substrate TUI panel.
3
+ *
4
+ * Migrated to SearchableListPanel<MemoryRecord> (Wave B1).
5
+ */
6
+
7
+ import type { Line } from '../types/grid.ts';
8
+ import type { MemoryRegistry } from '@pellux/goodvibes-sdk/platform/state';
9
+ import type { MemoryRecord, MemoryClass } from '@pellux/goodvibes-sdk/platform/state';
10
+ import { SearchableListPanel } from './scrollable-list-panel.ts';
11
+ import {
12
+ buildBodyText,
13
+ buildGuidanceLine,
14
+ buildKeyValueLine,
15
+ buildPanelLine,
16
+ extendPalette,
17
+ DEFAULT_PANEL_PALETTE,
18
+ } from './polish.ts';
19
+ import {
20
+ getPanelSearchFocusTransition,
21
+ isPanelSearchCancel,
22
+ } from './search-focus.ts';
23
+
24
+ const C = extendPalette(DEFAULT_PANEL_PALETTE, {
25
+ header: '#94a3b8',
26
+ headerBg: '#1e293b',
27
+ decision: '#38bdf8',
28
+ constraint: '#f97316',
29
+ incident: '#ef4444',
30
+ pattern: '#a78bfa',
31
+ fact: '#22c55e',
32
+ risk: '#f43f5e',
33
+ runbook: '#eab308',
34
+ architecture: '#60a5fa',
35
+ ownership: '#14b8a6',
36
+ selected: '#1e3a5f',
37
+ searchBg: '#0f172a',
38
+ searchFg: '#e2e8f0',
39
+ });
40
+
41
+ function fmtTime(ts: number): string {
42
+ const d = new Date(ts);
43
+ return d.toISOString().slice(0, 16).replace('T', ' ');
44
+ }
45
+
46
+ function classColor(cls: MemoryClass): string {
47
+ switch (cls) {
48
+ case 'decision': return C.decision;
49
+ case 'constraint': return C.constraint;
50
+ case 'incident': return C.incident;
51
+ case 'pattern': return C.pattern;
52
+ case 'fact': return C.fact;
53
+ case 'risk': return C.risk;
54
+ case 'runbook': return C.runbook;
55
+ case 'architecture': return C.architecture;
56
+ case 'ownership': return C.ownership;
57
+ }
58
+ }
59
+
60
+ export class MemoryPanel extends SearchableListPanel<MemoryRecord> {
61
+ private registry: MemoryRegistry;
62
+ private filterFocused = false;
63
+ private unsubscribe?: () => void;
64
+
65
+ constructor(registry: MemoryRegistry) {
66
+ super('memory', 'Memory', 'M', 'agent');
67
+ this.registry = registry;
68
+ }
69
+
70
+ onActivate(): void {
71
+ super.onActivate();
72
+ this.searchQuery = '';
73
+ this.invalidateFilter();
74
+ this.filterFocused = false;
75
+ this.unsubscribe = this.registry.subscribe(() => {
76
+ this.invalidateFilter();
77
+ this.markDirty();
78
+ });
79
+ }
80
+
81
+ onDeactivate(): void {
82
+ super.onDeactivate();
83
+ }
84
+
85
+ onDestroy(): void {
86
+ this.unsubscribe?.();
87
+ this.unsubscribe = undefined;
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // SearchableListPanel implementation
92
+ // ---------------------------------------------------------------------------
93
+
94
+ protected getAllItems(): readonly MemoryRecord[] {
95
+ return this.registry.search({ limit: 100 });
96
+ }
97
+
98
+ protected matchesSearch(record: MemoryRecord, query: string): boolean {
99
+ const q = query.trim().toLowerCase();
100
+ if (!q) return true;
101
+ const haystack = [
102
+ record.summary,
103
+ record.detail ?? '',
104
+ record.cls,
105
+ record.scope,
106
+ record.tags.join(' '),
107
+ ].join(' ').toLowerCase();
108
+ return haystack.includes(q);
109
+ }
110
+
111
+ protected renderItem(record: MemoryRecord, index: number, selected: boolean, width: number): Line {
112
+ const bg = selected ? C.selected : undefined;
113
+ return buildPanelLine(width, [
114
+ [' ', C.label, bg],
115
+ [`[${record.scope.slice(0, 1).toUpperCase()}/${record.cls.slice(0, 3).toUpperCase()}] `, classColor(record.cls), bg],
116
+ [record.id.slice(-8), C.dim, bg],
117
+ [' ', C.label, bg],
118
+ [fmtTime(record.createdAt), C.dim, bg],
119
+ [' ', C.label, bg],
120
+ [record.summary.slice(0, Math.max(0, width - 33)), C.value, bg],
121
+ ]);
122
+ }
123
+
124
+ protected override getPalette() { return C; }
125
+ protected override getEmptyStateMessage() {
126
+ return this.searchQuery
127
+ ? ` No records matching "${this.searchQuery}"`
128
+ : ' No memory records. Use /recall add <class> <summary> to create one.';
129
+ }
130
+ protected override getEmptyStateActions() {
131
+ return [
132
+ { command: '/recall add fact <summary>', summary: 'capture a durable fact directly' },
133
+ { command: '/recall capture incident latest', summary: 'promote the latest incident into memory' },
134
+ ];
135
+ }
136
+
137
+ handleInput(key: string): boolean {
138
+ // Filter-focus mode: typing goes into the search query
139
+ if (this.filterFocused) {
140
+ const items = this.getItems();
141
+ const transition = getPanelSearchFocusTransition(key, { selectedIndex: this.selectedIndex, itemCount: items.length });
142
+ if (transition === 'focus-list') {
143
+ this.filterFocused = false;
144
+ this.markDirty();
145
+ return true;
146
+ }
147
+ if (isPanelSearchCancel(key)) {
148
+ this.filterFocused = false;
149
+ return super.handleInput(key);
150
+ }
151
+ return super.handleInput(key);
152
+ }
153
+
154
+ const items = this.getItems();
155
+ const transition = getPanelSearchFocusTransition(key, { selectedIndex: this.selectedIndex, itemCount: items.length });
156
+ if (transition === 'focus-search') {
157
+ this.filterFocused = true;
158
+ this.markDirty();
159
+ return true;
160
+ }
161
+
162
+ if (key === 'r') {
163
+ this.invalidateFilter();
164
+ this.markDirty();
165
+ return true;
166
+ }
167
+
168
+ return super.handleInput(key);
169
+ }
170
+
171
+ render(width: number, height: number): Line[] {
172
+ this.clampSelection();
173
+ const intro = 'Durable project memory across decisions, constraints, incidents, patterns, risks, runbooks, and related provenance.';
174
+
175
+ const records = this.getItems();
176
+ const byClass = new Map<MemoryClass, number>();
177
+ for (const record of records) {
178
+ byClass.set(record.cls, (byClass.get(record.cls) ?? 0) + 1);
179
+ }
180
+
181
+ const filterLine = this.buildFilterInputLine(width, 'Filter', this.filterFocused);
182
+
183
+ const summaryLines: Line[] = [
184
+ buildKeyValueLine(width, [
185
+ { label: 'records', value: String(records.length), valueColor: C.value },
186
+ { label: 'facts', value: String(byClass.get('fact') ?? 0), valueColor: C.fact },
187
+ { label: 'decisions', value: String(byClass.get('decision') ?? 0), valueColor: C.decision },
188
+ { label: 'incidents', value: String(byClass.get('incident') ?? 0), valueColor: C.incident },
189
+ { label: 'runbooks', value: String(byClass.get('runbook') ?? 0), valueColor: C.runbook },
190
+ ], C),
191
+ filterLine,
192
+ buildGuidanceLine(width, '/recall review', 'review durable knowledge and queue posture from the command surface', C),
193
+ ];
194
+
195
+ const selected = records[this.selectedIndex];
196
+ const selectedLines: Line[] = [];
197
+ if (selected) {
198
+ selectedLines.push(buildKeyValueLine(width, [
199
+ { label: 'scope', value: selected.scope, valueColor: C.info },
200
+ { label: 'class', value: selected.cls, valueColor: classColor(selected.cls) },
201
+ { label: 'created', value: fmtTime(selected.createdAt), valueColor: C.dim },
202
+ ], C));
203
+ selectedLines.push(...buildBodyText(width, selected.summary, C, C.value));
204
+ if (selected.detail) selectedLines.push(...buildBodyText(width, `Detail: ${selected.detail}`, C, C.dim));
205
+ if (selected.tags.length) selectedLines.push(buildPanelLine(width, [[` Tags: ${selected.tags.join(', ')}`, C.good]]));
206
+ if (selected.provenance.length) {
207
+ selectedLines.push(...buildBodyText(
208
+ width,
209
+ `Provenance: ${selected.provenance.map((p) => `${p.kind}:${p.ref}`).join(' ')}`,
210
+ C,
211
+ C.dim,
212
+ ));
213
+ }
214
+ }
215
+
216
+ return this.renderList(width, height, {
217
+ title: 'Memory',
218
+ header: summaryLines,
219
+ footer: [
220
+ ...selectedLines,
221
+ buildPanelLine(width, [[' / search j/k or Up/Down move r reload Esc clear search', C.dim]]),
222
+ ],
223
+ });
224
+ }
225
+ }
@@ -0,0 +1,150 @@
1
+ /**
2
+ * OpsControlPanel — operator control plane UI panel.
3
+ *
4
+ * Renders the ops audit log sourced from the OpsPanel diagnostics subscriber.
5
+ * Each entry shows: seq, timestamp, action, target, outcome, and optional note.
6
+ *
7
+ * Requires the `operator-control-plane` feature flag to be enabled.
8
+ * Open via Ctrl+O keybind or `/ops view` command.
9
+ */
10
+ import type { Line } from '../types/grid.ts';
11
+ import type { OpsEvent } from '@/runtime/index.ts';
12
+ import type { UiEventFeed } from '../runtime/ui-events.ts';
13
+ import type { OpsAuditEntry } from '../runtime/diagnostics/panels/ops.ts';
14
+ import { OpsPanel } from '../runtime/diagnostics/panels/ops.ts';
15
+ import { ScrollableListPanel } from './scrollable-list-panel.ts';
16
+ import {
17
+ buildPanelLine,
18
+ DEFAULT_PANEL_PALETTE,
19
+ type PanelPalette,
20
+ } from './polish.ts';
21
+
22
+ // ── Colour palette ──────────────────────────────────────────────────────────
23
+ const C = {
24
+ ...DEFAULT_PANEL_PALETTE,
25
+ header: '#94a3b8',
26
+ headerBg: '#1e293b',
27
+ success: '#22c55e',
28
+ rejected: '#f97316',
29
+ error: '#ef4444',
30
+ dim: '#4b5563',
31
+ label: '#64748b',
32
+ value: '#cbd5e1',
33
+ note: '#eab308',
34
+ seq: '#475569',
35
+ taskColor: '#22d3ee',
36
+ agentColor: '#a78bfa',
37
+ empty: '#334155',
38
+ selectBg: '#0f172a',
39
+ } as const;
40
+
41
+ // ── Helpers ──────────────────────────────────────────────────────────────────
42
+
43
+ function fmtTime(ts: number): string {
44
+ const d = new Date(ts);
45
+ const hh = String(d.getHours()).padStart(2, '0');
46
+ const mm = String(d.getMinutes()).padStart(2, '0');
47
+ const ss = String(d.getSeconds()).padStart(2, '0');
48
+ return `${hh}:${mm}:${ss}`;
49
+ }
50
+
51
+ function outcomeColor(outcome: OpsAuditEntry['outcome']): string {
52
+ switch (outcome) {
53
+ case 'success': return C.success;
54
+ case 'rejected': return C.rejected;
55
+ case 'error': return C.error;
56
+ }
57
+ }
58
+
59
+ function outcomeLabel(outcome: OpsAuditEntry['outcome']): string {
60
+ switch (outcome) {
61
+ case 'success': return 'OK ';
62
+ case 'rejected': return 'REJECT';
63
+ case 'error': return 'ERR ';
64
+ }
65
+ }
66
+
67
+ function targetColor(kind: OpsAuditEntry['targetKind']): string {
68
+ return kind === 'task' ? C.taskColor : C.agentColor;
69
+ }
70
+
71
+ // ── OpsControlPanel ──────────────────────────────────────────────────────────
72
+
73
+ export class OpsControlPanel extends ScrollableListPanel<OpsAuditEntry> {
74
+ private readonly _opsPanel: OpsPanel;
75
+ private _unsub: (() => void) | null = null;
76
+
77
+ public constructor(eventFeed: UiEventFeed<OpsEvent>) {
78
+ super('ops-control', 'Ops Control', 'Q', 'agent');
79
+ this.showSelectionGutter = true; // I5: non-color selection affordance
80
+ this._opsPanel = new OpsPanel(eventFeed);
81
+ this._unsub = this._opsPanel.subscribe(() => this.markDirty());
82
+ }
83
+
84
+ public override onActivate(): void {
85
+ super.onActivate();
86
+ this.selectedIndex = 0;
87
+ }
88
+
89
+ public override onDestroy(): void {
90
+ if (this._unsub) {
91
+ this._unsub();
92
+ this._unsub = null;
93
+ }
94
+ this._opsPanel.dispose();
95
+ }
96
+
97
+ protected override getPalette(): PanelPalette {
98
+ return C;
99
+ }
100
+
101
+ protected getItems(): readonly OpsAuditEntry[] {
102
+ // Return reversed so newest entries appear at top
103
+ return [...this._opsPanel.getSnapshot()].reverse();
104
+ }
105
+
106
+ protected renderItem(entry: OpsAuditEntry, _index: number, _selected: boolean, width: number): Line {
107
+ const seqStr = String(entry.seq).padStart(4, ' ');
108
+ const timeStr = fmtTime(entry.ts);
109
+ const action = entry.action.slice(0, 15).padEnd(15, ' ');
110
+ const kindTag = entry.targetKind === 'task' ? 'T:' : 'A:';
111
+ // Truncation is intentional: TUI column width limits target ID display to 14 chars
112
+ const shortId = entry.targetId.slice(-10);
113
+ const target = (kindTag + shortId).slice(0, 14).padEnd(14, ' ');
114
+ const outLabel = outcomeLabel(entry.outcome);
115
+ const noteRaw = (entry.note ?? entry.errorMessage ?? '').slice(0, Math.max(0, width - 63));
116
+
117
+ const segs: Array<[string, string, string?]> = [
118
+ [` ${seqStr} `, C.seq],
119
+ [`${timeStr} `, C.dim],
120
+ [`${action} `, C.value],
121
+ [`${target} `, targetColor(entry.targetKind)],
122
+ [outLabel, outcomeColor(entry.outcome)],
123
+ ];
124
+ if (noteRaw) segs.push([` ${noteRaw}`, C.note]);
125
+ return buildPanelLine(width, segs);
126
+ }
127
+
128
+ protected override getEmptyStateMessage(): string {
129
+ return ' No operator interventions recorded.';
130
+ }
131
+
132
+ protected override getEmptyStateActions(): Array<{ command: string; summary: string }> {
133
+ return [{ command: '/cockpit', summary: 'open the cockpit and drive runtime interventions from the control rooms' }];
134
+ }
135
+
136
+ public render(width: number, height: number): Line[] {
137
+ const headerLines: Line[] = [
138
+ buildPanelLine(width, [[' SEQ TIME ACTION TARGET OUT NOTE', C.label]]),
139
+ ];
140
+ const footerLines: Line[] = [
141
+ buildPanelLine(width, [[' Up/Down scroll the intervention log', C.dim]]),
142
+ ];
143
+
144
+ return this.renderList(width, height, {
145
+ title: 'Operator Control Plane',
146
+ header: headerLines,
147
+ footer: footerLines,
148
+ });
149
+ }
150
+ }
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Ops Strategy Timeline Panel.
3
+ *
4
+ * Renders the Adaptive Execution Planner state: current strategy, reason
5
+ * code, mode, override status, and a scrollable history of past decisions.
6
+ *
7
+ * Registered as panel id 'ops' in builtin-panels.
8
+ */
9
+
10
+ import { BasePanel } from './base-panel.ts';
11
+ import type { Line } from '../types/grid.ts';
12
+ import type { PlannerDecision, ExecutionStrategy } from '@pellux/goodvibes-sdk/platform/core';
13
+ import type { PlannerEvent } from '@/runtime/index.ts';
14
+ import type { UiEventFeed } from '../runtime/ui-events.ts';
15
+ import type { OpsStrategyQuery } from '../runtime/ui-service-queries.ts';
16
+ import {
17
+ buildEmptyState,
18
+ buildPanelLine,
19
+ buildStyledPanelLine,
20
+ buildPanelWorkspace,
21
+ resolveScrollablePanelSection,
22
+ DEFAULT_PANEL_PALETTE,
23
+ } from './polish.ts';
24
+
25
+ const STRATEGY_FG: Record<ExecutionStrategy, string> = {
26
+ auto: '#00cccc',
27
+ single: '#00cc66',
28
+ cohort: '#cccc00',
29
+ background: '#cc66cc',
30
+ remote: '#cccccc',
31
+ };
32
+
33
+ const STRATEGY_ICON: Record<ExecutionStrategy, string> = {
34
+ auto: '~',
35
+ single: '▸',
36
+ cohort: '◆',
37
+ background: '.',
38
+ remote: '▸',
39
+ };
40
+
41
+ // ---------------------------------------------------------------------------
42
+ // OpsStrategyPanel
43
+ // ---------------------------------------------------------------------------
44
+
45
+ export class OpsStrategyPanel extends BasePanel {
46
+ private unsubscribers: Array<() => void> = [];
47
+ private scrollOffset = 0;
48
+ private history: PlannerDecision[] = [];
49
+ private readonly adaptivePlanner: OpsStrategyQuery;
50
+
51
+ constructor(
52
+ private readonly plannerEvents: UiEventFeed<PlannerEvent>,
53
+ adaptivePlanner: OpsStrategyQuery,
54
+ ) {
55
+ super('ops', 'Ops', 'O', 'agent');
56
+ this.adaptivePlanner = adaptivePlanner;
57
+ }
58
+
59
+ override onActivate(): void {
60
+ super.onActivate();
61
+ this._syncHistory();
62
+ this.unsubscribers.push(
63
+ this.plannerEvents.on('PLAN_STRATEGY_SELECTED', () => {
64
+ this._syncHistory();
65
+ this.markDirty();
66
+ }),
67
+ this.plannerEvents.on('PLAN_STRATEGY_OVERRIDDEN', () => {
68
+ this._syncHistory();
69
+ this.markDirty();
70
+ }),
71
+ );
72
+ }
73
+
74
+ override onDestroy(): void {
75
+ for (const unsub of this.unsubscribers) unsub();
76
+ this.unsubscribers = [];
77
+ }
78
+
79
+ handleInput(key: string): boolean {
80
+ if (key === 'up' || key === 'k') {
81
+ this.scrollOffset = Math.max(0, this.scrollOffset - 1);
82
+ this.markDirty();
83
+ return true;
84
+ }
85
+ if (key === 'down' || key === 'j') {
86
+ this.scrollOffset++;
87
+ this.markDirty();
88
+ return true;
89
+ }
90
+ if (key === 'g') {
91
+ this.scrollOffset = 0;
92
+ this.markDirty();
93
+ return true;
94
+ }
95
+ if (key === 'G') {
96
+ this.scrollOffset = Math.max(0, this.history.length - 1);
97
+ this.markDirty();
98
+ return true;
99
+ }
100
+ return false;
101
+ }
102
+
103
+ render(width: number, height: number): Line[] {
104
+ const latest = this.adaptivePlanner.getLatest();
105
+ const mode = this.adaptivePlanner.getMode();
106
+ const override = this.adaptivePlanner.getOverride();
107
+ const statusLines: Line[] = [
108
+ buildPanelLine(width, [
109
+ [' Mode ', DEFAULT_PANEL_PALETTE.label],
110
+ [mode.toUpperCase(), DEFAULT_PANEL_PALETTE.value],
111
+ [' Override ', DEFAULT_PANEL_PALETTE.label],
112
+ [override ? `${override.toUpperCase()} [ACTIVE]` : 'none', override ? DEFAULT_PANEL_PALETTE.warn : DEFAULT_PANEL_PALETTE.dim],
113
+ ]),
114
+ ];
115
+
116
+ if (latest) {
117
+ statusLines.push(buildPanelLine(width, [
118
+ [' Last ', DEFAULT_PANEL_PALETTE.label],
119
+ [`${STRATEGY_ICON[latest.selected]} ${latest.selected.toUpperCase()}`, STRATEGY_FG[latest.selected]],
120
+ [' Reason ', DEFAULT_PANEL_PALETTE.label],
121
+ [latest.reasonCode, DEFAULT_PANEL_PALETTE.dim],
122
+ ]));
123
+ }
124
+
125
+ if (this.history.length === 0) {
126
+ return buildPanelWorkspace(width, height, {
127
+ title: ' Ops Strategy',
128
+ intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
129
+ sections: [
130
+ { title: 'Status', lines: statusLines },
131
+ {
132
+ lines: buildEmptyState(
133
+ width,
134
+ ' No decisions recorded yet',
135
+ 'Adaptive planner decisions appear here once the planner begins selecting strategies.',
136
+ [],
137
+ DEFAULT_PANEL_PALETTE,
138
+ ),
139
+ },
140
+ ],
141
+ footerLines: [
142
+ buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
143
+ ],
144
+ palette: DEFAULT_PANEL_PALETTE,
145
+ });
146
+ }
147
+
148
+ const historyLines = this._renderHistory(width);
149
+ const statusSection = { title: 'Status', lines: statusLines } as const;
150
+ const historySection = resolveScrollablePanelSection(width, height, {
151
+ intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
152
+ footerLines: [
153
+ buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
154
+ ],
155
+ palette: DEFAULT_PANEL_PALETTE,
156
+ beforeSections: [statusSection],
157
+ section: {
158
+ title: 'Decision History',
159
+ scrollableLines: historyLines,
160
+ scrollOffset: Math.min(this.scrollOffset, Math.max(0, historyLines.length - 1)),
161
+ minRows: 8,
162
+ },
163
+ });
164
+ this.scrollOffset = historySection.scrollOffset;
165
+
166
+ return buildPanelWorkspace(width, height, {
167
+ title: ' Ops Strategy',
168
+ intro: 'Review adaptive execution planner decisions, overrides, and recent strategy history.',
169
+ sections: [
170
+ statusSection,
171
+ historySection.section,
172
+ ],
173
+ footerLines: [
174
+ buildPanelLine(width, [[' Up/Down', DEFAULT_PANEL_PALETTE.info], [' scroll history', DEFAULT_PANEL_PALETTE.dim], [' g/G', DEFAULT_PANEL_PALETTE.info], [' top/bottom', DEFAULT_PANEL_PALETTE.dim]]),
175
+ ],
176
+ palette: DEFAULT_PANEL_PALETTE,
177
+ });
178
+ }
179
+
180
+ // -------------------------------------------------------------------------
181
+ // Private helpers
182
+ // -------------------------------------------------------------------------
183
+
184
+ private _syncHistory(): void {
185
+ this.history = this.adaptivePlanner.getHistory(50);
186
+ }
187
+
188
+ private _renderHistory(width: number): Line[] {
189
+ if (this.history.length === 0) {
190
+ return [buildStyledPanelLine(width, [{ text: ' No history yet.', fg: DEFAULT_PANEL_PALETTE.dim, dim: true }])];
191
+ }
192
+
193
+ const lines: Line[] = [];
194
+ lines.push(buildStyledPanelLine(width, [{ text: ' Decision History', fg: DEFAULT_PANEL_PALETTE.value, bold: true }]));
195
+
196
+ const reversed = [...this.history].reverse();
197
+ for (let i = 0; i < reversed.length; i++) {
198
+ const d = reversed[i]!;
199
+ const ts = new Date(d.timestamp).toLocaleTimeString();
200
+ const fg = STRATEGY_FG[d.selected];
201
+ const icon = STRATEGY_ICON[d.selected];
202
+ const num = String(i + 1).padStart(3);
203
+ const overrideMark = d.overrideActive ? ' [O]' : '';
204
+
205
+ // Row 1: index + icon + strategy + timestamp (right-aligned)
206
+ const leftBase = ` ${num}. ${icon} ${d.selected.toUpperCase()}${overrideMark}`;
207
+ const rightText = ` ${ts}`;
208
+ const pad = Math.max(1, width - leftBase.length - rightText.length);
209
+ lines.push(buildStyledPanelLine(width, [
210
+ { text: ` ${num}. `, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
211
+ { text: `${icon} ${d.selected.toUpperCase()}`, fg, bold: true },
212
+ { text: overrideMark, fg: DEFAULT_PANEL_PALETTE.warn },
213
+ { text: ' '.repeat(pad), fg: DEFAULT_PANEL_PALETTE.dim },
214
+ { text: rightText, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
215
+ ]));
216
+
217
+ // Row 2: reason code
218
+ lines.push(buildStyledPanelLine(width, [{ text: ` ${d.reasonCode}`, fg: DEFAULT_PANEL_PALETTE.dim, dim: true }]));
219
+
220
+ // Row 3+: top-2 scored candidates (auto mode only)
221
+ if (!d.overrideActive && d.candidates.length > 1) {
222
+ const top2 = d.candidates.slice(0, 2);
223
+ for (const c of top2) {
224
+ lines.push(buildStyledPanelLine(width, [
225
+ { text: ' ', fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
226
+ { text: c.strategy.padEnd(12), fg: STRATEGY_FG[c.strategy] },
227
+ { text: ` score ${c.score}`, fg: DEFAULT_PANEL_PALETTE.dim, dim: true },
228
+ ]));
229
+ }
230
+ }
231
+ }
232
+
233
+ return lines;
234
+ }
235
+ }