bosun 0.42.6 → 0.43.1

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 (426) hide show
  1. package/.env.example +26 -15
  2. package/README.md +1 -11
  3. package/agent/agent-event-bus.mjs +33 -2
  4. package/agent/agent-hooks.mjs +4 -52
  5. package/agent/agent-launcher.mjs +6210 -0
  6. package/agent/agent-pool.mjs +7 -4182
  7. package/agent/agent-prompt-catalog.mjs +3 -4
  8. package/agent/agent-sdk.mjs +1 -4
  9. package/agent/agent-supervisor.mjs +30 -6
  10. package/agent/auth/_shared.mjs +129 -0
  11. package/agent/auth/anthropic-api-key.mjs +13 -0
  12. package/agent/auth/azure-openai.mjs +17 -0
  13. package/agent/auth/cerebras.mjs +14 -0
  14. package/agent/auth/chatgpt-codex-subscription.mjs +15 -0
  15. package/agent/auth/claude-subscription.mjs +15 -0
  16. package/agent/auth/copilot-oauth.mjs +13 -0
  17. package/agent/auth/deepinfra.mjs +14 -0
  18. package/agent/auth/fireworks.mjs +14 -0
  19. package/agent/auth/gemini-api-key.mjs +14 -0
  20. package/agent/auth/groq.mjs +14 -0
  21. package/agent/auth/index.mjs +85 -0
  22. package/agent/auth/nebius.mjs +14 -0
  23. package/agent/auth/ollama.mjs +14 -0
  24. package/agent/auth/openai-api-key.mjs +13 -0
  25. package/agent/auth/openai-compatible.mjs +15 -0
  26. package/agent/auth/openrouter.mjs +14 -0
  27. package/agent/auth/perplexity.mjs +14 -0
  28. package/agent/auth/sambanova.mjs +14 -0
  29. package/agent/auth/together.mjs +14 -0
  30. package/agent/auth/xai.mjs +14 -0
  31. package/agent/bosun-skills.mjs +177 -68
  32. package/agent/fleet-coordinator.mjs +153 -37
  33. package/agent/harness/agent-loop.mjs +26 -0
  34. package/agent/harness/event-contract.mjs +125 -0
  35. package/agent/harness/followup-queue.mjs +33 -0
  36. package/agent/harness/message-normalizer.mjs +43 -0
  37. package/agent/harness/module-boundaries.md +73 -0
  38. package/agent/harness/run-contract.mjs +122 -0
  39. package/agent/harness/runtime-config.mjs +132 -0
  40. package/agent/harness/session-state.mjs +80 -0
  41. package/agent/harness/steering-queue.mjs +35 -0
  42. package/agent/harness/tool-runner.mjs +95 -0
  43. package/agent/harness/turn-runner.mjs +135 -0
  44. package/agent/harness-agent-service.mjs +852 -0
  45. package/agent/harness-executor-config.mjs +384 -0
  46. package/agent/hook-library.mjs +139 -0
  47. package/agent/hook-profiles.mjs +46 -108
  48. package/agent/internal-harness-control-plane.mjs +672 -0
  49. package/agent/internal-harness-profile.mjs +519 -0
  50. package/agent/internal-harness-runtime.mjs +1219 -0
  51. package/agent/lineage-graph.mjs +141 -0
  52. package/agent/primary-agent.mjs +593 -646
  53. package/agent/provider-auth-manager.mjs +830 -0
  54. package/agent/provider-auth-state.mjs +440 -0
  55. package/agent/provider-capabilities.mjs +116 -0
  56. package/agent/provider-kernel.mjs +596 -0
  57. package/agent/provider-message-transform.mjs +583 -0
  58. package/agent/provider-model-catalog.mjs +163 -0
  59. package/agent/provider-registry.mjs +657 -0
  60. package/agent/provider-runtime-discovery.mjs +147 -0
  61. package/agent/provider-session.mjs +767 -0
  62. package/agent/providers/_shared.mjs +397 -0
  63. package/agent/providers/anthropic-messages.mjs +64 -0
  64. package/agent/providers/azure-openai-responses.mjs +69 -0
  65. package/agent/providers/cerebras.mjs +66 -0
  66. package/agent/providers/claude-subscription-shim.mjs +68 -0
  67. package/agent/providers/copilot-oauth.mjs +66 -0
  68. package/agent/providers/deepinfra.mjs +66 -0
  69. package/agent/providers/fireworks.mjs +66 -0
  70. package/agent/providers/gemini-generate-content.mjs +66 -0
  71. package/agent/providers/groq.mjs +66 -0
  72. package/agent/providers/index.mjs +208 -0
  73. package/agent/providers/nebius.mjs +66 -0
  74. package/agent/providers/ollama.mjs +66 -0
  75. package/agent/providers/openai-codex-subscription.mjs +75 -0
  76. package/agent/providers/openai-compatible.mjs +65 -0
  77. package/agent/providers/openai-responses.mjs +67 -0
  78. package/agent/providers/openrouter.mjs +66 -0
  79. package/agent/providers/perplexity.mjs +66 -0
  80. package/agent/providers/provider-contract.mjs +138 -0
  81. package/agent/providers/provider-errors.mjs +63 -0
  82. package/agent/providers/provider-model-pricing.mjs +246 -0
  83. package/agent/providers/provider-stream-normalizer.mjs +7 -0
  84. package/agent/providers/provider-usage-normalizer.mjs +48 -0
  85. package/agent/providers/sambanova.mjs +66 -0
  86. package/agent/providers/together.mjs +66 -0
  87. package/agent/providers/xai.mjs +66 -0
  88. package/agent/query-engine.mjs +260 -0
  89. package/agent/retry-queue.mjs +1 -0
  90. package/agent/session-contract.mjs +127 -0
  91. package/agent/session-manager.mjs +1859 -0
  92. package/agent/session-replay.mjs +617 -0
  93. package/agent/session-snapshot-store.mjs +379 -0
  94. package/agent/skills/agent-coordination.md +6 -0
  95. package/agent/skills/background-task-execution.md +6 -0
  96. package/agent/skills/bosun-agent-api.md +6 -0
  97. package/agent/skills/code-quality-anti-patterns.md +7 -0
  98. package/agent/skills/commit-conventions.md +6 -0
  99. package/agent/skills/custom-tool-creation.md +6 -0
  100. package/agent/skills/error-recovery.md +6 -0
  101. package/agent/skills/pr-workflow.md +6 -0
  102. package/agent/skills/tdd-pattern.md +6 -0
  103. package/agent/subagent-contract.mjs +104 -0
  104. package/agent/subagent-control.mjs +633 -0
  105. package/agent/subagent-pool.mjs +260 -0
  106. package/agent/thread-contract.mjs +88 -0
  107. package/agent/thread-registry.mjs +552 -0
  108. package/agent/tool-approval-manager.mjs +259 -0
  109. package/agent/tool-builtin-catalog.mjs +855 -0
  110. package/agent/tool-contract.mjs +101 -0
  111. package/agent/tool-event-contract.mjs +99 -0
  112. package/agent/tool-execution-ledger.mjs +32 -0
  113. package/agent/tool-network-policy.mjs +86 -0
  114. package/agent/tool-orchestrator.mjs +382 -0
  115. package/agent/tool-output-truncation.mjs +70 -0
  116. package/agent/tool-registry.mjs +200 -0
  117. package/agent/tool-retry-policy.mjs +57 -0
  118. package/agent/tool-runtime-context.mjs +220 -0
  119. package/bench/harness-load-bench.mjs +281 -0
  120. package/bench/harness-parity-bench.mjs +214 -0
  121. package/bench/swebench/bosun-swebench.mjs +1 -0
  122. package/bosun-tui.mjs +59 -13
  123. package/bosun.schema.json +358 -1
  124. package/cli.mjs +641 -190
  125. package/config/config-doctor.mjs +78 -4
  126. package/config/config-editor.mjs +417 -0
  127. package/config/config.mjs +217 -117
  128. package/config/repo-config.mjs +78 -10
  129. package/config/repo-root.mjs +33 -1
  130. package/desktop/main.mjs +438 -99
  131. package/desktop/package.json +1 -1
  132. package/git/diff-stats.mjs +7 -5
  133. package/infra/anomaly-detector.mjs +115 -15
  134. package/infra/approval-projection-store.mjs +75 -0
  135. package/infra/config-reload-bus.mjs +33 -0
  136. package/infra/container-runner.mjs +37 -3
  137. package/infra/error-detector.mjs +109 -34
  138. package/infra/event-schema.mjs +353 -0
  139. package/infra/guardrails.mjs +383 -0
  140. package/infra/heartbeat-monitor.mjs +432 -0
  141. package/infra/library-manager.mjs +359 -233
  142. package/infra/live-event-projector.mjs +197 -0
  143. package/infra/maintenance.mjs +176 -37
  144. package/infra/monitor.mjs +1292 -194
  145. package/infra/preflight.mjs +71 -5
  146. package/infra/presence.mjs +33 -9
  147. package/infra/projection-contract.mjs +27 -0
  148. package/infra/provider-usage-ledger.mjs +73 -0
  149. package/infra/replay-reader.mjs +140 -0
  150. package/infra/runtime-accumulator.mjs +303 -8
  151. package/infra/runtime-metrics.mjs +156 -0
  152. package/infra/session-projection-store.mjs +169 -0
  153. package/infra/session-telemetry-runtime.mjs +580 -0
  154. package/infra/session-telemetry.mjs +338 -0
  155. package/infra/session-tracker.mjs +1407 -174
  156. package/infra/startup-service.mjs +0 -2
  157. package/infra/storage-janitor.mjs +1046 -0
  158. package/infra/subagent-projection-store.mjs +89 -0
  159. package/infra/test-runtime.mjs +53 -20
  160. package/infra/trace-export.mjs +103 -0
  161. package/infra/tracing.mjs +13 -125
  162. package/infra/tui-bridge.mjs +607 -5
  163. package/infra/update-check.mjs +7 -8
  164. package/infra/windows-hidden-child-processes.mjs +99 -0
  165. package/infra/worktree-recovery-state.mjs +15 -5
  166. package/kanban/kanban-adapter.mjs +674 -41
  167. package/kanban/repo-mirror-projection-store.mjs +871 -0
  168. package/lib/agent-configuration-guide.mjs +280 -0
  169. package/lib/hot-path-runtime.mjs +1061 -0
  170. package/lib/integrations-registry.mjs +294 -0
  171. package/lib/log-tail.mjs +101 -0
  172. package/lib/mojibake-repair.mjs +40 -0
  173. package/lib/repo-map.mjs +137 -24
  174. package/lib/request-json-api.mjs +59 -0
  175. package/lib/safe-box.mjs +56 -0
  176. package/lib/session-insights.mjs +3 -65
  177. package/lib/state-ledger-sqlite.mjs +4462 -0
  178. package/lib/vault-keychain.mjs +259 -0
  179. package/lib/vault.mjs +374 -0
  180. package/lib/workflow-flowchart-utils.mjs +326 -0
  181. package/native/bosun-telemetry/Cargo.toml +9 -0
  182. package/native/bosun-telemetry/src/export.rs +151 -0
  183. package/native/bosun-telemetry/src/main.rs +76 -0
  184. package/native/bosun-telemetry/src/metrics.rs +114 -0
  185. package/native/bosun-telemetry/src/session_telemetry.rs +178 -0
  186. package/native/bosun-unified-exec/Cargo.lock +107 -0
  187. package/native/bosun-unified-exec/Cargo.toml +8 -0
  188. package/native/bosun-unified-exec/src/async_watcher.rs +145 -0
  189. package/native/bosun-unified-exec/src/head_tail_buffer.rs +241 -0
  190. package/native/bosun-unified-exec/src/main.rs +86 -0
  191. package/native/bosun-unified-exec/src/process_manager.rs +308 -0
  192. package/native/bosun-unified-exec/src/tool_orchestrator.rs +187 -0
  193. package/package.json +215 -37
  194. package/postinstall.mjs +182 -12
  195. package/server/bosun-mcp-server.mjs +333 -3
  196. package/server/routes/harness-agent-bridge.mjs +128 -0
  197. package/server/routes/harness-approvals.mjs +290 -0
  198. package/server/routes/harness-events.mjs +469 -0
  199. package/server/routes/harness-providers.mjs +385 -0
  200. package/server/routes/harness-sessions.mjs +2230 -0
  201. package/server/routes/harness-subagents.mjs +138 -0
  202. package/server/routes/harness-surface-payload.mjs +74 -0
  203. package/server/setup-web-server.mjs +199 -24
  204. package/server/ui-server.mjs +12171 -4297
  205. package/setup.mjs +25 -2
  206. package/shell/anthropic-native-adapter.mjs +1218 -0
  207. package/shell/auth-resolver.mjs +247 -0
  208. package/shell/claude-shell.mjs +85 -2
  209. package/shell/codex-config-file.mjs +9 -0
  210. package/shell/codex-config.mjs +49 -5
  211. package/shell/codex-model-profiles.mjs +29 -59
  212. package/shell/codex-sdk-import.mjs +7 -0
  213. package/shell/codex-shell.mjs +574 -159
  214. package/shell/context-compaction.mjs +898 -0
  215. package/shell/copilot-shell.mjs +362 -130
  216. package/shell/gemini-native-adapter.mjs +411 -0
  217. package/shell/gemini-shell.mjs +121 -13
  218. package/shell/mcp-client.mjs +401 -0
  219. package/shell/mcp-registry.mjs +72 -0
  220. package/shell/message-pruner.mjs +248 -0
  221. package/shell/openai-native-adapter.mjs +1975 -0
  222. package/shell/opencode-providers.mjs +16 -816
  223. package/shell/opencode-shell.mjs +180 -9
  224. package/shell/provider-transform.mjs +386 -0
  225. package/shell/retry-fetch.mjs +244 -0
  226. package/shell/session-resume.mjs +97 -0
  227. package/shell/session-store.mjs +215 -0
  228. package/shell/shell-adapter-registry.mjs +346 -0
  229. package/shell/shell-session-compat.mjs +442 -0
  230. package/shell/smooth-stream.mjs +233 -0
  231. package/shell/stop-condition.mjs +238 -0
  232. package/shell/tool-call-repair.mjs +345 -0
  233. package/shell/tool-executor.mjs +571 -0
  234. package/task/pipeline.mjs +3 -1
  235. package/task/task-assessment.mjs +312 -6
  236. package/task/task-claims.mjs +311 -47
  237. package/task/task-cli.mjs +79 -4
  238. package/task/task-context.mjs +37 -0
  239. package/task/task-debt-ledger.mjs +110 -0
  240. package/task/task-executor.mjs +1093 -107
  241. package/task/task-replanner.mjs +553 -0
  242. package/task/task-simulate-cli.mjs +1481 -0
  243. package/task/task-store.mjs +960 -64
  244. package/telegram/executor-health-region-cache.mjs +75 -0
  245. package/telegram/harness-api-client.mjs +124 -0
  246. package/telegram/sticky-menu-state.mjs +384 -0
  247. package/telegram/telegram-bot.mjs +418 -812
  248. package/telegram/telegram-sentinel.mjs +24 -13
  249. package/telegram/telegram-surface-runtime.mjs +53 -0
  250. package/tools/generate-demo-defaults.mjs +23 -4
  251. package/tools/harness-hotpath-bench.mjs +246 -0
  252. package/tools/import-check.mjs +56 -11
  253. package/tools/install-git-hooks.mjs +96 -20
  254. package/tools/native-rust.mjs +124 -0
  255. package/tools/packed-cli-smoke.mjs +139 -53
  256. package/tools/prepublish-check.mjs +53 -1
  257. package/tools/run-workflow-guaranteed-suite.mjs +56 -0
  258. package/tools/sync-demo-ui.mjs +188 -0
  259. package/tools/syntax-check.mjs +32 -28
  260. package/tools/vite-windows-realpath-shim.mjs +274 -0
  261. package/tools/vitest-esbuild-shim.mjs +45 -0
  262. package/tools/vitest-full-suite.mjs +310 -0
  263. package/tools/vitest-runner.mjs +474 -7
  264. package/tools/workflow-orphan-worktree-recovery.mjs +24 -7
  265. package/tui/CommandPalette.js +87 -0
  266. package/tui/app.mjs +422 -36
  267. package/tui/components/status-header.mjs +39 -0
  268. package/tui/lib/command-palette.mjs +191 -0
  269. package/tui/lib/connection-target.mjs +577 -0
  270. package/tui/lib/navigation.mjs +6 -2
  271. package/tui/lib/ws-bridge.mjs +141 -51
  272. package/tui/screens/agents-screen-helpers.mjs +87 -3
  273. package/tui/screens/agents.mjs +1068 -200
  274. package/tui/screens/connection-setup.mjs +363 -0
  275. package/tui/screens/harness-approvals.mjs +7 -0
  276. package/tui/screens/harness-sessions.mjs +109 -0
  277. package/tui/screens/harness-subagents.mjs +18 -0
  278. package/tui/screens/harness-telemetry.mjs +67 -0
  279. package/tui/screens/logs.mjs +1 -5
  280. package/tui/screens/settings-screen-helpers.mjs +75 -0
  281. package/tui/screens/settings.mjs +397 -0
  282. package/tui/screens/status.mjs +126 -4
  283. package/tui/screens/telemetry-screen-helpers.mjs +158 -0
  284. package/tui/screens/telemetry.mjs +246 -0
  285. package/tui/screens/workflows.mjs +984 -0
  286. package/ui/app.js +450 -183
  287. package/ui/assets/toastui-editor-all.min.js +24 -0
  288. package/ui/components/agent-selector.js +706 -49
  289. package/ui/components/charts.js +16 -12
  290. package/ui/components/chat-view.js +536 -35
  291. package/ui/components/context-menu.js +89 -0
  292. package/ui/components/diff-viewer.js +83 -34
  293. package/ui/components/kanban-board.js +567 -96
  294. package/ui/components/session-list.js +292 -65
  295. package/ui/components/task-markdown.js +272 -0
  296. package/ui/components/workspace-switcher.js +11 -2
  297. package/ui/demo-defaults.js +13817 -3976
  298. package/ui/demo.html +817 -3
  299. package/ui/index.html +32 -6
  300. package/ui/modules/agent-events.js +309 -36
  301. package/ui/modules/api.js +236 -13
  302. package/ui/modules/chat-turn-groups.js +101 -0
  303. package/ui/modules/harness-client.js +56 -0
  304. package/ui/modules/icons.js +18 -2
  305. package/ui/modules/router.js +2 -0
  306. package/ui/modules/session-api.js +158 -14
  307. package/ui/modules/session-insights-worker.js +28 -0
  308. package/ui/modules/session-insights.js +173 -4
  309. package/ui/modules/session-surface.js +221 -0
  310. package/ui/modules/settings-schema.js +146 -26
  311. package/ui/modules/state.js +56 -3
  312. package/ui/modules/streaming.js +196 -60
  313. package/ui/modules/structured-values.js +47 -0
  314. package/ui/modules/task-hierarchy.js +374 -0
  315. package/ui/modules/worktree-recovery.js +10 -1
  316. package/ui/setup.html +174 -6
  317. package/ui/styles/components.css +982 -130
  318. package/ui/styles/kanban.css +229 -0
  319. package/ui/styles/layout.css +404 -144
  320. package/ui/styles/toastui-editor-dark.css +1 -0
  321. package/ui/styles/toastui-editor-viewer.css +6 -0
  322. package/ui/styles/toastui-editor.css +6 -0
  323. package/ui/styles/variables.css +14 -2
  324. package/ui/styles/workspace-switcher.css +22 -0
  325. package/ui/styles.css +19 -0
  326. package/ui/tabs/agents.js +1033 -97
  327. package/ui/tabs/chat.js +588 -146
  328. package/ui/tabs/context-compression-lab.js +962 -0
  329. package/ui/tabs/control.js +121 -0
  330. package/ui/tabs/dashboard.js +302 -86
  331. package/ui/tabs/guardrails.js +1140 -0
  332. package/ui/tabs/integrations.js +388 -0
  333. package/ui/tabs/library.js +19 -4
  334. package/ui/tabs/manual-flows.js +268 -52
  335. package/ui/tabs/settings.js +1604 -104
  336. package/ui/tabs/tasks.js +2550 -301
  337. package/ui/tabs/telemetry.js +102 -6
  338. package/ui/tabs/workflow-canvas-utils.mjs +172 -15
  339. package/ui/tabs/workflows.js +2421 -196
  340. package/ui/tui/App.js +117 -117
  341. package/ui/tui/HelpScreen.js +201 -0
  342. package/ui/tui/SettingsScreen.js +388 -0
  343. package/ui/tui/TasksScreen.js +25 -11
  344. package/ui/tui/TelemetryScreen.js +155 -0
  345. package/ui/tui/WorkflowsScreen.js +350 -0
  346. package/ui/tui/config-events.js +13 -0
  347. package/ui/tui/constants.js +1 -1
  348. package/ui/tui/tasks-screen-helpers.js +52 -0
  349. package/ui/tui/telemetry-helpers.js +158 -0
  350. package/ui/tui/useTasks.js +1 -6
  351. package/ui/tui/useWebSocket.js +1 -7
  352. package/ui/tui/useWorkflows.js +120 -5
  353. package/ui/tui/workflows-screen-helpers.js +220 -0
  354. package/utils.mjs +9 -22
  355. package/voice/vision-session-state.mjs +257 -0
  356. package/voice/voice-action-dispatcher.mjs +57 -12
  357. package/voice/voice-agents-sdk.mjs +1 -1
  358. package/voice/voice-auth-manager.mjs +102 -123
  359. package/voice/voice-tool-definitions.mjs +7 -7
  360. package/voice/voice-tools.mjs +837 -101
  361. package/workflow/action-approval.mjs +415 -0
  362. package/workflow/approval-queue.mjs +1254 -0
  363. package/workflow/credential-store.mjs +553 -0
  364. package/workflow/cron-scheduler.mjs +512 -0
  365. package/workflow/declarative-workflows.mjs +21 -2
  366. package/workflow/delegation-runtime.mjs +557 -0
  367. package/workflow/execution-ledger.mjs +1317 -33
  368. package/workflow/harness-approval-node.mjs +237 -0
  369. package/workflow/harness-output-contract.mjs +86 -0
  370. package/workflow/harness-session-node.mjs +160 -0
  371. package/workflow/harness-subagent-node.mjs +483 -0
  372. package/workflow/harness-tool-node.mjs +176 -0
  373. package/workflow/heavy-runner-pool.mjs +71 -4
  374. package/workflow/manual-flows.mjs +969 -28
  375. package/workflow/mcp-discovery-proxy.mjs +349 -137
  376. package/workflow/mcp-registry.mjs +331 -27
  377. package/workflow/meeting-workflow-service.mjs +24 -12
  378. package/workflow/pipeline-workflows.mjs +44 -2
  379. package/workflow/pipeline.mjs +72 -28
  380. package/workflow/project-detection.mjs +31 -6
  381. package/workflow/research-evidence-sidecar.mjs +1246 -0
  382. package/workflow/run-evaluator.mjs +2155 -0
  383. package/workflow/workflow-cli.mjs +229 -2
  384. package/workflow/workflow-contract.mjs +130 -2
  385. package/workflow/workflow-engine.mjs +5533 -299
  386. package/workflow/workflow-nodes/actions.mjs +15558 -0
  387. package/workflow/workflow-nodes/agent.mjs +1905 -0
  388. package/workflow/workflow-nodes/conditions.mjs +307 -0
  389. package/workflow/workflow-nodes/definitions.mjs +176 -15
  390. package/workflow/workflow-nodes/flow.mjs +749 -0
  391. package/workflow/workflow-nodes/loop.mjs +449 -0
  392. package/workflow/workflow-nodes/meetings.mjs +456 -0
  393. package/workflow/workflow-nodes/notifications.mjs +169 -0
  394. package/workflow/workflow-nodes/transforms.mjs +50 -24
  395. package/workflow/workflow-nodes/triggers.mjs +1405 -0
  396. package/workflow/workflow-nodes/validation.mjs +722 -0
  397. package/workflow/workflow-nodes.mjs +38 -15781
  398. package/workflow/workflow-serializer.mjs +294 -0
  399. package/workflow/workflow-templates.mjs +197 -12
  400. package/workflow-templates/_helpers.mjs +1 -3
  401. package/workflow-templates/agents.mjs +255 -29
  402. package/workflow-templates/bosun-native.mjs +3 -1
  403. package/workflow-templates/code-quality.mjs +1 -217
  404. package/workflow-templates/continuation-loop.mjs +22 -7
  405. package/workflow-templates/coverage.mjs +6 -2
  406. package/workflow-templates/github.mjs +2263 -136
  407. package/workflow-templates/planning.mjs +3 -25
  408. package/workflow-templates/reliability.mjs +383 -12
  409. package/workflow-templates/research-evidence.mjs +389 -0
  410. package/workflow-templates/security.mjs +75 -62
  411. package/workflow-templates/sub-workflows.mjs +11 -3
  412. package/workflow-templates/task-batch.mjs +101 -11
  413. package/workflow-templates/task-lifecycle.mjs +313 -49
  414. package/workspace/context-cache.mjs +934 -102
  415. package/workspace/context-indexer.mjs +915 -9
  416. package/workspace/context-injector.mjs +144 -0
  417. package/workspace/execution-journal.mjs +255 -0
  418. package/workspace/scope-locks.mjs +481 -0
  419. package/workspace/shared-knowledge.mjs +496 -91
  420. package/workspace/shared-state-manager.mjs +344 -1
  421. package/workspace/skillbook-store.mjs +681 -0
  422. package/workspace/workspace-manager.mjs +14 -8
  423. package/workspace/workspace-monitor.mjs +3 -3
  424. package/workspace/worktree-manager.mjs +54 -12
  425. package/workspace/worktree-setup.mjs +634 -43
  426. package/agent/rotate-agent-logs.sh +0 -134
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { performance } from "node:perf_hooks";
4
+
5
+ import { createInternalHarnessSession } from "../agent/internal-harness-runtime.mjs";
6
+ import { createProviderKernel } from "../agent/provider-kernel.mjs";
7
+ import { createBosunSessionManager } from "../agent/session-manager.mjs";
8
+ import { createToolOrchestrator } from "../agent/tool-orchestrator.mjs";
9
+ import {
10
+ createHarnessObservabilitySpine,
11
+ resetHarnessObservabilitySpinesForTests,
12
+ } from "../infra/session-telemetry.mjs";
13
+
14
+ const SESSION_COUNT = Math.max(4, Number.parseInt(process.env.BOSUN_HARNESS_LOAD_SESSIONS || "18", 10) || 18);
15
+ const ABORT_EVERY = Math.max(0, Number.parseInt(process.env.BOSUN_HARNESS_LOAD_ABORT_EVERY || "6", 10) || 6);
16
+ const STAGE_DELAY_MS = Math.max(1, Number.parseInt(process.env.BOSUN_HARNESS_LOAD_STAGE_DELAY_MS || "12", 10) || 12);
17
+
18
+ function percentile(values = [], ratio = 0.95) {
19
+ if (!values.length) return 0;
20
+ const sorted = values.slice().sort((a, b) => a - b);
21
+ const index = Math.max(0, Math.min(sorted.length - 1, Math.ceil(sorted.length * ratio) - 1));
22
+ return Number(sorted[index].toFixed(2));
23
+ }
24
+
25
+ function sleepWithSignal(ms, signal) {
26
+ return new Promise((resolve, reject) => {
27
+ const timer = setTimeout(resolve, ms);
28
+ if (!signal) return;
29
+ if (signal.aborted) {
30
+ clearTimeout(timer);
31
+ const error = new Error("aborted");
32
+ error.name = "AbortError";
33
+ reject(error);
34
+ return;
35
+ }
36
+ signal.addEventListener("abort", () => {
37
+ clearTimeout(timer);
38
+ const error = new Error("aborted");
39
+ error.name = "AbortError";
40
+ reject(error);
41
+ }, { once: true });
42
+ });
43
+ }
44
+
45
+ function createLoadRuntime() {
46
+ const providerKernel = createProviderKernel({
47
+ adapters: {
48
+ "bench-sdk": {
49
+ name: "bench-sdk",
50
+ provider: "BENCH",
51
+ exec: async (message, options = {}) => ({
52
+ finalResponse: `load:${message}`,
53
+ sessionId: options.sessionId || null,
54
+ threadId: options.threadId || null,
55
+ providerId: options.provider || null,
56
+ usage: {
57
+ inputTokens: 18,
58
+ outputTokens: 9,
59
+ totalTokens: 27,
60
+ },
61
+ }),
62
+ },
63
+ },
64
+ config: {
65
+ providers: {
66
+ defaultProvider: "openai-compatible",
67
+ openaiCompatible: {
68
+ enabled: true,
69
+ defaultModel: "bench-model",
70
+ baseUrl: "http://127.0.0.1:11434/v1",
71
+ },
72
+ },
73
+ },
74
+ env: {},
75
+ });
76
+ const toolOrchestrator = createToolOrchestrator({
77
+ toolSources: [{
78
+ source: "bench",
79
+ definitions: [{
80
+ id: "load_echo",
81
+ handler: async (args = {}) => ({
82
+ ok: true,
83
+ sessionId: args.sessionId,
84
+ }),
85
+ }],
86
+ }],
87
+ });
88
+ return { providerKernel, toolOrchestrator };
89
+ }
90
+
91
+ function createProfile() {
92
+ return {
93
+ agentId: "bosun-harness-load-bench",
94
+ entryStageId: "plan",
95
+ provider: "openai-compatible",
96
+ stages: [
97
+ {
98
+ id: "plan",
99
+ type: "prompt",
100
+ prompt: "Plan the benchmark action.",
101
+ transitions: [{ on: "success", to: "apply" }],
102
+ },
103
+ {
104
+ id: "apply",
105
+ type: "prompt",
106
+ prompt: "Apply the benchmark action.",
107
+ transitions: [{ on: "success", to: "finalize" }],
108
+ },
109
+ {
110
+ id: "finalize",
111
+ type: "finalize",
112
+ prompt: "Finalize the benchmark response.",
113
+ },
114
+ ],
115
+ };
116
+ }
117
+
118
+ async function runLoadSession(index, telemetry) {
119
+ const sessionManager = createBosunSessionManager();
120
+ const { providerKernel, toolOrchestrator } = createLoadRuntime();
121
+ const profile = createProfile();
122
+ const sessionId = `load-session-${index}`;
123
+ const abortController = new AbortController();
124
+ const shouldAbort = ABORT_EVERY > 0 && index > 0 && index % ABORT_EVERY === 0;
125
+
126
+ sessionManager.beginExternalSession({
127
+ sessionId,
128
+ threadId: `${sessionId}:root`,
129
+ scope: index % 2 === 0 ? "tui:bench" : "workflow:bench",
130
+ sessionType: index % 2 === 0 ? "tui" : "workflow",
131
+ taskKey: `TASK-LOAD-${index}`,
132
+ cwd: process.cwd(),
133
+ source: "load-bench",
134
+ });
135
+ sessionManager.registerExecution(sessionId, {
136
+ sessionType: index % 2 === 0 ? "tui" : "workflow",
137
+ taskKey: `TASK-LOAD-${index}`,
138
+ threadId: `${sessionId}:root`,
139
+ cwd: process.cwd(),
140
+ status: "running",
141
+ scope: index % 2 === 0 ? "tui:bench" : "workflow:bench",
142
+ metadata: {
143
+ benchmark: "load",
144
+ },
145
+ });
146
+
147
+ const session = createInternalHarnessSession(profile, {
148
+ runId: `load-run-${index}`,
149
+ sessionId,
150
+ taskKey: `TASK-LOAD-${index}`,
151
+ surface: index % 2 === 0 ? "tui" : "workflow",
152
+ channel: index % 2 === 0 ? "tui:bench" : "workflow:bench",
153
+ requestedBy: "harness-load-bench",
154
+ abortController,
155
+ onEvent: (event) => {
156
+ telemetry.recordEvent({
157
+ timestamp: event.timestamp || new Date().toISOString(),
158
+ eventType: event.type || "harness:event",
159
+ source: "internal-harness-runtime",
160
+ category: "harness",
161
+ sessionId,
162
+ runId: `load-run-${index}`,
163
+ taskId: `TASK-LOAD-${index}`,
164
+ status: event.status || null,
165
+ summary: event.summary || event.type || null,
166
+ });
167
+ },
168
+ executeTurn: async ({ stage, signal }) => {
169
+ await toolOrchestrator.execute("load_echo", {
170
+ sessionId,
171
+ stageId: stage.id,
172
+ }, {
173
+ sessionId,
174
+ turnId: `${sessionId}:${stage.id}`,
175
+ approval: { mode: "auto" },
176
+ });
177
+ await sleepWithSignal(STAGE_DELAY_MS, signal);
178
+ const providerSession = providerKernel.createExecutionSession({
179
+ adapterName: "bench-sdk",
180
+ selectionId: "openai-compatible",
181
+ sessionId,
182
+ threadId: `${sessionId}:${stage.id}`,
183
+ model: "bench-model",
184
+ });
185
+ const providerResult = await providerSession.runTurn(stage.prompt, {
186
+ sessionId,
187
+ threadId: `${sessionId}:${stage.id}`,
188
+ model: "bench-model",
189
+ });
190
+ return {
191
+ success: true,
192
+ outcome: "success",
193
+ status: "completed",
194
+ output: providerResult.finalResponse,
195
+ sessionId: providerResult.sessionId,
196
+ threadId: providerResult.threadId,
197
+ providerId: providerResult.providerId,
198
+ };
199
+ },
200
+ });
201
+
202
+ let abortScheduledAt = null;
203
+ if (shouldAbort) {
204
+ const timer = setTimeout(() => {
205
+ abortScheduledAt = performance.now();
206
+ abortController.abort("bench-abort");
207
+ }, Math.max(1, Math.floor(STAGE_DELAY_MS / 2)));
208
+ }
209
+
210
+ const startedAt = performance.now();
211
+ const result = await session.run().catch((error) => ({
212
+ success: false,
213
+ status: error?.name === "AbortError" ? "aborted" : "failed",
214
+ error: error?.message || String(error),
215
+ }));
216
+ const finishedAt = performance.now();
217
+
218
+ sessionManager.finalizeExternalExecution(sessionId, {
219
+ success: result.success,
220
+ status: result.status,
221
+ error: result.error || null,
222
+ threadId: `${sessionId}:final`,
223
+ result,
224
+ });
225
+
226
+ return {
227
+ sessionId,
228
+ status: result.status,
229
+ durationMs: finishedAt - startedAt,
230
+ cancellationLatencyMs: abortScheduledAt == null ? null : finishedAt - abortScheduledAt,
231
+ };
232
+ }
233
+
234
+ async function main() {
235
+ resetHarnessObservabilitySpinesForTests();
236
+ const telemetry = createHarnessObservabilitySpine({
237
+ persist: false,
238
+ maxInMemoryEvents: SESSION_COUNT * 18,
239
+ });
240
+
241
+ const startedAt = performance.now();
242
+ const results = await Promise.all(
243
+ Array.from({ length: SESSION_COUNT }, (_, index) => runLoadSession(index + 1, telemetry)),
244
+ );
245
+ const flushStartedAt = performance.now();
246
+ await telemetry.flush();
247
+ const flushDurationMs = performance.now() - flushStartedAt;
248
+ const totalDurationMs = performance.now() - startedAt;
249
+
250
+ const completed = results.filter((entry) => entry.status === "completed");
251
+ const aborted = results.filter((entry) => entry.status === "aborted");
252
+ const failed = results.filter((entry) => entry.status === "failed");
253
+ const durations = results.map((entry) => entry.durationMs);
254
+ const cancellationLatencies = aborted
255
+ .map((entry) => entry.cancellationLatencyMs)
256
+ .filter((value) => Number.isFinite(value));
257
+
258
+ console.log(JSON.stringify({
259
+ benchmark: "harness-load",
260
+ sessions: SESSION_COUNT,
261
+ completed: completed.length,
262
+ aborted: aborted.length,
263
+ failed: failed.length,
264
+ totalDurationMs: Number(totalDurationMs.toFixed(2)),
265
+ throughputSessionsPerSecond: Number(((SESSION_COUNT / totalDurationMs) * 1000).toFixed(2)),
266
+ latency: {
267
+ p50Ms: percentile(durations, 0.5),
268
+ p95Ms: percentile(durations, 0.95),
269
+ p99Ms: percentile(durations, 0.99),
270
+ },
271
+ cancellationLatency: {
272
+ count: cancellationLatencies.length,
273
+ p50Ms: percentile(cancellationLatencies, 0.5),
274
+ p95Ms: percentile(cancellationLatencies, 0.95),
275
+ },
276
+ projectionFreshnessMs: Number(flushDurationMs.toFixed(2)),
277
+ telemetry: telemetry.getSummary(),
278
+ }, null, 2));
279
+ }
280
+
281
+ await main();
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { performance } from "node:perf_hooks";
4
+
5
+ import { createInternalHarnessSession } from "../agent/internal-harness-runtime.mjs";
6
+ import { createProviderKernel } from "../agent/provider-kernel.mjs";
7
+ import { createBosunSessionManager } from "../agent/session-manager.mjs";
8
+ import { createToolOrchestrator } from "../agent/tool-orchestrator.mjs";
9
+
10
+ const ITERATIONS = Math.max(1, Number.parseInt(process.env.BOSUN_HARNESS_PARITY_ITERATIONS || "3", 10) || 3);
11
+ const SURFACES = Object.freeze([
12
+ { surface: "chat", scope: "primary", sessionType: "primary" },
13
+ { surface: "workflow", scope: "workflow:bench", sessionType: "workflow" },
14
+ { surface: "tui", scope: "tui:bench", sessionType: "tui" },
15
+ { surface: "web-ui", scope: "web-ui:bench", sessionType: "web-ui" },
16
+ { surface: "telegram", scope: "telegram:bench", sessionType: "telegram" },
17
+ ]);
18
+
19
+ function percentile(values = [], ratio = 0.95) {
20
+ if (!values.length) return 0;
21
+ const sorted = values.slice().sort((a, b) => a - b);
22
+ const index = Math.max(0, Math.min(sorted.length - 1, Math.ceil(sorted.length * ratio) - 1));
23
+ return Number(sorted[index].toFixed(2));
24
+ }
25
+
26
+ function createBenchRuntime() {
27
+ const providerKernel = createProviderKernel({
28
+ adapters: {
29
+ "bench-sdk": {
30
+ name: "bench-sdk",
31
+ provider: "BENCH",
32
+ exec: async (message, options = {}) => ({
33
+ finalResponse: `[${options.provider}] ${message}`,
34
+ sessionId: options.sessionId || null,
35
+ threadId: options.threadId || null,
36
+ providerId: options.provider || null,
37
+ usage: {
38
+ inputTokens: 10,
39
+ outputTokens: 6,
40
+ totalTokens: 16,
41
+ },
42
+ }),
43
+ },
44
+ },
45
+ config: {
46
+ providers: {
47
+ defaultProvider: "openai-compatible",
48
+ openaiCompatible: {
49
+ enabled: true,
50
+ defaultModel: "bench-model",
51
+ baseUrl: "http://127.0.0.1:11434/v1",
52
+ },
53
+ },
54
+ },
55
+ env: {},
56
+ });
57
+
58
+ const toolOrchestrator = createToolOrchestrator({
59
+ toolSources: [{
60
+ source: "bench",
61
+ definitions: [{
62
+ id: "surface_echo",
63
+ handler: async (args = {}) => ({
64
+ ok: true,
65
+ surface: args.surface,
66
+ stageId: args.stageId,
67
+ }),
68
+ }],
69
+ }],
70
+ });
71
+
72
+ return { providerKernel, toolOrchestrator };
73
+ }
74
+
75
+ function createProfile() {
76
+ return {
77
+ agentId: "bosun-harness-parity-bench",
78
+ entryStageId: "plan",
79
+ provider: "openai-compatible",
80
+ stages: [
81
+ {
82
+ id: "plan",
83
+ type: "prompt",
84
+ prompt: "Plan the next action.",
85
+ transitions: [{ on: "success", to: "finalize" }],
86
+ },
87
+ {
88
+ id: "finalize",
89
+ type: "finalize",
90
+ prompt: "Finalize the response.",
91
+ },
92
+ ],
93
+ };
94
+ }
95
+
96
+ async function runSurfaceIteration(surfaceEntry, iteration) {
97
+ const sessionManager = createBosunSessionManager();
98
+ const { providerKernel, toolOrchestrator } = createBenchRuntime();
99
+ const sessionId = `bench-${surfaceEntry.surface}-${iteration}`;
100
+ const profile = createProfile();
101
+
102
+ sessionManager.beginExternalSession({
103
+ sessionId,
104
+ scope: surfaceEntry.scope,
105
+ sessionType: surfaceEntry.sessionType,
106
+ taskKey: `task-${surfaceEntry.surface}-${iteration}`,
107
+ cwd: process.cwd(),
108
+ source: surfaceEntry.surface,
109
+ });
110
+ sessionManager.registerExecution(sessionId, {
111
+ scope: surfaceEntry.scope,
112
+ sessionType: surfaceEntry.sessionType,
113
+ taskKey: `task-${surfaceEntry.surface}-${iteration}`,
114
+ cwd: process.cwd(),
115
+ status: "running",
116
+ threadId: `${sessionId}:bootstrap`,
117
+ providerSelection: "openai-compatible",
118
+ adapterName: "bench-sdk",
119
+ });
120
+
121
+ const session = createInternalHarnessSession(profile, {
122
+ runId: `run-${surfaceEntry.surface}-${iteration}`,
123
+ sessionId,
124
+ taskKey: `task-${surfaceEntry.surface}-${iteration}`,
125
+ surface: surfaceEntry.surface,
126
+ channel: surfaceEntry.scope,
127
+ requestedBy: "harness-parity-bench",
128
+ executeTurn: async ({ stage }) => {
129
+ const toolResult = await toolOrchestrator.execute("surface_echo", {
130
+ surface: surfaceEntry.surface,
131
+ stageId: stage.id,
132
+ }, {
133
+ sessionId,
134
+ turnId: `${sessionId}:${stage.id}`,
135
+ approval: { mode: "auto" },
136
+ });
137
+ const providerSession = providerKernel.createExecutionSession({
138
+ adapterName: "bench-sdk",
139
+ selectionId: "openai-compatible",
140
+ sessionId,
141
+ threadId: `${sessionId}:${stage.id}`,
142
+ model: "bench-model",
143
+ });
144
+ const providerResult = await providerSession.runTurn(
145
+ `${surfaceEntry.surface}:${stage.prompt}`,
146
+ {
147
+ sessionId,
148
+ threadId: `${sessionId}:${stage.id}`,
149
+ model: "bench-model",
150
+ },
151
+ );
152
+ return {
153
+ success: true,
154
+ outcome: "success",
155
+ status: "completed",
156
+ output: `${providerResult.finalResponse} :: ${toolResult.surface}`,
157
+ sessionId: providerResult.sessionId,
158
+ threadId: providerResult.threadId,
159
+ providerId: providerResult.providerId,
160
+ };
161
+ },
162
+ });
163
+
164
+ const startedAt = performance.now();
165
+ const result = await session.run();
166
+ const durationMs = performance.now() - startedAt;
167
+
168
+ sessionManager.finalizeExternalExecution(sessionId, {
169
+ success: result.success,
170
+ status: result.status,
171
+ threadId: `${sessionId}:final`,
172
+ result,
173
+ });
174
+
175
+ return {
176
+ surface: surfaceEntry.surface,
177
+ durationMs,
178
+ status: result.status,
179
+ providerId: session.getState().provider,
180
+ events: session.getState().events.length,
181
+ };
182
+ }
183
+
184
+ async function main() {
185
+ const results = [];
186
+ for (let iteration = 0; iteration < ITERATIONS; iteration += 1) {
187
+ for (const surface of SURFACES) {
188
+ results.push(await runSurfaceIteration(surface, iteration));
189
+ }
190
+ }
191
+
192
+ const bySurface = Object.fromEntries(
193
+ SURFACES.map(({ surface }) => {
194
+ const surfaceResults = results.filter((entry) => entry.surface === surface);
195
+ const durations = surfaceResults.map((entry) => entry.durationMs);
196
+ return [surface, {
197
+ iterations: surfaceResults.length,
198
+ avgDurationMs: Number((durations.reduce((sum, value) => sum + value, 0) / Math.max(1, durations.length)).toFixed(2)),
199
+ p95DurationMs: percentile(durations, 0.95),
200
+ statuses: [...new Set(surfaceResults.map((entry) => entry.status))],
201
+ providerIds: [...new Set(surfaceResults.map((entry) => entry.providerId))],
202
+ minEvents: Math.min(...surfaceResults.map((entry) => entry.events)),
203
+ }];
204
+ }),
205
+ );
206
+
207
+ console.log(JSON.stringify({
208
+ benchmark: "harness-parity",
209
+ iterations: ITERATIONS,
210
+ surfaces: bySurface,
211
+ }, null, 2));
212
+ }
213
+
214
+ await main();
@@ -5,6 +5,7 @@ import { execFileSync } from "node:child_process";
5
5
  import { createHash } from "node:crypto";
