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.
- package/.env.example +26 -15
- package/README.md +1 -11
- package/agent/agent-event-bus.mjs +33 -2
- package/agent/agent-hooks.mjs +4 -52
- package/agent/agent-launcher.mjs +6210 -0
- package/agent/agent-pool.mjs +7 -4182
- package/agent/agent-prompt-catalog.mjs +3 -4
- package/agent/agent-sdk.mjs +1 -4
- package/agent/agent-supervisor.mjs +30 -6
- package/agent/auth/_shared.mjs +129 -0
- package/agent/auth/anthropic-api-key.mjs +13 -0
- package/agent/auth/azure-openai.mjs +17 -0
- package/agent/auth/cerebras.mjs +14 -0
- package/agent/auth/chatgpt-codex-subscription.mjs +15 -0
- package/agent/auth/claude-subscription.mjs +15 -0
- package/agent/auth/copilot-oauth.mjs +13 -0
- package/agent/auth/deepinfra.mjs +14 -0
- package/agent/auth/fireworks.mjs +14 -0
- package/agent/auth/gemini-api-key.mjs +14 -0
- package/agent/auth/groq.mjs +14 -0
- package/agent/auth/index.mjs +85 -0
- package/agent/auth/nebius.mjs +14 -0
- package/agent/auth/ollama.mjs +14 -0
- package/agent/auth/openai-api-key.mjs +13 -0
- package/agent/auth/openai-compatible.mjs +15 -0
- package/agent/auth/openrouter.mjs +14 -0
- package/agent/auth/perplexity.mjs +14 -0
- package/agent/auth/sambanova.mjs +14 -0
- package/agent/auth/together.mjs +14 -0
- package/agent/auth/xai.mjs +14 -0
- package/agent/bosun-skills.mjs +177 -68
- package/agent/fleet-coordinator.mjs +153 -37
- package/agent/harness/agent-loop.mjs +26 -0
- package/agent/harness/event-contract.mjs +125 -0
- package/agent/harness/followup-queue.mjs +33 -0
- package/agent/harness/message-normalizer.mjs +43 -0
- package/agent/harness/module-boundaries.md +73 -0
- package/agent/harness/run-contract.mjs +122 -0
- package/agent/harness/runtime-config.mjs +132 -0
- package/agent/harness/session-state.mjs +80 -0
- package/agent/harness/steering-queue.mjs +35 -0
- package/agent/harness/tool-runner.mjs +95 -0
- package/agent/harness/turn-runner.mjs +135 -0
- package/agent/harness-agent-service.mjs +852 -0
- package/agent/harness-executor-config.mjs +384 -0
- package/agent/hook-library.mjs +139 -0
- package/agent/hook-profiles.mjs +46 -108
- package/agent/internal-harness-control-plane.mjs +672 -0
- package/agent/internal-harness-profile.mjs +519 -0
- package/agent/internal-harness-runtime.mjs +1219 -0
- package/agent/lineage-graph.mjs +141 -0
- package/agent/primary-agent.mjs +593 -646
- package/agent/provider-auth-manager.mjs +830 -0
- package/agent/provider-auth-state.mjs +440 -0
- package/agent/provider-capabilities.mjs +116 -0
- package/agent/provider-kernel.mjs +596 -0
- package/agent/provider-message-transform.mjs +583 -0
- package/agent/provider-model-catalog.mjs +163 -0
- package/agent/provider-registry.mjs +657 -0
- package/agent/provider-runtime-discovery.mjs +147 -0
- package/agent/provider-session.mjs +767 -0
- package/agent/providers/_shared.mjs +397 -0
- package/agent/providers/anthropic-messages.mjs +64 -0
- package/agent/providers/azure-openai-responses.mjs +69 -0
- package/agent/providers/cerebras.mjs +66 -0
- package/agent/providers/claude-subscription-shim.mjs +68 -0
- package/agent/providers/copilot-oauth.mjs +66 -0
- package/agent/providers/deepinfra.mjs +66 -0
- package/agent/providers/fireworks.mjs +66 -0
- package/agent/providers/gemini-generate-content.mjs +66 -0
- package/agent/providers/groq.mjs +66 -0
- package/agent/providers/index.mjs +208 -0
- package/agent/providers/nebius.mjs +66 -0
- package/agent/providers/ollama.mjs +66 -0
- package/agent/providers/openai-codex-subscription.mjs +75 -0
- package/agent/providers/openai-compatible.mjs +65 -0
- package/agent/providers/openai-responses.mjs +67 -0
- package/agent/providers/openrouter.mjs +66 -0
- package/agent/providers/perplexity.mjs +66 -0
- package/agent/providers/provider-contract.mjs +138 -0
- package/agent/providers/provider-errors.mjs +63 -0
- package/agent/providers/provider-model-pricing.mjs +246 -0
- package/agent/providers/provider-stream-normalizer.mjs +7 -0
- package/agent/providers/provider-usage-normalizer.mjs +48 -0
- package/agent/providers/sambanova.mjs +66 -0
- package/agent/providers/together.mjs +66 -0
- package/agent/providers/xai.mjs +66 -0
- package/agent/query-engine.mjs +260 -0
- package/agent/retry-queue.mjs +1 -0
- package/agent/session-contract.mjs +127 -0
- package/agent/session-manager.mjs +1859 -0
- package/agent/session-replay.mjs +617 -0
- package/agent/session-snapshot-store.mjs +379 -0
- package/agent/skills/agent-coordination.md +6 -0
- package/agent/skills/background-task-execution.md +6 -0
- package/agent/skills/bosun-agent-api.md +6 -0
- package/agent/skills/code-quality-anti-patterns.md +7 -0
- package/agent/skills/commit-conventions.md +6 -0
- package/agent/skills/custom-tool-creation.md +6 -0
- package/agent/skills/error-recovery.md +6 -0
- package/agent/skills/pr-workflow.md +6 -0
- package/agent/skills/tdd-pattern.md +6 -0
- package/agent/subagent-contract.mjs +104 -0
- package/agent/subagent-control.mjs +633 -0
- package/agent/subagent-pool.mjs +260 -0
- package/agent/thread-contract.mjs +88 -0
- package/agent/thread-registry.mjs +552 -0
- package/agent/tool-approval-manager.mjs +259 -0
- package/agent/tool-builtin-catalog.mjs +855 -0
- package/agent/tool-contract.mjs +101 -0
- package/agent/tool-event-contract.mjs +99 -0
- package/agent/tool-execution-ledger.mjs +32 -0
- package/agent/tool-network-policy.mjs +86 -0
- package/agent/tool-orchestrator.mjs +382 -0
- package/agent/tool-output-truncation.mjs +70 -0
- package/agent/tool-registry.mjs +200 -0
- package/agent/tool-retry-policy.mjs +57 -0
- package/agent/tool-runtime-context.mjs +220 -0
- package/bench/harness-load-bench.mjs +281 -0
- package/bench/harness-parity-bench.mjs +214 -0
- package/bench/swebench/bosun-swebench.mjs +1 -0
- package/bosun-tui.mjs +59 -13
- package/bosun.schema.json +358 -1
- package/cli.mjs +641 -190
- package/config/config-doctor.mjs +78 -4
- package/config/config-editor.mjs +417 -0
- package/config/config.mjs +217 -117
- package/config/repo-config.mjs +78 -10
- package/config/repo-root.mjs +33 -1
- package/desktop/main.mjs +438 -99
- package/desktop/package.json +1 -1
- package/git/diff-stats.mjs +7 -5
- package/infra/anomaly-detector.mjs +115 -15
- package/infra/approval-projection-store.mjs +75 -0
- package/infra/config-reload-bus.mjs +33 -0
- package/infra/container-runner.mjs +37 -3
- package/infra/error-detector.mjs +109 -34
- package/infra/event-schema.mjs +353 -0
- package/infra/guardrails.mjs +383 -0
- package/infra/heartbeat-monitor.mjs +432 -0
- package/infra/library-manager.mjs +359 -233
- package/infra/live-event-projector.mjs +197 -0
- package/infra/maintenance.mjs +176 -37
- package/infra/monitor.mjs +1292 -194
- package/infra/preflight.mjs +71 -5
- package/infra/presence.mjs +33 -9
- package/infra/projection-contract.mjs +27 -0
- package/infra/provider-usage-ledger.mjs +73 -0
- package/infra/replay-reader.mjs +140 -0
- package/infra/runtime-accumulator.mjs +303 -8
- package/infra/runtime-metrics.mjs +156 -0
- package/infra/session-projection-store.mjs +169 -0
- package/infra/session-telemetry-runtime.mjs +580 -0
- package/infra/session-telemetry.mjs +338 -0
- package/infra/session-tracker.mjs +1407 -174
- package/infra/startup-service.mjs +0 -2
- package/infra/storage-janitor.mjs +1046 -0
- package/infra/subagent-projection-store.mjs +89 -0
- package/infra/test-runtime.mjs +53 -20
- package/infra/trace-export.mjs +103 -0
- package/infra/tracing.mjs +13 -125
- package/infra/tui-bridge.mjs +607 -5
- package/infra/update-check.mjs +7 -8
- package/infra/windows-hidden-child-processes.mjs +99 -0
- package/infra/worktree-recovery-state.mjs +15 -5
- package/kanban/kanban-adapter.mjs +674 -41
- package/kanban/repo-mirror-projection-store.mjs +871 -0
- package/lib/agent-configuration-guide.mjs +280 -0
- package/lib/hot-path-runtime.mjs +1061 -0
- package/lib/integrations-registry.mjs +294 -0
- package/lib/log-tail.mjs +101 -0
- package/lib/mojibake-repair.mjs +40 -0
- package/lib/repo-map.mjs +137 -24
- package/lib/request-json-api.mjs +59 -0
- package/lib/safe-box.mjs +56 -0
- package/lib/session-insights.mjs +3 -65
- package/lib/state-ledger-sqlite.mjs +4462 -0
- package/lib/vault-keychain.mjs +259 -0
- package/lib/vault.mjs +374 -0
- package/lib/workflow-flowchart-utils.mjs +326 -0
- package/native/bosun-telemetry/Cargo.toml +9 -0
- package/native/bosun-telemetry/src/export.rs +151 -0
- package/native/bosun-telemetry/src/main.rs +76 -0
- package/native/bosun-telemetry/src/metrics.rs +114 -0
- package/native/bosun-telemetry/src/session_telemetry.rs +178 -0
- package/native/bosun-unified-exec/Cargo.lock +107 -0
- package/native/bosun-unified-exec/Cargo.toml +8 -0
- package/native/bosun-unified-exec/src/async_watcher.rs +145 -0
- package/native/bosun-unified-exec/src/head_tail_buffer.rs +241 -0
- package/native/bosun-unified-exec/src/main.rs +86 -0
- package/native/bosun-unified-exec/src/process_manager.rs +308 -0
- package/native/bosun-unified-exec/src/tool_orchestrator.rs +187 -0
- package/package.json +215 -37
- package/postinstall.mjs +182 -12
- package/server/bosun-mcp-server.mjs +333 -3
- package/server/routes/harness-agent-bridge.mjs +128 -0
- package/server/routes/harness-approvals.mjs +290 -0
- package/server/routes/harness-events.mjs +469 -0
- package/server/routes/harness-providers.mjs +385 -0
- package/server/routes/harness-sessions.mjs +2230 -0
- package/server/routes/harness-subagents.mjs +138 -0
- package/server/routes/harness-surface-payload.mjs +74 -0
- package/server/setup-web-server.mjs +199 -24
- package/server/ui-server.mjs +12171 -4297
- package/setup.mjs +25 -2
- package/shell/anthropic-native-adapter.mjs +1218 -0
- package/shell/auth-resolver.mjs +247 -0
- package/shell/claude-shell.mjs +85 -2
- package/shell/codex-config-file.mjs +9 -0
- package/shell/codex-config.mjs +49 -5
- package/shell/codex-model-profiles.mjs +29 -59
- package/shell/codex-sdk-import.mjs +7 -0
- package/shell/codex-shell.mjs +574 -159
- package/shell/context-compaction.mjs +898 -0
- package/shell/copilot-shell.mjs +362 -130
- package/shell/gemini-native-adapter.mjs +411 -0
- package/shell/gemini-shell.mjs +121 -13
- package/shell/mcp-client.mjs +401 -0
- package/shell/mcp-registry.mjs +72 -0
- package/shell/message-pruner.mjs +248 -0
- package/shell/openai-native-adapter.mjs +1975 -0
- package/shell/opencode-providers.mjs +16 -816
- package/shell/opencode-shell.mjs +180 -9
- package/shell/provider-transform.mjs +386 -0
- package/shell/retry-fetch.mjs +244 -0
- package/shell/session-resume.mjs +97 -0
- package/shell/session-store.mjs +215 -0
- package/shell/shell-adapter-registry.mjs +346 -0
- package/shell/shell-session-compat.mjs +442 -0
- package/shell/smooth-stream.mjs +233 -0
- package/shell/stop-condition.mjs +238 -0
- package/shell/tool-call-repair.mjs +345 -0
- package/shell/tool-executor.mjs +571 -0
- package/task/pipeline.mjs +3 -1
- package/task/task-assessment.mjs +312 -6
- package/task/task-claims.mjs +311 -47
- package/task/task-cli.mjs +79 -4
- package/task/task-context.mjs +37 -0
- package/task/task-debt-ledger.mjs +110 -0
- package/task/task-executor.mjs +1093 -107
- package/task/task-replanner.mjs +553 -0
- package/task/task-simulate-cli.mjs +1481 -0
- package/task/task-store.mjs +960 -64
- package/telegram/executor-health-region-cache.mjs +75 -0
- package/telegram/harness-api-client.mjs +124 -0
- package/telegram/sticky-menu-state.mjs +384 -0
- package/telegram/telegram-bot.mjs +418 -812
- package/telegram/telegram-sentinel.mjs +24 -13
- package/telegram/telegram-surface-runtime.mjs +53 -0
- package/tools/generate-demo-defaults.mjs +23 -4
- package/tools/harness-hotpath-bench.mjs +246 -0
- package/tools/import-check.mjs +56 -11
- package/tools/install-git-hooks.mjs +96 -20
- package/tools/native-rust.mjs +124 -0
- package/tools/packed-cli-smoke.mjs +139 -53
- package/tools/prepublish-check.mjs +53 -1
- package/tools/run-workflow-guaranteed-suite.mjs +56 -0
- package/tools/sync-demo-ui.mjs +188 -0
- package/tools/syntax-check.mjs +32 -28
- package/tools/vite-windows-realpath-shim.mjs +274 -0
- package/tools/vitest-esbuild-shim.mjs +45 -0
- package/tools/vitest-full-suite.mjs +310 -0
- package/tools/vitest-runner.mjs +474 -7
- package/tools/workflow-orphan-worktree-recovery.mjs +24 -7
- package/tui/CommandPalette.js +87 -0
- package/tui/app.mjs +422 -36
- package/tui/components/status-header.mjs +39 -0
- package/tui/lib/command-palette.mjs +191 -0
- package/tui/lib/connection-target.mjs +577 -0
- package/tui/lib/navigation.mjs +6 -2
- package/tui/lib/ws-bridge.mjs +141 -51
- package/tui/screens/agents-screen-helpers.mjs +87 -3
- package/tui/screens/agents.mjs +1068 -200
- package/tui/screens/connection-setup.mjs +363 -0
- package/tui/screens/harness-approvals.mjs +7 -0
- package/tui/screens/harness-sessions.mjs +109 -0
- package/tui/screens/harness-subagents.mjs +18 -0
- package/tui/screens/harness-telemetry.mjs +67 -0
- package/tui/screens/logs.mjs +1 -5
- package/tui/screens/settings-screen-helpers.mjs +75 -0
- package/tui/screens/settings.mjs +397 -0
- package/tui/screens/status.mjs +126 -4
- package/tui/screens/telemetry-screen-helpers.mjs +158 -0
- package/tui/screens/telemetry.mjs +246 -0
- package/tui/screens/workflows.mjs +984 -0
- package/ui/app.js +450 -183
- package/ui/assets/toastui-editor-all.min.js +24 -0
- package/ui/components/agent-selector.js +706 -49
- package/ui/components/charts.js +16 -12
- package/ui/components/chat-view.js +536 -35
- package/ui/components/context-menu.js +89 -0
- package/ui/components/diff-viewer.js +83 -34
- package/ui/components/kanban-board.js +567 -96
- package/ui/components/session-list.js +292 -65
- package/ui/components/task-markdown.js +272 -0
- package/ui/components/workspace-switcher.js +11 -2
- package/ui/demo-defaults.js +13817 -3976
- package/ui/demo.html +817 -3
- package/ui/index.html +32 -6
- package/ui/modules/agent-events.js +309 -36
- package/ui/modules/api.js +236 -13
- package/ui/modules/chat-turn-groups.js +101 -0
- package/ui/modules/harness-client.js +56 -0
- package/ui/modules/icons.js +18 -2
- package/ui/modules/router.js +2 -0
- package/ui/modules/session-api.js +158 -14
- package/ui/modules/session-insights-worker.js +28 -0
- package/ui/modules/session-insights.js +173 -4
- package/ui/modules/session-surface.js +221 -0
- package/ui/modules/settings-schema.js +146 -26
- package/ui/modules/state.js +56 -3
- package/ui/modules/streaming.js +196 -60
- package/ui/modules/structured-values.js +47 -0
- package/ui/modules/task-hierarchy.js +374 -0
- package/ui/modules/worktree-recovery.js +10 -1
- package/ui/setup.html +174 -6
- package/ui/styles/components.css +982 -130
- package/ui/styles/kanban.css +229 -0
- package/ui/styles/layout.css +404 -144
- package/ui/styles/toastui-editor-dark.css +1 -0
- package/ui/styles/toastui-editor-viewer.css +6 -0
- package/ui/styles/toastui-editor.css +6 -0
- package/ui/styles/variables.css +14 -2
- package/ui/styles/workspace-switcher.css +22 -0
- package/ui/styles.css +19 -0
- package/ui/tabs/agents.js +1033 -97
- package/ui/tabs/chat.js +588 -146
- package/ui/tabs/context-compression-lab.js +962 -0
- package/ui/tabs/control.js +121 -0
- package/ui/tabs/dashboard.js +302 -86
- package/ui/tabs/guardrails.js +1140 -0
- package/ui/tabs/integrations.js +388 -0
- package/ui/tabs/library.js +19 -4
- package/ui/tabs/manual-flows.js +268 -52
- package/ui/tabs/settings.js +1604 -104
- package/ui/tabs/tasks.js +2550 -301
- package/ui/tabs/telemetry.js +102 -6
- package/ui/tabs/workflow-canvas-utils.mjs +172 -15
- package/ui/tabs/workflows.js +2421 -196
- package/ui/tui/App.js +117 -117
- package/ui/tui/HelpScreen.js +201 -0
- package/ui/tui/SettingsScreen.js +388 -0
- package/ui/tui/TasksScreen.js +25 -11
- package/ui/tui/TelemetryScreen.js +155 -0
- package/ui/tui/WorkflowsScreen.js +350 -0
- package/ui/tui/config-events.js +13 -0
- package/ui/tui/constants.js +1 -1
- package/ui/tui/tasks-screen-helpers.js +52 -0
- package/ui/tui/telemetry-helpers.js +158 -0
- package/ui/tui/useTasks.js +1 -6
- package/ui/tui/useWebSocket.js +1 -7
- package/ui/tui/useWorkflows.js +120 -5
- package/ui/tui/workflows-screen-helpers.js +220 -0
- package/utils.mjs +9 -22
- package/voice/vision-session-state.mjs +257 -0
- package/voice/voice-action-dispatcher.mjs +57 -12
- package/voice/voice-agents-sdk.mjs +1 -1
- package/voice/voice-auth-manager.mjs +102 -123
- package/voice/voice-tool-definitions.mjs +7 -7
- package/voice/voice-tools.mjs +837 -101
- package/workflow/action-approval.mjs +415 -0
- package/workflow/approval-queue.mjs +1254 -0
- package/workflow/credential-store.mjs +553 -0
- package/workflow/cron-scheduler.mjs +512 -0
- package/workflow/declarative-workflows.mjs +21 -2
- package/workflow/delegation-runtime.mjs +557 -0
- package/workflow/execution-ledger.mjs +1317 -33
- package/workflow/harness-approval-node.mjs +237 -0
- package/workflow/harness-output-contract.mjs +86 -0
- package/workflow/harness-session-node.mjs +160 -0
- package/workflow/harness-subagent-node.mjs +483 -0
- package/workflow/harness-tool-node.mjs +176 -0
- package/workflow/heavy-runner-pool.mjs +71 -4
- package/workflow/manual-flows.mjs +969 -28
- package/workflow/mcp-discovery-proxy.mjs +349 -137
- package/workflow/mcp-registry.mjs +331 -27
- package/workflow/meeting-workflow-service.mjs +24 -12
- package/workflow/pipeline-workflows.mjs +44 -2
- package/workflow/pipeline.mjs +72 -28
- package/workflow/project-detection.mjs +31 -6
- package/workflow/research-evidence-sidecar.mjs +1246 -0
- package/workflow/run-evaluator.mjs +2155 -0
- package/workflow/workflow-cli.mjs +229 -2
- package/workflow/workflow-contract.mjs +130 -2
- package/workflow/workflow-engine.mjs +5533 -299
- package/workflow/workflow-nodes/actions.mjs +15558 -0
- package/workflow/workflow-nodes/agent.mjs +1905 -0
- package/workflow/workflow-nodes/conditions.mjs +307 -0
- package/workflow/workflow-nodes/definitions.mjs +176 -15
- package/workflow/workflow-nodes/flow.mjs +749 -0
- package/workflow/workflow-nodes/loop.mjs +449 -0
- package/workflow/workflow-nodes/meetings.mjs +456 -0
- package/workflow/workflow-nodes/notifications.mjs +169 -0
- package/workflow/workflow-nodes/transforms.mjs +50 -24
- package/workflow/workflow-nodes/triggers.mjs +1405 -0
- package/workflow/workflow-nodes/validation.mjs +722 -0
- package/workflow/workflow-nodes.mjs +38 -15781
- package/workflow/workflow-serializer.mjs +294 -0
- package/workflow/workflow-templates.mjs +197 -12
- package/workflow-templates/_helpers.mjs +1 -3
- package/workflow-templates/agents.mjs +255 -29
- package/workflow-templates/bosun-native.mjs +3 -1
- package/workflow-templates/code-quality.mjs +1 -217
- package/workflow-templates/continuation-loop.mjs +22 -7
- package/workflow-templates/coverage.mjs +6 -2
- package/workflow-templates/github.mjs +2263 -136
- package/workflow-templates/planning.mjs +3 -25
- package/workflow-templates/reliability.mjs +383 -12
- package/workflow-templates/research-evidence.mjs +389 -0
- package/workflow-templates/security.mjs +75 -62
- package/workflow-templates/sub-workflows.mjs +11 -3
- package/workflow-templates/task-batch.mjs +101 -11
- package/workflow-templates/task-lifecycle.mjs +313 -49
- package/workspace/context-cache.mjs +934 -102
- package/workspace/context-indexer.mjs +915 -9
- package/workspace/context-injector.mjs +144 -0
- package/workspace/execution-journal.mjs +255 -0
- package/workspace/scope-locks.mjs +481 -0
- package/workspace/shared-knowledge.mjs +496 -91
- package/workspace/shared-state-manager.mjs +344 -1
- package/workspace/skillbook-store.mjs +681 -0
- package/workspace/workspace-manager.mjs +14 -8
- package/workspace/workspace-monitor.mjs +3 -3
- package/workspace/worktree-manager.mjs +54 -12
- package/workspace/worktree-setup.mjs +634 -43
- 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 {
|
|
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 ||
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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("./
|
|
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
|
-
|