6
6
  import { taskCreate, taskList } from "../../task/task-cli.mjs";
7
7
  import { WorkflowEngine } from "../../workflow/workflow-engine.mjs";
8
+ import "../../workflow/workflow-nodes.mjs";
8
9
  import { installTemplateSet } from "../../workflow/workflow-templates.mjs";
9
10
 
10
11
  const DEFAULT_SWEBENCH_TEMPLATE_IDS = Object.freeze(["template-task-lifecycle"]);
package/bosun-tui.mjs CHANGED
@@ -5,7 +5,15 @@ import { dirname, resolve } from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
 
7
7
  import loadConfig from "./config/config.mjs";
8
- import { resolveWebSocketProtocol } from "./tui/lib/ws-bridge.mjs";
8
+ import {
9
+ clearRemoteConnectionConfig,
10
+ defaultConfigDir,
11
+ normalizeHttpProtocol,
12
+ readRemoteConnectionConfig,
13
+ resolveTuiConnectionTarget,
14
+ saveRemoteConnectionConfig,
15
+ upsertRemoteConnection,
16
+ } from "./tui/lib/connection-target.mjs";
9
17
 
10
18
  const MIN_COLUMNS = 120;
11
19
  const MIN_ROWS = 30;
@@ -25,8 +33,13 @@ function showHelp() {
25
33
  node bosun-tui.mjs [options]
26
34
 
27
35
  OPTIONS
36
+ --endpoint <url> Connect to an existing Bosun server (example: https://host:4400)
28
37
  --host <host> WebSocket host (default: 127.0.0.1)
29
- --port <n> WebSocket/UI port (default: TELEGRAM_UI_PORT or 3080)
38
+ --port <n> WebSocket/UI port (default: configured local backend, TELEGRAM_UI_PORT, or 3080 fallback)
39
+ --protocol <proto> Connection protocol (ws|wss|http|https)
40
+ --api-key <key> API key for remote/existing Bosun server (BOSUN_API_KEY)
41
+ --save-connection Persist endpoint + API key to remote-connection.json
42
+ --clear-connection Clear the saved remote connection target
30
43
  --screen <name> Initial screen (agents|tasks|logs|workflows|telemetry|settings|help)
31
44
  --help Show this help
32
45
  --version Show version
@@ -86,19 +99,51 @@ export async function runBosunTui(argv = process.argv.slice(2), options = {}) {
86
99
  globalThis.WebSocket = globalThis.WebSocket || (await import("ws")).WebSocket;
87
100
 
88
101
  const config = loadConfig([process.argv[0], __filename, ...args]);
89
- const configDir = String(config?.configDir || process.env.BOSUN_DIR || resolve(process.cwd(), ".bosun")).trim();
90
- const host = getArgValue(args, "--host", "127.0.0.1");
91
- const port = Number(getArgValue(args, "--port", String(resolvePort(config)))) || resolvePort(config);
92
- const protocol = getArgValue(
93
- args,
94
- "--protocol",
95
- resolveWebSocketProtocol({ configDir }),
96
- );
102
+ const configDir = String(config?.configDir || process.env.BOSUN_DIR || defaultConfigDir()).trim();
103
+ if (hasFlag(args, "--clear-connection")) {
104
+ clearRemoteConnectionConfig(configDir);
105
+ stdout.write(`[bosun-tui] Cleared saved remote connection in ${configDir}\\remote-connection.json\n`);
106
+ return 0;
107
+ }
108
+
109
+ const explicitEndpoint = getArgValue(args, "--endpoint", "");
110
+ const explicitHost = getArgValue(args, "--host", "");
111
+ const explicitPort = Number(getArgValue(args, "--port", "")) || 0;
112
+ const explicitProtocol = getArgValue(args, "--protocol", "");
113
+ const explicitApiKey = getArgValue(args, "--api-key", String(process.env.BOSUN_API_KEY || "").trim());
114
+ const target = resolveTuiConnectionTarget({
115
+ configDir,
116
+ config,
117
+ env: process.env,
118
+ endpoint: explicitEndpoint,
119
+ host: explicitHost,
120
+ port: explicitPort,
121
+ protocol: explicitProtocol,
122
+ apiKey: explicitApiKey,
123
+ });
124
+
125
+ if (hasFlag(args, "--save-connection")) {
126
+ const remoteEndpoint = explicitEndpoint
127
+ || `${normalizeHttpProtocol(target.protocol)}://${target.host}:${target.port}`;
128
+ const nextConfig = upsertRemoteConnection(readRemoteConnectionConfig(configDir), {
129
+ name: remoteEndpoint,
130
+ endpoint: remoteEndpoint,
131
+ apiKey: target.apiKey || explicitApiKey,
132
+ enabled: true,
133
+ });
134
+ saveRemoteConnectionConfig(nextConfig, configDir);
135
+ stdout.write(`[bosun-tui] Saved remote connection target ${remoteEndpoint}\n`);
136
+ }
137
+
138
+ const host = target.host || "127.0.0.1";
139
+ const port = Number(target.port || resolvePort(config)) || resolvePort(config);
140
+ const protocol = target.protocol || "ws";
141
+ const apiKey = target.apiKey || "";
97
142
  const initialScreen = getArgValue(args, "--screen", "agents");
98
143
 
99
144
  const React = await import("react");
100
145
  const ink = await import("ink");
101
- const { default: App } = await import("./ui/tui/App.js");
146
+ const { default: App } = await import("./tui/app.mjs");
102
147
 
103
148
  let terminalSize = getTerminalSize(stdout);
104
149
  const props = {
@@ -107,8 +152,11 @@ export async function runBosunTui(argv = process.argv.slice(2), options = {}) {
107
152
  host,
108
153
  port,
109
154
  protocol,
155
+ apiKey,
110
156
  initialScreen,
111
157
  terminalSize,
158
+ connectionSource: target.source,
159
+ connectionEndpoint: target.endpoint,
112
160
  };
113
161
 
114
162
  const instance = ink.render(React.createElement(App, props), { exitOnCtrlC: true });
@@ -140,5 +188,3 @@ if (process.argv[1] && resolve(process.argv[1]) === __filename) {
140
188
  process.exit(1);
141
189
  });
142
190
  }
143
-
144
-