@probelabs/visor 0.1.186 → 0.1.187
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/defaults/assistant.yaml +31 -0
- package/defaults/code-talk.yaml +32 -0
- package/defaults/skills/code-explorer.yaml +6 -0
- package/dist/action-cli-bridge.d.ts.map +1 -1
- package/dist/agent-protocol/a2a-frontend.d.ts.map +1 -1
- package/dist/agent-protocol/index.d.ts.map +1 -1
- package/dist/agent-protocol/push-notification-manager.d.ts.map +1 -1
- package/dist/agent-protocol/state-transitions.d.ts.map +1 -1
- package/dist/agent-protocol/task-evaluator.d.ts.map +1 -1
- package/dist/agent-protocol/task-live-update-slack.d.ts +10 -0
- package/dist/agent-protocol/task-live-update-slack.d.ts.map +1 -1
- package/dist/agent-protocol/task-live-update-teams.d.ts.map +1 -1
- package/dist/agent-protocol/task-live-update-telegram.d.ts.map +1 -1
- package/dist/agent-protocol/task-live-updates.d.ts +10 -0
- package/dist/agent-protocol/task-live-updates.d.ts.map +1 -1
- package/dist/agent-protocol/task-progress-tool.d.ts.map +1 -1
- package/dist/agent-protocol/task-queue.d.ts.map +1 -1
- package/dist/agent-protocol/task-store.d.ts.map +1 -1
- package/dist/agent-protocol/task-stream-manager.d.ts.map +1 -1
- package/dist/agent-protocol/task-trace-resolution.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-cli-handler.d.ts.map +1 -1
- package/dist/agent-protocol/tasks-tui.d.ts.map +1 -1
- package/dist/agent-protocol/trace-serializer.d.ts +43 -1
- package/dist/agent-protocol/trace-serializer.d.ts.map +1 -1
- package/dist/agent-protocol/track-execution.d.ts.map +1 -1
- package/dist/agent-protocol/types.d.ts.map +1 -1
- package/dist/ai-review-service.d.ts +22 -1
- package/dist/ai-review-service.d.ts.map +1 -1
- package/dist/check-execution-engine.d.ts.map +1 -1
- package/dist/cli-main.d.ts +1 -0
- package/dist/cli-main.d.ts.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/commands.d.ts.map +1 -1
- package/dist/config/cli-handler.d.ts.map +1 -1
- package/dist/config/config-reloader.d.ts.map +1 -1
- package/dist/config/config-snapshot-store.d.ts.map +1 -1
- package/dist/config/config-watcher.d.ts.map +1 -1
- package/dist/config/types.d.ts.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/cron-scheduler.d.ts.map +1 -1
- package/dist/debug-visualizer/debug-span-exporter.d.ts.map +1 -1
- package/dist/debug-visualizer/trace-reader.d.ts.map +1 -1
- package/dist/debug-visualizer/ws-server.d.ts.map +1 -1
- package/dist/defaults/assistant.yaml +31 -0
- package/dist/defaults/code-talk.yaml +32 -0
- package/dist/defaults/skills/code-explorer.yaml +6 -0
- package/dist/dependency-resolver.d.ts.map +1 -1
- package/dist/docs/telemetry-setup.md +18 -2
- package/dist/email/adapter.d.ts.map +1 -1
- package/dist/email/client.d.ts.map +1 -1
- package/dist/email/markdown.d.ts.map +1 -1
- package/dist/email/polling-runner.d.ts +1 -0
- package/dist/email/polling-runner.d.ts.map +1 -1
- package/dist/engine/on-finish/orchestrator.d.ts.map +1 -1
- package/dist/engine/on-finish/utils.d.ts.map +1 -1
- package/dist/event-bus/event-bus.d.ts.map +1 -1
- package/dist/event-bus/types.d.ts.map +1 -1
- package/dist/event-mapper.d.ts.map +1 -1
- package/dist/failure-condition-evaluator.d.ts.map +1 -1
- package/dist/footer.d.ts.map +1 -1
- package/dist/frontends/email-frontend.d.ts.map +1 -1
- package/dist/frontends/github-frontend.d.ts.map +1 -1
- package/dist/frontends/host.d.ts.map +1 -1
- package/dist/frontends/ndjson-sink.d.ts.map +1 -1
- package/dist/frontends/slack-frontend.d.ts.map +1 -1
- package/dist/frontends/teams-frontend.d.ts.map +1 -1
- package/dist/frontends/telegram-frontend.d.ts.map +1 -1
- package/dist/frontends/whatsapp-frontend.d.ts.map +1 -1
- package/dist/generated/config-schema.d.ts +16 -8
- package/dist/generated/config-schema.d.ts.map +1 -1
- package/dist/git-repository-analyzer.d.ts.map +1 -1
- package/dist/github-auth.d.ts.map +1 -1
- package/dist/github-check-service.d.ts.map +1 -1
- package/dist/github-comments.d.ts.map +1 -1
- package/dist/github-reactions.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7656 -1458
- package/dist/issue-filter.d.ts.map +1 -1
- package/dist/liquid-extensions.d.ts.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/mcp-job-manager.d.ts.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/memory-store.d.ts.map +1 -1
- package/dist/output/traces/{run-2026-05-05T12-31-47-571Z.ndjson → run-2026-08-20T13-50-06-218Z.ndjson} +2699 -2699
- package/dist/output-formatters.d.ts.map +1 -1
- package/dist/policy/default-engine.d.ts.map +1 -1
- package/dist/policy/index.d.ts.map +1 -1
- package/dist/policy/policy-check-command.d.ts.map +1 -1
- package/dist/policy/types.d.ts.map +1 -1
- package/dist/pr-analyzer.d.ts.map +1 -1
- package/dist/pr-detector.d.ts.map +1 -1
- package/dist/providers/a2a-check-provider.d.ts.map +1 -1
- package/dist/providers/ai-check-provider.d.ts.map +1 -1
- package/dist/providers/api-tool-executor.d.ts.map +1 -1
- package/dist/providers/check-provider-registry.d.ts.map +1 -1
- package/dist/providers/check-provider.interface.d.ts.map +1 -1
- package/dist/providers/claude-code-check-provider.d.ts.map +1 -1
- package/dist/providers/claude-code-types.d.ts.map +1 -1
- package/dist/providers/command-check-provider.d.ts.map +1 -1
- package/dist/providers/custom-tool-executor.d.ts.map +1 -1
- package/dist/providers/git-checkout-provider.d.ts.map +1 -1
- package/dist/providers/github-ops-provider.d.ts.map +1 -1
- package/dist/providers/http-check-provider.d.ts.map +1 -1
- package/dist/providers/http-client-provider.d.ts.map +1 -1
- package/dist/providers/http-input-provider.d.ts.map +1 -1
- package/dist/providers/human-input-check-provider.d.ts.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/log-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-check-provider.d.ts.map +1 -1
- package/dist/providers/mcp-custom-sse-server.d.ts.map +1 -1
- package/dist/providers/mcp-tools.d.ts.map +1 -1
- package/dist/providers/memory-check-provider.d.ts.map +1 -1
- package/dist/providers/noop-check-provider.d.ts.map +1 -1
- package/dist/providers/script-check-provider.d.ts.map +1 -1
- package/dist/providers/utcp-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-check-provider.d.ts.map +1 -1
- package/dist/providers/workflow-tool-executor.d.ts.map +1 -1
- package/dist/reviewer.d.ts.map +1 -1
- package/dist/runners/config-reload-runtime.d.ts.map +1 -1
- package/dist/runners/graceful-restart.d.ts.map +1 -1
- package/dist/runners/mcp-server-runner.d.ts.map +1 -1
- package/dist/runners/process-cli-handler.d.ts.map +1 -1
- package/dist/runners/process-discovery.d.ts.map +1 -1
- package/dist/runners/runner-factory.d.ts.map +1 -1
- package/dist/runners/runner-host.d.ts.map +1 -1
- package/dist/runners/runner.d.ts.map +1 -1
- package/dist/runners/telemetry-init.d.ts.map +1 -1
- package/dist/sandbox/bubblewrap-sandbox.d.ts.map +1 -1
- package/dist/sandbox/cache-volume-manager.d.ts.map +1 -1
- package/dist/sandbox/check-runner.d.ts.map +1 -1
- package/dist/sandbox/compose-generator.d.ts.map +1 -1
- package/dist/sandbox/docker-compose-sandbox.d.ts.map +1 -1
- package/dist/sandbox/docker-image-sandbox.d.ts.map +1 -1
- package/dist/sandbox/env-filter.d.ts.map +1 -1
- package/dist/sandbox/index.d.ts.map +1 -1
- package/dist/sandbox/sandbox-manager.d.ts.map +1 -1
- package/dist/sandbox/sandbox-telemetry.d.ts.map +1 -1
- package/dist/sandbox/seatbelt-sandbox.d.ts.map +1 -1
- package/dist/sandbox/trace-ingester.d.ts.map +1 -1
- package/dist/sandbox/types.d.ts.map +1 -1
- package/dist/scheduler/cli-handler.d.ts.map +1 -1
- package/dist/scheduler/index.d.ts.map +1 -1
- package/dist/scheduler/message-trigger.d.ts.map +1 -1
- package/dist/scheduler/schedule-parser.d.ts.map +1 -1
- package/dist/scheduler/schedule-store.d.ts.map +1 -1
- package/dist/scheduler/schedule-tool.d.ts.map +1 -1
- package/dist/scheduler/scheduler.d.ts.map +1 -1
- package/dist/scheduler/store/index.d.ts.map +1 -1
- package/dist/scheduler/store/json-migrator.d.ts.map +1 -1
- package/dist/scheduler/store/sqlite-store.d.ts.map +1 -1
- package/dist/scheduler/store/types.d.ts.map +1 -1
- package/dist/sdk/{a2a-frontend-L3TQ7QQ6.mjs → a2a-frontend-XCQ4CQUF.mjs} +3 -3
- package/dist/sdk/{check-provider-registry-RQYIEQLJ.mjs → check-provider-registry-2D3MCWP3.mjs} +14 -11
- package/dist/sdk/{chunk-WZQMTD7W.mjs → chunk-2LLKENYD.mjs} +3 -3
- package/dist/sdk/{chunk-WZQMTD7W.mjs.map → chunk-2LLKENYD.mjs.map} +1 -1
- package/dist/sdk/{chunk-XXAEN5KU.mjs → chunk-77AKSSW2.mjs} +43 -65
- package/dist/sdk/chunk-77AKSSW2.mjs.map +1 -0
- package/dist/sdk/chunk-7QI6F66G.mjs +145 -0
- package/dist/sdk/chunk-7QI6F66G.mjs.map +1 -0
- package/dist/sdk/{chunk-NEZY2MXM.mjs → chunk-B2LQT6QU.mjs} +4 -4
- package/dist/sdk/{chunk-NEZY2MXM.mjs.map → chunk-B2LQT6QU.mjs.map} +1 -1
- package/dist/sdk/{chunk-IY5PQ5EN.mjs → chunk-BVEATT5V.mjs} +822 -51
- package/dist/sdk/chunk-BVEATT5V.mjs.map +1 -0
- package/dist/sdk/{chunk-TSX3YS3F.mjs → chunk-HW4MPCOW.mjs} +5 -139
- package/dist/sdk/chunk-HW4MPCOW.mjs.map +1 -0
- package/dist/sdk/chunk-PGBV3VZB.mjs +122 -0
- package/dist/sdk/chunk-PGBV3VZB.mjs.map +1 -0
- package/dist/sdk/{chunk-PDQTEBOJ.mjs → chunk-QT766ZLD.mjs} +5 -1
- package/dist/sdk/chunk-QT766ZLD.mjs.map +1 -0
- package/dist/sdk/{chunk-5TXIAPRA.mjs → chunk-S3GSZRYC.mjs} +2 -2
- package/dist/sdk/{chunk-NPOVLLKH.mjs → chunk-TGQBEM3Y.mjs} +570 -259
- package/dist/sdk/chunk-TGQBEM3Y.mjs.map +1 -0
- package/dist/sdk/chunk-TL3ZTYFY.mjs +141 -0
- package/dist/sdk/chunk-TL3ZTYFY.mjs.map +1 -0
- package/dist/sdk/{chunk-4UMDSYC7.mjs → chunk-VS3JE2DL.mjs} +3 -3
- package/dist/sdk/{chunk-A4WHMRK2.mjs → chunk-YETG675X.mjs} +2 -2
- package/dist/sdk/{chunk-XX6WT332.mjs → chunk-YTG5DN72.mjs} +17 -9
- package/dist/sdk/chunk-YTG5DN72.mjs.map +1 -0
- package/dist/sdk/{chunk-SRU5TFNY.mjs → chunk-YU3VR5SZ.mjs} +145 -16
- package/dist/sdk/chunk-YU3VR5SZ.mjs.map +1 -0
- package/dist/sdk/{config-BCYAVJ5S.mjs → config-H5IOLBFA.mjs} +2 -2
- package/dist/sdk/{email-frontend-P7QE4UUJ.mjs → email-frontend-AE5TYWNC.mjs} +18 -3
- package/dist/sdk/{email-frontend-P7QE4UUJ.mjs.map → email-frontend-AE5TYWNC.mjs.map} +1 -1
- package/dist/sdk/{failure-condition-evaluator-ZBOSD574.mjs → failure-condition-evaluator-66PWLV3D.mjs} +4 -4
- package/dist/sdk/{github-frontend-4WHT5JEU.mjs → github-frontend-BBFKCY4M.mjs} +4 -4
- package/dist/sdk/{host-FKR7H26C.mjs → host-UQRZDLT7.mjs} +8 -8
- package/dist/sdk/oauth2-token-cache-OS7WHLDI.mjs +13 -0
- package/dist/sdk/{routing-IB2PXYFC.mjs → routing-Q647KUMO.mjs} +5 -5
- package/dist/sdk/{schedule-tool-LB33KL2W.mjs → schedule-tool-WMGSM6IT.mjs} +14 -11
- package/dist/sdk/{schedule-tool-handler-KYWZHKSS.mjs → schedule-tool-handler-JMDHCGZ2.mjs} +14 -11
- package/dist/sdk/sdk.d.mts +14 -2
- package/dist/sdk/sdk.d.ts +14 -2
- package/dist/sdk/sdk.js +1927 -643
- package/dist/sdk/sdk.js.map +1 -1
- package/dist/sdk/sdk.mjs +13 -10
- package/dist/sdk/sdk.mjs.map +1 -1
- package/dist/sdk/session-registry-CENXWIFR.mjs +12 -0
- package/dist/sdk/{slack-frontend-K3TPGERM.mjs → slack-frontend-JMX5MUVZ.mjs} +42 -12
- package/dist/sdk/slack-frontend-JMX5MUVZ.mjs.map +1 -0
- package/dist/sdk/{task-evaluator-H3BXC7SE.mjs → task-evaluator-4QG4CYQA.mjs} +7 -6
- package/dist/sdk/task-evaluator-4QG4CYQA.mjs.map +1 -0
- package/dist/sdk/{teams-frontend-6RRW533K.mjs → teams-frontend-6MDAN7OR.mjs} +17 -4
- package/dist/sdk/teams-frontend-6MDAN7OR.mjs.map +1 -0
- package/dist/sdk/{telegram-frontend-5UA77YAT.mjs → telegram-frontend-NLX2ZHS4.mjs} +17 -4
- package/dist/sdk/telegram-frontend-NLX2ZHS4.mjs.map +1 -0
- package/dist/sdk/{trace-helpers-AMZ2524G.mjs → trace-helpers-AIUGQLK5.mjs} +3 -3
- package/dist/sdk/{trace-serializer-EBHTHZYT.mjs → trace-serializer-4TOIAVQL.mjs} +10 -2
- package/dist/sdk/{track-execution-VW6P3OYO.mjs → track-execution-CFRZXMEC.mjs} +91 -20
- package/dist/sdk/track-execution-CFRZXMEC.mjs.map +1 -0
- package/dist/sdk/{utcp-check-provider-Q4PD5EP2.mjs → utcp-check-provider-WDTSQ7DZ.mjs} +3 -2
- package/dist/sdk/{whatsapp-frontend-72XEIUIR.mjs → whatsapp-frontend-3EMMNC7R.mjs} +15 -2
- package/dist/sdk/whatsapp-frontend-3EMMNC7R.mjs.map +1 -0
- package/dist/sdk/{workflow-check-provider-N6KSDPWU.mjs → workflow-check-provider-G7A67EPP.mjs} +14 -11
- package/dist/sdk/workflow-check-provider-G7A67EPP.mjs.map +1 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/session-registry.d.ts +2 -7
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/slack/adapter.d.ts.map +1 -1
- package/dist/slack/cache-prewarmer.d.ts.map +1 -1
- package/dist/slack/client.d.ts +8 -0
- package/dist/slack/client.d.ts.map +1 -1
- package/dist/slack/markdown.d.ts.map +1 -1
- package/dist/slack/prompt-state.d.ts.map +1 -1
- package/dist/slack/rate-limiter.d.ts.map +1 -1
- package/dist/slack/schedule-tool-handler.d.ts.map +1 -1
- package/dist/slack/signature.d.ts.map +1 -1
- package/dist/slack/slack-output-adapter.d.ts.map +1 -1
- package/dist/slack/socket-runner.d.ts +1 -0
- package/dist/slack/socket-runner.d.ts.map +1 -1
- package/dist/slack/thread-cache.d.ts.map +1 -1
- package/dist/snapshot-store.d.ts.map +1 -1
- package/dist/state-machine/context/build-engine-context.d.ts.map +1 -1
- package/dist/state-machine/context/workflow-inputs.d.ts.map +1 -1
- package/dist/state-machine/dispatch/debounce-manager.d.ts +44 -0
- package/dist/state-machine/dispatch/debounce-manager.d.ts.map +1 -0
- package/dist/state-machine/dispatch/dependency-gating.d.ts.map +1 -1
- package/dist/state-machine/dispatch/execution-invoker.d.ts.map +1 -1
- package/dist/state-machine/dispatch/foreach-processor.d.ts.map +1 -1
- package/dist/state-machine/dispatch/history-snapshot.d.ts.map +1 -1
- package/dist/state-machine/dispatch/on-init-handlers.d.ts.map +1 -1
- package/dist/state-machine/dispatch/policy-gate.d.ts.map +1 -1
- package/dist/state-machine/dispatch/renderer-schema.d.ts.map +1 -1
- package/dist/state-machine/dispatch/sandbox-routing.d.ts.map +1 -1
- package/dist/state-machine/dispatch/stats-manager.d.ts.map +1 -1
- package/dist/state-machine/dispatch/template-renderer.d.ts.map +1 -1
- package/dist/state-machine/execution/summary.d.ts.map +1 -1
- package/dist/state-machine/runner.d.ts.map +1 -1
- package/dist/state-machine/states/check-running.d.ts.map +1 -1
- package/dist/state-machine/states/completed.d.ts.map +1 -1
- package/dist/state-machine/states/error.d.ts.map +1 -1
- package/dist/state-machine/states/init.d.ts.map +1 -1
- package/dist/state-machine/states/level-dispatch.d.ts.map +1 -1
- package/dist/state-machine/states/plan-ready.d.ts.map +1 -1
- package/dist/state-machine/states/routing.d.ts.map +1 -1
- package/dist/state-machine/states/wave-planning.d.ts.map +1 -1
- package/dist/state-machine/workflow-projection.d.ts.map +1 -1
- package/dist/state-machine-execution-engine.d.ts +1 -0
- package/dist/state-machine-execution-engine.d.ts.map +1 -1
- package/dist/teams/adapter.d.ts.map +1 -1
- package/dist/teams/client.d.ts.map +1 -1
- package/dist/teams/markdown.d.ts.map +1 -1
- package/dist/teams/webhook-runner.d.ts +1 -0
- package/dist/teams/webhook-runner.d.ts.map +1 -1
- package/dist/telegram/adapter.d.ts.map +1 -1
- package/dist/telegram/client.d.ts.map +1 -1
- package/dist/telegram/markdown.d.ts.map +1 -1
- package/dist/telegram/polling-runner.d.ts +1 -0
- package/dist/telegram/polling-runner.d.ts.map +1 -1
- package/dist/telemetry/fallback-ndjson.d.ts.map +1 -1
- package/dist/telemetry/file-span-exporter.d.ts.map +1 -1
- package/dist/telemetry/lazy-otel.d.ts.map +1 -1
- package/dist/telemetry/metrics.d.ts.map +1 -1
- package/dist/telemetry/opentelemetry.d.ts.map +1 -1
- package/dist/telemetry/state-capture.d.ts.map +1 -1
- package/dist/telemetry/trace-helpers.d.ts.map +1 -1
- package/dist/telemetry/trace-report-exporter.d.ts.map +1 -1
- package/dist/test-runner/assertions.d.ts.map +1 -1
- package/dist/test-runner/conversation-sugar.d.ts.map +1 -1
- package/dist/test-runner/core/environment.d.ts.map +1 -1
- package/dist/test-runner/core/fixture.d.ts.map +1 -1
- package/dist/test-runner/core/flow-stage.d.ts.map +1 -1
- package/dist/test-runner/core/mocks.d.ts.map +1 -1
- package/dist/test-runner/core/test-execution-wrapper.d.ts.map +1 -1
- package/dist/test-runner/evaluators.d.ts.map +1 -1
- package/dist/test-runner/fixture-loader.d.ts.map +1 -1
- package/dist/test-runner/index.d.ts.map +1 -1
- package/dist/test-runner/llm-judge.d.ts.map +1 -1
- package/dist/test-runner/recorders/github-recorder.d.ts.map +1 -1
- package/dist/test-runner/recorders/global-recorder.d.ts.map +1 -1
- package/dist/test-runner/recorders/slack-recorder.d.ts.map +1 -1
- package/dist/test-runner/utils/selectors.d.ts.map +1 -1
- package/dist/test-runner/validator.d.ts.map +1 -1
- package/dist/traces/{run-2026-05-05T12-31-47-571Z.ndjson → run-2026-08-20T13-50-06-218Z.ndjson} +2699 -2699
- package/dist/tui/chat-runner.d.ts.map +1 -1
- package/dist/tui/chat-state.d.ts.map +1 -1
- package/dist/tui/chat-tui.d.ts.map +1 -1
- package/dist/tui/components/chat-box.d.ts.map +1 -1
- package/dist/tui/components/input-bar.d.ts.map +1 -1
- package/dist/tui/components/status-bar.d.ts.map +1 -1
- package/dist/tui/components/trace-viewer.d.ts.map +1 -1
- package/dist/tui/index.d.ts.map +1 -1
- package/dist/tui/tui-frontend.d.ts.map +1 -1
- package/dist/types/bot.d.ts.map +1 -1
- package/dist/types/cli.d.ts.map +1 -1
- package/dist/types/config.d.ts +14 -2
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/engine.d.ts.map +1 -1
- package/dist/types/execution.d.ts.map +1 -1
- package/dist/types/git-checkout.d.ts.map +1 -1
- package/dist/types/github.d.ts.map +1 -1
- package/dist/types/workflow.d.ts.map +1 -1
- package/dist/utils/author-permissions.d.ts.map +1 -1
- package/dist/utils/child-process-error-handler.d.ts +19 -0
- package/dist/utils/child-process-error-handler.d.ts.map +1 -0
- package/dist/utils/command-executor.d.ts.map +1 -1
- package/dist/utils/comment-metadata.d.ts.map +1 -1
- package/dist/utils/config-loader.d.ts.map +1 -1
- package/dist/utils/config-merger.d.ts.map +1 -1
- package/dist/utils/diff-processor.d.ts.map +1 -1
- package/dist/utils/env-exposure.d.ts.map +1 -1
- package/dist/utils/env-resolver.d.ts.map +1 -1
- package/dist/utils/fair-concurrency-limiter.d.ts.map +1 -1
- package/dist/utils/file-exclusion.d.ts.map +1 -1
- package/dist/utils/head-sha.d.ts.map +1 -1
- package/dist/utils/human-id.d.ts.map +1 -1
- package/dist/utils/instance-id.d.ts.map +1 -1
- package/dist/utils/interactive-prompt.d.ts.map +1 -1
- package/dist/utils/issue-normalizer.d.ts.map +1 -1
- package/dist/utils/json-text-extractor.d.ts.map +1 -1
- package/dist/utils/mermaid-telemetry.d.ts.map +1 -1
- package/dist/utils/oauth2-token-cache.d.ts.map +1 -1
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/sandbox.d.ts.map +1 -1
- package/dist/utils/script-memory-ops.d.ts.map +1 -1
- package/dist/utils/script-tool-environment.d.ts.map +1 -1
- package/dist/utils/stdin-reader.d.ts.map +1 -1
- package/dist/utils/template-context.d.ts.map +1 -1
- package/dist/utils/tool-resolver.d.ts.map +1 -1
- package/dist/utils/toolkit-expander.d.ts.map +1 -1
- package/dist/utils/tracer-init.d.ts.map +1 -1
- package/dist/utils/ui-helpers.d.ts.map +1 -1
- package/dist/utils/user-facing-error.d.ts +4 -0
- package/dist/utils/user-facing-error.d.ts.map +1 -0
- package/dist/utils/workspace-manager.d.ts +5 -0
- package/dist/utils/workspace-manager.d.ts.map +1 -1
- package/dist/utils/worktree-cleanup.d.ts +28 -0
- package/dist/utils/worktree-cleanup.d.ts.map +1 -1
- package/dist/utils/worktree-manager.d.ts +6 -0
- package/dist/utils/worktree-manager.d.ts.map +1 -1
- package/dist/webhook-server.d.ts.map +1 -1
- package/dist/whatsapp/adapter.d.ts.map +1 -1
- package/dist/whatsapp/client.d.ts.map +1 -1
- package/dist/whatsapp/markdown.d.ts.map +1 -1
- package/dist/whatsapp/webhook-runner.d.ts +1 -0
- package/dist/whatsapp/webhook-runner.d.ts.map +1 -1
- package/dist/workflow-executor.d.ts +7 -0
- package/dist/workflow-executor.d.ts.map +1 -1
- package/dist/workflow-registry.d.ts.map +1 -1
- package/package.json +3 -3
- package/dist/output/traces/run-2026-05-05T12-31-09-587Z.ndjson +0 -252
- package/dist/sdk/chunk-IY5PQ5EN.mjs.map +0 -1
- package/dist/sdk/chunk-NPOVLLKH.mjs.map +0 -1
- package/dist/sdk/chunk-PDQTEBOJ.mjs.map +0 -1
- package/dist/sdk/chunk-SRU5TFNY.mjs.map +0 -1
- package/dist/sdk/chunk-TSX3YS3F.mjs.map +0 -1
- package/dist/sdk/chunk-XX6WT332.mjs.map +0 -1
- package/dist/sdk/chunk-XXAEN5KU.mjs.map +0 -1
- package/dist/sdk/session-registry-6PV6SGEJ.mjs +0 -10
- package/dist/sdk/slack-frontend-K3TPGERM.mjs.map +0 -1
- package/dist/sdk/task-evaluator-H3BXC7SE.mjs.map +0 -1
- package/dist/sdk/teams-frontend-6RRW533K.mjs.map +0 -1
- package/dist/sdk/telegram-frontend-5UA77YAT.mjs.map +0 -1
- package/dist/sdk/trace-reader-OVE4DL2D.mjs +0 -270
- package/dist/sdk/trace-reader-OVE4DL2D.mjs.map +0 -1
- package/dist/sdk/track-execution-VW6P3OYO.mjs.map +0 -1
- package/dist/sdk/whatsapp-frontend-72XEIUIR.mjs.map +0 -1
- package/dist/traces/run-2026-05-05T12-31-09-587Z.ndjson +0 -252
- /package/dist/sdk/{a2a-frontend-L3TQ7QQ6.mjs.map → a2a-frontend-XCQ4CQUF.mjs.map} +0 -0
- /package/dist/sdk/{check-provider-registry-RQYIEQLJ.mjs.map → check-provider-registry-2D3MCWP3.mjs.map} +0 -0
- /package/dist/sdk/{chunk-5TXIAPRA.mjs.map → chunk-S3GSZRYC.mjs.map} +0 -0
- /package/dist/sdk/{chunk-4UMDSYC7.mjs.map → chunk-VS3JE2DL.mjs.map} +0 -0
- /package/dist/sdk/{chunk-A4WHMRK2.mjs.map → chunk-YETG675X.mjs.map} +0 -0
- /package/dist/sdk/{config-BCYAVJ5S.mjs.map → config-H5IOLBFA.mjs.map} +0 -0
- /package/dist/sdk/{failure-condition-evaluator-ZBOSD574.mjs.map → failure-condition-evaluator-66PWLV3D.mjs.map} +0 -0
- /package/dist/sdk/{github-frontend-4WHT5JEU.mjs.map → github-frontend-BBFKCY4M.mjs.map} +0 -0
- /package/dist/sdk/{host-FKR7H26C.mjs.map → host-UQRZDLT7.mjs.map} +0 -0
- /package/dist/sdk/{routing-IB2PXYFC.mjs.map → oauth2-token-cache-OS7WHLDI.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-LB33KL2W.mjs.map → routing-Q647KUMO.mjs.map} +0 -0
- /package/dist/sdk/{schedule-tool-handler-KYWZHKSS.mjs.map → schedule-tool-WMGSM6IT.mjs.map} +0 -0
- /package/dist/sdk/{session-registry-6PV6SGEJ.mjs.map → schedule-tool-handler-JMDHCGZ2.mjs.map} +0 -0
- /package/dist/sdk/{trace-helpers-AMZ2524G.mjs.map → session-registry-CENXWIFR.mjs.map} +0 -0
- /package/dist/sdk/{trace-serializer-EBHTHZYT.mjs.map → trace-helpers-AIUGQLK5.mjs.map} +0 -0
- /package/dist/sdk/{utcp-check-provider-Q4PD5EP2.mjs.map → trace-serializer-4TOIAVQL.mjs.map} +0 -0
- /package/dist/sdk/{workflow-check-provider-N6KSDPWU.mjs.map → utcp-check-provider-WDTSQ7DZ.mjs.map} +0 -0
package/dist/sdk/sdk.js
CHANGED
|
@@ -1529,7 +1529,7 @@ var require_package = __commonJS({
|
|
|
1529
1529
|
"package.json"(exports2, module2) {
|
|
1530
1530
|
module2.exports = {
|
|
1531
1531
|
name: "@probelabs/visor",
|
|
1532
|
-
version: "0.1.
|
|
1532
|
+
version: "0.1.187",
|
|
1533
1533
|
main: "dist/index.js",
|
|
1534
1534
|
bin: {
|
|
1535
1535
|
visor: "./dist/index.js"
|
|
@@ -1630,7 +1630,7 @@ var require_package = __commonJS({
|
|
|
1630
1630
|
"@apidevtools/swagger-parser": "^12.1.0",
|
|
1631
1631
|
"@grammyjs/runner": "^2.0.3",
|
|
1632
1632
|
"@modelcontextprotocol/sdk": "^1.25.3",
|
|
1633
|
-
"@nyariv/sandboxjs": "github:probelabs/SandboxJS#
|
|
1633
|
+
"@nyariv/sandboxjs": "github:probelabs/SandboxJS#d0d8c8ab3113d3b73e08b79b88a4b834ba3fae11",
|
|
1634
1634
|
"@octokit/action": "^8.0.2",
|
|
1635
1635
|
"@octokit/auth-app": "^8.1.0",
|
|
1636
1636
|
"@octokit/core": "^7.0.3",
|
|
@@ -1649,7 +1649,7 @@ var require_package = __commonJS({
|
|
|
1649
1649
|
"@opentelemetry/sdk-node": "^0.203.0",
|
|
1650
1650
|
"@opentelemetry/sdk-trace-base": "^1.30.1",
|
|
1651
1651
|
"@opentelemetry/semantic-conventions": "^1.30.1",
|
|
1652
|
-
"@probelabs/probe": "^0.6.0-
|
|
1652
|
+
"@probelabs/probe": "^0.6.0-rc318",
|
|
1653
1653
|
"@types/commander": "^2.12.0",
|
|
1654
1654
|
"@types/uuid": "^10.0.0",
|
|
1655
1655
|
"@utcp/file": "^1.1.0",
|
|
@@ -8756,6 +8756,126 @@ var init_policy_gate = __esm({
|
|
|
8756
8756
|
}
|
|
8757
8757
|
});
|
|
8758
8758
|
|
|
8759
|
+
// src/state-machine/dispatch/debounce-manager.ts
|
|
8760
|
+
function effectiveDebounceMs(requested) {
|
|
8761
|
+
const override = process.env.VISOR_DEBOUNCE_OVERRIDE;
|
|
8762
|
+
if (override !== void 0) {
|
|
8763
|
+
const ms = parseInt(override, 10);
|
|
8764
|
+
if (!isNaN(ms) && ms >= 0) return ms;
|
|
8765
|
+
}
|
|
8766
|
+
return requested;
|
|
8767
|
+
}
|
|
8768
|
+
function getDebounceManager() {
|
|
8769
|
+
if (!__instance) __instance = new DebounceManager();
|
|
8770
|
+
return __instance;
|
|
8771
|
+
}
|
|
8772
|
+
var DebounceManager, __instance;
|
|
8773
|
+
var init_debounce_manager = __esm({
|
|
8774
|
+
"src/state-machine/dispatch/debounce-manager.ts"() {
|
|
8775
|
+
"use strict";
|
|
8776
|
+
init_logger();
|
|
8777
|
+
DebounceManager = class {
|
|
8778
|
+
pending = /* @__PURE__ */ new Map();
|
|
8779
|
+
/** Waiters that were superseded and should resolve as 'debounced' */
|
|
8780
|
+
superseded = /* @__PURE__ */ new Map();
|
|
8781
|
+
/** Keys currently executing — prevents parallel runs */
|
|
8782
|
+
executing = /* @__PURE__ */ new Set();
|
|
8783
|
+
/**
|
|
8784
|
+
* Enqueue an execution. Returns 'executed' if this invocation won the
|
|
8785
|
+
* debounce race, or 'debounced' if it was superseded by a later one
|
|
8786
|
+
* or if the function is already executing for this key.
|
|
8787
|
+
*/
|
|
8788
|
+
enqueue(key, debounceMs, fn) {
|
|
8789
|
+
if (this.executing.has(key)) {
|
|
8790
|
+
logger.info(`[DebounceManager] ${key}: already executing, skipping this invocation`);
|
|
8791
|
+
return Promise.resolve({ outcome: "debounced" });
|
|
8792
|
+
}
|
|
8793
|
+
const actualMs = effectiveDebounceMs(debounceMs);
|
|
8794
|
+
return new Promise(
|
|
8795
|
+
(resolve17, reject) => {
|
|
8796
|
+
const existing = this.pending.get(key);
|
|
8797
|
+
if (existing) {
|
|
8798
|
+
clearTimeout(existing.timer);
|
|
8799
|
+
if (!this.superseded.has(key)) {
|
|
8800
|
+
this.superseded.set(key, []);
|
|
8801
|
+
}
|
|
8802
|
+
this.superseded.get(key).push(existing.resolve);
|
|
8803
|
+
existing.invocations++;
|
|
8804
|
+
logger.info(
|
|
8805
|
+
`[DebounceManager] ${key}: invocation #${existing.invocations}, resetting ${actualMs}ms timer`
|
|
8806
|
+
);
|
|
8807
|
+
} else {
|
|
8808
|
+
logger.info(
|
|
8809
|
+
`[DebounceManager] ${key}: first invocation, starting ${actualMs}ms debounce`
|
|
8810
|
+
);
|
|
8811
|
+
}
|
|
8812
|
+
const invocations = existing ? existing.invocations : 1;
|
|
8813
|
+
const firstTime = existing ? existing.firstInvocationTime : Date.now();
|
|
8814
|
+
const timer = setTimeout(async () => {
|
|
8815
|
+
this.pending.delete(key);
|
|
8816
|
+
const waiters2 = this.superseded.get(key) || [];
|
|
8817
|
+
this.superseded.delete(key);
|
|
8818
|
+
for (const w of waiters2) {
|
|
8819
|
+
w({ outcome: "debounced" });
|
|
8820
|
+
}
|
|
8821
|
+
const elapsed = Date.now() - firstTime;
|
|
8822
|
+
logger.info(
|
|
8823
|
+
`[DebounceManager] ${key}: executing after ${invocations} invocation(s) over ${elapsed}ms`
|
|
8824
|
+
);
|
|
8825
|
+
this.executing.add(key);
|
|
8826
|
+
try {
|
|
8827
|
+
const result = await fn();
|
|
8828
|
+
resolve17({ outcome: "executed", result });
|
|
8829
|
+
} catch (err) {
|
|
8830
|
+
reject(err);
|
|
8831
|
+
} finally {
|
|
8832
|
+
this.executing.delete(key);
|
|
8833
|
+
logger.info(`[DebounceManager] ${key}: execution finished`);
|
|
8834
|
+
}
|
|
8835
|
+
}, actualMs);
|
|
8836
|
+
this.pending.set(key, {
|
|
8837
|
+
timer,
|
|
8838
|
+
invocations,
|
|
8839
|
+
firstInvocationTime: firstTime,
|
|
8840
|
+
resolve: resolve17,
|
|
8841
|
+
reject,
|
|
8842
|
+
fn
|
|
8843
|
+
});
|
|
8844
|
+
}
|
|
8845
|
+
);
|
|
8846
|
+
}
|
|
8847
|
+
/** Cancel a pending debounce. All waiters resolve as 'debounced'. */
|
|
8848
|
+
cancel(key) {
|
|
8849
|
+
const entry = this.pending.get(key);
|
|
8850
|
+
if (!entry) return false;
|
|
8851
|
+
clearTimeout(entry.timer);
|
|
8852
|
+
this.pending.delete(key);
|
|
8853
|
+
entry.resolve({ outcome: "debounced" });
|
|
8854
|
+
const waiters2 = this.superseded.get(key) || [];
|
|
8855
|
+
this.superseded.delete(key);
|
|
8856
|
+
for (const w of waiters2) {
|
|
8857
|
+
w({ outcome: "debounced" });
|
|
8858
|
+
}
|
|
8859
|
+
return true;
|
|
8860
|
+
}
|
|
8861
|
+
/** Cancel all pending debounces. */
|
|
8862
|
+
clear() {
|
|
8863
|
+
for (const [key] of this.pending) {
|
|
8864
|
+
this.cancel(key);
|
|
8865
|
+
}
|
|
8866
|
+
}
|
|
8867
|
+
/** Number of pending debounce keys. */
|
|
8868
|
+
get size() {
|
|
8869
|
+
return this.pending.size;
|
|
8870
|
+
}
|
|
8871
|
+
/** Check if a key is currently executing. */
|
|
8872
|
+
isExecuting(key) {
|
|
8873
|
+
return this.executing.has(key);
|
|
8874
|
+
}
|
|
8875
|
+
};
|
|
8876
|
+
}
|
|
8877
|
+
});
|
|
8878
|
+
|
|
8759
8879
|
// src/state-machine/dispatch/history-snapshot.ts
|
|
8760
8880
|
var history_snapshot_exports = {};
|
|
8761
8881
|
__export(history_snapshot_exports, {
|
|
@@ -9192,12 +9312,11 @@ var SessionRegistry;
|
|
|
9192
9312
|
var init_session_registry = __esm({
|
|
9193
9313
|
"src/session-registry.ts"() {
|
|
9194
9314
|
"use strict";
|
|
9315
|
+
init_logger();
|
|
9195
9316
|
SessionRegistry = class _SessionRegistry {
|
|
9196
9317
|
static instance;
|
|
9197
9318
|
sessions = /* @__PURE__ */ new Map();
|
|
9198
|
-
exitHandlerRegistered = false;
|
|
9199
9319
|
constructor() {
|
|
9200
|
-
this.registerExitHandlers();
|
|
9201
9320
|
}
|
|
9202
9321
|
/**
|
|
9203
9322
|
* Get the singleton instance of SessionRegistry
|
|
@@ -9212,7 +9331,7 @@ var init_session_registry = __esm({
|
|
|
9212
9331
|
* Register a ProbeAgent session
|
|
9213
9332
|
*/
|
|
9214
9333
|
registerSession(sessionId, agent) {
|
|
9215
|
-
|
|
9334
|
+
logger.debug(`[SessionRegistry] Registering AI session: ${sessionId}`);
|
|
9216
9335
|
this.sessions.set(sessionId, agent);
|
|
9217
9336
|
}
|
|
9218
9337
|
/**
|
|
@@ -9221,23 +9340,24 @@ var init_session_registry = __esm({
|
|
|
9221
9340
|
getSession(sessionId) {
|
|
9222
9341
|
const agent = this.sessions.get(sessionId);
|
|
9223
9342
|
if (agent) {
|
|
9224
|
-
|
|
9343
|
+
logger.debug(`[SessionRegistry] Reusing AI session: ${sessionId}`);
|
|
9225
9344
|
}
|
|
9226
9345
|
return agent;
|
|
9227
9346
|
}
|
|
9228
9347
|
/**
|
|
9229
9348
|
* Remove a session from the registry
|
|
9230
9349
|
*/
|
|
9231
|
-
unregisterSession(sessionId) {
|
|
9350
|
+
async unregisterSession(sessionId) {
|
|
9232
9351
|
if (this.sessions.has(sessionId)) {
|
|
9233
|
-
console.error(`\u{1F5D1}\uFE0F Unregistering AI session: ${sessionId}`);
|
|
9234
9352
|
const agent = this.sessions.get(sessionId);
|
|
9235
9353
|
this.sessions.delete(sessionId);
|
|
9236
9354
|
if (agent && typeof agent.cleanup === "function") {
|
|
9237
9355
|
try {
|
|
9238
|
-
agent.cleanup();
|
|
9356
|
+
await agent.cleanup();
|
|
9239
9357
|
} catch (error) {
|
|
9240
|
-
|
|
9358
|
+
logger.warn(
|
|
9359
|
+
`[SessionRegistry] Failed to cleanup ProbeAgent session ${sessionId}: ${error}`
|
|
9360
|
+
);
|
|
9241
9361
|
}
|
|
9242
9362
|
}
|
|
9243
9363
|
}
|
|
@@ -9245,17 +9365,26 @@ var init_session_registry = __esm({
|
|
|
9245
9365
|
/**
|
|
9246
9366
|
* Clear all sessions (useful for cleanup)
|
|
9247
9367
|
*/
|
|
9248
|
-
clearAllSessions() {
|
|
9249
|
-
|
|
9250
|
-
|
|
9251
|
-
if (agent && typeof agent.cleanup === "function") {
|
|
9252
|
-
try {
|
|
9253
|
-
agent.cleanup();
|
|
9254
|
-
} catch {
|
|
9255
|
-
}
|
|
9256
|
-
}
|
|
9368
|
+
async clearAllSessions() {
|
|
9369
|
+
if (this.sessions.size === 0) {
|
|
9370
|
+
return;
|
|
9257
9371
|
}
|
|
9372
|
+
logger.debug(`[SessionRegistry] Clearing all AI sessions (${this.sessions.size} sessions)`);
|
|
9373
|
+
const sessions = Array.from(this.sessions.entries());
|
|
9258
9374
|
this.sessions.clear();
|
|
9375
|
+
await Promise.allSettled(
|
|
9376
|
+
sessions.map(async ([sessionId, agent]) => {
|
|
9377
|
+
if (agent && typeof agent.cleanup === "function") {
|
|
9378
|
+
try {
|
|
9379
|
+
await agent.cleanup();
|
|
9380
|
+
} catch (error) {
|
|
9381
|
+
logger.warn(
|
|
9382
|
+
`[SessionRegistry] Failed to cleanup ProbeAgent session ${sessionId}: ${error}`
|
|
9383
|
+
);
|
|
9384
|
+
}
|
|
9385
|
+
}
|
|
9386
|
+
})
|
|
9387
|
+
);
|
|
9259
9388
|
}
|
|
9260
9389
|
/**
|
|
9261
9390
|
* Get all active session IDs
|
|
@@ -9280,7 +9409,7 @@ var init_session_registry = __esm({
|
|
|
9280
9409
|
async cloneSession(sourceSessionId, newSessionId, checkName) {
|
|
9281
9410
|
const sourceAgent = this.sessions.get(sourceSessionId);
|
|
9282
9411
|
if (!sourceAgent) {
|
|
9283
|
-
|
|
9412
|
+
logger.warn(`[SessionRegistry] Cannot clone session: ${sourceSessionId} not found`);
|
|
9284
9413
|
return void 0;
|
|
9285
9414
|
}
|
|
9286
9415
|
try {
|
|
@@ -9303,69 +9432,34 @@ var init_session_registry = __esm({
|
|
|
9303
9432
|
clonedAgent._traceFilePath = tracerResult.filePath;
|
|
9304
9433
|
}
|
|
9305
9434
|
} catch (traceError) {
|
|
9306
|
-
|
|
9307
|
-
|
|
9308
|
-
traceError
|
|
9435
|
+
logger.warn(
|
|
9436
|
+
`[SessionRegistry] Failed to initialize tracing for cloned session ${newSessionId}: ${traceError}`
|
|
9309
9437
|
);
|
|
9310
9438
|
}
|
|
9311
9439
|
}
|
|
9312
9440
|
if (sourceAgent._mcpInitialized && typeof clonedAgent.initialize === "function") {
|
|
9313
9441
|
try {
|
|
9314
9442
|
await clonedAgent.initialize();
|
|
9315
|
-
|
|
9443
|
+
logger.debug(
|
|
9444
|
+
`[SessionRegistry] Initialized MCP tools for cloned session ${newSessionId}`
|
|
9445
|
+
);
|
|
9316
9446
|
} catch (initError) {
|
|
9317
|
-
|
|
9447
|
+
logger.warn(
|
|
9448
|
+
`[SessionRegistry] Failed to initialize cloned agent ${newSessionId}: ${initError}`
|
|
9449
|
+
);
|
|
9318
9450
|
}
|
|
9319
9451
|
}
|
|
9320
9452
|
const historyLength = clonedAgent.history?.length || 0;
|
|
9321
|
-
|
|
9322
|
-
|
|
9453
|
+
logger.debug(
|
|
9454
|
+
`[SessionRegistry] Cloned session ${sourceSessionId} -> ${newSessionId} using ProbeAgent.clone() (${historyLength} messages, internal messages filtered)`
|
|
9323
9455
|
);
|
|
9324
9456
|
this.registerSession(newSessionId, clonedAgent);
|
|
9325
9457
|
return clonedAgent;
|
|
9326
9458
|
} catch (error) {
|
|
9327
|
-
|
|
9459
|
+
logger.warn(`[SessionRegistry] Failed to clone session ${sourceSessionId}: ${error}`);
|
|
9328
9460
|
return void 0;
|
|
9329
9461
|
}
|
|
9330
9462
|
}
|
|
9331
|
-
/**
|
|
9332
|
-
* Register process exit handlers to cleanup sessions on exit
|
|
9333
|
-
*/
|
|
9334
|
-
registerExitHandlers() {
|
|
9335
|
-
if (this.exitHandlerRegistered) {
|
|
9336
|
-
return;
|
|
9337
|
-
}
|
|
9338
|
-
const cleanupAndExit = (signal) => {
|
|
9339
|
-
if (this.sessions.size > 0) {
|
|
9340
|
-
console.error(`
|
|
9341
|
-
\u{1F9F9} [${signal}] Cleaning up ${this.sessions.size} active AI sessions...`);
|
|
9342
|
-
this.clearAllSessions();
|
|
9343
|
-
}
|
|
9344
|
-
};
|
|
9345
|
-
process.on("exit", () => {
|
|
9346
|
-
if (this.sessions.size > 0) {
|
|
9347
|
-
console.error(`\u{1F9F9} [exit] Cleaning up ${this.sessions.size} active AI sessions...`);
|
|
9348
|
-
for (const [, agent] of this.sessions.entries()) {
|
|
9349
|
-
if (agent && typeof agent.cleanup === "function") {
|
|
9350
|
-
try {
|
|
9351
|
-
agent.cleanup();
|
|
9352
|
-
} catch {
|
|
9353
|
-
}
|
|
9354
|
-
}
|
|
9355
|
-
}
|
|
9356
|
-
this.sessions.clear();
|
|
9357
|
-
}
|
|
9358
|
-
});
|
|
9359
|
-
process.on("SIGINT", () => {
|
|
9360
|
-
cleanupAndExit("SIGINT");
|
|
9361
|
-
process.exit(0);
|
|
9362
|
-
});
|
|
9363
|
-
process.on("SIGTERM", () => {
|
|
9364
|
-
cleanupAndExit("SIGTERM");
|
|
9365
|
-
process.exit(0);
|
|
9366
|
-
});
|
|
9367
|
-
this.exitHandlerRegistered = true;
|
|
9368
|
-
}
|
|
9369
9463
|
};
|
|
9370
9464
|
}
|
|
9371
9465
|
});
|
|
@@ -9465,6 +9559,136 @@ var init_comment_metadata = __esm({
|
|
|
9465
9559
|
}
|
|
9466
9560
|
});
|
|
9467
9561
|
|
|
9562
|
+
// src/utils/user-facing-error.ts
|
|
9563
|
+
function normalizeWhitespace(text) {
|
|
9564
|
+
return text.replace(/\s+/g, " ").trim();
|
|
9565
|
+
}
|
|
9566
|
+
function stripWrapperPrefixes(text) {
|
|
9567
|
+
let cleaned = text.trim();
|
|
9568
|
+
const wrapperPatterns = [
|
|
9569
|
+
/^AI analysis failed:\s*/i,
|
|
9570
|
+
/^Claude Code analysis failed:\s*/i,
|
|
9571
|
+
/^ProbeAgent execution failed:\s*/i,
|
|
9572
|
+
/^Workflow step '[^']+' failed:\s*/i,
|
|
9573
|
+
/^Error:\s*/i
|
|
9574
|
+
];
|
|
9575
|
+
let changed = true;
|
|
9576
|
+
while (changed) {
|
|
9577
|
+
changed = false;
|
|
9578
|
+
for (const pattern of wrapperPatterns) {
|
|
9579
|
+
const next = cleaned.replace(pattern, "");
|
|
9580
|
+
if (next !== cleaned) {
|
|
9581
|
+
cleaned = next.trim();
|
|
9582
|
+
changed = true;
|
|
9583
|
+
}
|
|
9584
|
+
}
|
|
9585
|
+
}
|
|
9586
|
+
return cleaned;
|
|
9587
|
+
}
|
|
9588
|
+
function collectErrorStrings(value, seen = /* @__PURE__ */ new WeakSet(), depth = 0, out = []) {
|
|
9589
|
+
if (value == null || depth > 3) return out;
|
|
9590
|
+
if (typeof value === "string") {
|
|
9591
|
+
if (value.trim()) out.push(value.trim());
|
|
9592
|
+
return out;
|
|
9593
|
+
}
|
|
9594
|
+
if (typeof value !== "object") {
|
|
9595
|
+
out.push(String(value));
|
|
9596
|
+
return out;
|
|
9597
|
+
}
|
|
9598
|
+
if (seen.has(value)) return out;
|
|
9599
|
+
seen.add(value);
|
|
9600
|
+
const obj = value;
|
|
9601
|
+
const candidateKeys = ["message", "responseBody", "error", "details", "body", "cause"];
|
|
9602
|
+
for (const key of candidateKeys) {
|
|
9603
|
+
if (key in obj) collectErrorStrings(obj[key], seen, depth + 1, out);
|
|
9604
|
+
}
|
|
9605
|
+
return out;
|
|
9606
|
+
}
|
|
9607
|
+
function matchUserFacingErrorMessage(raw) {
|
|
9608
|
+
if (/no api key provided and neither claude nor codex command found/i.test(raw)) {
|
|
9609
|
+
return "No API key provided and neither claude nor codex command found.";
|
|
9610
|
+
}
|
|
9611
|
+
if (/budget limit exceeded|budget exceeded/i.test(raw)) {
|
|
9612
|
+
return "The AI provider budget limit was exceeded before a response could be generated. Please retry later or switch model/provider.";
|
|
9613
|
+
}
|
|
9614
|
+
if (/currently experiencing high demand|status.?unavailable|spikes in demand are usually temporary/i.test(
|
|
9615
|
+
raw
|
|
9616
|
+
)) {
|
|
9617
|
+
return "The AI provider is currently experiencing high demand and did not generate a response. Please retry shortly.";
|
|
9618
|
+
}
|
|
9619
|
+
if (/rate limit/i.test(raw)) {
|
|
9620
|
+
return "The AI provider rate limit was reached before a response could be generated. Please retry shortly.";
|
|
9621
|
+
}
|
|
9622
|
+
if (/401|authentication|api key|invalid api key|unauthorized/i.test(raw)) {
|
|
9623
|
+
return "The AI provider request was rejected before a response could be generated. Please check credentials and provider access.";
|
|
9624
|
+
}
|
|
9625
|
+
if (/403|forbidden/i.test(raw)) {
|
|
9626
|
+
return "The AI provider rejected the request before a response could be generated. This is usually caused by budget limits, access policy, or model permissions.";
|
|
9627
|
+
}
|
|
9628
|
+
if (/^request timed out$/i.test(raw) || /timed out|timeout/i.test(raw) && /(failed to get response from ai model|ai request|probeagent|provider|model|streamtext)/i.test(
|
|
9629
|
+
raw
|
|
9630
|
+
)) {
|
|
9631
|
+
return "The AI request timed out before a response could be generated. Please retry.";
|
|
9632
|
+
}
|
|
9633
|
+
if (/no output generated|failed to get response from ai model/i.test(raw)) {
|
|
9634
|
+
return "The AI provider failed before generating any response. This is usually caused by a provider-side limit, rate limit, or outage. Please retry.";
|
|
9635
|
+
}
|
|
9636
|
+
return null;
|
|
9637
|
+
}
|
|
9638
|
+
function formatUserFacingExecutionError(error) {
|
|
9639
|
+
const collected = collectErrorStrings(error);
|
|
9640
|
+
const raw = normalizeWhitespace(collected.join(" | ") || String(error ?? "Unknown error"));
|
|
9641
|
+
const matched = matchUserFacingErrorMessage(raw);
|
|
9642
|
+
if (matched) return matched;
|
|
9643
|
+
const cleaned = normalizeWhitespace(stripWrapperPrefixes(raw));
|
|
9644
|
+
return cleaned || "The request failed before a response could be generated. Please retry.";
|
|
9645
|
+
}
|
|
9646
|
+
function formatUserFacingExecutionMessage(message) {
|
|
9647
|
+
return formatUserFacingExecutionError(message);
|
|
9648
|
+
}
|
|
9649
|
+
function issuePriority(issue) {
|
|
9650
|
+
const ruleId = String(issue.ruleId || "");
|
|
9651
|
+
const message = String(issue.message || "");
|
|
9652
|
+
if (ruleId.startsWith("system/") || ruleId.endsWith("/error")) return 0;
|
|
9653
|
+
if (ruleId.includes("timeout") || /timed out|timeout/i.test(message)) return 1;
|
|
9654
|
+
if (ruleId.includes("contract/guarantee_failed") && /output\??\.\s*text|\btext\b.*length\s*>\s*0|\btext\b.*trim/i.test(message)) {
|
|
9655
|
+
return 2;
|
|
9656
|
+
}
|
|
9657
|
+
return 3;
|
|
9658
|
+
}
|
|
9659
|
+
function formatGuaranteeFailureMessage(issue) {
|
|
9660
|
+
const ruleId = String(issue.ruleId || "");
|
|
9661
|
+
const message = String(issue.message || "");
|
|
9662
|
+
if (!ruleId.includes("contract/guarantee_failed")) return null;
|
|
9663
|
+
if (/output\??\.\s*text|\btext\b.*length\s*>\s*0|\btext\b.*trim/i.test(message)) {
|
|
9664
|
+
return "The assistant failed to produce a response. This is usually caused by a provider-side limit, outage, or invalid structured output. Please retry.";
|
|
9665
|
+
}
|
|
9666
|
+
return null;
|
|
9667
|
+
}
|
|
9668
|
+
function summarizeUserFacingIssues(issues) {
|
|
9669
|
+
if (!Array.isArray(issues) || issues.length === 0) return null;
|
|
9670
|
+
const candidates = issues.filter(
|
|
9671
|
+
(issue) => !!issue && typeof issue === "object" && String(issue.severity || "").toLowerCase() === "error"
|
|
9672
|
+
);
|
|
9673
|
+
if (candidates.length === 0) return null;
|
|
9674
|
+
const prioritized = [...candidates].sort((a, b) => issuePriority(a) - issuePriority(b));
|
|
9675
|
+
const seen = /* @__PURE__ */ new Set();
|
|
9676
|
+
const messages = [];
|
|
9677
|
+
for (const issue of prioritized) {
|
|
9678
|
+
const formatted = formatGuaranteeFailureMessage(issue) || formatUserFacingExecutionMessage(String(issue.message || "Execution error"));
|
|
9679
|
+
if (!formatted || seen.has(formatted)) continue;
|
|
9680
|
+
seen.add(formatted);
|
|
9681
|
+
messages.push(formatted);
|
|
9682
|
+
if (messages.length >= 2) break;
|
|
9683
|
+
}
|
|
9684
|
+
return messages.length > 0 ? messages.join("\n") : null;
|
|
9685
|
+
}
|
|
9686
|
+
var init_user_facing_error = __esm({
|
|
9687
|
+
"src/utils/user-facing-error.ts"() {
|
|
9688
|
+
"use strict";
|
|
9689
|
+
}
|
|
9690
|
+
});
|
|
9691
|
+
|
|
9468
9692
|
// src/slack/markdown.ts
|
|
9469
9693
|
async function renderMermaidToPng(mermaidCode) {
|
|
9470
9694
|
const tmpDir = os.tmpdir();
|
|
@@ -9523,6 +9747,10 @@ async function renderMermaidToPng(mermaidCode) {
|
|
|
9523
9747
|
proc.stderr?.on("data", (data) => {
|
|
9524
9748
|
stderr += data.toString();
|
|
9525
9749
|
});
|
|
9750
|
+
proc.stderr?.on("error", () => {
|
|
9751
|
+
});
|
|
9752
|
+
proc.stdout?.on("error", () => {
|
|
9753
|
+
});
|
|
9526
9754
|
proc.on("close", (code) => {
|
|
9527
9755
|
if (code === 0) {
|
|
9528
9756
|
resolve17({ success: true });
|
|
@@ -9716,16 +9944,36 @@ function createProbeTracerAdapter(fallbackTracer) {
|
|
|
9716
9944
|
"negotiated_timeout.observer_exhausted",
|
|
9717
9945
|
"negotiated_timeout.abort_summary"
|
|
9718
9946
|
]);
|
|
9947
|
+
const isSpanWorthyEvent = (name) => SPAN_WORTHY_EVENTS.has(name) || name.startsWith("task.");
|
|
9719
9948
|
const emitEvent = (name, attrs) => {
|
|
9720
9949
|
try {
|
|
9721
9950
|
const flat = flattenAttrs(attrs);
|
|
9722
|
-
if (
|
|
9951
|
+
if (isSpanWorthyEvent(name)) {
|
|
9723
9952
|
try {
|
|
9953
|
+
const startHr = process.hrtime();
|
|
9724
9954
|
const tracer = trace.getTracer("visor");
|
|
9725
9955
|
const childSpan = tracer.startSpan(`probe.event.${name}`, {
|
|
9726
9956
|
attributes: { "probe.event.name": name, ...flat }
|
|
9727
9957
|
});
|
|
9728
9958
|
childSpan.end();
|
|
9959
|
+
try {
|
|
9960
|
+
const activeParent = trace.getActiveSpan?.();
|
|
9961
|
+
const parentCtx = activeParent?.spanContext?.();
|
|
9962
|
+
const childCtx = childSpan.spanContext?.();
|
|
9963
|
+
const endHr = process.hrtime();
|
|
9964
|
+
emitNdjsonFullSpan({
|
|
9965
|
+
name: `probe.event.${name}`,
|
|
9966
|
+
traceId: childCtx?.traceId || parentCtx?.traceId,
|
|
9967
|
+
spanId: childCtx?.spanId,
|
|
9968
|
+
parentSpanId: parentCtx?.spanId,
|
|
9969
|
+
startTime: [startHr[0], startHr[1]],
|
|
9970
|
+
endTime: [endHr[0], endHr[1]],
|
|
9971
|
+
attributes: { "probe.event.name": name, ...flat },
|
|
9972
|
+
events: [],
|
|
9973
|
+
status: { code: 1 }
|
|
9974
|
+
});
|
|
9975
|
+
} catch {
|
|
9976
|
+
}
|
|
9729
9977
|
} catch {
|
|
9730
9978
|
}
|
|
9731
9979
|
}
|
|
@@ -9916,6 +10164,15 @@ function createProbeTracerAdapter(fallbackTracer) {
|
|
|
9916
10164
|
}
|
|
9917
10165
|
}
|
|
9918
10166
|
},
|
|
10167
|
+
recordTaskEvent: (phase, attrs) => {
|
|
10168
|
+
emitEvent(`task.${phase}`, attrs);
|
|
10169
|
+
if (fallback && typeof fallback.recordTaskEvent === "function") {
|
|
10170
|
+
try {
|
|
10171
|
+
fallback.recordTaskEvent(phase, attrs);
|
|
10172
|
+
} catch {
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
},
|
|
9919
10176
|
createToolSpan: (toolName, attrs) => {
|
|
9920
10177
|
let fallbackSpan = null;
|
|
9921
10178
|
if (fallback && typeof fallback.createToolSpan === "function") {
|
|
@@ -10123,6 +10380,7 @@ var init_ai_review_service = __esm({
|
|
|
10123
10380
|
init_tracer_init();
|
|
10124
10381
|
init_diff_processor();
|
|
10125
10382
|
init_comment_metadata();
|
|
10383
|
+
init_user_facing_error();
|
|
10126
10384
|
init_markdown();
|
|
10127
10385
|
PROBE_GRACEFUL_MARGIN_MS = 9e4;
|
|
10128
10386
|
MIN_TIMEOUT_FOR_MARGIN_MS = PROBE_GRACEFUL_MARGIN_MS + 3e4;
|
|
@@ -10541,8 +10799,8 @@ var init_ai_review_service = __esm({
|
|
|
10541
10799
|
/**
|
|
10542
10800
|
* Cleanup a session from the registry
|
|
10543
10801
|
*/
|
|
10544
|
-
cleanupSession(sessionId) {
|
|
10545
|
-
this.sessionRegistry.unregisterSession(sessionId);
|
|
10802
|
+
async cleanupSession(sessionId) {
|
|
10803
|
+
await this.sessionRegistry.unregisterSession(sessionId);
|
|
10546
10804
|
}
|
|
10547
10805
|
/**
|
|
10548
10806
|
* Build a custom prompt for AI review with XML-formatted data
|
|
@@ -11947,9 +12205,7 @@ ${"=".repeat(60)}
|
|
|
11947
12205
|
return { response, effectiveSchema, sessionId };
|
|
11948
12206
|
} catch (error) {
|
|
11949
12207
|
console.error("\u274C ProbeAgent failed:", error);
|
|
11950
|
-
throw new Error(
|
|
11951
|
-
`ProbeAgent execution failed: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
11952
|
-
);
|
|
12208
|
+
throw new Error(formatUserFacingExecutionError(error));
|
|
11953
12209
|
} finally {
|
|
11954
12210
|
Object.keys(originalEnv).forEach((key) => {
|
|
11955
12211
|
if (originalEnv[key] === void 0) {
|
|
@@ -14431,7 +14687,7 @@ var init_workflow_executor = __esm({
|
|
|
14431
14687
|
if (step.if) {
|
|
14432
14688
|
const shouldRun = this.evaluateCondition(step.if, {
|
|
14433
14689
|
inputs: executionContext.inputs,
|
|
14434
|
-
outputs:
|
|
14690
|
+
outputs: this.unwrapOutputs(stepResults),
|
|
14435
14691
|
pr: runOptions.prInfo
|
|
14436
14692
|
});
|
|
14437
14693
|
if (!shouldRun) {
|
|
@@ -14594,18 +14850,14 @@ var init_workflow_executor = __esm({
|
|
|
14594
14850
|
throw new Error("Expression mapping requires expression field");
|
|
14595
14851
|
}
|
|
14596
14852
|
const sandbox = createSecureSandbox();
|
|
14853
|
+
const unwrappedExpr = this.unwrapOutputs(stepResults);
|
|
14597
14854
|
return compileAndRun(
|
|
14598
14855
|
sandbox,
|
|
14599
14856
|
typedMapping.expression,
|
|
14600
14857
|
{
|
|
14601
14858
|
inputs: executionContext.inputs,
|
|
14602
|
-
outputs:
|
|
14603
|
-
steps:
|
|
14604
|
-
Array.from(stepResults.entries()).map(([id, result]) => [
|
|
14605
|
-
id,
|
|
14606
|
-
result.output
|
|
14607
|
-
])
|
|
14608
|
-
)
|
|
14859
|
+
outputs: unwrappedExpr,
|
|
14860
|
+
steps: unwrappedExpr
|
|
14609
14861
|
},
|
|
14610
14862
|
{ injectLog: true, logPrefix: "workflow.input.expression" }
|
|
14611
14863
|
);
|
|
@@ -14618,7 +14870,7 @@ var init_workflow_executor = __esm({
|
|
|
14618
14870
|
if (typedMapping.template) {
|
|
14619
14871
|
return await this.liquid.parseAndRender(typedMapping.template, {
|
|
14620
14872
|
inputs: executionContext.inputs,
|
|
14621
|
-
outputs:
|
|
14873
|
+
outputs: this.unwrapOutputs(stepResults)
|
|
14622
14874
|
});
|
|
14623
14875
|
}
|
|
14624
14876
|
}
|
|
@@ -14645,32 +14897,44 @@ var init_workflow_executor = __esm({
|
|
|
14645
14897
|
for (const output of workflow.outputs) {
|
|
14646
14898
|
if (output.value_js) {
|
|
14647
14899
|
const sandbox = createSecureSandbox();
|
|
14900
|
+
const unwrapped = this.unwrapOutputs(stepResults);
|
|
14648
14901
|
outputs[output.name] = compileAndRun(
|
|
14649
14902
|
sandbox,
|
|
14650
14903
|
output.value_js,
|
|
14651
14904
|
{
|
|
14652
14905
|
inputs: executionContext.inputs,
|
|
14653
|
-
steps:
|
|
14654
|
-
|
|
14655
|
-
),
|
|
14656
|
-
outputs: Object.fromEntries(stepResults),
|
|
14906
|
+
steps: unwrapped,
|
|
14907
|
+
outputs: unwrapped,
|
|
14657
14908
|
pr: prInfo
|
|
14658
14909
|
},
|
|
14659
14910
|
{ injectLog: true, logPrefix: `workflow.output.${output.name}` }
|
|
14660
14911
|
);
|
|
14661
14912
|
} else if (output.value) {
|
|
14913
|
+
const unwrappedLiquid = this.unwrapOutputs(stepResults);
|
|
14662
14914
|
outputs[output.name] = await this.liquid.parseAndRender(output.value, {
|
|
14663
14915
|
inputs: executionContext.inputs,
|
|
14664
|
-
steps:
|
|
14665
|
-
|
|
14666
|
-
),
|
|
14667
|
-
outputs: Object.fromEntries(stepResults),
|
|
14916
|
+
steps: unwrappedLiquid,
|
|
14917
|
+
outputs: unwrappedLiquid,
|
|
14668
14918
|
pr: prInfo
|
|
14669
14919
|
});
|
|
14670
14920
|
}
|
|
14671
14921
|
}
|
|
14672
14922
|
return outputs;
|
|
14673
14923
|
}
|
|
14924
|
+
/**
|
|
14925
|
+
* Unwrap step outputs from ReviewSummary wrappers.
|
|
14926
|
+
* Script steps and MCP steps store results as { issues: [], output: <actual> }.
|
|
14927
|
+
* Workflow value_js/if/Liquid should see the unwrapped output, consistent with
|
|
14928
|
+
* how script step contexts already unwrap via buildProviderTemplateContext.
|
|
14929
|
+
*/
|
|
14930
|
+
unwrapOutputs(stepResults) {
|
|
14931
|
+
const unwrapped = {};
|
|
14932
|
+
for (const [id, result] of stepResults.entries()) {
|
|
14933
|
+
const summary = result;
|
|
14934
|
+
unwrapped[id] = summary.output !== void 0 ? summary.output : summary;
|
|
14935
|
+
}
|
|
14936
|
+
return unwrapped;
|
|
14937
|
+
}
|
|
14674
14938
|
/**
|
|
14675
14939
|
* Aggregate results from all steps
|
|
14676
14940
|
*/
|
|
@@ -16495,6 +16759,14 @@ var init_config_schema = __esm({
|
|
|
16495
16759
|
},
|
|
16496
16760
|
description: 'Tags for categorizing and filtering checks (e.g., ["local", "fast", "security"])'
|
|
16497
16761
|
},
|
|
16762
|
+
debounce: {
|
|
16763
|
+
type: "number",
|
|
16764
|
+
description: "Debounce window in milliseconds. When set, multiple invocations of this step (across concurrent engine instances) within the window are coalesced \u2014 only the last invocation actually executes after the window expires. Useful for steps that should run once after a burst of triggers settles.\n\nThe debounce key defaults to the step's fully-qualified check ID. Use `debounce_key` to group different steps under the same debounce."
|
|
16765
|
+
},
|
|
16766
|
+
debounce_key: {
|
|
16767
|
+
type: "string",
|
|
16768
|
+
description: "Custom debounce key. Steps sharing the same key share the same debounce window."
|
|
16769
|
+
},
|
|
16498
16770
|
criticality: {
|
|
16499
16771
|
type: "string",
|
|
16500
16772
|
enum: ["external", "internal", "policy", "info"],
|
|
@@ -16704,7 +16976,7 @@ var init_config_schema = __esm({
|
|
|
16704
16976
|
description: "Arguments/inputs for the workflow"
|
|
16705
16977
|
},
|
|
16706
16978
|
overrides: {
|
|
16707
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-
|
|
16979
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102%3E%3E",
|
|
16708
16980
|
description: "Override specific step configurations in the workflow"
|
|
16709
16981
|
},
|
|
16710
16982
|
output_mapping: {
|
|
@@ -16720,7 +16992,7 @@ var init_config_schema = __esm({
|
|
|
16720
16992
|
description: "Config file path - alternative to workflow ID (loads a Visor config file as workflow)"
|
|
16721
16993
|
},
|
|
16722
16994
|
workflow_overrides: {
|
|
16723
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-
|
|
16995
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102%3E%3E",
|
|
16724
16996
|
description: "Alias for overrides - workflow step overrides (backward compatibility)"
|
|
16725
16997
|
},
|
|
16726
16998
|
ref: {
|
|
@@ -17448,7 +17720,7 @@ var init_config_schema = __esm({
|
|
|
17448
17720
|
description: "Custom output name (defaults to workflow name)"
|
|
17449
17721
|
},
|
|
17450
17722
|
overrides: {
|
|
17451
|
-
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-
|
|
17723
|
+
$ref: "#/definitions/Record%3Cstring%2CPartial%3Cinterface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102%3E%3E",
|
|
17452
17724
|
description: "Step overrides"
|
|
17453
17725
|
},
|
|
17454
17726
|
output_mapping: {
|
|
@@ -17463,13 +17735,13 @@ var init_config_schema = __esm({
|
|
|
17463
17735
|
"^x-": {}
|
|
17464
17736
|
}
|
|
17465
17737
|
},
|
|
17466
|
-
"Record<string,Partial<interface-src_types_config.ts-15521-
|
|
17738
|
+
"Record<string,Partial<interface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102>>": {
|
|
17467
17739
|
type: "object",
|
|
17468
17740
|
additionalProperties: {
|
|
17469
|
-
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-15521-
|
|
17741
|
+
$ref: "#/definitions/Partial%3Cinterface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102%3E"
|
|
17470
17742
|
}
|
|
17471
17743
|
},
|
|
17472
|
-
"Partial<interface-src_types_config.ts-15521-
|
|
17744
|
+
"Partial<interface-src_types_config.ts-15521-31208-src_types_config.ts-0-63102>": {
|
|
17473
17745
|
type: "object",
|
|
17474
17746
|
additionalProperties: false
|
|
17475
17747
|
},
|
|
@@ -18841,7 +19113,7 @@ var init_config_schema = __esm({
|
|
|
18841
19113
|
},
|
|
18842
19114
|
workflow: {
|
|
18843
19115
|
type: "string",
|
|
18844
|
-
description: "Workflow/check ID to run"
|
|
19116
|
+
description: "Workflow/check ID to run. Omit to treat inputs.text as a user message through the full pipeline."
|
|
18845
19117
|
},
|
|
18846
19118
|
inputs: {
|
|
18847
19119
|
$ref: "#/definitions/Record%3Cstring%2Cunknown%3E",
|
|
@@ -18884,7 +19156,7 @@ var init_config_schema = __esm({
|
|
|
18884
19156
|
description: "Timezone for schedule (default: UTC or scheduler default)"
|
|
18885
19157
|
}
|
|
18886
19158
|
},
|
|
18887
|
-
required: ["schedule"
|
|
19159
|
+
required: ["schedule"],
|
|
18888
19160
|
additionalProperties: false,
|
|
18889
19161
|
description: "Static cron job defined in YAML configuration These are always executed by the scheduler daemon",
|
|
18890
19162
|
patternProperties: {
|
|
@@ -21228,6 +21500,7 @@ var init_workflow_check_provider = __esm({
|
|
|
21228
21500
|
init_liquid_extensions();
|
|
21229
21501
|
path15 = __toESM(require("path"));
|
|
21230
21502
|
yaml5 = __toESM(require("js-yaml"));
|
|
21503
|
+
init_user_facing_error();
|
|
21231
21504
|
WorkflowCheckProvider = class extends CheckProvider {
|
|
21232
21505
|
registry;
|
|
21233
21506
|
executor;
|
|
@@ -21845,6 +22118,18 @@ var init_workflow_check_provider = __esm({
|
|
|
21845
22118
|
scoreCount++;
|
|
21846
22119
|
}
|
|
21847
22120
|
}
|
|
22121
|
+
for (const checkStats of result.statistics?.checks || []) {
|
|
22122
|
+
if (checkStats.errorMessage) {
|
|
22123
|
+
allIssues.push({
|
|
22124
|
+
file: "system",
|
|
22125
|
+
line: 0,
|
|
22126
|
+
ruleId: "system/error",
|
|
22127
|
+
message: formatUserFacingExecutionMessage(checkStats.errorMessage),
|
|
22128
|
+
severity: "error",
|
|
22129
|
+
category: "logic"
|
|
22130
|
+
});
|
|
22131
|
+
}
|
|
22132
|
+
}
|
|
21848
22133
|
const outputs = await this.computeWorkflowOutputsFromState(
|
|
21849
22134
|
workflow,
|
|
21850
22135
|
inputs,
|
|
@@ -22161,7 +22446,7 @@ function createWorkflowToolDefinition(workflow, argsOverrides, nameOverride) {
|
|
|
22161
22446
|
for (const key of Object.keys(argsOverrides)) {
|
|
22162
22447
|
delete filteredProperties[key];
|
|
22163
22448
|
}
|
|
22164
|
-
const filteredRequired = baseRequired ? baseRequired.filter((r) => !argsOverrides
|
|
22449
|
+
const filteredRequired = baseRequired ? baseRequired.filter((r) => !(r in argsOverrides)) : void 0;
|
|
22165
22450
|
inputSchema = {
|
|
22166
22451
|
...baseSchema,
|
|
22167
22452
|
properties: filteredProperties,
|
|
@@ -23495,283 +23780,19 @@ var init_github_auth = __esm({
|
|
|
23495
23780
|
}
|
|
23496
23781
|
});
|
|
23497
23782
|
|
|
23498
|
-
// src/debug-visualizer/trace-reader.ts
|
|
23499
|
-
var trace_reader_exports = {};
|
|
23500
|
-
__export(trace_reader_exports, {
|
|
23501
|
-
buildExecutionTree: () => buildExecutionTree,
|
|
23502
|
-
computeTimeline: () => computeTimeline,
|
|
23503
|
-
extractStateSnapshots: () => extractStateSnapshots,
|
|
23504
|
-
parseNDJSONTrace: () => parseNDJSONTrace
|
|
23505
|
-
});
|
|
23506
|
-
async function parseNDJSONTrace(filePath) {
|
|
23507
|
-
const spans = [];
|
|
23508
|
-
let lineNumber = 0;
|
|
23509
|
-
const fileStream = fs16.createReadStream(filePath);
|
|
23510
|
-
const rl = readline.createInterface({
|
|
23511
|
-
input: fileStream,
|
|
23512
|
-
crlfDelay: Infinity
|
|
23513
|
-
});
|
|
23514
|
-
for await (const line of rl) {
|
|
23515
|
-
lineNumber++;
|
|
23516
|
-
if (!line.trim()) {
|
|
23517
|
-
continue;
|
|
23518
|
-
}
|
|
23519
|
-
try {
|
|
23520
|
-
const rawSpan = JSON.parse(line);
|
|
23521
|
-
const processedSpan = processRawSpan(rawSpan);
|
|
23522
|
-
spans.push(processedSpan);
|
|
23523
|
-
} catch (error) {
|
|
23524
|
-
console.warn(`[trace-reader] Malformed JSON at line ${lineNumber}: ${error}`);
|
|
23525
|
-
}
|
|
23526
|
-
}
|
|
23527
|
-
if (spans.length === 0) {
|
|
23528
|
-
throw new Error("No valid spans found in trace file");
|
|
23529
|
-
}
|
|
23530
|
-
const tree = buildExecutionTree(spans);
|
|
23531
|
-
const snapshots = extractStateSnapshots(spans);
|
|
23532
|
-
const timeline = computeTimeline(spans);
|
|
23533
|
-
const sortedSpans = [...spans].sort((a, b) => compareTimeValues(a.startTime, b.startTime));
|
|
23534
|
-
const firstSpan = sortedSpans[0];
|
|
23535
|
-
const lastSpan = sortedSpans[sortedSpans.length - 1];
|
|
23536
|
-
const startTimeMs = timeValueToMillis(firstSpan.startTime);
|
|
23537
|
-
const endTimeMs = timeValueToMillis(lastSpan.endTime);
|
|
23538
|
-
return {
|
|
23539
|
-
runId: tree.checkId,
|
|
23540
|
-
traceId: firstSpan.traceId,
|
|
23541
|
-
spans,
|
|
23542
|
-
tree,
|
|
23543
|
-
timeline,
|
|
23544
|
-
snapshots,
|
|
23545
|
-
metadata: {
|
|
23546
|
-
startTime: timeValueToISO(firstSpan.startTime),
|
|
23547
|
-
endTime: timeValueToISO(lastSpan.endTime),
|
|
23548
|
-
duration: endTimeMs - startTimeMs,
|
|
23549
|
-
totalSpans: spans.length,
|
|
23550
|
-
totalSnapshots: snapshots.length
|
|
23551
|
-
}
|
|
23552
|
-
};
|
|
23553
|
-
}
|
|
23554
|
-
function processRawSpan(rawSpan) {
|
|
23555
|
-
const traceId = rawSpan.traceId || "";
|
|
23556
|
-
const spanId = rawSpan.spanId || "";
|
|
23557
|
-
const parentSpanId = rawSpan.parentSpanId || void 0;
|
|
23558
|
-
const name = rawSpan.name || "unknown";
|
|
23559
|
-
const startTime = rawSpan.startTime || [0, 0];
|
|
23560
|
-
const endTime = rawSpan.endTime || rawSpan.startTime || [0, 0];
|
|
23561
|
-
const startMs = timeValueToMillis(startTime);
|
|
23562
|
-
const endMs = timeValueToMillis(endTime);
|
|
23563
|
-
const duration = endMs - startMs;
|
|
23564
|
-
const attributes = rawSpan.attributes || {};
|
|
23565
|
-
const events = (rawSpan.events || []).map((evt) => ({
|
|
23566
|
-
name: evt.name || "unknown",
|
|
23567
|
-
time: evt.time || [0, 0],
|
|
23568
|
-
timestamp: evt.timestamp || timeValueToISO(evt.time || [0, 0]),
|
|
23569
|
-
attributes: evt.attributes || {}
|
|
23570
|
-
}));
|
|
23571
|
-
const status = rawSpan.status?.code === 2 ? "error" : "ok";
|
|
23572
|
-
return {
|
|
23573
|
-
traceId,
|
|
23574
|
-
spanId,
|
|
23575
|
-
parentSpanId,
|
|
23576
|
-
name,
|
|
23577
|
-
startTime,
|
|
23578
|
-
endTime,
|
|
23579
|
-
duration,
|
|
23580
|
-
attributes,
|
|
23581
|
-
events,
|
|
23582
|
-
status
|
|
23583
|
-
};
|
|
23584
|
-
}
|
|
23585
|
-
function buildExecutionTree(spans) {
|
|
23586
|
-
const nodeMap = /* @__PURE__ */ new Map();
|
|
23587
|
-
for (const span of spans) {
|
|
23588
|
-
const node = createExecutionNode(span);
|
|
23589
|
-
nodeMap.set(span.spanId, node);
|
|
23590
|
-
}
|
|
23591
|
-
let rootNode;
|
|
23592
|
-
const orphans = [];
|
|
23593
|
-
for (const span of spans) {
|
|
23594
|
-
const node = nodeMap.get(span.spanId);
|
|
23595
|
-
if (!span.parentSpanId) {
|
|
23596
|
-
rootNode = node;
|
|
23597
|
-
} else {
|
|
23598
|
-
const parent = nodeMap.get(span.parentSpanId);
|
|
23599
|
-
if (parent) {
|
|
23600
|
-
parent.children.push(node);
|
|
23601
|
-
} else {
|
|
23602
|
-
orphans.push(node);
|
|
23603
|
-
}
|
|
23604
|
-
}
|
|
23605
|
-
}
|
|
23606
|
-
if (!rootNode) {
|
|
23607
|
-
console.warn("[trace-reader] No root span found, creating synthetic root");
|
|
23608
|
-
rootNode = {
|
|
23609
|
-
checkId: "synthetic-root",
|
|
23610
|
-
type: "run",
|
|
23611
|
-
status: "completed",
|
|
23612
|
-
children: Array.from(nodeMap.values()).filter((n) => !n.span.parentSpanId),
|
|
23613
|
-
span: spans[0],
|
|
23614
|
-
// Use first span as placeholder
|
|
23615
|
-
state: {}
|
|
23616
|
-
};
|
|
23617
|
-
}
|
|
23618
|
-
if (orphans.length > 0 && rootNode) {
|
|
23619
|
-
rootNode.children.push(...orphans);
|
|
23620
|
-
}
|
|
23621
|
-
return rootNode;
|
|
23622
|
-
}
|
|
23623
|
-
function createExecutionNode(span) {
|
|
23624
|
-
const attrs = span.attributes;
|
|
23625
|
-
const checkId = attrs["visor.check.id"] || attrs["visor.run.id"] || span.spanId;
|
|
23626
|
-
let type = "unknown";
|
|
23627
|
-
if (span.name === "visor.run") {
|
|
23628
|
-
type = "run";
|
|
23629
|
-
} else if (span.name === "visor.check") {
|
|
23630
|
-
type = "check";
|
|
23631
|
-
} else if (span.name.startsWith("visor.provider.")) {
|
|
23632
|
-
type = "provider";
|
|
23633
|
-
}
|
|
23634
|
-
let status = "completed";
|
|
23635
|
-
if (span.status === "error") {
|
|
23636
|
-
status = "error";
|
|
23637
|
-
} else if (attrs["visor.check.skipped"] === true) {
|
|
23638
|
-
status = "skipped";
|
|
23639
|
-
}
|
|
23640
|
-
const state = {};
|
|
23641
|
-
if (attrs["visor.check.input.context"]) {
|
|
23642
|
-
try {
|
|
23643
|
-
state.inputContext = JSON.parse(attrs["visor.check.input.context"]);
|
|
23644
|
-
} catch {
|
|
23645
|
-
state.inputContext = attrs["visor.check.input.context"];
|
|
23646
|
-
}
|
|
23647
|
-
}
|
|
23648
|
-
if (attrs["visor.check.output"]) {
|
|
23649
|
-
try {
|
|
23650
|
-
state.output = JSON.parse(attrs["visor.check.output"]);
|
|
23651
|
-
} catch {
|
|
23652
|
-
state.output = attrs["visor.check.output"];
|
|
23653
|
-
}
|
|
23654
|
-
}
|
|
23655
|
-
if (span.status === "error" || attrs["visor.check.error"]) {
|
|
23656
|
-
state.errors = [attrs["visor.check.error"] || "Unknown error"];
|
|
23657
|
-
}
|
|
23658
|
-
state.metadata = {
|
|
23659
|
-
type: attrs["visor.check.type"],
|
|
23660
|
-
duration: span.duration,
|
|
23661
|
-
provider: attrs["visor.provider.type"]
|
|
23662
|
-
};
|
|
23663
|
-
return {
|
|
23664
|
-
checkId,
|
|
23665
|
-
type,
|
|
23666
|
-
status,
|
|
23667
|
-
children: [],
|
|
23668
|
-
span,
|
|
23669
|
-
state
|
|
23670
|
-
};
|
|
23671
|
-
}
|
|
23672
|
-
function extractStateSnapshots(spans) {
|
|
23673
|
-
const snapshots = [];
|
|
23674
|
-
for (const span of spans) {
|
|
23675
|
-
for (const event of span.events) {
|
|
23676
|
-
if (event.name === "state.snapshot") {
|
|
23677
|
-
const attrs = event.attributes || {};
|
|
23678
|
-
const snapshot = {
|
|
23679
|
-
checkId: attrs["visor.snapshot.check_id"] || span.attributes["visor.check.id"] || "unknown",
|
|
23680
|
-
timestamp: attrs["visor.snapshot.timestamp"] || event.timestamp || timeValueToISO(event.time),
|
|
23681
|
-
timestampNanos: event.time,
|
|
23682
|
-
outputs: parseJSON(attrs["visor.snapshot.outputs"], {}),
|
|
23683
|
-
memory: parseJSON(attrs["visor.snapshot.memory"], {})
|
|
23684
|
-
};
|
|
23685
|
-
snapshots.push(snapshot);
|
|
23686
|
-
}
|
|
23687
|
-
}
|
|
23688
|
-
}
|
|
23689
|
-
snapshots.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
|
|
23690
|
-
return snapshots;
|
|
23691
|
-
}
|
|
23692
|
-
function computeTimeline(spans) {
|
|
23693
|
-
const events = [];
|
|
23694
|
-
for (const span of spans) {
|
|
23695
|
-
const checkId = span.attributes["visor.check.id"] || span.spanId;
|
|
23696
|
-
events.push({
|
|
23697
|
-
type: "check.started",
|
|
23698
|
-
checkId,
|
|
23699
|
-
timestamp: timeValueToISO(span.startTime),
|
|
23700
|
-
timestampNanos: span.startTime,
|
|
23701
|
-
metadata: {
|
|
23702
|
-
name: span.name,
|
|
23703
|
-
type: span.attributes["visor.check.type"]
|
|
23704
|
-
}
|
|
23705
|
-
});
|
|
23706
|
-
events.push({
|
|
23707
|
-
type: span.status === "error" ? "check.failed" : "check.completed",
|
|
23708
|
-
checkId,
|
|
23709
|
-
timestamp: timeValueToISO(span.endTime),
|
|
23710
|
-
timestampNanos: span.endTime,
|
|
23711
|
-
duration: span.duration,
|
|
23712
|
-
status: span.status,
|
|
23713
|
-
metadata: {
|
|
23714
|
-
name: span.name
|
|
23715
|
-
}
|
|
23716
|
-
});
|
|
23717
|
-
for (const evt of span.events) {
|
|
23718
|
-
events.push({
|
|
23719
|
-
type: evt.name === "state.snapshot" ? "state.snapshot" : "event",
|
|
23720
|
-
checkId: evt.attributes?.["visor.snapshot.check_id"] || checkId,
|
|
23721
|
-
timestamp: evt.timestamp || timeValueToISO(evt.time),
|
|
23722
|
-
timestampNanos: evt.time,
|
|
23723
|
-
metadata: {
|
|
23724
|
-
eventName: evt.name,
|
|
23725
|
-
attributes: evt.attributes
|
|
23726
|
-
}
|
|
23727
|
-
});
|
|
23728
|
-
}
|
|
23729
|
-
}
|
|
23730
|
-
events.sort((a, b) => compareTimeValues(a.timestampNanos, b.timestampNanos));
|
|
23731
|
-
return events;
|
|
23732
|
-
}
|
|
23733
|
-
function timeValueToMillis(timeValue) {
|
|
23734
|
-
const [seconds, nanos] = timeValue;
|
|
23735
|
-
return seconds * 1e3 + nanos / 1e6;
|
|
23736
|
-
}
|
|
23737
|
-
function timeValueToISO(timeValue) {
|
|
23738
|
-
const millis = timeValueToMillis(timeValue);
|
|
23739
|
-
return new Date(millis).toISOString();
|
|
23740
|
-
}
|
|
23741
|
-
function compareTimeValues(a, b) {
|
|
23742
|
-
if (a[0] !== b[0]) {
|
|
23743
|
-
return a[0] - b[0];
|
|
23744
|
-
}
|
|
23745
|
-
return a[1] - b[1];
|
|
23746
|
-
}
|
|
23747
|
-
function parseJSON(value, defaultValue) {
|
|
23748
|
-
if (typeof value !== "string") {
|
|
23749
|
-
return defaultValue;
|
|
23750
|
-
}
|
|
23751
|
-
try {
|
|
23752
|
-
return JSON.parse(value);
|
|
23753
|
-
} catch {
|
|
23754
|
-
return defaultValue;
|
|
23755
|
-
}
|
|
23756
|
-
}
|
|
23757
|
-
var fs16, readline;
|
|
23758
|
-
var init_trace_reader = __esm({
|
|
23759
|
-
"src/debug-visualizer/trace-reader.ts"() {
|
|
23760
|
-
"use strict";
|
|
23761
|
-
fs16 = __toESM(require("fs"));
|
|
23762
|
-
readline = __toESM(require("readline"));
|
|
23763
|
-
}
|
|
23764
|
-
});
|
|
23765
|
-
|
|
23766
23783
|
// src/agent-protocol/trace-serializer.ts
|
|
23767
23784
|
var trace_serializer_exports = {};
|
|
23768
23785
|
__export(trace_serializer_exports, {
|
|
23786
|
+
buildTraceHeaderLines: () => buildTraceHeaderLines,
|
|
23787
|
+
buildTraceReport: () => buildTraceReport,
|
|
23788
|
+
extractProbeTaskSummary: () => extractProbeTaskSummary,
|
|
23769
23789
|
fetchTraceSpans: () => fetchTraceSpans,
|
|
23770
23790
|
findTraceFile: () => findTraceFile,
|
|
23771
23791
|
readTraceIdFromFile: () => readTraceIdFromFile,
|
|
23772
23792
|
renderSpanTree: () => renderSpanTree,
|
|
23773
23793
|
renderSpanYaml: () => renderSpanYaml,
|
|
23774
23794
|
renderTraceTree: () => renderTraceTree,
|
|
23795
|
+
resolveTraceSpans: () => resolveTraceSpans,
|
|
23775
23796
|
serializeTraceForPrompt: () => serializeTraceForPrompt
|
|
23776
23797
|
});
|
|
23777
23798
|
function resolveBackendConfig(overrides) {
|
|
@@ -23782,7 +23803,8 @@ function resolveBackendConfig(overrides) {
|
|
|
23782
23803
|
grafanaDatasourceId: overrides?.grafanaDatasourceId || process.env.GRAFANA_TEMPO_DATASOURCE_ID,
|
|
23783
23804
|
jaegerUrl: overrides?.jaegerUrl || process.env.JAEGER_URL,
|
|
23784
23805
|
traceDir: overrides?.traceDir || process.env.VISOR_TRACE_DIR || "output/traces",
|
|
23785
|
-
authToken: overrides?.authToken || process.env.GRAFANA_TOKEN
|
|
23806
|
+
authToken: overrides?.authToken || process.env.GRAFANA_TOKEN,
|
|
23807
|
+
targetTraceId: overrides?.targetTraceId
|
|
23786
23808
|
};
|
|
23787
23809
|
}
|
|
23788
23810
|
function getAutoBackendOrder() {
|
|
@@ -23943,6 +23965,60 @@ async function fetchTraceSpans(traceRef, config) {
|
|
|
23943
23965
|
}
|
|
23944
23966
|
return [];
|
|
23945
23967
|
}
|
|
23968
|
+
async function resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId) {
|
|
23969
|
+
const cfg = resolveBackendConfig(backendConfig);
|
|
23970
|
+
const backendOrder = getBackendOrder(cfg);
|
|
23971
|
+
const isFilePath = isTraceFilePath(traceIdOrPath);
|
|
23972
|
+
const localTracePath = isFilePath ? traceIdOrPath : void 0;
|
|
23973
|
+
const remoteTraceId = fallbackTraceId || (!isFilePath ? traceIdOrPath : await readTraceIdFromFile(traceIdOrPath) || void 0);
|
|
23974
|
+
const preferLocalFirst = backendOrder[0] === "file";
|
|
23975
|
+
logger.debug(
|
|
23976
|
+
`[TraceSerializer] serializeTraceForPrompt ref=${traceIdOrPath} remoteTraceId=${remoteTraceId || "-"} backendOrder=${backendOrder.join(">")}`
|
|
23977
|
+
);
|
|
23978
|
+
if (preferLocalFirst && localTracePath) {
|
|
23979
|
+
logger.debug(`[TraceSerializer] Trying local trace file first: ${localTracePath}`);
|
|
23980
|
+
const spans = await fetchTraceSpans(localTracePath, {
|
|
23981
|
+
...cfg,
|
|
23982
|
+
type: "file",
|
|
23983
|
+
targetTraceId: remoteTraceId
|
|
23984
|
+
});
|
|
23985
|
+
if (spans.length > 0) {
|
|
23986
|
+
return { spans, source: "file", remoteTraceId, localTracePath };
|
|
23987
|
+
}
|
|
23988
|
+
}
|
|
23989
|
+
if (remoteTraceId) {
|
|
23990
|
+
logger.debug(`[TraceSerializer] Trying remote trace backends for trace_id=${remoteTraceId}`);
|
|
23991
|
+
const remoteCfg = resolveBackendConfig(backendConfig);
|
|
23992
|
+
const remoteOrder = getBackendOrder(remoteCfg).filter((kind) => kind !== "file");
|
|
23993
|
+
for (const backend of remoteOrder) {
|
|
23994
|
+
if (backend === "grafana") {
|
|
23995
|
+
const spans = await fetchFromGrafanaTempo(remoteTraceId, remoteCfg);
|
|
23996
|
+
if (spans && spans.length > 0) {
|
|
23997
|
+
return { spans, source: "grafana", remoteTraceId, localTracePath };
|
|
23998
|
+
}
|
|
23999
|
+
continue;
|
|
24000
|
+
}
|
|
24001
|
+
if (backend === "jaeger") {
|
|
24002
|
+
const spans = await fetchFromJaeger(remoteTraceId, remoteCfg);
|
|
24003
|
+
if (spans && spans.length > 0) {
|
|
24004
|
+
return { spans, source: "jaeger", remoteTraceId, localTracePath };
|
|
24005
|
+
}
|
|
24006
|
+
}
|
|
24007
|
+
}
|
|
24008
|
+
}
|
|
24009
|
+
if (localTracePath) {
|
|
24010
|
+
logger.debug(`[TraceSerializer] Falling back to local trace file: ${localTracePath}`);
|
|
24011
|
+
const spans = await fetchTraceSpans(localTracePath, {
|
|
24012
|
+
...cfg,
|
|
24013
|
+
type: "file",
|
|
24014
|
+
targetTraceId: remoteTraceId
|
|
24015
|
+
});
|
|
24016
|
+
if (spans.length > 0) {
|
|
24017
|
+
return { spans, source: "file", remoteTraceId, localTracePath };
|
|
24018
|
+
}
|
|
24019
|
+
}
|
|
24020
|
+
return { spans: [], source: null, remoteTraceId, localTracePath };
|
|
24021
|
+
}
|
|
23946
24022
|
async function fetchFromGrafanaTempo(traceId, cfg) {
|
|
23947
24023
|
let grafanaUrl = cfg.grafanaUrl;
|
|
23948
24024
|
if (!grafanaUrl) {
|
|
@@ -24018,9 +24094,29 @@ async function fetchFromLocalFiles(traceRef, cfg) {
|
|
|
24018
24094
|
const traceFile = isTraceFilePath(traceRef) ? traceRef : await findTraceFile(traceRef, cfg.traceDir);
|
|
24019
24095
|
if (!traceFile) return null;
|
|
24020
24096
|
try {
|
|
24021
|
-
const
|
|
24022
|
-
const
|
|
24023
|
-
|
|
24097
|
+
const targetTraceId = cfg.targetTraceId || (!isTraceFilePath(traceRef) ? traceRef : await readTraceIdFromFile(traceFile) || void 0);
|
|
24098
|
+
const rawSpans = [];
|
|
24099
|
+
const rl = readline.createInterface({
|
|
24100
|
+
input: fs16.createReadStream(traceFile, { encoding: "utf8" }),
|
|
24101
|
+
crlfDelay: Infinity
|
|
24102
|
+
});
|
|
24103
|
+
try {
|
|
24104
|
+
for await (const line of rl) {
|
|
24105
|
+
const trimmed = line.trim();
|
|
24106
|
+
if (!trimmed) continue;
|
|
24107
|
+
try {
|
|
24108
|
+
const parsed = JSON.parse(trimmed);
|
|
24109
|
+
if (!targetTraceId || parsed.traceId === targetTraceId) {
|
|
24110
|
+
rawSpans.push(parsed);
|
|
24111
|
+
}
|
|
24112
|
+
} catch {
|
|
24113
|
+
}
|
|
24114
|
+
}
|
|
24115
|
+
} finally {
|
|
24116
|
+
rl.close();
|
|
24117
|
+
}
|
|
24118
|
+
if (rawSpans.length === 0) return [];
|
|
24119
|
+
return parseLocalNDJSONSpans(rawSpans);
|
|
24024
24120
|
} catch (err) {
|
|
24025
24121
|
logger.debug(`[TraceSerializer] Local file parse failed: ${err}`);
|
|
24026
24122
|
return null;
|
|
@@ -24045,15 +24141,27 @@ async function httpGet(url, authToken, timeoutMs) {
|
|
|
24045
24141
|
}
|
|
24046
24142
|
async function findTraceFile(traceId, traceDir) {
|
|
24047
24143
|
const dir = traceDir || process.env.VISOR_TRACE_DIR || "output/traces";
|
|
24048
|
-
if (!
|
|
24049
|
-
const files =
|
|
24144
|
+
if (!fs16.existsSync(dir)) return null;
|
|
24145
|
+
const files = fs16.readdirSync(dir).filter((f) => f.endsWith(".ndjson"));
|
|
24050
24146
|
for (const file of files) {
|
|
24051
24147
|
const filePath = path19.join(dir, file);
|
|
24052
24148
|
try {
|
|
24053
|
-
const
|
|
24054
|
-
|
|
24055
|
-
|
|
24056
|
-
|
|
24149
|
+
const rl = readline.createInterface({
|
|
24150
|
+
input: fs16.createReadStream(filePath, { encoding: "utf8" }),
|
|
24151
|
+
crlfDelay: Infinity
|
|
24152
|
+
});
|
|
24153
|
+
try {
|
|
24154
|
+
for await (const line of rl) {
|
|
24155
|
+
if (!line.trim()) continue;
|
|
24156
|
+
const parsed = JSON.parse(line);
|
|
24157
|
+
if (parsed.traceId === traceId) {
|
|
24158
|
+
rl.close();
|
|
24159
|
+
return filePath;
|
|
24160
|
+
}
|
|
24161
|
+
}
|
|
24162
|
+
} finally {
|
|
24163
|
+
rl.close();
|
|
24164
|
+
}
|
|
24057
24165
|
} catch {
|
|
24058
24166
|
}
|
|
24059
24167
|
}
|
|
@@ -24061,8 +24169,8 @@ async function findTraceFile(traceId, traceDir) {
|
|
|
24061
24169
|
}
|
|
24062
24170
|
async function readTraceIdFromFile(traceFile) {
|
|
24063
24171
|
try {
|
|
24064
|
-
const rl =
|
|
24065
|
-
input:
|
|
24172
|
+
const rl = readline.createInterface({
|
|
24173
|
+
input: fs16.createReadStream(traceFile, { encoding: "utf8" }),
|
|
24066
24174
|
crlfDelay: Infinity
|
|
24067
24175
|
});
|
|
24068
24176
|
for await (const line of rl) {
|
|
@@ -24082,25 +24190,6 @@ async function readTraceIdFromFile(traceFile) {
|
|
|
24082
24190
|
}
|
|
24083
24191
|
return null;
|
|
24084
24192
|
}
|
|
24085
|
-
async function readFirstLine(filePath) {
|
|
24086
|
-
return new Promise((resolve17, reject) => {
|
|
24087
|
-
const stream = fs17.createReadStream(filePath, { encoding: "utf-8" });
|
|
24088
|
-
const rl = readline2.createInterface({ input: stream, crlfDelay: Infinity });
|
|
24089
|
-
let resolved = false;
|
|
24090
|
-
rl.on("line", (line) => {
|
|
24091
|
-
if (!resolved) {
|
|
24092
|
-
resolved = true;
|
|
24093
|
-
rl.close();
|
|
24094
|
-
stream.destroy();
|
|
24095
|
-
resolve17(line.trim() || null);
|
|
24096
|
-
}
|
|
24097
|
-
});
|
|
24098
|
-
rl.on("close", () => {
|
|
24099
|
-
if (!resolved) resolve17(null);
|
|
24100
|
-
});
|
|
24101
|
-
rl.on("error", reject);
|
|
24102
|
-
});
|
|
24103
|
-
}
|
|
24104
24193
|
function isNoiseSpan(span) {
|
|
24105
24194
|
return NOISE_SPAN_NAMES.has(span.name);
|
|
24106
24195
|
}
|
|
@@ -24258,6 +24347,454 @@ function getLifecycleSpanInfo(name) {
|
|
|
24258
24347
|
}
|
|
24259
24348
|
return null;
|
|
24260
24349
|
}
|
|
24350
|
+
function parseTaskStatusSnapshot(raw) {
|
|
24351
|
+
if (!raw || !raw.includes("<task_status>")) return [];
|
|
24352
|
+
const items = [];
|
|
24353
|
+
const taskRegex = /<task\s+id="([^"]+)"\s+status="([^"]+)"(?:\s+priority="[^"]*")?>([\s\S]*?)<\/task>/g;
|
|
24354
|
+
let match;
|
|
24355
|
+
while ((match = taskRegex.exec(raw)) !== null) {
|
|
24356
|
+
items.push({
|
|
24357
|
+
id: match[1],
|
|
24358
|
+
status: match[2],
|
|
24359
|
+
title: match[3].replace(/\s+/g, " ").trim()
|
|
24360
|
+
});
|
|
24361
|
+
}
|
|
24362
|
+
return items;
|
|
24363
|
+
}
|
|
24364
|
+
function parseTaskIds(value) {
|
|
24365
|
+
if (!value) return [];
|
|
24366
|
+
return String(value).split(",").map((part) => part.trim()).filter(Boolean);
|
|
24367
|
+
}
|
|
24368
|
+
function parseBatchItemsJson(value) {
|
|
24369
|
+
if (!value) return [];
|
|
24370
|
+
try {
|
|
24371
|
+
const parsed = JSON.parse(String(value));
|
|
24372
|
+
if (!Array.isArray(parsed)) return [];
|
|
24373
|
+
return parsed.filter((item) => item && typeof item.id === "string");
|
|
24374
|
+
} catch {
|
|
24375
|
+
return [];
|
|
24376
|
+
}
|
|
24377
|
+
}
|
|
24378
|
+
function isTaskTelemetrySpan(span) {
|
|
24379
|
+
const toolName = span.attributes["tool.name"] || span.attributes["visor.tool.name"];
|
|
24380
|
+
if (toolName === "task") return true;
|
|
24381
|
+
return /(?:^probe\.event\.)?task\.[a-z_]+$/.test(normalizeSpanName(span.name));
|
|
24382
|
+
}
|
|
24383
|
+
function summarizeTaskTelemetrySpans(spans) {
|
|
24384
|
+
const ordered = [...spans].sort((a, b) => {
|
|
24385
|
+
if (a.startTimeMs !== b.startTimeMs) return a.startTimeMs - b.startTimeMs;
|
|
24386
|
+
if (a.endTimeMs !== b.endTimeMs) return a.endTimeMs - b.endTimeMs;
|
|
24387
|
+
return a.spanId.localeCompare(b.spanId);
|
|
24388
|
+
});
|
|
24389
|
+
const tasks = /* @__PURE__ */ new Map();
|
|
24390
|
+
let tasksEnabled = false;
|
|
24391
|
+
let snapshotCount = 0;
|
|
24392
|
+
let eventCount = 0;
|
|
24393
|
+
let sawSnapshot = false;
|
|
24394
|
+
const upsertTask = (id, patch) => {
|
|
24395
|
+
const existing = tasks.get(id) || { id, title: "", status: "pending" };
|
|
24396
|
+
const next = {
|
|
24397
|
+
id,
|
|
24398
|
+
title: patch.title ?? existing.title,
|
|
24399
|
+
status: patch.status ?? existing.status
|
|
24400
|
+
};
|
|
24401
|
+
tasks.set(id, next);
|
|
24402
|
+
};
|
|
24403
|
+
for (const span of ordered) {
|
|
24404
|
+
const name = normalizeSpanName(span.name);
|
|
24405
|
+
const attrs = span.attributes;
|
|
24406
|
+
const toolName = attrs["tool.name"] || attrs["visor.tool.name"];
|
|
24407
|
+
if (toolName === "task") {
|
|
24408
|
+
const snapshot = parseTaskStatusSnapshot(String(attrs["tool.result"] || ""));
|
|
24409
|
+
if (snapshot.length > 0) {
|
|
24410
|
+
tasksEnabled = true;
|
|
24411
|
+
snapshotCount += 1;
|
|
24412
|
+
sawSnapshot = true;
|
|
24413
|
+
tasks.clear();
|
|
24414
|
+
for (const task of snapshot) {
|
|
24415
|
+
tasks.set(task.id, {
|
|
24416
|
+
id: task.id,
|
|
24417
|
+
title: task.title,
|
|
24418
|
+
status: task.status
|
|
24419
|
+
});
|
|
24420
|
+
}
|
|
24421
|
+
}
|
|
24422
|
+
}
|
|
24423
|
+
const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
|
|
24424
|
+
if (!taskEventMatch) continue;
|
|
24425
|
+
tasksEnabled = true;
|
|
24426
|
+
eventCount += 1;
|
|
24427
|
+
const eventType = taskEventMatch[1];
|
|
24428
|
+
const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
|
|
24429
|
+
const taskTitle = attrs["task.title"] ? String(attrs["task.title"]).trim() : "";
|
|
24430
|
+
const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
|
|
24431
|
+
const taskIds = parseTaskIds(attrs["task.ids"]);
|
|
24432
|
+
switch (eventType) {
|
|
24433
|
+
case "created":
|
|
24434
|
+
if (taskId) upsertTask(taskId, { title: taskTitle, status: "pending" });
|
|
24435
|
+
break;
|
|
24436
|
+
case "updated":
|
|
24437
|
+
if (taskId) {
|
|
24438
|
+
upsertTask(taskId, {
|
|
24439
|
+
title: taskTitle || void 0,
|
|
24440
|
+
status: newStatus && newStatus !== "unchanged" ? newStatus : void 0
|
|
24441
|
+
});
|
|
24442
|
+
}
|
|
24443
|
+
break;
|
|
24444
|
+
case "completed":
|
|
24445
|
+
if (taskId) upsertTask(taskId, { title: taskTitle || void 0, status: "completed" });
|
|
24446
|
+
break;
|
|
24447
|
+
case "deleted":
|
|
24448
|
+
if (taskId) upsertTask(taskId, { title: taskTitle || void 0, status: "deleted" });
|
|
24449
|
+
break;
|
|
24450
|
+
case "batch_created": {
|
|
24451
|
+
const items = parseBatchItemsJson(attrs["task.items_json"]);
|
|
24452
|
+
if (items.length > 0) {
|
|
24453
|
+
for (const item of items)
|
|
24454
|
+
upsertTask(item.id, { title: item.title || "", status: item.status || "pending" });
|
|
24455
|
+
} else {
|
|
24456
|
+
for (const id of taskIds) upsertTask(id, { status: "pending" });
|
|
24457
|
+
}
|
|
24458
|
+
break;
|
|
24459
|
+
}
|
|
24460
|
+
case "batch_updated": {
|
|
24461
|
+
const items = parseBatchItemsJson(attrs["task.items_json"]);
|
|
24462
|
+
if (items.length > 0) {
|
|
24463
|
+
for (const item of items)
|
|
24464
|
+
upsertTask(item.id, {
|
|
24465
|
+
title: item.title || void 0,
|
|
24466
|
+
status: item.status || void 0
|
|
24467
|
+
});
|
|
24468
|
+
} else {
|
|
24469
|
+
for (const id of taskIds) {
|
|
24470
|
+
upsertTask(id, {
|
|
24471
|
+
status: newStatus && newStatus !== "unchanged" ? newStatus : void 0
|
|
24472
|
+
});
|
|
24473
|
+
}
|
|
24474
|
+
}
|
|
24475
|
+
break;
|
|
24476
|
+
}
|
|
24477
|
+
case "batch_completed": {
|
|
24478
|
+
const items = parseBatchItemsJson(attrs["task.items_json"]);
|
|
24479
|
+
if (items.length > 0) {
|
|
24480
|
+
for (const item of items)
|
|
24481
|
+
upsertTask(item.id, { title: item.title || void 0, status: "completed" });
|
|
24482
|
+
} else {
|
|
24483
|
+
for (const id of taskIds) upsertTask(id, { status: "completed" });
|
|
24484
|
+
}
|
|
24485
|
+
break;
|
|
24486
|
+
}
|
|
24487
|
+
case "batch_deleted": {
|
|
24488
|
+
const items = parseBatchItemsJson(attrs["task.items_json"]);
|
|
24489
|
+
if (items.length > 0) {
|
|
24490
|
+
for (const item of items)
|
|
24491
|
+
upsertTask(item.id, { title: item.title || void 0, status: "deleted" });
|
|
24492
|
+
} else {
|
|
24493
|
+
for (const id of taskIds) upsertTask(id, { status: "deleted" });
|
|
24494
|
+
}
|
|
24495
|
+
break;
|
|
24496
|
+
}
|
|
24497
|
+
default:
|
|
24498
|
+
break;
|
|
24499
|
+
}
|
|
24500
|
+
}
|
|
24501
|
+
if (!tasksEnabled) return null;
|
|
24502
|
+
return {
|
|
24503
|
+
tasksEnabled,
|
|
24504
|
+
source: sawSnapshot ? "snapshot" : "events",
|
|
24505
|
+
eventCount,
|
|
24506
|
+
snapshotCount,
|
|
24507
|
+
tasks: [...tasks.values()].sort((a, b) => a.id.localeCompare(b.id)),
|
|
24508
|
+
scopes: []
|
|
24509
|
+
};
|
|
24510
|
+
}
|
|
24511
|
+
function titleCaseWords(raw) {
|
|
24512
|
+
return raw.split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
24513
|
+
}
|
|
24514
|
+
function getTaskScopeCheckId(span) {
|
|
24515
|
+
const concreteName = getConcreteCheckName(span);
|
|
24516
|
+
if (!concreteName) return null;
|
|
24517
|
+
return concreteName.replace(/^visor\.check\./, "");
|
|
24518
|
+
}
|
|
24519
|
+
function isTaskScopeNode(span) {
|
|
24520
|
+
const checkId = getTaskScopeCheckId(span);
|
|
24521
|
+
if (checkId) return !TASK_SCOPE_EXCLUDED_CHECKS.has(checkId);
|
|
24522
|
+
const normalized = normalizeSpanName(span.name);
|
|
24523
|
+
return normalized === "search.delegate" || normalized.startsWith("engineer.");
|
|
24524
|
+
}
|
|
24525
|
+
function getTaskScopeLabel(span) {
|
|
24526
|
+
if (!span) return ROOT_TASK_SCOPE_LABEL;
|
|
24527
|
+
const checkId = getTaskScopeCheckId(span);
|
|
24528
|
+
if (checkId) {
|
|
24529
|
+
if (checkId === "explore-code") return "Code Explorer";
|
|
24530
|
+
return titleCaseWords(checkId);
|
|
24531
|
+
}
|
|
24532
|
+
const normalized = normalizeSpanName(span.name);
|
|
24533
|
+
if (normalized === "search.delegate") return "Search Delegate";
|
|
24534
|
+
if (normalized.startsWith("engineer.")) {
|
|
24535
|
+
return `Engineer ${titleCaseWords(normalized.slice("engineer.".length))}`;
|
|
24536
|
+
}
|
|
24537
|
+
return titleCaseWords(normalized);
|
|
24538
|
+
}
|
|
24539
|
+
function collectTaskSpansForScope(node, stopAtNestedScopes) {
|
|
24540
|
+
const spans = [];
|
|
24541
|
+
const visit = (current, isRoot) => {
|
|
24542
|
+
if (!isRoot && stopAtNestedScopes && isTaskScopeNode(current.span)) return;
|
|
24543
|
+
if (isTaskTelemetrySpan(current.span)) spans.push(current.span);
|
|
24544
|
+
for (const child of current.children) visit(child, false);
|
|
24545
|
+
};
|
|
24546
|
+
visit(node, true);
|
|
24547
|
+
return spans;
|
|
24548
|
+
}
|
|
24549
|
+
function findImmediateNestedTaskScopeNodes(node) {
|
|
24550
|
+
const scopes = [];
|
|
24551
|
+
const visit = (current, isRoot) => {
|
|
24552
|
+
if (!isRoot && isTaskScopeNode(current.span)) {
|
|
24553
|
+
scopes.push(current);
|
|
24554
|
+
return;
|
|
24555
|
+
}
|
|
24556
|
+
for (const child of current.children) visit(child, false);
|
|
24557
|
+
};
|
|
24558
|
+
visit(node, true);
|
|
24559
|
+
return scopes;
|
|
24560
|
+
}
|
|
24561
|
+
function isSubAgentScope(span) {
|
|
24562
|
+
const checkId = getTaskScopeCheckId(span);
|
|
24563
|
+
if (checkId && SUB_AGENT_SCOPE_CHECKS.has(checkId)) return true;
|
|
24564
|
+
const normalized = normalizeSpanName(span.name);
|
|
24565
|
+
return normalized.startsWith("engineer.") || normalized === "search.delegate";
|
|
24566
|
+
}
|
|
24567
|
+
function getSubAgentStatus(node) {
|
|
24568
|
+
const normalized = normalizeSpanName(node.span.name);
|
|
24569
|
+
if (normalized.endsWith(".completed")) return "completed";
|
|
24570
|
+
if (normalized.endsWith(".started") || normalized.endsWith(".progress")) return "in_progress";
|
|
24571
|
+
let hasCompleted = false;
|
|
24572
|
+
let hasProgress = false;
|
|
24573
|
+
const visit = (current) => {
|
|
24574
|
+
const n = normalizeSpanName(current.span.name);
|
|
24575
|
+
if (n.endsWith(".completed")) hasCompleted = true;
|
|
24576
|
+
if (n.endsWith(".progress")) hasProgress = true;
|
|
24577
|
+
for (const child of current.children) visit(child);
|
|
24578
|
+
};
|
|
24579
|
+
visit(node);
|
|
24580
|
+
if (hasCompleted) return "completed";
|
|
24581
|
+
if (hasProgress) return "in_progress";
|
|
24582
|
+
return "pending";
|
|
24583
|
+
}
|
|
24584
|
+
function buildProbeTaskScopeSummary(node, label, isRootScope = false, allSpans) {
|
|
24585
|
+
const nestedScopeNodes = findImmediateNestedTaskScopeNodes(node);
|
|
24586
|
+
if (!isRootScope && isSubAgentScope(node.span)) {
|
|
24587
|
+
const localTasks = summarizeTaskTelemetrySpans(collectTaskSpansForScope(node, true));
|
|
24588
|
+
const hasMeaningfulTitles = localTasks && localTasks.tasks.some(
|
|
24589
|
+
(t) => t.title && t.title.length > 3 && !/^(task|engineer|code.?explorer)\s*/i.test(t.title)
|
|
24590
|
+
);
|
|
24591
|
+
if (hasMeaningfulTitles) {
|
|
24592
|
+
return {
|
|
24593
|
+
label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
|
|
24594
|
+
source: localTasks.source,
|
|
24595
|
+
eventCount: localTasks.eventCount,
|
|
24596
|
+
snapshotCount: localTasks.snapshotCount,
|
|
24597
|
+
tasks: localTasks.tasks,
|
|
24598
|
+
children: []
|
|
24599
|
+
};
|
|
24600
|
+
}
|
|
24601
|
+
const status = getSubAgentStatus(node);
|
|
24602
|
+
const scopeLabel = getTaskScopeLabel(node.span);
|
|
24603
|
+
return {
|
|
24604
|
+
label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
|
|
24605
|
+
source: "events",
|
|
24606
|
+
eventCount: 0,
|
|
24607
|
+
snapshotCount: 0,
|
|
24608
|
+
tasks: [{ id: `__scope_${node.span.spanId}`, title: scopeLabel, status, synthetic: true }],
|
|
24609
|
+
children: []
|
|
24610
|
+
};
|
|
24611
|
+
}
|
|
24612
|
+
const local = summarizeTaskTelemetrySpans(collectTaskSpansForScope(node, true));
|
|
24613
|
+
const children = [];
|
|
24614
|
+
const subAgentGroups = /* @__PURE__ */ new Map();
|
|
24615
|
+
const nonSubAgentNodes = [];
|
|
24616
|
+
for (const child of nestedScopeNodes) {
|
|
24617
|
+
if (isSubAgentScope(child.span)) {
|
|
24618
|
+
const checkId = getTaskScopeCheckId(child.span) || normalizeSpanName(child.span.name);
|
|
24619
|
+
const group = subAgentGroups.get(checkId) || [];
|
|
24620
|
+
group.push(child);
|
|
24621
|
+
subAgentGroups.set(checkId, group);
|
|
24622
|
+
} else {
|
|
24623
|
+
nonSubAgentNodes.push(child);
|
|
24624
|
+
}
|
|
24625
|
+
}
|
|
24626
|
+
for (const child of nonSubAgentNodes) {
|
|
24627
|
+
const childScope = buildProbeTaskScopeSummary(
|
|
24628
|
+
child,
|
|
24629
|
+
getTaskScopeLabel(child.span),
|
|
24630
|
+
false,
|
|
24631
|
+
allSpans
|
|
24632
|
+
);
|
|
24633
|
+
if (childScope) children.push(childScope);
|
|
24634
|
+
}
|
|
24635
|
+
for (const [checkId, nodes] of subAgentGroups) {
|
|
24636
|
+
const scopeLabel = checkId === "engineer-task" ? "Engineer" : checkId === "explore-code" ? "Code Explorer" : titleCaseWords(checkId);
|
|
24637
|
+
const tasks = [];
|
|
24638
|
+
for (const n of nodes) {
|
|
24639
|
+
const iterScope = buildProbeTaskScopeSummary(n, scopeLabel, false, allSpans);
|
|
24640
|
+
if (iterScope && iterScope.tasks.length > 0) {
|
|
24641
|
+
tasks.push(...iterScope.tasks);
|
|
24642
|
+
} else {
|
|
24643
|
+
const status = getSubAgentStatus(n);
|
|
24644
|
+
tasks.push({
|
|
24645
|
+
id: `__scope_${n.span.spanId}`,
|
|
24646
|
+
title: scopeLabel,
|
|
24647
|
+
status,
|
|
24648
|
+
synthetic: true
|
|
24649
|
+
});
|
|
24650
|
+
}
|
|
24651
|
+
}
|
|
24652
|
+
const dedupedTasks = [];
|
|
24653
|
+
for (const task of tasks) {
|
|
24654
|
+
const prev = dedupedTasks[dedupedTasks.length - 1];
|
|
24655
|
+
if (prev && prev.title === task.title && prev.status === task.status) continue;
|
|
24656
|
+
dedupedTasks.push(task);
|
|
24657
|
+
}
|
|
24658
|
+
children.push({
|
|
24659
|
+
label: scopeLabel,
|
|
24660
|
+
source: "events",
|
|
24661
|
+
eventCount: nodes.length,
|
|
24662
|
+
snapshotCount: 0,
|
|
24663
|
+
tasks: dedupedTasks,
|
|
24664
|
+
children: []
|
|
24665
|
+
});
|
|
24666
|
+
}
|
|
24667
|
+
if (!local && children.length === 0) return null;
|
|
24668
|
+
return {
|
|
24669
|
+
label: isRootScope ? ROOT_TASK_SCOPE_LABEL : label,
|
|
24670
|
+
source: local?.source ?? "events",
|
|
24671
|
+
eventCount: local?.eventCount ?? 0,
|
|
24672
|
+
snapshotCount: local?.snapshotCount ?? 0,
|
|
24673
|
+
tasks: local?.tasks ?? [],
|
|
24674
|
+
children
|
|
24675
|
+
};
|
|
24676
|
+
}
|
|
24677
|
+
function buildTaskSummaryRoot(spans) {
|
|
24678
|
+
const nodeMap = /* @__PURE__ */ new Map();
|
|
24679
|
+
for (const span of spans) {
|
|
24680
|
+
nodeMap.set(span.spanId, { span, children: [] });
|
|
24681
|
+
}
|
|
24682
|
+
const roots = [];
|
|
24683
|
+
for (const span of spans) {
|
|
24684
|
+
const node = nodeMap.get(span.spanId);
|
|
24685
|
+
if (span.parentSpanId && nodeMap.has(span.parentSpanId)) {
|
|
24686
|
+
nodeMap.get(span.parentSpanId).children.push(node);
|
|
24687
|
+
} else {
|
|
24688
|
+
roots.push(node);
|
|
24689
|
+
}
|
|
24690
|
+
}
|
|
24691
|
+
const sortChildren = (node) => {
|
|
24692
|
+
node.children.sort((a, b) => a.span.startTimeMs - b.span.startTimeMs);
|
|
24693
|
+
node.children.forEach(sortChildren);
|
|
24694
|
+
};
|
|
24695
|
+
roots.forEach(sortChildren);
|
|
24696
|
+
return {
|
|
24697
|
+
span: {
|
|
24698
|
+
traceId: spans[0]?.traceId || "",
|
|
24699
|
+
spanId: "__task-root__",
|
|
24700
|
+
name: "__task_root__",
|
|
24701
|
+
startTimeMs: roots[0]?.span.startTimeMs || 0,
|
|
24702
|
+
endTimeMs: roots[roots.length - 1]?.span.endTimeMs || 0,
|
|
24703
|
+
durationMs: 0,
|
|
24704
|
+
attributes: {},
|
|
24705
|
+
events: [],
|
|
24706
|
+
status: "ok"
|
|
24707
|
+
},
|
|
24708
|
+
children: roots
|
|
24709
|
+
};
|
|
24710
|
+
}
|
|
24711
|
+
function flattenTaskScopes(scopes) {
|
|
24712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
24713
|
+
const items = [];
|
|
24714
|
+
const visit = (scope) => {
|
|
24715
|
+
for (const task of scope.tasks) {
|
|
24716
|
+
if (task.synthetic) continue;
|
|
24717
|
+
if (seen.has(task.id)) continue;
|
|
24718
|
+
seen.add(task.id);
|
|
24719
|
+
items.push(task);
|
|
24720
|
+
}
|
|
24721
|
+
for (const child of scope.children) visit(child);
|
|
24722
|
+
};
|
|
24723
|
+
for (const scope of scopes) visit(scope);
|
|
24724
|
+
return items;
|
|
24725
|
+
}
|
|
24726
|
+
function buildTemporalTaskScopes(spans) {
|
|
24727
|
+
const ordered = [...spans].sort((a, b) => {
|
|
24728
|
+
if (a.startTimeMs !== b.startTimeMs) return a.startTimeMs - b.startTimeMs;
|
|
24729
|
+
if (a.endTimeMs !== b.endTimeMs) return a.endTimeMs - b.endTimeMs;
|
|
24730
|
+
return a.spanId.localeCompare(b.spanId);
|
|
24731
|
+
});
|
|
24732
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
24733
|
+
const scopeOrder = [];
|
|
24734
|
+
const ensureGroup = (label) => {
|
|
24735
|
+
if (!grouped.has(label)) {
|
|
24736
|
+
grouped.set(label, []);
|
|
24737
|
+
scopeOrder.push(label);
|
|
24738
|
+
}
|
|
24739
|
+
return grouped.get(label);
|
|
24740
|
+
};
|
|
24741
|
+
for (let i = 0; i < ordered.length; i += 1) {
|
|
24742
|
+
const span = ordered[i];
|
|
24743
|
+
if (!isTaskTelemetrySpan(span)) continue;
|
|
24744
|
+
let label = ROOT_TASK_SCOPE_LABEL;
|
|
24745
|
+
for (let j = i - 1; j >= 0; j -= 1) {
|
|
24746
|
+
if (isTaskScopeNode(ordered[j])) {
|
|
24747
|
+
label = getTaskScopeLabel(ordered[j]);
|
|
24748
|
+
break;
|
|
24749
|
+
}
|
|
24750
|
+
}
|
|
24751
|
+
ensureGroup(label).push(span);
|
|
24752
|
+
}
|
|
24753
|
+
if (grouped.size === 0) return [];
|
|
24754
|
+
const rootSummary = summarizeTaskTelemetrySpans(grouped.get(ROOT_TASK_SCOPE_LABEL) || []);
|
|
24755
|
+
const children = [];
|
|
24756
|
+
for (const label of scopeOrder) {
|
|
24757
|
+
if (label === ROOT_TASK_SCOPE_LABEL) continue;
|
|
24758
|
+
const summary = summarizeTaskTelemetrySpans(grouped.get(label) || []);
|
|
24759
|
+
if (!summary) continue;
|
|
24760
|
+
children.push({
|
|
24761
|
+
label,
|
|
24762
|
+
source: summary.source,
|
|
24763
|
+
eventCount: summary.eventCount,
|
|
24764
|
+
snapshotCount: summary.snapshotCount,
|
|
24765
|
+
tasks: summary.tasks,
|
|
24766
|
+
children: []
|
|
24767
|
+
});
|
|
24768
|
+
}
|
|
24769
|
+
return [
|
|
24770
|
+
{
|
|
24771
|
+
label: ROOT_TASK_SCOPE_LABEL,
|
|
24772
|
+
source: rootSummary?.source ?? "events",
|
|
24773
|
+
eventCount: rootSummary?.eventCount ?? 0,
|
|
24774
|
+
snapshotCount: rootSummary?.snapshotCount ?? 0,
|
|
24775
|
+
tasks: rootSummary?.tasks ?? [],
|
|
24776
|
+
children
|
|
24777
|
+
}
|
|
24778
|
+
];
|
|
24779
|
+
}
|
|
24780
|
+
function formatTaskStatusMarker(status) {
|
|
24781
|
+
switch (status) {
|
|
24782
|
+
case "completed":
|
|
24783
|
+
return "[x]";
|
|
24784
|
+
case "in_progress":
|
|
24785
|
+
return "[~]";
|
|
24786
|
+
case "blocked":
|
|
24787
|
+
case "failed":
|
|
24788
|
+
case "error":
|
|
24789
|
+
return "[!]";
|
|
24790
|
+
case "cancelled":
|
|
24791
|
+
case "canceled":
|
|
24792
|
+
case "deleted":
|
|
24793
|
+
return "[-]";
|
|
24794
|
+
default:
|
|
24795
|
+
return "[ ]";
|
|
24796
|
+
}
|
|
24797
|
+
}
|
|
24261
24798
|
function buildRenderContext(allSpans) {
|
|
24262
24799
|
const realSpanNames = /* @__PURE__ */ new Set();
|
|
24263
24800
|
let hasChildWorkSpans = false;
|
|
@@ -24284,28 +24821,7 @@ function shouldSkipLifecycleSpan(span, renderContext) {
|
|
|
24284
24821
|
return renderContext.realSpanNames.has(lifecycle.baseName);
|
|
24285
24822
|
}
|
|
24286
24823
|
async function serializeTraceForPrompt(traceIdOrPath, maxChars, backendConfig, taskResponse, fallbackTraceId) {
|
|
24287
|
-
|
|
24288
|
-
const cfg = resolveBackendConfig(backendConfig);
|
|
24289
|
-
const backendOrder = getBackendOrder(cfg);
|
|
24290
|
-
const isFilePath = isTraceFilePath(traceIdOrPath);
|
|
24291
|
-
const localTracePath = isFilePath ? traceIdOrPath : void 0;
|
|
24292
|
-
const remoteTraceId = fallbackTraceId || (!isFilePath ? traceIdOrPath : await readTraceIdFromFile(traceIdOrPath) || void 0);
|
|
24293
|
-
const preferLocalFirst = backendOrder[0] === "file";
|
|
24294
|
-
logger.debug(
|
|
24295
|
-
`[TraceSerializer] serializeTraceForPrompt ref=${traceIdOrPath} remoteTraceId=${remoteTraceId || "-"} backendOrder=${backendOrder.join(">")}`
|
|
24296
|
-
);
|
|
24297
|
-
if (preferLocalFirst && localTracePath) {
|
|
24298
|
-
logger.debug(`[TraceSerializer] Trying local trace file first: ${localTracePath}`);
|
|
24299
|
-
spans = await fetchTraceSpans(localTracePath, { ...cfg, type: "file" });
|
|
24300
|
-
}
|
|
24301
|
-
if (spans.length === 0 && remoteTraceId) {
|
|
24302
|
-
logger.debug(`[TraceSerializer] Trying remote trace backends for trace_id=${remoteTraceId}`);
|
|
24303
|
-
spans = await fetchTraceSpans(remoteTraceId, cfg);
|
|
24304
|
-
}
|
|
24305
|
-
if (spans.length === 0 && localTracePath) {
|
|
24306
|
-
logger.debug(`[TraceSerializer] Falling back to local trace file: ${localTracePath}`);
|
|
24307
|
-
spans = await fetchTraceSpans(localTracePath, { ...cfg, type: "file" });
|
|
24308
|
-
}
|
|
24824
|
+
const { spans } = await resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId);
|
|
24309
24825
|
if (spans.length === 0) {
|
|
24310
24826
|
return "(no trace data available)";
|
|
24311
24827
|
}
|
|
@@ -24319,6 +24835,101 @@ async function serializeTraceForPrompt(traceIdOrPath, maxChars, backendConfig, t
|
|
|
24319
24835
|
taskResponse
|
|
24320
24836
|
});
|
|
24321
24837
|
}
|
|
24838
|
+
function renderTraceTreeFromSpans(spans, maxChars, taskResponse) {
|
|
24839
|
+
const tree = buildSpanTree(spans);
|
|
24840
|
+
const routeIntentTopic = extractRouteIntentTopic(spans);
|
|
24841
|
+
const fullOutput = (maxChars ?? 4e3) > 1e5;
|
|
24842
|
+
return renderSpanYaml(tree, spans, {
|
|
24843
|
+
maxChars: maxChars ?? 4e3,
|
|
24844
|
+
fallbackIntent: routeIntentTopic,
|
|
24845
|
+
fullOutput,
|
|
24846
|
+
taskResponse
|
|
24847
|
+
});
|
|
24848
|
+
}
|
|
24849
|
+
function formatTaskStatus(status) {
|
|
24850
|
+
return status === "completed" ? "[x]" : status === "in_progress" ? "[~]" : status === "blocked" || status === "failed" || status === "error" ? "[!]" : status === "deleted" || status === "cancelled" || status === "canceled" ? "[-]" : "[ ]";
|
|
24851
|
+
}
|
|
24852
|
+
function formatTaskLabel(task) {
|
|
24853
|
+
const title = String(task.title || "").trim();
|
|
24854
|
+
return title || task.id || task.status;
|
|
24855
|
+
}
|
|
24856
|
+
function scopeHasRenderableTasks(scope) {
|
|
24857
|
+
if (scope.tasks.some((task) => !task.synthetic)) return true;
|
|
24858
|
+
return scope.children.some((child) => scopeHasRenderableTasks(child));
|
|
24859
|
+
}
|
|
24860
|
+
function appendTaskScopeLines(lines, scope, depth = 0) {
|
|
24861
|
+
if (!scopeHasRenderableTasks(scope)) return;
|
|
24862
|
+
const prefix = " ".repeat(depth);
|
|
24863
|
+
if (depth > 0 || scope.label !== ROOT_TASK_SCOPE_LABEL) {
|
|
24864
|
+
lines.push(`${prefix}${scope.label}`);
|
|
24865
|
+
}
|
|
24866
|
+
for (const task of scope.tasks.filter((task2) => !task2.synthetic)) {
|
|
24867
|
+
lines.push(`${prefix} ${formatTaskStatus(task.status)} ${formatTaskLabel(task)}`);
|
|
24868
|
+
}
|
|
24869
|
+
for (const child of scope.children) {
|
|
24870
|
+
appendTaskScopeLines(lines, child, depth + 1);
|
|
24871
|
+
}
|
|
24872
|
+
}
|
|
24873
|
+
function buildTraceHeaderLines(traceData, taskSummary) {
|
|
24874
|
+
const lines = [`Trace source: ${traceData.source || "unknown"}`];
|
|
24875
|
+
if (!taskSummary || taskSummary.tasks.length === 0) {
|
|
24876
|
+
lines.push("Tasks: no task telemetry found in this trace");
|
|
24877
|
+
return lines;
|
|
24878
|
+
}
|
|
24879
|
+
const eventsSuffix = taskSummary.snapshotCount > 0 ? `, ${taskSummary.eventCount} task events, ${taskSummary.snapshotCount} snapshots` : `, ${taskSummary.eventCount} task events`;
|
|
24880
|
+
lines.push(`Tasks: ${taskSummary.tasks.length} tracked (${taskSummary.source}${eventsSuffix})`);
|
|
24881
|
+
if (taskSummary.scopes.length > 0) {
|
|
24882
|
+
for (const scope of taskSummary.scopes) {
|
|
24883
|
+
appendTaskScopeLines(lines, scope);
|
|
24884
|
+
}
|
|
24885
|
+
} else {
|
|
24886
|
+
for (const task of taskSummary.tasks) {
|
|
24887
|
+
lines.push(` ${formatTaskStatus(task.status)} ${formatTaskLabel(task)}`);
|
|
24888
|
+
}
|
|
24889
|
+
}
|
|
24890
|
+
return lines;
|
|
24891
|
+
}
|
|
24892
|
+
async function buildTraceReport(traceIdOrPath, maxChars, backendConfig, taskResponse, fallbackTraceId) {
|
|
24893
|
+
const traceData = await resolveTraceSpans(traceIdOrPath, backendConfig, fallbackTraceId);
|
|
24894
|
+
if (traceData.spans.length === 0) {
|
|
24895
|
+
return null;
|
|
24896
|
+
}
|
|
24897
|
+
const taskSummary = extractProbeTaskSummary(traceData.spans);
|
|
24898
|
+
const headerLines = buildTraceHeaderLines(traceData, taskSummary);
|
|
24899
|
+
const headerText = headerLines.join("\n");
|
|
24900
|
+
const tree = renderTraceTreeFromSpans(traceData.spans, maxChars, taskResponse);
|
|
24901
|
+
return {
|
|
24902
|
+
traceData,
|
|
24903
|
+
tree,
|
|
24904
|
+
taskSummary,
|
|
24905
|
+
headerText,
|
|
24906
|
+
text: `${headerText}
|
|
24907
|
+
|
|
24908
|
+
${tree}`
|
|
24909
|
+
};
|
|
24910
|
+
}
|
|
24911
|
+
function extractProbeTaskSummary(spans) {
|
|
24912
|
+
const overall = summarizeTaskTelemetrySpans(spans);
|
|
24913
|
+
if (!overall) return null;
|
|
24914
|
+
const rootScope = buildProbeTaskScopeSummary(
|
|
24915
|
+
buildTaskSummaryRoot(spans),
|
|
24916
|
+
ROOT_TASK_SCOPE_LABEL,
|
|
24917
|
+
true,
|
|
24918
|
+
spans
|
|
24919
|
+
);
|
|
24920
|
+
let scopes = rootScope ? [rootScope] : [];
|
|
24921
|
+
if (scopes.length === 0 || scopes[0].children.length === 0) {
|
|
24922
|
+
const temporalScopes = buildTemporalTaskScopes(spans);
|
|
24923
|
+
if (temporalScopes.length > 0 && temporalScopes[0].children.length > 0) {
|
|
24924
|
+
scopes = temporalScopes;
|
|
24925
|
+
}
|
|
24926
|
+
}
|
|
24927
|
+
return {
|
|
24928
|
+
...overall,
|
|
24929
|
+
tasks: scopes.length > 0 ? flattenTaskScopes(scopes) : overall.tasks,
|
|
24930
|
+
scopes
|
|
24931
|
+
};
|
|
24932
|
+
}
|
|
24322
24933
|
function renderSpanTree(tree, opts) {
|
|
24323
24934
|
const maxChars = opts?.maxChars ?? 4e3;
|
|
24324
24935
|
const maxDepth = opts?.maxDepth ?? 20;
|
|
@@ -24446,6 +25057,19 @@ function renderYamlNode(node, indent, lines, dedup, renderContext, fallbackInten
|
|
|
24446
25057
|
const toolInput = extractToolInput(String(toolName), attrs);
|
|
24447
25058
|
const toolResultLen = attrs["tool.result.length"] || attrs["tool.result.count"];
|
|
24448
25059
|
const tn = String(toolName);
|
|
25060
|
+
const taskSnapshot = tn === "task" ? parseTaskStatusSnapshot(String(attrs["tool.result"] || "")) : [];
|
|
25061
|
+
if (tn === "task" && taskSnapshot.length > 0) {
|
|
25062
|
+
lines.push(`${pad}tasks snapshot${childSuffix}:`);
|
|
25063
|
+
for (const task of taskSnapshot) {
|
|
25064
|
+
lines.push(
|
|
25065
|
+
`${pad} ${formatTaskStatusMarker(task.status)} ${task.id}: ${truncate(task.title, 100)}`
|
|
25066
|
+
);
|
|
25067
|
+
}
|
|
25068
|
+
return;
|
|
25069
|
+
}
|
|
25070
|
+
if (tn === "task") {
|
|
25071
|
+
return;
|
|
25072
|
+
}
|
|
24449
25073
|
const isSearchTool = tn === "search" || tn === "searchCode" || tn === "search_code";
|
|
24450
25074
|
const numLen = toolResultLen ? Number(toolResultLen) : -1;
|
|
24451
25075
|
const noResults = isSearchTool && numLen >= 0 && numLen < 500;
|
|
@@ -24459,10 +25083,12 @@ function renderYamlNode(node, indent, lines, dedup, renderContext, fallbackInten
|
|
|
24459
25083
|
const action = attrs["dedup.action"] || "?";
|
|
24460
25084
|
const reason = attrs["dedup.reason"] || "";
|
|
24461
25085
|
const rewritten = attrs["dedup.rewritten"] || "";
|
|
25086
|
+
const error = attrs["dedup.error"] || "";
|
|
24462
25087
|
const prevCount = attrs["dedup.previous_count"] || "0";
|
|
24463
25088
|
let detail = `${action}`;
|
|
24464
25089
|
if (rewritten) detail += ` \u2192 "${truncate(String(rewritten), 60)}"`;
|
|
24465
25090
|
if (reason) detail += ` (${truncate(String(reason), 80)})`;
|
|
25091
|
+
if (error) detail += ` [error: ${truncate(String(error), 120)}]`;
|
|
24466
25092
|
lines.push(
|
|
24467
25093
|
`${pad}dedup("${truncate(String(query), 60)}") [${prevCount} prior]: ${detail} \u2014 ${duration}`
|
|
24468
25094
|
);
|
|
@@ -24656,6 +25282,86 @@ function renderYamlNode(node, indent, lines, dedup, renderContext, fallbackInten
|
|
|
24656
25282
|
}
|
|
24657
25283
|
return;
|
|
24658
25284
|
}
|
|
25285
|
+
const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
|
|
25286
|
+
if (taskEventMatch) {
|
|
25287
|
+
const eventType = taskEventMatch[1];
|
|
25288
|
+
const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
|
|
25289
|
+
const taskTitle = attrs["task.title"] ? truncate(String(attrs["task.title"]), 80) : "";
|
|
25290
|
+
const taskIds = attrs["task.ids"] ? truncate(String(attrs["task.ids"]), 120) : "";
|
|
25291
|
+
const count = attrs["task.count"] ? Number(attrs["task.count"]) : void 0;
|
|
25292
|
+
const totalCount = attrs["task.total_count"] ? Number(attrs["task.total_count"]) : void 0;
|
|
25293
|
+
const incompleteCount = attrs["task.incomplete_count"] ? Number(attrs["task.incomplete_count"]) : void 0;
|
|
25294
|
+
const completedCount = attrs["task.completed_count"] ? Number(attrs["task.completed_count"]) : void 0;
|
|
25295
|
+
const remaining = attrs["task.incomplete_remaining"] ? Number(attrs["task.incomplete_remaining"]) : void 0;
|
|
25296
|
+
const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
|
|
25297
|
+
const fieldsUpdated = attrs["task.fields_updated"] ? truncate(String(attrs["task.fields_updated"]), 80) : "";
|
|
25298
|
+
const error = attrs["task.error"] ? truncate(String(attrs["task.error"]), 100) : "";
|
|
25299
|
+
const action = attrs["task.action"] ? String(attrs["task.action"]) : "";
|
|
25300
|
+
switch (eventType) {
|
|
25301
|
+
case "session_started":
|
|
25302
|
+
lines.push(`${pad}tasks enabled \u2014 ${duration}`);
|
|
25303
|
+
return;
|
|
25304
|
+
case "created":
|
|
25305
|
+
lines.push(`${pad}[ ] ${taskId}${taskTitle ? `: ${taskTitle}` : ""} \u2014 ${duration}`);
|
|
25306
|
+
return;
|
|
25307
|
+
case "batch_created": {
|
|
25308
|
+
const totalSuffix = totalCount !== void 0 ? `, total ${totalCount}` : "";
|
|
25309
|
+
lines.push(
|
|
25310
|
+
`${pad}tasks created (${count ?? "?"}${totalSuffix}): ${taskIds || "unknown"} \u2014 ${duration}`
|
|
25311
|
+
);
|
|
25312
|
+
return;
|
|
25313
|
+
}
|
|
25314
|
+
case "updated": {
|
|
25315
|
+
const statusSuffix = newStatus && newStatus !== "unchanged" ? ` -> ${newStatus}` : "";
|
|
25316
|
+
const fieldSuffix = fieldsUpdated ? ` [${fieldsUpdated}]` : "";
|
|
25317
|
+
lines.push(`${pad}task updated: ${taskId}${statusSuffix}${fieldSuffix} \u2014 ${duration}`);
|
|
25318
|
+
return;
|
|
25319
|
+
}
|
|
25320
|
+
case "batch_updated":
|
|
25321
|
+
lines.push(`${pad}tasks updated (${count ?? "?"}): ${taskIds || "unknown"} \u2014 ${duration}`);
|
|
25322
|
+
return;
|
|
25323
|
+
case "completed": {
|
|
25324
|
+
const remainingSuffix = remaining !== void 0 ? ` (${remaining} remaining)` : "";
|
|
25325
|
+
lines.push(
|
|
25326
|
+
`${pad}[x] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${remainingSuffix} \u2014 ${duration}`
|
|
25327
|
+
);
|
|
25328
|
+
return;
|
|
25329
|
+
}
|
|
25330
|
+
case "batch_completed": {
|
|
25331
|
+
const remainingSuffix = remaining !== void 0 ? `, ${remaining} remaining` : "";
|
|
25332
|
+
lines.push(
|
|
25333
|
+
`${pad}tasks completed (${count ?? "?"}${remainingSuffix}): ${taskIds || "unknown"} \u2014 ${duration}`
|
|
25334
|
+
);
|
|
25335
|
+
return;
|
|
25336
|
+
}
|
|
25337
|
+
case "deleted":
|
|
25338
|
+
lines.push(`${pad}task deleted: ${taskId} \u2014 ${duration}`);
|
|
25339
|
+
return;
|
|
25340
|
+
case "batch_deleted":
|
|
25341
|
+
lines.push(`${pad}tasks deleted (${count ?? "?"}): ${taskIds || "unknown"} \u2014 ${duration}`);
|
|
25342
|
+
return;
|
|
25343
|
+
case "listed": {
|
|
25344
|
+
const stats = [];
|
|
25345
|
+
if (totalCount !== void 0) stats.push(`${totalCount} total`);
|
|
25346
|
+
if (incompleteCount !== void 0) stats.push(`${incompleteCount} incomplete`);
|
|
25347
|
+
if (completedCount !== void 0) stats.push(`${completedCount} completed`);
|
|
25348
|
+
lines.push(
|
|
25349
|
+
`${pad}tasks listed${stats.length > 0 ? ` (${stats.join(", ")})` : ""} \u2014 ${duration}`
|
|
25350
|
+
);
|
|
25351
|
+
return;
|
|
25352
|
+
}
|
|
25353
|
+
case "validation_error":
|
|
25354
|
+
case "error":
|
|
25355
|
+
lines.push(`${pad}task.${eventType}: ${error || "unknown error"} \u2014 ${duration}`);
|
|
25356
|
+
return;
|
|
25357
|
+
case "unknown_action":
|
|
25358
|
+
lines.push(`${pad}task.unknown_action: ${action || "unknown"} \u2014 ${duration}`);
|
|
25359
|
+
return;
|
|
25360
|
+
default:
|
|
25361
|
+
lines.push(`${pad}task.${eventType}${taskId ? `: ${taskId}` : ""} \u2014 ${duration}`);
|
|
25362
|
+
return;
|
|
25363
|
+
}
|
|
25364
|
+
}
|
|
24659
25365
|
const errMark = node.span.status === "error" ? " \u2717" : "";
|
|
24660
25366
|
const hasChildren = node.children.length > 0;
|
|
24661
25367
|
lines.push(`${pad}${name}${childSuffix} \u2014 ${duration}${errMark}${hasChildren ? ":" : ""}`);
|
|
@@ -24935,9 +25641,11 @@ function formatSpanLine(span, parentSpan, fallbackIntent, fullOutput, dedup, has
|
|
|
24935
25641
|
const action = attrs["dedup.action"] || "?";
|
|
24936
25642
|
const reason = attrs["dedup.reason"] || "";
|
|
24937
25643
|
const rewritten = attrs["dedup.rewritten"] || "";
|
|
25644
|
+
const error = attrs["dedup.error"] || "";
|
|
24938
25645
|
let detail = `${action}`;
|
|
24939
25646
|
if (rewritten) detail += ` \u2192 "${truncate(String(rewritten), 50)}"`;
|
|
24940
25647
|
if (reason) detail += ` \u2014 ${truncate(String(reason), 60)}`;
|
|
25648
|
+
if (error) detail += ` [error: ${truncate(String(error), 80)}]`;
|
|
24941
25649
|
return { line: `dedup("${truncate(String(query), 50)}") ${detail} (${duration})` };
|
|
24942
25650
|
}
|
|
24943
25651
|
if (name === "search.delegate") {
|
|
@@ -25122,6 +25830,72 @@ function formatSpanLine(span, parentSpan, fallbackIntent, fullOutput, dedup, has
|
|
|
25122
25830
|
const reasonStr = reason ? `: ${truncate(String(reason), 80)}` : "";
|
|
25123
25831
|
return { line: `graceful_stop${reasonStr} (${duration})` };
|
|
25124
25832
|
}
|
|
25833
|
+
const taskEventMatch = name.match(/(?:^probe\.event\.)?task\.([a-z_]+)$/);
|
|
25834
|
+
if (taskEventMatch) {
|
|
25835
|
+
const eventType = taskEventMatch[1];
|
|
25836
|
+
const taskId = attrs["task.id"] ? String(attrs["task.id"]) : "";
|
|
25837
|
+
const taskTitle = attrs["task.title"] ? truncate(String(attrs["task.title"]), 80) : "";
|
|
25838
|
+
const taskIds = attrs["task.ids"] ? truncate(String(attrs["task.ids"]), 120) : "";
|
|
25839
|
+
const count = attrs["task.count"] ? Number(attrs["task.count"]) : void 0;
|
|
25840
|
+
const totalCount = attrs["task.total_count"] ? Number(attrs["task.total_count"]) : void 0;
|
|
25841
|
+
const incompleteCount = attrs["task.incomplete_count"] ? Number(attrs["task.incomplete_count"]) : void 0;
|
|
25842
|
+
const completedCount = attrs["task.completed_count"] ? Number(attrs["task.completed_count"]) : void 0;
|
|
25843
|
+
const remaining = attrs["task.incomplete_remaining"] ? Number(attrs["task.incomplete_remaining"]) : void 0;
|
|
25844
|
+
const newStatus = attrs["task.new_status"] ? String(attrs["task.new_status"]) : "";
|
|
25845
|
+
const fieldsUpdated = attrs["task.fields_updated"] ? truncate(String(attrs["task.fields_updated"]), 80) : "";
|
|
25846
|
+
const error = attrs["task.error"] ? truncate(String(attrs["task.error"]), 100) : "";
|
|
25847
|
+
const action = attrs["task.action"] ? String(attrs["task.action"]) : "";
|
|
25848
|
+
const tail = ` (${duration})`;
|
|
25849
|
+
switch (eventType) {
|
|
25850
|
+
case "session_started":
|
|
25851
|
+
return { line: `tasks enabled${tail}` };
|
|
25852
|
+
case "created":
|
|
25853
|
+
return { line: `[ ] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${tail}` };
|
|
25854
|
+
case "batch_created": {
|
|
25855
|
+
const totalSuffix = totalCount !== void 0 ? `, total ${totalCount}` : "";
|
|
25856
|
+
return {
|
|
25857
|
+
line: `tasks created (${count ?? "?"}${totalSuffix}): ${taskIds || "unknown"}${tail}`
|
|
25858
|
+
};
|
|
25859
|
+
}
|
|
25860
|
+
case "updated": {
|
|
25861
|
+
const statusSuffix = newStatus && newStatus !== "unchanged" ? ` -> ${newStatus}` : "";
|
|
25862
|
+
const fieldSuffix = fieldsUpdated ? ` [${fieldsUpdated}]` : "";
|
|
25863
|
+
return { line: `task updated: ${taskId}${statusSuffix}${fieldSuffix}${tail}` };
|
|
25864
|
+
}
|
|
25865
|
+
case "batch_updated":
|
|
25866
|
+
return { line: `tasks updated (${count ?? "?"}): ${taskIds || "unknown"}${tail}` };
|
|
25867
|
+
case "completed": {
|
|
25868
|
+
const remainingSuffix = remaining !== void 0 ? ` (${remaining} remaining)` : "";
|
|
25869
|
+
return {
|
|
25870
|
+
line: `[x] ${taskId}${taskTitle ? `: ${taskTitle}` : ""}${remainingSuffix}${tail}`
|
|
25871
|
+
};
|
|
25872
|
+
}
|
|
25873
|
+
case "batch_completed": {
|
|
25874
|
+
const remainingSuffix = remaining !== void 0 ? `, ${remaining} remaining` : "";
|
|
25875
|
+
return {
|
|
25876
|
+
line: `tasks completed (${count ?? "?"}${remainingSuffix}): ${taskIds || "unknown"}${tail}`
|
|
25877
|
+
};
|
|
25878
|
+
}
|
|
25879
|
+
case "deleted":
|
|
25880
|
+
return { line: `task deleted: ${taskId}${tail}` };
|
|
25881
|
+
case "batch_deleted":
|
|
25882
|
+
return { line: `tasks deleted (${count ?? "?"}): ${taskIds || "unknown"}${tail}` };
|
|
25883
|
+
case "listed": {
|
|
25884
|
+
const stats = [];
|
|
25885
|
+
if (totalCount !== void 0) stats.push(`${totalCount} total`);
|
|
25886
|
+
if (incompleteCount !== void 0) stats.push(`${incompleteCount} incomplete`);
|
|
25887
|
+
if (completedCount !== void 0) stats.push(`${completedCount} completed`);
|
|
25888
|
+
return { line: `tasks listed${stats.length > 0 ? ` (${stats.join(", ")})` : ""}${tail}` };
|
|
25889
|
+
}
|
|
25890
|
+
case "validation_error":
|
|
25891
|
+
case "error":
|
|
25892
|
+
return { line: `task.${eventType}: ${error || "unknown error"}${tail}` };
|
|
25893
|
+
case "unknown_action":
|
|
25894
|
+
return { line: `task.unknown_action: ${action || "unknown"}${tail}` };
|
|
25895
|
+
default:
|
|
25896
|
+
return { line: `task.${eventType}${taskId ? `: ${taskId}` : ""}${tail}` };
|
|
25897
|
+
}
|
|
25898
|
+
}
|
|
25125
25899
|
return { line: `${name} (${duration})${span.status === "error" ? " \u2717" : ""}` };
|
|
25126
25900
|
}
|
|
25127
25901
|
function parseWorkspacePath(fullPath) {
|
|
@@ -25457,12 +26231,12 @@ function truncate(str, max) {
|
|
|
25457
26231
|
if (head < 10) return str.slice(0, max - 3) + "...";
|
|
25458
26232
|
return str.slice(0, head) + " ...[truncated]... " + str.slice(-tail);
|
|
25459
26233
|
}
|
|
25460
|
-
var
|
|
26234
|
+
var fs16, readline, path19, NOISE_SPAN_NAMES, WRAPPER_SPAN_NAMES, ROOT_TASK_SCOPE_LABEL, TASK_SCOPE_EXCLUDED_CHECKS, SUB_AGENT_SCOPE_CHECKS;
|
|
25461
26235
|
var init_trace_serializer = __esm({
|
|
25462
26236
|
"src/agent-protocol/trace-serializer.ts"() {
|
|
25463
26237
|
"use strict";
|
|
25464
|
-
|
|
25465
|
-
|
|
26238
|
+
fs16 = __toESM(require("fs"));
|
|
26239
|
+
readline = __toESM(require("readline"));
|
|
25466
26240
|
path19 = __toESM(require("path"));
|
|
25467
26241
|
init_logger();
|
|
25468
26242
|
NOISE_SPAN_NAMES = /* @__PURE__ */ new Set([
|
|
@@ -25476,6 +26250,20 @@ var init_trace_serializer = __esm({
|
|
|
25476
26250
|
"visor.ai_check",
|
|
25477
26251
|
"probe.delegation"
|
|
25478
26252
|
]);
|
|
26253
|
+
ROOT_TASK_SCOPE_LABEL = "Main Agent";
|
|
26254
|
+
TASK_SCOPE_EXCLUDED_CHECKS = /* @__PURE__ */ new Set([
|
|
26255
|
+
"chat",
|
|
26256
|
+
"route-intent",
|
|
26257
|
+
"classify",
|
|
26258
|
+
"build-config",
|
|
26259
|
+
"generate-response",
|
|
26260
|
+
"setup-projects",
|
|
26261
|
+
"checkout-docs",
|
|
26262
|
+
"route-projects",
|
|
26263
|
+
"project-items",
|
|
26264
|
+
"checkout-projects"
|
|
26265
|
+
]);
|
|
26266
|
+
SUB_AGENT_SCOPE_CHECKS = /* @__PURE__ */ new Set(["engineer-task", "explore-code"]);
|
|
25479
26267
|
}
|
|
25480
26268
|
});
|
|
25481
26269
|
|
|
@@ -25603,6 +26391,67 @@ function formatProgressMetadata(timing) {
|
|
|
25603
26391
|
}
|
|
25604
26392
|
return `_Metadata: ${parts.join(" | ")}_`;
|
|
25605
26393
|
}
|
|
26394
|
+
function formatTaskStatusMarker2(status) {
|
|
26395
|
+
switch (status) {
|
|
26396
|
+
case "completed":
|
|
26397
|
+
return "[x]";
|
|
26398
|
+
case "in_progress":
|
|
26399
|
+
return "[~]";
|
|
26400
|
+
case "blocked":
|
|
26401
|
+
case "failed":
|
|
26402
|
+
case "error":
|
|
26403
|
+
return "[!]";
|
|
26404
|
+
case "deleted":
|
|
26405
|
+
case "cancelled":
|
|
26406
|
+
case "canceled":
|
|
26407
|
+
return "[-]";
|
|
26408
|
+
default:
|
|
26409
|
+
return "[ ]";
|
|
26410
|
+
}
|
|
26411
|
+
}
|
|
26412
|
+
function formatTaskLabel2(task) {
|
|
26413
|
+
const title = String(task.title || "").trim();
|
|
26414
|
+
return title || task.id || task.status;
|
|
26415
|
+
}
|
|
26416
|
+
function scopeHasRenderableTasks2(scope) {
|
|
26417
|
+
if (scope.tasks.some((task) => !task.synthetic)) return true;
|
|
26418
|
+
return scope.children.some((child) => scopeHasRenderableTasks2(child));
|
|
26419
|
+
}
|
|
26420
|
+
function appendTaskScopeLines2(lines, scope, depth = 0) {
|
|
26421
|
+
if (!scopeHasRenderableTasks2(scope)) return;
|
|
26422
|
+
const prefix = " ".repeat(depth);
|
|
26423
|
+
if (depth > 0 || scope.label !== "Main Agent") {
|
|
26424
|
+
lines.push(`${prefix}${scope.label}`);
|
|
26425
|
+
}
|
|
26426
|
+
const visibleTasks = scope.tasks.filter((task) => !task.synthetic).slice(0, 8);
|
|
26427
|
+
for (const task of visibleTasks) {
|
|
26428
|
+
lines.push(`${prefix}\u2022 ${formatTaskStatusMarker2(task.status)} ${formatTaskLabel2(task)}`);
|
|
26429
|
+
}
|
|
26430
|
+
const hiddenCount = scope.tasks.filter((task) => !task.synthetic).length - visibleTasks.length;
|
|
26431
|
+
if (hiddenCount > 0) {
|
|
26432
|
+
lines.push(`${prefix}\u2022 ... ${hiddenCount} more`);
|
|
26433
|
+
}
|
|
26434
|
+
for (const child of scope.children) {
|
|
26435
|
+
appendTaskScopeLines2(lines, child, depth + 1);
|
|
26436
|
+
}
|
|
26437
|
+
}
|
|
26438
|
+
function formatTaskSummary(summary) {
|
|
26439
|
+
if (!summary || summary.tasks.length === 0) return void 0;
|
|
26440
|
+
const lines = ["*Tasks*"];
|
|
26441
|
+
if (summary.scopes.length > 0) {
|
|
26442
|
+
for (const scope of summary.scopes) appendTaskScopeLines2(lines, scope);
|
|
26443
|
+
} else {
|
|
26444
|
+
const visibleTasks = summary.tasks.filter((task) => !task.synthetic).slice(0, 8);
|
|
26445
|
+
for (const task of visibleTasks) {
|
|
26446
|
+
lines.push(`\u2022 ${formatTaskStatusMarker2(task.status)} ${formatTaskLabel2(task)}`);
|
|
26447
|
+
}
|
|
26448
|
+
const hiddenCount = summary.tasks.filter((task) => !task.synthetic).length - visibleTasks.length;
|
|
26449
|
+
if (hiddenCount > 0) {
|
|
26450
|
+
lines.push(`\u2022 ... ${hiddenCount} more`);
|
|
26451
|
+
}
|
|
26452
|
+
}
|
|
26453
|
+
return lines.length > 1 ? lines.join("\n") : void 0;
|
|
26454
|
+
}
|
|
25606
26455
|
function formatSkillList(skills) {
|
|
25607
26456
|
const normalized = dedupeStrings(skills);
|
|
25608
26457
|
if (normalized.length <= 4) return normalized.join(", ");
|
|
@@ -25615,7 +26464,7 @@ function dedupeStrings(values) {
|
|
|
25615
26464
|
async function extractTraceSkillMetadata(traceRef, traceId) {
|
|
25616
26465
|
if (!traceRef && !traceId) return void 0;
|
|
25617
26466
|
try {
|
|
25618
|
-
const spans = await fetchTraceSpans(
|
|
26467
|
+
const spans = await fetchTraceSpans(traceRef || traceId, { targetTraceId: traceId });
|
|
25619
26468
|
if (!spans.length) return void 0;
|
|
25620
26469
|
const routeIntentSpan = spans.find(
|
|
25621
26470
|
(span) => span.attributes["visor.check.id"] === "route-intent"
|
|
@@ -25634,8 +26483,12 @@ async function extractTraceSkillMetadata(traceRef, traceId) {
|
|
|
25634
26483
|
(Array.isArray(routeIntentOutput?.skills) ? routeIntentOutput.skills : void 0) || (Array.isArray(classifyOutput?.skills) ? classifyOutput.skills : void 0)
|
|
25635
26484
|
);
|
|
25636
26485
|
const finalActivatedSkills = activatedSkills.length > 0 ? activatedSkills : fallbackActivatedSkills;
|
|
25637
|
-
|
|
25638
|
-
|
|
26486
|
+
const taskSummary = extractProbeTaskSummary(spans);
|
|
26487
|
+
if (!finalActivatedSkills.length && !taskSummary) return void 0;
|
|
26488
|
+
return {
|
|
26489
|
+
activatedSkills: finalActivatedSkills.length ? finalActivatedSkills : void 0,
|
|
26490
|
+
taskSummary: taskSummary || void 0
|
|
26491
|
+
};
|
|
25639
26492
|
} catch (err) {
|
|
25640
26493
|
logger.debug(
|
|
25641
26494
|
`[TaskLiveUpdates] Failed to extract skill metadata from trace: ${err instanceof Error ? err.message : String(err)}`
|
|
@@ -25691,7 +26544,7 @@ function extractProgressField(text, label) {
|
|
|
25691
26544
|
function escapeRegExp(value) {
|
|
25692
26545
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
25693
26546
|
}
|
|
25694
|
-
var import_crypto5, DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS, DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS, DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS, DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS, DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS, DEFAULT_TASK_LIVE_UPDATE_MODEL, DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE, DEFAULT_TASK_LIVE_UPDATE_PROMPT, TaskLiveUpdateManager;
|
|
26547
|
+
var import_crypto5, DEFAULT_TASK_LIVE_UPDATE_INTERVAL_SECONDS, DEFAULT_TASK_LIVE_UPDATE_MAX_TRACE_CHARS, DEFAULT_TASK_LIVE_UPDATE_FIRST_UPDATE_DELAY_SECONDS, DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS, DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS, DEFAULT_TASK_LIVE_UPDATE_MODEL, DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE, GENERIC_COMPLETION_FALLBACK, DEFAULT_TASK_LIVE_UPDATE_PROMPT, TaskLiveUpdateManager;
|
|
25695
26548
|
var init_task_live_updates = __esm({
|
|
25696
26549
|
"src/agent-protocol/task-live-updates.ts"() {
|
|
25697
26550
|
"use strict";
|
|
@@ -25705,6 +26558,7 @@ var init_task_live_updates = __esm({
|
|
|
25705
26558
|
DEFAULT_TASK_LIVE_UPDATE_STALL_FALLBACK_SECONDS = 60;
|
|
25706
26559
|
DEFAULT_TASK_LIVE_UPDATE_MODEL = "gemini-3.1-flash-lite-preview";
|
|
25707
26560
|
DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE = "_No new meaningful progress is visible yet. Some steps can stay quiet for up to 5 minutes before there is new news._";
|
|
26561
|
+
GENERIC_COMPLETION_FALLBACK = "Execution completed";
|
|
25708
26562
|
DEFAULT_TASK_LIVE_UPDATE_PROMPT = `You are generating a short live progress update for a user while an AI task is still running.
|
|
25709
26563
|
|
|
25710
26564
|
This is NOT the final answer.
|
|
@@ -25787,6 +26641,9 @@ Good update example:
|
|
|
25787
26641
|
firstTickTimer;
|
|
25788
26642
|
metadataRefreshTimer;
|
|
25789
26643
|
running = false;
|
|
26644
|
+
inflightTick;
|
|
26645
|
+
inflightMetadataRefresh;
|
|
26646
|
+
inflightPublishes = /* @__PURE__ */ new Set();
|
|
25790
26647
|
started = false;
|
|
25791
26648
|
completed = false;
|
|
25792
26649
|
startedAt = /* @__PURE__ */ new Date();
|
|
@@ -25822,6 +26679,14 @@ Good update example:
|
|
|
25822
26679
|
if (this.completed) return;
|
|
25823
26680
|
this.completed = true;
|
|
25824
26681
|
this.stop();
|
|
26682
|
+
await this.waitForInflightSinkPublishes();
|
|
26683
|
+
const cleanedFinalText = finalText.trim();
|
|
26684
|
+
if (!this.lastPostedMessage && cleanedFinalText === GENERIC_COMPLETION_FALLBACK) {
|
|
26685
|
+
logger.info(
|
|
26686
|
+
`[TaskLiveUpdates] Suppressing generic final update for task ${this.ctx.taskId}: no progress update was posted`
|
|
26687
|
+
);
|
|
26688
|
+
return;
|
|
26689
|
+
}
|
|
25825
26690
|
try {
|
|
25826
26691
|
logger.info(`[TaskLiveUpdates] Publishing final success update for task ${this.ctx.taskId}`);
|
|
25827
26692
|
const result = await this.ctx.sink.complete(this.decorateText(finalText));
|
|
@@ -25837,6 +26702,7 @@ Good update example:
|
|
|
25837
26702
|
if (this.completed) return;
|
|
25838
26703
|
this.completed = true;
|
|
25839
26704
|
this.stop();
|
|
26705
|
+
await this.waitForInflightSinkPublishes();
|
|
25840
26706
|
try {
|
|
25841
26707
|
logger.info(`[TaskLiveUpdates] Publishing final failure update for task ${this.ctx.taskId}`);
|
|
25842
26708
|
const result = await this.ctx.sink.fail(this.decorateText(finalText));
|
|
@@ -25863,6 +26729,16 @@ Good update example:
|
|
|
25863
26729
|
}
|
|
25864
26730
|
}
|
|
25865
26731
|
async tick() {
|
|
26732
|
+
if (this.inflightTick) return this.inflightTick;
|
|
26733
|
+
const promise = this.runTick().finally(() => {
|
|
26734
|
+
if (this.inflightTick === promise) {
|
|
26735
|
+
this.inflightTick = void 0;
|
|
26736
|
+
}
|
|
26737
|
+
});
|
|
26738
|
+
this.inflightTick = promise;
|
|
26739
|
+
return promise;
|
|
26740
|
+
}
|
|
26741
|
+
async runTick() {
|
|
25866
26742
|
if (this.completed || this.running) return;
|
|
25867
26743
|
const traceState = this.getTraceState();
|
|
25868
26744
|
if (!traceState.traceRef && !traceState.traceId) {
|
|
@@ -25935,11 +26811,18 @@ Good update example:
|
|
|
25935
26811
|
elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1e3)),
|
|
25936
26812
|
previousUpdateAt: this.lastUpdateAt,
|
|
25937
26813
|
secondsSincePreviousUpdate: this.lastUpdateAt ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1e3)) : void 0,
|
|
25938
|
-
activatedSkills: this.lastSkillMetadata?.activatedSkills
|
|
26814
|
+
activatedSkills: this.lastSkillMetadata?.activatedSkills,
|
|
26815
|
+
taskSummary: this.lastSkillMetadata?.taskSummary
|
|
25939
26816
|
},
|
|
25940
26817
|
traceState.traceId
|
|
25941
26818
|
);
|
|
25942
|
-
|
|
26819
|
+
if (this.completed) {
|
|
26820
|
+
logger.debug(
|
|
26821
|
+
`[TaskLiveUpdates] Aborting progress publish for task ${this.ctx.taskId}: task already completed before sink update`
|
|
26822
|
+
);
|
|
26823
|
+
return;
|
|
26824
|
+
}
|
|
26825
|
+
const result = await this.trackPublish(this.ctx.sink.update(message));
|
|
25943
26826
|
this.recordSinkRef(result);
|
|
25944
26827
|
this.ctx.appendHistory?.(cleaned, "progress");
|
|
25945
26828
|
this.lastUpdateText = cleaned;
|
|
@@ -25967,15 +26850,31 @@ Good update example:
|
|
|
25967
26850
|
this.timer.unref();
|
|
25968
26851
|
}
|
|
25969
26852
|
this.metadataRefreshTimer = setInterval(() => {
|
|
25970
|
-
void this.
|
|
26853
|
+
void this.scheduleMetadataRefresh();
|
|
25971
26854
|
}, DEFAULT_TASK_LIVE_UPDATE_METADATA_REFRESH_SECONDS * 1e3);
|
|
25972
26855
|
if (typeof this.metadataRefreshTimer?.unref === "function") {
|
|
25973
26856
|
this.metadataRefreshTimer.unref();
|
|
25974
26857
|
}
|
|
25975
26858
|
}
|
|
26859
|
+
async waitForInflightSinkPublishes() {
|
|
26860
|
+
const pending = [...this.inflightPublishes];
|
|
26861
|
+
if (pending.length === 0) return;
|
|
26862
|
+
try {
|
|
26863
|
+
await Promise.allSettled(pending);
|
|
26864
|
+
} catch {
|
|
26865
|
+
}
|
|
26866
|
+
}
|
|
25976
26867
|
recordSinkRef(result) {
|
|
25977
26868
|
if (result?.ref) this.ctx.onPostedRef?.(result.ref);
|
|
25978
26869
|
}
|
|
26870
|
+
async trackPublish(publish) {
|
|
26871
|
+
this.inflightPublishes.add(publish);
|
|
26872
|
+
try {
|
|
26873
|
+
return await publish;
|
|
26874
|
+
} finally {
|
|
26875
|
+
this.inflightPublishes.delete(publish);
|
|
26876
|
+
}
|
|
26877
|
+
}
|
|
25979
26878
|
getTraceState() {
|
|
25980
26879
|
const resolved = this.ctx.resolveTraceState?.();
|
|
25981
26880
|
return {
|
|
@@ -25990,16 +26889,30 @@ Good update example:
|
|
|
25990
26889
|
|
|
25991
26890
|
\`task_id: ${this.ctx.taskId}\``;
|
|
25992
26891
|
}
|
|
25993
|
-
decorateProgressText(text, timing,
|
|
26892
|
+
decorateProgressText(text, timing, _traceId) {
|
|
25994
26893
|
const normalized = normalizeProgressSummary(text);
|
|
26894
|
+
const taskSummary = formatTaskSummary(timing.taskSummary);
|
|
25995
26895
|
const blocks = [
|
|
25996
26896
|
"*Live Update*",
|
|
25997
26897
|
"_Current task is still running. This message updates in place until the final answer is ready._",
|
|
25998
26898
|
this.lastUpdateKind === "stall" ? DEFAULT_TASK_LIVE_UPDATE_STALL_NOTICE : "",
|
|
26899
|
+
taskSummary,
|
|
25999
26900
|
normalized,
|
|
26000
|
-
formatProgressMetadata(timing)
|
|
26901
|
+
formatProgressMetadata(timing),
|
|
26902
|
+
`\`task_id: ${this.ctx.taskId}\``,
|
|
26903
|
+
"`live_update: true`"
|
|
26001
26904
|
].filter(Boolean);
|
|
26002
|
-
return
|
|
26905
|
+
return blocks.join("\n\n");
|
|
26906
|
+
}
|
|
26907
|
+
scheduleMetadataRefresh() {
|
|
26908
|
+
if (this.inflightMetadataRefresh) return this.inflightMetadataRefresh;
|
|
26909
|
+
const promise = this.refreshProgressMetadata().finally(() => {
|
|
26910
|
+
if (this.inflightMetadataRefresh === promise) {
|
|
26911
|
+
this.inflightMetadataRefresh = void 0;
|
|
26912
|
+
}
|
|
26913
|
+
});
|
|
26914
|
+
this.inflightMetadataRefresh = promise;
|
|
26915
|
+
return promise;
|
|
26003
26916
|
}
|
|
26004
26917
|
async refreshProgressMetadata() {
|
|
26005
26918
|
if (this.completed || this.running || !this.lastUpdateText) return;
|
|
@@ -26010,7 +26923,8 @@ Good update example:
|
|
|
26010
26923
|
elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1e3)),
|
|
26011
26924
|
previousUpdateAt: this.lastUpdateAt,
|
|
26012
26925
|
secondsSincePreviousUpdate: this.lastUpdateAt ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1e3)) : void 0,
|
|
26013
|
-
activatedSkills: this.lastSkillMetadata?.activatedSkills
|
|
26926
|
+
activatedSkills: this.lastSkillMetadata?.activatedSkills,
|
|
26927
|
+
taskSummary: this.lastSkillMetadata?.taskSummary
|
|
26014
26928
|
},
|
|
26015
26929
|
traceState.traceId
|
|
26016
26930
|
);
|
|
@@ -26020,7 +26934,7 @@ Good update example:
|
|
|
26020
26934
|
logger.debug(
|
|
26021
26935
|
`[TaskLiveUpdates] Refreshing metadata-only live update for task ${this.ctx.taskId}`
|
|
26022
26936
|
);
|
|
26023
|
-
const result = await this.ctx.sink.update(message);
|
|
26937
|
+
const result = await this.trackPublish(this.ctx.sink.update(message));
|
|
26024
26938
|
this.recordSinkRef(result);
|
|
26025
26939
|
this.lastPostedMessage = message;
|
|
26026
26940
|
} catch (err) {
|
|
@@ -26056,7 +26970,8 @@ Good update example:
|
|
|
26056
26970
|
elapsedSeconds: Math.max(0, Math.floor((Date.now() - this.startedAt.getTime()) / 1e3)),
|
|
26057
26971
|
previousUpdateAt: this.lastUpdateAt,
|
|
26058
26972
|
secondsSincePreviousUpdate: this.lastUpdateAt ? Math.max(0, Math.floor((Date.now() - this.lastUpdateAt.getTime()) / 1e3)) : void 0,
|
|
26059
|
-
activatedSkills: this.lastSkillMetadata?.activatedSkills
|
|
26973
|
+
activatedSkills: this.lastSkillMetadata?.activatedSkills,
|
|
26974
|
+
taskSummary: this.lastSkillMetadata?.taskSummary
|
|
26060
26975
|
},
|
|
26061
26976
|
traceId
|
|
26062
26977
|
);
|
|
@@ -26064,7 +26979,8 @@ Good update example:
|
|
|
26064
26979
|
this.lastStallFallbackAt = now;
|
|
26065
26980
|
return;
|
|
26066
26981
|
}
|
|
26067
|
-
|
|
26982
|
+
if (this.completed) return;
|
|
26983
|
+
const result = await this.trackPublish(this.ctx.sink.update(message));
|
|
26068
26984
|
this.recordSinkRef(result);
|
|
26069
26985
|
if (!this.lastUpdateText) {
|
|
26070
26986
|
this.ctx.appendHistory?.(baseText, "progress");
|
|
@@ -26093,7 +27009,7 @@ async function resolveTaskTraceReference(metadata) {
|
|
|
26093
27009
|
return {
|
|
26094
27010
|
traceId,
|
|
26095
27011
|
traceFile,
|
|
26096
|
-
primaryRef:
|
|
27012
|
+
primaryRef: traceFile || traceId || void 0
|
|
26097
27013
|
};
|
|
26098
27014
|
}
|
|
26099
27015
|
var init_task_trace_resolution = __esm({
|
|
@@ -26187,14 +27103,15 @@ async function evaluateTask(taskId, store, config) {
|
|
|
26187
27103
|
if (traceFile || traceId) {
|
|
26188
27104
|
try {
|
|
26189
27105
|
const traceRef = traceFile || traceId;
|
|
26190
|
-
|
|
27106
|
+
const report = await buildTraceReport(
|
|
26191
27107
|
traceRef,
|
|
26192
27108
|
1e6,
|
|
26193
27109
|
{ traceDir: config?.traceDir },
|
|
26194
27110
|
responseText !== "No response available" ? responseText : void 0,
|
|
26195
27111
|
traceId
|
|
26196
27112
|
);
|
|
26197
|
-
|
|
27113
|
+
traceTree = report?.text;
|
|
27114
|
+
if (!traceTree || traceTree === "(no trace data available)") {
|
|
26198
27115
|
traceTree = void 0;
|
|
26199
27116
|
}
|
|
26200
27117
|
} catch (err) {
|
|
@@ -26357,6 +27274,77 @@ var track_execution_exports = {};
|
|
|
26357
27274
|
__export(track_execution_exports, {
|
|
26358
27275
|
trackExecution: () => trackExecution
|
|
26359
27276
|
});
|
|
27277
|
+
function unwrapJsonText(text) {
|
|
27278
|
+
if (text.startsWith("{") && text.includes('"text"')) {
|
|
27279
|
+
try {
|
|
27280
|
+
const parsed = JSON.parse(text);
|
|
27281
|
+
if (typeof parsed?.text === "string" && parsed.text.trim().length > 0) {
|
|
27282
|
+
return parsed.text.trim();
|
|
27283
|
+
}
|
|
27284
|
+
} catch {
|
|
27285
|
+
try {
|
|
27286
|
+
const sanitized = text.replace(
|
|
27287
|
+
/[\x00-\x1F\x7F]/g,
|
|
27288
|
+
(ch) => "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0")
|
|
27289
|
+
);
|
|
27290
|
+
const parsed = JSON.parse(sanitized);
|
|
27291
|
+
if (typeof parsed?.text === "string" && parsed.text.trim().length > 0) {
|
|
27292
|
+
return parsed.text.trim();
|
|
27293
|
+
}
|
|
27294
|
+
} catch {
|
|
27295
|
+
}
|
|
27296
|
+
}
|
|
27297
|
+
}
|
|
27298
|
+
return text;
|
|
27299
|
+
}
|
|
27300
|
+
function isToolOutput(text) {
|
|
27301
|
+
if (/^[├│└─┬┘┤┼]{1,3}/.test(text)) return true;
|
|
27302
|
+
if (text.startsWith("[task:")) return true;
|
|
27303
|
+
return false;
|
|
27304
|
+
}
|
|
27305
|
+
function extractTextFromEntries(entries) {
|
|
27306
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
27307
|
+
const outputs = entries[i];
|
|
27308
|
+
if (!Array.isArray(outputs)) continue;
|
|
27309
|
+
for (let j = outputs.length - 1; j >= 0; j--) {
|
|
27310
|
+
const text = outputs[j]?.text;
|
|
27311
|
+
if (typeof text === "string" && text.trim().length > 0) {
|
|
27312
|
+
return unwrapJsonText(text.trim());
|
|
27313
|
+
}
|
|
27314
|
+
}
|
|
27315
|
+
}
|
|
27316
|
+
return void 0;
|
|
27317
|
+
}
|
|
27318
|
+
function extractBestResponseText(history, allKeys) {
|
|
27319
|
+
const topLevelEntries = Object.entries(history).filter(([key]) => !key.includes(".")).map(([, v]) => v);
|
|
27320
|
+
const topLevelText = extractTextFromEntries(topLevelEntries);
|
|
27321
|
+
if (topLevelText && !isToolOutput(topLevelText)) {
|
|
27322
|
+
logger.info(
|
|
27323
|
+
`[TaskTracking] Extracted responseText (${topLevelText.length} chars): "${topLevelText.substring(0, 100)}..."`
|
|
27324
|
+
);
|
|
27325
|
+
return topLevelText;
|
|
27326
|
+
}
|
|
27327
|
+
const genResponseKeys = allKeys.filter(
|
|
27328
|
+
(k) => k.endsWith(".generate-response") || k === "generate-response"
|
|
27329
|
+
);
|
|
27330
|
+
if (genResponseKeys.length > 0) {
|
|
27331
|
+
const genEntries = genResponseKeys.map((k) => history[k]);
|
|
27332
|
+
const genText = extractTextFromEntries(genEntries);
|
|
27333
|
+
if (genText && !isToolOutput(genText)) {
|
|
27334
|
+
logger.info(
|
|
27335
|
+
`[TaskTracking] Fallback to generate-response entry (${genText.length} chars): "${genText.substring(0, 100)}..."`
|
|
27336
|
+
);
|
|
27337
|
+
return genText;
|
|
27338
|
+
}
|
|
27339
|
+
}
|
|
27340
|
+
if (topLevelText) {
|
|
27341
|
+
logger.warn(
|
|
27342
|
+
`[TaskTracking] Using tool output as responseText (${topLevelText.length} chars) \u2014 no better candidate found`
|
|
27343
|
+
);
|
|
27344
|
+
return topLevelText;
|
|
27345
|
+
}
|
|
27346
|
+
return "Execution completed";
|
|
27347
|
+
}
|
|
26360
27348
|
function getPackageVersion() {
|
|
26361
27349
|
try {
|
|
26362
27350
|
return require_package()?.version || "dev";
|
|
@@ -26435,7 +27423,7 @@ async function trackExecution(opts, executor) {
|
|
|
26435
27423
|
};
|
|
26436
27424
|
}
|
|
26437
27425
|
return {
|
|
26438
|
-
traceRef: current?.metadata?.
|
|
27426
|
+
traceRef: current?.metadata?.trace_file || current?.metadata?.trace_id || initialResolvedTrace.primaryRef,
|
|
26439
27427
|
traceId: current?.metadata?.trace_id
|
|
26440
27428
|
};
|
|
26441
27429
|
},
|
|
@@ -26488,18 +27476,13 @@ async function trackExecution(opts, executor) {
|
|
|
26488
27476
|
try {
|
|
26489
27477
|
const history = result?.reviewSummary?.history;
|
|
26490
27478
|
if (history) {
|
|
26491
|
-
const
|
|
26492
|
-
|
|
26493
|
-
|
|
26494
|
-
|
|
26495
|
-
|
|
26496
|
-
|
|
26497
|
-
|
|
26498
|
-
responseText = text.trim();
|
|
26499
|
-
break;
|
|
26500
|
-
}
|
|
26501
|
-
}
|
|
26502
|
-
if (responseText !== "Execution completed") break;
|
|
27479
|
+
const allKeys = Object.keys(history);
|
|
27480
|
+
responseText = extractBestResponseText(history, allKeys);
|
|
27481
|
+
}
|
|
27482
|
+
if (responseText === "Execution completed") {
|
|
27483
|
+
const issueSummary = summarizeUserFacingIssues(result?.reviewSummary?.issues);
|
|
27484
|
+
if (issueSummary) {
|
|
27485
|
+
responseText = issueSummary;
|
|
26503
27486
|
}
|
|
26504
27487
|
}
|
|
26505
27488
|
} catch {
|
|
@@ -26573,6 +27556,7 @@ var init_track_execution = __esm({
|
|
|
26573
27556
|
init_instance_id();
|
|
26574
27557
|
init_task_live_updates();
|
|
26575
27558
|
init_task_trace_resolution();
|
|
27559
|
+
init_user_facing_error();
|
|
26576
27560
|
}
|
|
26577
27561
|
});
|
|
26578
27562
|
|
|
@@ -26755,7 +27739,11 @@ var init_scheduler = __esm({
|
|
|
26755
27739
|
if (!import_node_cron.default.validate(job.schedule)) {
|
|
26756
27740
|
throw new Error(`Invalid cron expression: ${job.schedule}`);
|
|
26757
27741
|
}
|
|
26758
|
-
if (job.workflow
|
|
27742
|
+
if (!job.workflow) {
|
|
27743
|
+
if (!job.inputs?.text) {
|
|
27744
|
+
throw new Error("Cron job without a workflow must have inputs.text set");
|
|
27745
|
+
}
|
|
27746
|
+
} else if (job.workflow !== "default") {
|
|
26759
27747
|
const allChecks = Object.keys(this.visorConfig.checks || {});
|
|
26760
27748
|
if (!allChecks.includes(job.workflow)) {
|
|
26761
27749
|
throw new Error(`Workflow "${job.workflow}" not found in configuration`);
|
|
@@ -26778,7 +27766,7 @@ var init_scheduler = __esm({
|
|
|
26778
27766
|
const nextRun = getNextRunTime(job.schedule, job.timezone || this.defaultTimezone);
|
|
26779
27767
|
const description = job.description ? ` (${job.description})` : "";
|
|
26780
27768
|
logger.debug(
|
|
26781
|
-
`[Scheduler] Scheduled static cron job '${jobId}'${description}: ${job.schedule} \u2192 ${job.workflow}, next run: ${nextRun.toISOString()}`
|
|
27769
|
+
`[Scheduler] Scheduled static cron job '${jobId}'${description}: ${job.schedule} \u2192 ${job.workflow || "(reminder)"}, next run: ${nextRun.toISOString()}`
|
|
26782
27770
|
);
|
|
26783
27771
|
} catch {
|
|
26784
27772
|
}
|
|
@@ -28698,6 +29686,116 @@ var init_schedule_tool_handler = __esm({
|
|
|
28698
29686
|
}
|
|
28699
29687
|
});
|
|
28700
29688
|
|
|
29689
|
+
// src/utils/oauth2-token-cache.ts
|
|
29690
|
+
var oauth2_token_cache_exports = {};
|
|
29691
|
+
__export(oauth2_token_cache_exports, {
|
|
29692
|
+
OAuth2TokenCache: () => OAuth2TokenCache
|
|
29693
|
+
});
|
|
29694
|
+
var OAuth2TokenCache;
|
|
29695
|
+
var init_oauth2_token_cache = __esm({
|
|
29696
|
+
"src/utils/oauth2-token-cache.ts"() {
|
|
29697
|
+
"use strict";
|
|
29698
|
+
init_logger();
|
|
29699
|
+
init_env_resolver();
|
|
29700
|
+
OAuth2TokenCache = class _OAuth2TokenCache {
|
|
29701
|
+
static instance;
|
|
29702
|
+
cache = /* @__PURE__ */ new Map();
|
|
29703
|
+
static getInstance() {
|
|
29704
|
+
if (!_OAuth2TokenCache.instance) {
|
|
29705
|
+
_OAuth2TokenCache.instance = new _OAuth2TokenCache();
|
|
29706
|
+
}
|
|
29707
|
+
return _OAuth2TokenCache.instance;
|
|
29708
|
+
}
|
|
29709
|
+
/** Visible for testing */
|
|
29710
|
+
static resetInstance() {
|
|
29711
|
+
_OAuth2TokenCache.instance = void 0;
|
|
29712
|
+
}
|
|
29713
|
+
/**
|
|
29714
|
+
* Get a valid Bearer token for the given config.
|
|
29715
|
+
* Returns a cached token if still valid, otherwise fetches a new one.
|
|
29716
|
+
*/
|
|
29717
|
+
async getToken(config) {
|
|
29718
|
+
const clientId = String(EnvironmentResolver.resolveValue(config.client_id));
|
|
29719
|
+
const clientSecret = String(EnvironmentResolver.resolveValue(config.client_secret));
|
|
29720
|
+
const tokenUrl = String(EnvironmentResolver.resolveValue(config.token_url));
|
|
29721
|
+
const bufferMs = (config.token_ttl_buffer ?? 300) * 1e3;
|
|
29722
|
+
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
29723
|
+
const cached = this.cache.get(cacheKey);
|
|
29724
|
+
if (cached && cached.expires_at - bufferMs > Date.now()) {
|
|
29725
|
+
logger.verbose("[oauth2] Using cached token");
|
|
29726
|
+
return cached.access_token;
|
|
29727
|
+
}
|
|
29728
|
+
if (cached?.refreshPromise) {
|
|
29729
|
+
logger.verbose("[oauth2] Awaiting in-flight token refresh");
|
|
29730
|
+
return cached.refreshPromise;
|
|
29731
|
+
}
|
|
29732
|
+
const refreshPromise = this.fetchToken(tokenUrl, clientId, clientSecret, config.scopes);
|
|
29733
|
+
if (cached) {
|
|
29734
|
+
cached.refreshPromise = refreshPromise;
|
|
29735
|
+
} else {
|
|
29736
|
+
this.cache.set(cacheKey, {
|
|
29737
|
+
access_token: "",
|
|
29738
|
+
expires_at: 0,
|
|
29739
|
+
refreshPromise
|
|
29740
|
+
});
|
|
29741
|
+
}
|
|
29742
|
+
try {
|
|
29743
|
+
const token = await refreshPromise;
|
|
29744
|
+
return token;
|
|
29745
|
+
} finally {
|
|
29746
|
+
const entry = this.cache.get(cacheKey);
|
|
29747
|
+
if (entry) {
|
|
29748
|
+
entry.refreshPromise = void 0;
|
|
29749
|
+
}
|
|
29750
|
+
}
|
|
29751
|
+
}
|
|
29752
|
+
async fetchToken(tokenUrl, clientId, clientSecret, scopes) {
|
|
29753
|
+
logger.verbose(`[oauth2] Fetching token from ${tokenUrl}`);
|
|
29754
|
+
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
29755
|
+
const bodyParams = new URLSearchParams({ grant_type: "client_credentials" });
|
|
29756
|
+
if (scopes?.length) {
|
|
29757
|
+
bodyParams.set("scope", scopes.join(" "));
|
|
29758
|
+
}
|
|
29759
|
+
const response = await fetch(tokenUrl, {
|
|
29760
|
+
method: "POST",
|
|
29761
|
+
headers: {
|
|
29762
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
29763
|
+
Authorization: `Basic ${credentials}`
|
|
29764
|
+
},
|
|
29765
|
+
body: bodyParams.toString()
|
|
29766
|
+
});
|
|
29767
|
+
if (!response.ok) {
|
|
29768
|
+
let errorDetail = "";
|
|
29769
|
+
try {
|
|
29770
|
+
errorDetail = await response.text();
|
|
29771
|
+
} catch {
|
|
29772
|
+
}
|
|
29773
|
+
throw new Error(
|
|
29774
|
+
`OAuth2 token request failed: HTTP ${response.status} ${response.statusText}${errorDetail ? ` - ${errorDetail.substring(0, 200)}` : ""}`
|
|
29775
|
+
);
|
|
29776
|
+
}
|
|
29777
|
+
const data = await response.json();
|
|
29778
|
+
if (!data.access_token) {
|
|
29779
|
+
throw new Error("OAuth2 token response missing access_token");
|
|
29780
|
+
}
|
|
29781
|
+
const expiresIn = data.expires_in ?? 3600;
|
|
29782
|
+
const expiresAt = Date.now() + expiresIn * 1e3;
|
|
29783
|
+
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
29784
|
+
this.cache.set(cacheKey, {
|
|
29785
|
+
access_token: data.access_token,
|
|
29786
|
+
expires_at: expiresAt
|
|
29787
|
+
});
|
|
29788
|
+
logger.verbose(`[oauth2] Token acquired, expires in ${expiresIn}s`);
|
|
29789
|
+
return data.access_token;
|
|
29790
|
+
}
|
|
29791
|
+
/** Clear all cached tokens (for testing or credential rotation) */
|
|
29792
|
+
clear() {
|
|
29793
|
+
this.cache.clear();
|
|
29794
|
+
}
|
|
29795
|
+
};
|
|
29796
|
+
}
|
|
29797
|
+
});
|
|
29798
|
+
|
|
28701
29799
|
// src/utils/issue-normalizer.ts
|
|
28702
29800
|
function extractIssuesFromOutput(output, defaultRuleId) {
|
|
28703
29801
|
if (output === null || output === void 0) {
|
|
@@ -28888,7 +29986,7 @@ var utcp_check_provider_exports = {};
|
|
|
28888
29986
|
__export(utcp_check_provider_exports, {
|
|
28889
29987
|
UtcpCheckProvider: () => UtcpCheckProvider
|
|
28890
29988
|
});
|
|
28891
|
-
var
|
|
29989
|
+
var fs18, path21, UtcpCheckProvider;
|
|
28892
29990
|
var init_utcp_check_provider = __esm({
|
|
28893
29991
|
"src/providers/utcp-check-provider.ts"() {
|
|
28894
29992
|
"use strict";
|
|
@@ -28898,7 +29996,7 @@ var init_utcp_check_provider = __esm({
|
|
|
28898
29996
|
init_sandbox();
|
|
28899
29997
|
init_env_resolver();
|
|
28900
29998
|
init_issue_normalizer();
|
|
28901
|
-
|
|
29999
|
+
fs18 = __toESM(require("fs"));
|
|
28902
30000
|
path21 = __toESM(require("path"));
|
|
28903
30001
|
UtcpCheckProvider = class _UtcpCheckProvider extends CheckProvider {
|
|
28904
30002
|
liquid;
|
|
@@ -29152,20 +30250,20 @@ var init_utcp_check_provider = __esm({
|
|
|
29152
30250
|
`Path traversal detected: "${manual}" resolves outside the project directory. UTCP manual paths must be within the project directory.`
|
|
29153
30251
|
);
|
|
29154
30252
|
}
|
|
29155
|
-
if (
|
|
29156
|
-
const realPath =
|
|
30253
|
+
if (fs18.existsSync(resolvedPath)) {
|
|
30254
|
+
const realPath = fs18.realpathSync(resolvedPath);
|
|
29157
30255
|
if (realPath !== cwd && !realPath.startsWith(cwdPrefix)) {
|
|
29158
30256
|
throw new Error(
|
|
29159
30257
|
`Symlink traversal detected: "${manual}" points outside the project directory via symlink.`
|
|
29160
30258
|
);
|
|
29161
30259
|
}
|
|
29162
30260
|
}
|
|
29163
|
-
if (!
|
|
30261
|
+
if (!fs18.existsSync(resolvedPath)) {
|
|
29164
30262
|
throw new Error(`UTCP manual file not found: ${resolvedPath}`);
|
|
29165
30263
|
}
|
|
29166
30264
|
let content;
|
|
29167
30265
|
try {
|
|
29168
|
-
content =
|
|
30266
|
+
content = fs18.readFileSync(resolvedPath, "utf-8");
|
|
29169
30267
|
} catch (err) {
|
|
29170
30268
|
throw new Error(
|
|
29171
30269
|
`Failed to read UTCP manual file: ${resolvedPath}: ${err instanceof Error ? err.message : "Unknown error"}`
|
|
@@ -29923,9 +31021,24 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
29923
31021
|
}
|
|
29924
31022
|
return record;
|
|
29925
31023
|
};
|
|
31024
|
+
const fixRequiredFields = (obj) => {
|
|
31025
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return obj;
|
|
31026
|
+
const record = obj;
|
|
31027
|
+
if (Array.isArray(record.required) && record.properties && typeof record.properties === "object") {
|
|
31028
|
+
const validProps = Object.keys(record.properties);
|
|
31029
|
+
record.required = record.required.filter((r) => validProps.includes(r));
|
|
31030
|
+
}
|
|
31031
|
+
for (const key of Object.keys(record)) {
|
|
31032
|
+
if (typeof record[key] === "object" && record[key] !== null) {
|
|
31033
|
+
fixRequiredFields(record[key]);
|
|
31034
|
+
}
|
|
31035
|
+
}
|
|
31036
|
+
return record;
|
|
31037
|
+
};
|
|
29926
31038
|
const normalizeInputSchema = (schema) => {
|
|
29927
31039
|
if (schema && schema.type === "object") {
|
|
29928
31040
|
fixArraySchemas(schema);
|
|
31041
|
+
fixRequiredFields(schema);
|
|
29929
31042
|
return schema;
|
|
29930
31043
|
}
|
|
29931
31044
|
return {
|
|
@@ -29934,7 +31047,10 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
29934
31047
|
required: []
|
|
29935
31048
|
};
|
|
29936
31049
|
};
|
|
29937
|
-
const regularTools = await this.toolExecutor.listMcpTools()
|
|
31050
|
+
const regularTools = (await this.toolExecutor.listMcpTools()).map((tool) => ({
|
|
31051
|
+
...tool,
|
|
31052
|
+
inputSchema: normalizeInputSchema(tool.inputSchema)
|
|
31053
|
+
}));
|
|
29938
31054
|
const workflowTools = Array.from(this.tools.values()).filter(isWorkflowTool).map((tool) => ({
|
|
29939
31055
|
name: tool.name,
|
|
29940
31056
|
description: tool.description || `Execute ${tool.name}`,
|
|
@@ -30319,6 +31435,11 @@ var init_mcp_custom_sse_server = __esm({
|
|
|
30319
31435
|
if (authType === "bearer" && tool.auth.token) {
|
|
30320
31436
|
const token = String(EnvironmentResolver.resolveValue(tool.auth.token));
|
|
30321
31437
|
resolvedHeaders["Authorization"] = `Bearer ${token}`;
|
|
31438
|
+
} else if (authType === "oauth2_client_credentials" && tool.auth.token_url) {
|
|
31439
|
+
const { OAuth2TokenCache: OAuth2TokenCache2 } = await Promise.resolve().then(() => (init_oauth2_token_cache(), oauth2_token_cache_exports));
|
|
31440
|
+
const tokenCache = OAuth2TokenCache2.getInstance();
|
|
31441
|
+
const token = await tokenCache.getToken(tool.auth);
|
|
31442
|
+
resolvedHeaders["Authorization"] = `Bearer ${token}`;
|
|
30322
31443
|
}
|
|
30323
31444
|
}
|
|
30324
31445
|
if (this.debug) {
|
|
@@ -30599,6 +31720,7 @@ var init_ai_check_provider = __esm({
|
|
|
30599
31720
|
init_schedule_tool();
|
|
30600
31721
|
init_task_progress_tool();
|
|
30601
31722
|
init_schedule_tool_handler();
|
|
31723
|
+
init_user_facing_error();
|
|
30602
31724
|
AICheckProvider = class _AICheckProvider extends CheckProvider {
|
|
30603
31725
|
aiReviewService;
|
|
30604
31726
|
liquidEngine;
|
|
@@ -32201,13 +33323,14 @@ ${processedPrompt}` : processedPrompt;
|
|
|
32201
33323
|
}
|
|
32202
33324
|
} catch (error) {
|
|
32203
33325
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
33326
|
+
const userFacingMessage = formatUserFacingExecutionError(error);
|
|
32204
33327
|
console.error(`\u274C AI Check Provider Error for check: ${errorMessage}`);
|
|
32205
33328
|
const isCriticalError = errorMessage.includes("API rate limit") || errorMessage.includes("403") || errorMessage.includes("401") || errorMessage.includes("authentication") || errorMessage.includes("API key");
|
|
32206
33329
|
if (isCriticalError) {
|
|
32207
33330
|
console.error(`\u{1F6A8} CRITICAL ERROR: AI provider authentication or rate limit issue detected`);
|
|
32208
33331
|
console.error(`\u{1F6A8} This check cannot proceed without valid API credentials`);
|
|
32209
33332
|
}
|
|
32210
|
-
throw new Error(
|
|
33333
|
+
throw new Error(userFacingMessage);
|
|
32211
33334
|
} finally {
|
|
32212
33335
|
if (customToolsServer) {
|
|
32213
33336
|
try {
|
|
@@ -33102,114 +34225,8 @@ var init_template_context = __esm({
|
|
|
33102
34225
|
}
|
|
33103
34226
|
});
|
|
33104
34227
|
|
|
33105
|
-
// src/utils/oauth2-token-cache.ts
|
|
33106
|
-
var OAuth2TokenCache;
|
|
33107
|
-
var init_oauth2_token_cache = __esm({
|
|
33108
|
-
"src/utils/oauth2-token-cache.ts"() {
|
|
33109
|
-
"use strict";
|
|
33110
|
-
init_logger();
|
|
33111
|
-
init_env_resolver();
|
|
33112
|
-
OAuth2TokenCache = class _OAuth2TokenCache {
|
|
33113
|
-
static instance;
|
|
33114
|
-
cache = /* @__PURE__ */ new Map();
|
|
33115
|
-
static getInstance() {
|
|
33116
|
-
if (!_OAuth2TokenCache.instance) {
|
|
33117
|
-
_OAuth2TokenCache.instance = new _OAuth2TokenCache();
|
|
33118
|
-
}
|
|
33119
|
-
return _OAuth2TokenCache.instance;
|
|
33120
|
-
}
|
|
33121
|
-
/** Visible for testing */
|
|
33122
|
-
static resetInstance() {
|
|
33123
|
-
_OAuth2TokenCache.instance = void 0;
|
|
33124
|
-
}
|
|
33125
|
-
/**
|
|
33126
|
-
* Get a valid Bearer token for the given config.
|
|
33127
|
-
* Returns a cached token if still valid, otherwise fetches a new one.
|
|
33128
|
-
*/
|
|
33129
|
-
async getToken(config) {
|
|
33130
|
-
const clientId = String(EnvironmentResolver.resolveValue(config.client_id));
|
|
33131
|
-
const clientSecret = String(EnvironmentResolver.resolveValue(config.client_secret));
|
|
33132
|
-
const tokenUrl = String(EnvironmentResolver.resolveValue(config.token_url));
|
|
33133
|
-
const bufferMs = (config.token_ttl_buffer ?? 300) * 1e3;
|
|
33134
|
-
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
33135
|
-
const cached = this.cache.get(cacheKey);
|
|
33136
|
-
if (cached && cached.expires_at - bufferMs > Date.now()) {
|
|
33137
|
-
logger.verbose("[oauth2] Using cached token");
|
|
33138
|
-
return cached.access_token;
|
|
33139
|
-
}
|
|
33140
|
-
if (cached?.refreshPromise) {
|
|
33141
|
-
logger.verbose("[oauth2] Awaiting in-flight token refresh");
|
|
33142
|
-
return cached.refreshPromise;
|
|
33143
|
-
}
|
|
33144
|
-
const refreshPromise = this.fetchToken(tokenUrl, clientId, clientSecret, config.scopes);
|
|
33145
|
-
if (cached) {
|
|
33146
|
-
cached.refreshPromise = refreshPromise;
|
|
33147
|
-
} else {
|
|
33148
|
-
this.cache.set(cacheKey, {
|
|
33149
|
-
access_token: "",
|
|
33150
|
-
expires_at: 0,
|
|
33151
|
-
refreshPromise
|
|
33152
|
-
});
|
|
33153
|
-
}
|
|
33154
|
-
try {
|
|
33155
|
-
const token = await refreshPromise;
|
|
33156
|
-
return token;
|
|
33157
|
-
} finally {
|
|
33158
|
-
const entry = this.cache.get(cacheKey);
|
|
33159
|
-
if (entry) {
|
|
33160
|
-
entry.refreshPromise = void 0;
|
|
33161
|
-
}
|
|
33162
|
-
}
|
|
33163
|
-
}
|
|
33164
|
-
async fetchToken(tokenUrl, clientId, clientSecret, scopes) {
|
|
33165
|
-
logger.verbose(`[oauth2] Fetching token from ${tokenUrl}`);
|
|
33166
|
-
const credentials = Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
|
33167
|
-
const bodyParams = new URLSearchParams({ grant_type: "client_credentials" });
|
|
33168
|
-
if (scopes?.length) {
|
|
33169
|
-
bodyParams.set("scope", scopes.join(" "));
|
|
33170
|
-
}
|
|
33171
|
-
const response = await fetch(tokenUrl, {
|
|
33172
|
-
method: "POST",
|
|
33173
|
-
headers: {
|
|
33174
|
-
"Content-Type": "application/x-www-form-urlencoded",
|
|
33175
|
-
Authorization: `Basic ${credentials}`
|
|
33176
|
-
},
|
|
33177
|
-
body: bodyParams.toString()
|
|
33178
|
-
});
|
|
33179
|
-
if (!response.ok) {
|
|
33180
|
-
let errorDetail = "";
|
|
33181
|
-
try {
|
|
33182
|
-
errorDetail = await response.text();
|
|
33183
|
-
} catch {
|
|
33184
|
-
}
|
|
33185
|
-
throw new Error(
|
|
33186
|
-
`OAuth2 token request failed: HTTP ${response.status} ${response.statusText}${errorDetail ? ` - ${errorDetail.substring(0, 200)}` : ""}`
|
|
33187
|
-
);
|
|
33188
|
-
}
|
|
33189
|
-
const data = await response.json();
|
|
33190
|
-
if (!data.access_token) {
|
|
33191
|
-
throw new Error("OAuth2 token response missing access_token");
|
|
33192
|
-
}
|
|
33193
|
-
const expiresIn = data.expires_in ?? 3600;
|
|
33194
|
-
const expiresAt = Date.now() + expiresIn * 1e3;
|
|
33195
|
-
const cacheKey = `${tokenUrl}|${clientId}`;
|
|
33196
|
-
this.cache.set(cacheKey, {
|
|
33197
|
-
access_token: data.access_token,
|
|
33198
|
-
expires_at: expiresAt
|
|
33199
|
-
});
|
|
33200
|
-
logger.verbose(`[oauth2] Token acquired, expires in ${expiresIn}s`);
|
|
33201
|
-
return data.access_token;
|
|
33202
|
-
}
|
|
33203
|
-
/** Clear all cached tokens (for testing or credential rotation) */
|
|
33204
|
-
clear() {
|
|
33205
|
-
this.cache.clear();
|
|
33206
|
-
}
|
|
33207
|
-
};
|
|
33208
|
-
}
|
|
33209
|
-
});
|
|
33210
|
-
|
|
33211
34228
|
// src/providers/http-client-provider.ts
|
|
33212
|
-
var
|
|
34229
|
+
var fs21, path23, HttpClientProvider;
|
|
33213
34230
|
var init_http_client_provider = __esm({
|
|
33214
34231
|
"src/providers/http-client-provider.ts"() {
|
|
33215
34232
|
"use strict";
|
|
@@ -33221,7 +34238,7 @@ var init_http_client_provider = __esm({
|
|
|
33221
34238
|
init_oauth2_token_cache();
|
|
33222
34239
|
init_logger();
|
|
33223
34240
|
init_rate_limiter();
|
|
33224
|
-
|
|
34241
|
+
fs21 = __toESM(require("fs"));
|
|
33225
34242
|
path23 = __toESM(require("path"));
|
|
33226
34243
|
HttpClientProvider = class extends CheckProvider {
|
|
33227
34244
|
liquid;
|
|
@@ -33355,8 +34372,8 @@ var init_http_client_provider = __esm({
|
|
|
33355
34372
|
`[http_client] Resolved relative output_file to workspace: ${resolvedOutputFile}`
|
|
33356
34373
|
);
|
|
33357
34374
|
}
|
|
33358
|
-
if (skipIfExists &&
|
|
33359
|
-
const stats =
|
|
34375
|
+
if (skipIfExists && fs21.existsSync(resolvedOutputFile)) {
|
|
34376
|
+
const stats = fs21.statSync(resolvedOutputFile);
|
|
33360
34377
|
logger.verbose(`[http_client] File cached: ${resolvedOutputFile} (${stats.size} bytes)`);
|
|
33361
34378
|
return {
|
|
33362
34379
|
issues: [],
|
|
@@ -33587,12 +34604,12 @@ var init_http_client_provider = __esm({
|
|
|
33587
34604
|
};
|
|
33588
34605
|
}
|
|
33589
34606
|
const parentDir = path23.dirname(outputFile);
|
|
33590
|
-
if (parentDir && !
|
|
33591
|
-
|
|
34607
|
+
if (parentDir && !fs21.existsSync(parentDir)) {
|
|
34608
|
+
fs21.mkdirSync(parentDir, { recursive: true });
|
|
33592
34609
|
}
|
|
33593
34610
|
const arrayBuffer = await response.arrayBuffer();
|
|
33594
34611
|
const buffer = Buffer.from(arrayBuffer);
|
|
33595
|
-
|
|
34612
|
+
fs21.writeFileSync(outputFile, buffer);
|
|
33596
34613
|
const contentType = response.headers.get("content-type") || "application/octet-stream";
|
|
33597
34614
|
logger.verbose(`[http_client] Downloaded: ${outputFile} (${buffer.length} bytes)`);
|
|
33598
34615
|
return {
|
|
@@ -53822,6 +54839,14 @@ var init_mcp_check_provider = __esm({
|
|
|
53822
54839
|
}
|
|
53823
54840
|
);
|
|
53824
54841
|
const transport = createTransport();
|
|
54842
|
+
if ("stderr" in transport && transport.stderr) {
|
|
54843
|
+
const stderrStream = transport.stderr;
|
|
54844
|
+
if (typeof stderrStream.on === "function") {
|
|
54845
|
+
stderrStream.on("error", (err) => {
|
|
54846
|
+
logger.debug(`MCP transport stderr error (${transportName}): ${err.message}`);
|
|
54847
|
+
});
|
|
54848
|
+
}
|
|
54849
|
+
}
|
|
53825
54850
|
try {
|
|
53826
54851
|
let timeoutId;
|
|
53827
54852
|
try {
|
|
@@ -54178,7 +55203,7 @@ async function interactivePrompt(options) {
|
|
|
54178
55203
|
} catch {
|
|
54179
55204
|
}
|
|
54180
55205
|
if (multiline) {
|
|
54181
|
-
rl =
|
|
55206
|
+
rl = readline2.createInterface({
|
|
54182
55207
|
input: process.stdin,
|
|
54183
55208
|
output: process.stdout,
|
|
54184
55209
|
terminal: true
|
|
@@ -54291,7 +55316,7 @@ async function interactivePrompt(options) {
|
|
|
54291
55316
|
}
|
|
54292
55317
|
async function simplePrompt(prompt) {
|
|
54293
55318
|
return new Promise((resolve17) => {
|
|
54294
|
-
const rl =
|
|
55319
|
+
const rl = readline2.createInterface({
|
|
54295
55320
|
input: process.stdin,
|
|
54296
55321
|
output: process.stdout
|
|
54297
55322
|
});
|
|
@@ -54310,11 +55335,11 @@ async function simplePrompt(prompt) {
|
|
|
54310
55335
|
});
|
|
54311
55336
|
});
|
|
54312
55337
|
}
|
|
54313
|
-
var
|
|
55338
|
+
var readline2, activePrompt, waiters;
|
|
54314
55339
|
var init_interactive_prompt = __esm({
|
|
54315
55340
|
"src/utils/interactive-prompt.ts"() {
|
|
54316
55341
|
"use strict";
|
|
54317
|
-
|
|
55342
|
+
readline2 = __toESM(require("readline"));
|
|
54318
55343
|
activePrompt = false;
|
|
54319
55344
|
waiters = [];
|
|
54320
55345
|
}
|
|
@@ -54531,7 +55556,7 @@ var init_stdin_reader = __esm({
|
|
|
54531
55556
|
});
|
|
54532
55557
|
|
|
54533
55558
|
// src/providers/human-input-check-provider.ts
|
|
54534
|
-
var
|
|
55559
|
+
var fs23, path25, HumanInputCheckProvider;
|
|
54535
55560
|
var init_human_input_check_provider = __esm({
|
|
54536
55561
|
"src/providers/human-input-check-provider.ts"() {
|
|
54537
55562
|
"use strict";
|
|
@@ -54540,7 +55565,7 @@ var init_human_input_check_provider = __esm({
|
|
|
54540
55565
|
init_prompt_state();
|
|
54541
55566
|
init_liquid_extensions();
|
|
54542
55567
|
init_stdin_reader();
|
|
54543
|
-
|
|
55568
|
+
fs23 = __toESM(require("fs"));
|
|
54544
55569
|
path25 = __toESM(require("path"));
|
|
54545
55570
|
HumanInputCheckProvider = class _HumanInputCheckProvider extends CheckProvider {
|
|
54546
55571
|
liquid;
|
|
@@ -54722,12 +55747,12 @@ var init_human_input_check_provider = __esm({
|
|
|
54722
55747
|
return null;
|
|
54723
55748
|
}
|
|
54724
55749
|
try {
|
|
54725
|
-
await
|
|
54726
|
-
const stats = await
|
|
55750
|
+
await fs23.promises.access(normalizedPath, fs23.constants.R_OK);
|
|
55751
|
+
const stats = await fs23.promises.stat(normalizedPath);
|
|
54727
55752
|
if (!stats.isFile()) {
|
|
54728
55753
|
return null;
|
|
54729
55754
|
}
|
|
54730
|
-
const content = await
|
|
55755
|
+
const content = await fs23.promises.readFile(normalizedPath, "utf-8");
|
|
54731
55756
|
return content.trim();
|
|
54732
55757
|
} catch {
|
|
54733
55758
|
return null;
|
|
@@ -55784,6 +56809,11 @@ var init_script_check_provider = __esm({
|
|
|
55784
56809
|
env,
|
|
55785
56810
|
stderr: "pipe"
|
|
55786
56811
|
});
|
|
56812
|
+
if (transport.stderr) {
|
|
56813
|
+
transport.stderr.on("error", (err) => {
|
|
56814
|
+
logger.debug(`MCP transport stderr error: ${err.message}`);
|
|
56815
|
+
});
|
|
56816
|
+
}
|
|
55787
56817
|
await Promise.race([
|
|
55788
56818
|
client.connect(transport),
|
|
55789
56819
|
new Promise(
|
|
@@ -55881,16 +56911,48 @@ var init_script_check_provider = __esm({
|
|
|
55881
56911
|
}
|
|
55882
56912
|
});
|
|
55883
56913
|
|
|
56914
|
+
// src/utils/child-process-error-handler.ts
|
|
56915
|
+
function isChildProcessIOError(error) {
|
|
56916
|
+
if (!(error instanceof Error)) return false;
|
|
56917
|
+
const errno = error.code;
|
|
56918
|
+
if (errno && TRANSIENT_IO_CODES.has(errno)) return true;
|
|
56919
|
+
const msg = error.message;
|
|
56920
|
+
if (msg.includes("read EIO") || msg.includes("write EPIPE")) return true;
|
|
56921
|
+
if (msg.includes("ERR_STREAM_DESTROYED")) return true;
|
|
56922
|
+
return false;
|
|
56923
|
+
}
|
|
56924
|
+
var TRANSIENT_IO_CODES, GUARD;
|
|
56925
|
+
var init_child_process_error_handler = __esm({
|
|
56926
|
+
"src/utils/child-process-error-handler.ts"() {
|
|
56927
|
+
"use strict";
|
|
56928
|
+
init_logger();
|
|
56929
|
+
TRANSIENT_IO_CODES = /* @__PURE__ */ new Set(["EIO", "EPIPE", "ECONNRESET"]);
|
|
56930
|
+
GUARD = Symbol.for("visor.childProcessErrorHandler");
|
|
56931
|
+
if (!globalThis[GUARD]) {
|
|
56932
|
+
globalThis[GUARD] = true;
|
|
56933
|
+
process.on("uncaughtException", (error, origin) => {
|
|
56934
|
+
if (isChildProcessIOError(error)) {
|
|
56935
|
+
logger.warn(
|
|
56936
|
+
`[child-process-error-handler] Suppressed transient I/O error (${origin}): ${error.message}`
|
|
56937
|
+
);
|
|
56938
|
+
return;
|
|
56939
|
+
}
|
|
56940
|
+
});
|
|
56941
|
+
}
|
|
56942
|
+
}
|
|
56943
|
+
});
|
|
56944
|
+
|
|
55884
56945
|
// src/utils/worktree-manager.ts
|
|
55885
|
-
var
|
|
56946
|
+
var fs24, fsp, path26, crypto4, WorktreeManager, worktreeManager;
|
|
55886
56947
|
var init_worktree_manager = __esm({
|
|
55887
56948
|
"src/utils/worktree-manager.ts"() {
|
|
55888
56949
|
"use strict";
|
|
55889
|
-
|
|
56950
|
+
fs24 = __toESM(require("fs"));
|
|
55890
56951
|
fsp = __toESM(require("fs/promises"));
|
|
55891
56952
|
path26 = __toESM(require("path"));
|
|
55892
56953
|
crypto4 = __toESM(require("crypto"));
|
|
55893
56954
|
init_command_executor();
|
|
56955
|
+
init_child_process_error_handler();
|
|
55894
56956
|
init_logger();
|
|
55895
56957
|
WorktreeManager = class _WorktreeManager {
|
|
55896
56958
|
static instance;
|
|
@@ -55941,12 +57003,12 @@ var init_worktree_manager = __esm({
|
|
|
55941
57003
|
}
|
|
55942
57004
|
const reposDir = this.getReposDir();
|
|
55943
57005
|
const worktreesDir = this.getWorktreesDir();
|
|
55944
|
-
if (!
|
|
55945
|
-
|
|
57006
|
+
if (!fs24.existsSync(reposDir)) {
|
|
57007
|
+
fs24.mkdirSync(reposDir, { recursive: true });
|
|
55946
57008
|
logger.debug(`Created repos directory: ${reposDir}`);
|
|
55947
57009
|
}
|
|
55948
|
-
if (!
|
|
55949
|
-
|
|
57010
|
+
if (!fs24.existsSync(worktreesDir)) {
|
|
57011
|
+
fs24.mkdirSync(worktreesDir, { recursive: true });
|
|
55950
57012
|
logger.debug(`Created worktrees directory: ${worktreesDir}`);
|
|
55951
57013
|
}
|
|
55952
57014
|
}
|
|
@@ -55980,7 +57042,7 @@ var init_worktree_manager = __esm({
|
|
|
55980
57042
|
const reposDir = this.getReposDir();
|
|
55981
57043
|
const repoName = repository.replace(/\//g, "-");
|
|
55982
57044
|
const bareRepoPath = path26.join(reposDir, `${repoName}.git`);
|
|
55983
|
-
if (
|
|
57045
|
+
if (fs24.existsSync(bareRepoPath)) {
|
|
55984
57046
|
logger.debug(`Bare repository already exists: ${bareRepoPath}`);
|
|
55985
57047
|
const verifyResult = await this.verifyBareRepoRemote(bareRepoPath, repoUrl);
|
|
55986
57048
|
if (verifyResult === "timeout") {
|
|
@@ -56049,6 +57111,7 @@ var init_worktree_manager = __esm({
|
|
|
56049
57111
|
const result = await this.executeGitCommand(updateCmd, { timeout: 9e4 });
|
|
56050
57112
|
if (result.exitCode === 0) {
|
|
56051
57113
|
logger.debug(`Successfully updated bare repository`);
|
|
57114
|
+
await this.gcBareRepo(bareRepoPath);
|
|
56052
57115
|
return;
|
|
56053
57116
|
}
|
|
56054
57117
|
logger.warn(`Bare repo update attempt ${attempt}/2 failed: ${result.stderr}`);
|
|
@@ -56062,6 +57125,36 @@ var init_worktree_manager = __esm({
|
|
|
56062
57125
|
}
|
|
56063
57126
|
logger.warn(`Failed to update bare repository after 2 attempts (will rely on per-ref fetch)`);
|
|
56064
57127
|
}
|
|
57128
|
+
/**
|
|
57129
|
+
* Consolidate pack files after fetch to prevent unbounded pack accumulation.
|
|
57130
|
+
* Each `git fetch` creates a new pack file; without periodic repacking these
|
|
57131
|
+
* pile up and waste disk (especially on COW filesystems like BTRFS).
|
|
57132
|
+
*/
|
|
57133
|
+
async gcBareRepo(bareRepoPath) {
|
|
57134
|
+
try {
|
|
57135
|
+
const packDir = path26.join(bareRepoPath, "objects", "pack");
|
|
57136
|
+
if (!fs24.existsSync(packDir)) return;
|
|
57137
|
+
const packFiles = fs24.readdirSync(packDir).filter((f) => f.endsWith(".pack"));
|
|
57138
|
+
if (packFiles.length <= 3) return;
|
|
57139
|
+
if (packFiles.length > 10) {
|
|
57140
|
+
logger.info(`Repacking bare repo (${packFiles.length} packs): ${bareRepoPath}`);
|
|
57141
|
+
const repackCmd = `git -C ${this.escapeShellArg(bareRepoPath)} repack -a -d --unpack-unreachable=now`;
|
|
57142
|
+
const result = await this.executeGitCommand(repackCmd, { timeout: 3e5 });
|
|
57143
|
+
if (result.exitCode === 0) {
|
|
57144
|
+
logger.info(`Repack complete for ${bareRepoPath}`);
|
|
57145
|
+
const pruneCmd = `git -C ${this.escapeShellArg(bareRepoPath)} prune --expire=now`;
|
|
57146
|
+
await this.executeGitCommand(pruneCmd, { timeout: 6e4 });
|
|
57147
|
+
} else {
|
|
57148
|
+
logger.warn(`Repack failed: ${result.stderr}`);
|
|
57149
|
+
}
|
|
57150
|
+
} else {
|
|
57151
|
+
const gcCmd = `git -C ${this.escapeShellArg(bareRepoPath)} gc --auto`;
|
|
57152
|
+
await this.executeGitCommand(gcCmd, { timeout: 12e4 });
|
|
57153
|
+
}
|
|
57154
|
+
} catch (error) {
|
|
57155
|
+
logger.debug(`gc/repack non-fatal error: ${error}`);
|
|
57156
|
+
}
|
|
57157
|
+
}
|
|
56065
57158
|
/**
|
|
56066
57159
|
* Verify that a bare repository has the correct remote URL.
|
|
56067
57160
|
* This prevents reusing corrupted repos that were cloned from a different repository.
|
|
@@ -56154,7 +57247,7 @@ var init_worktree_manager = __esm({
|
|
|
56154
57247
|
worktreePath = this.validatePath(options.workingDirectory);
|
|
56155
57248
|
}
|
|
56156
57249
|
let refreshFailedNeedsRecreate = false;
|
|
56157
|
-
if (
|
|
57250
|
+
if (fs24.existsSync(worktreePath)) {
|
|
56158
57251
|
logger.debug(`Worktree already exists: ${worktreePath}`);
|
|
56159
57252
|
const metadata2 = await this.loadMetadata(worktreePath);
|
|
56160
57253
|
if (metadata2) {
|
|
@@ -56483,9 +57576,13 @@ var init_worktree_manager = __esm({
|
|
|
56483
57576
|
* Remove a worktree
|
|
56484
57577
|
*/
|
|
56485
57578
|
async removeWorktree(worktreeId) {
|
|
56486
|
-
|
|
57579
|
+
let metadata = this.activeWorktrees.get(worktreeId);
|
|
57580
|
+
if (!metadata) {
|
|
57581
|
+
const worktreePath = path26.join(this.getWorktreesDir(), worktreeId);
|
|
57582
|
+
metadata = await this.loadMetadata(worktreePath) || void 0;
|
|
57583
|
+
}
|
|
56487
57584
|
if (!metadata) {
|
|
56488
|
-
logger.warn(`Worktree not found
|
|
57585
|
+
logger.warn(`Worktree metadata not found: ${worktreeId}`);
|
|
56489
57586
|
return;
|
|
56490
57587
|
}
|
|
56491
57588
|
const { bare_repo_path, worktree_path } = metadata;
|
|
@@ -56494,15 +57591,15 @@ var init_worktree_manager = __esm({
|
|
|
56494
57591
|
const result = await this.executeGitCommand(removeCmd, { timeout: 3e4 });
|
|
56495
57592
|
if (result.exitCode !== 0) {
|
|
56496
57593
|
logger.warn(`Failed to remove worktree via git: ${result.stderr}`);
|
|
56497
|
-
if (
|
|
57594
|
+
if (fs24.existsSync(worktree_path)) {
|
|
56498
57595
|
logger.debug(`Manually removing worktree directory`);
|
|
56499
|
-
|
|
57596
|
+
fs24.rmSync(worktree_path, { recursive: true, force: true });
|
|
56500
57597
|
}
|
|
56501
57598
|
}
|
|
56502
57599
|
const metadataPath = this.getMetadataPath(worktree_path);
|
|
56503
57600
|
try {
|
|
56504
|
-
if (
|
|
56505
|
-
|
|
57601
|
+
if (fs24.existsSync(metadataPath)) {
|
|
57602
|
+
fs24.unlinkSync(metadataPath);
|
|
56506
57603
|
}
|
|
56507
57604
|
} catch {
|
|
56508
57605
|
}
|
|
@@ -56522,7 +57619,7 @@ var init_worktree_manager = __esm({
|
|
|
56522
57619
|
*/
|
|
56523
57620
|
async saveMetadata(worktreePath, metadata) {
|
|
56524
57621
|
const metadataPath = this.getMetadataPath(worktreePath);
|
|
56525
|
-
|
|
57622
|
+
fs24.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2), "utf8");
|
|
56526
57623
|
}
|
|
56527
57624
|
/**
|
|
56528
57625
|
* Load worktree metadata
|
|
@@ -56530,12 +57627,12 @@ var init_worktree_manager = __esm({
|
|
|
56530
57627
|
async loadMetadata(worktreePath) {
|
|
56531
57628
|
const metadataPath = this.getMetadataPath(worktreePath);
|
|
56532
57629
|
const legacyPath = path26.join(worktreePath, ".visor-metadata.json");
|
|
56533
|
-
const pathToRead =
|
|
57630
|
+
const pathToRead = fs24.existsSync(metadataPath) ? metadataPath : fs24.existsSync(legacyPath) ? legacyPath : null;
|
|
56534
57631
|
if (!pathToRead) {
|
|
56535
57632
|
return null;
|
|
56536
57633
|
}
|
|
56537
57634
|
try {
|
|
56538
|
-
const content =
|
|
57635
|
+
const content = fs24.readFileSync(pathToRead, "utf8");
|
|
56539
57636
|
return JSON.parse(content);
|
|
56540
57637
|
} catch (error) {
|
|
56541
57638
|
logger.warn(`Failed to load metadata: ${error}`);
|
|
@@ -56547,10 +57644,10 @@ var init_worktree_manager = __esm({
|
|
|
56547
57644
|
*/
|
|
56548
57645
|
async listWorktrees() {
|
|
56549
57646
|
const worktreesDir = this.getWorktreesDir();
|
|
56550
|
-
if (!
|
|
57647
|
+
if (!fs24.existsSync(worktreesDir)) {
|
|
56551
57648
|
return [];
|
|
56552
57649
|
}
|
|
56553
|
-
const entries =
|
|
57650
|
+
const entries = fs24.readdirSync(worktreesDir, { withFileTypes: true });
|
|
56554
57651
|
const worktrees = [];
|
|
56555
57652
|
for (const entry of entries) {
|
|
56556
57653
|
if (!entry.isDirectory()) continue;
|
|
@@ -56638,6 +57735,10 @@ var init_worktree_manager = __esm({
|
|
|
56638
57735
|
process.exit(143);
|
|
56639
57736
|
});
|
|
56640
57737
|
process.on("uncaughtException", async (error) => {
|
|
57738
|
+
if (isChildProcessIOError(error)) {
|
|
57739
|
+
logger.warn(`Ignoring transient child-process I/O error: ${error.message}`);
|
|
57740
|
+
return;
|
|
57741
|
+
}
|
|
56641
57742
|
logger.error(`Uncaught exception, cleaning up worktrees: ${error}`);
|
|
56642
57743
|
await this.cleanupProcessWorktrees();
|
|
56643
57744
|
process.exit(1);
|
|
@@ -60138,6 +61239,36 @@ async function executeCheckWithForEachItems2(checkId, forEachParent, forEachItem
|
|
|
60138
61239
|
wave: state.wave
|
|
60139
61240
|
},
|
|
60140
61241
|
async (span) => {
|
|
61242
|
+
if (checkConfig.debounce && checkConfig.debounce > 0) {
|
|
61243
|
+
const debounceKey = checkConfig.debounce_key || checkId;
|
|
61244
|
+
const debounceResult = await getDebounceManager().enqueue(
|
|
61245
|
+
debounceKey,
|
|
61246
|
+
checkConfig.debounce,
|
|
61247
|
+
async () => {
|
|
61248
|
+
const r = await executeWithSandboxRouting(
|
|
61249
|
+
checkId,
|
|
61250
|
+
checkConfig,
|
|
61251
|
+
context2,
|
|
61252
|
+
prInfo,
|
|
61253
|
+
dependencyResults,
|
|
61254
|
+
checkConfig.timeout || checkConfig.ai?.timeout || 18e5,
|
|
61255
|
+
() => provider.execute(prInfo, providerConfig, dependencyResults, executionContext)
|
|
61256
|
+
);
|
|
61257
|
+
try {
|
|
61258
|
+
captureCheckOutput(span, r.output);
|
|
61259
|
+
} catch {
|
|
61260
|
+
}
|
|
61261
|
+
return r;
|
|
61262
|
+
}
|
|
61263
|
+
);
|
|
61264
|
+
if (debounceResult.outcome === "debounced") {
|
|
61265
|
+
logger.info(
|
|
61266
|
+
`[LevelDispatch] ${checkId}: debounced (superseded by later invocation)`
|
|
61267
|
+
);
|
|
61268
|
+
return { issues: [], output: { debounced: true } };
|
|
61269
|
+
}
|
|
61270
|
+
return debounceResult.result;
|
|
61271
|
+
}
|
|
60141
61272
|
const res = await executeWithSandboxRouting(
|
|
60142
61273
|
checkId,
|
|
60143
61274
|
checkConfig,
|
|
@@ -61289,6 +62420,34 @@ async function executeSingleCheck2(checkId, context2, state, emitEvent, transiti
|
|
|
61289
62420
|
wave: state.wave
|
|
61290
62421
|
},
|
|
61291
62422
|
async (span) => {
|
|
62423
|
+
if (checkConfig2.debounce && checkConfig2.debounce > 0) {
|
|
62424
|
+
const debounceKey = checkConfig2.debounce_key || checkId;
|
|
62425
|
+
const debounceResult = await getDebounceManager().enqueue(
|
|
62426
|
+
debounceKey,
|
|
62427
|
+
checkConfig2.debounce,
|
|
62428
|
+
async () => {
|
|
62429
|
+
const r = await executeWithSandboxRouting(
|
|
62430
|
+
checkId,
|
|
62431
|
+
checkConfig2,
|
|
62432
|
+
context2,
|
|
62433
|
+
prInfo,
|
|
62434
|
+
dependencyResults,
|
|
62435
|
+
checkConfig2.timeout || checkConfig2.ai?.timeout || 18e5,
|
|
62436
|
+
() => provider.execute(prInfo, providerConfig, dependencyResults, executionContext)
|
|
62437
|
+
);
|
|
62438
|
+
try {
|
|
62439
|
+
captureCheckOutput(span, r.output);
|
|
62440
|
+
} catch {
|
|
62441
|
+
}
|
|
62442
|
+
return r;
|
|
62443
|
+
}
|
|
62444
|
+
);
|
|
62445
|
+
if (debounceResult.outcome === "debounced") {
|
|
62446
|
+
logger.info(`[LevelDispatch] ${checkId}: debounced (superseded by later invocation)`);
|
|
62447
|
+
return { issues: [], output: { debounced: true } };
|
|
62448
|
+
}
|
|
62449
|
+
return debounceResult.result;
|
|
62450
|
+
}
|
|
61292
62451
|
const res = await executeWithSandboxRouting(
|
|
61293
62452
|
checkId,
|
|
61294
62453
|
checkConfig2,
|
|
@@ -61422,13 +62581,10 @@ async function executeSingleCheck2(checkId, context2, state, emitEvent, transiti
|
|
|
61422
62581
|
}
|
|
61423
62582
|
let isForEach = result.isForEach;
|
|
61424
62583
|
let forEachItems2 = result.forEachItems;
|
|
61425
|
-
logger.info(
|
|
61426
|
-
`[LevelDispatch][DEBUG] After execution ${checkId}: checkConfig.forEach=${checkConfig2.forEach}, output type=${typeof result.output}, isArray=${Array.isArray(result.output)}`
|
|
61427
|
-
);
|
|
61428
62584
|
if (checkConfig2.forEach === true) {
|
|
61429
62585
|
const output = result.output;
|
|
61430
|
-
logger.
|
|
61431
|
-
`[LevelDispatch]
|
|
62586
|
+
logger.debug(
|
|
62587
|
+
`[LevelDispatch] Processing forEach=true for ${checkId}, output=${JSON.stringify(output)?.substring(0, 200)}`
|
|
61432
62588
|
);
|
|
61433
62589
|
if (output === void 0) {
|
|
61434
62590
|
logger.error(`[LevelDispatch] forEach check "${checkId}" produced undefined output`);
|
|
@@ -62026,6 +63182,7 @@ var init_level_dispatch = __esm({
|
|
|
62026
63182
|
init_workflow_inputs();
|
|
62027
63183
|
init_sandbox_routing();
|
|
62028
63184
|
init_policy_gate();
|
|
63185
|
+
init_debounce_manager();
|
|
62029
63186
|
init_liquid_extensions();
|
|
62030
63187
|
}
|
|
62031
63188
|
});
|
|
@@ -62573,12 +63730,12 @@ var init_runner = __esm({
|
|
|
62573
63730
|
});
|
|
62574
63731
|
|
|
62575
63732
|
// src/utils/file-exclusion.ts
|
|
62576
|
-
var import_ignore,
|
|
63733
|
+
var import_ignore, fs25, path27, DEFAULT_EXCLUSION_PATTERNS, FileExclusionHelper;
|
|
62577
63734
|
var init_file_exclusion = __esm({
|
|
62578
63735
|
"src/utils/file-exclusion.ts"() {
|
|
62579
63736
|
"use strict";
|
|
62580
63737
|
import_ignore = __toESM(require("ignore"));
|
|
62581
|
-
|
|
63738
|
+
fs25 = __toESM(require("fs"));
|
|
62582
63739
|
path27 = __toESM(require("path"));
|
|
62583
63740
|
DEFAULT_EXCLUSION_PATTERNS = [
|
|
62584
63741
|
"dist/",
|
|
@@ -62624,8 +63781,8 @@ var init_file_exclusion = __esm({
|
|
|
62624
63781
|
if (additionalPatterns && additionalPatterns.length > 0) {
|
|
62625
63782
|
this.gitignore.add(additionalPatterns);
|
|
62626
63783
|
}
|
|
62627
|
-
if (
|
|
62628
|
-
const rawContent =
|
|
63784
|
+
if (fs25.existsSync(gitignorePath)) {
|
|
63785
|
+
const rawContent = fs25.readFileSync(gitignorePath, "utf8");
|
|
62629
63786
|
const gitignoreContent = rawContent.replace(/[\r\n]+/g, "\n").replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "").split("\n").filter((line) => line.length < 1e3).join("\n").trim();
|
|
62630
63787
|
this.gitignore.add(gitignoreContent);
|
|
62631
63788
|
if (process.env.VISOR_DEBUG === "true") {
|
|
@@ -62657,13 +63814,13 @@ var git_repository_analyzer_exports = {};
|
|
|
62657
63814
|
__export(git_repository_analyzer_exports, {
|
|
62658
63815
|
GitRepositoryAnalyzer: () => GitRepositoryAnalyzer
|
|
62659
63816
|
});
|
|
62660
|
-
var import_simple_git2, path28,
|
|
63817
|
+
var import_simple_git2, path28, fs26, MAX_PATCH_SIZE, GitRepositoryAnalyzer;
|
|
62661
63818
|
var init_git_repository_analyzer = __esm({
|
|
62662
63819
|
"src/git-repository-analyzer.ts"() {
|
|
62663
63820
|
"use strict";
|
|
62664
63821
|
import_simple_git2 = require("simple-git");
|
|
62665
63822
|
path28 = __toESM(require("path"));
|
|
62666
|
-
|
|
63823
|
+
fs26 = __toESM(require("fs"));
|
|
62667
63824
|
init_file_exclusion();
|
|
62668
63825
|
MAX_PATCH_SIZE = 50 * 1024;
|
|
62669
63826
|
GitRepositoryAnalyzer = class {
|
|
@@ -62959,7 +64116,7 @@ ${file.patch}`).join("\n\n");
|
|
|
62959
64116
|
let content;
|
|
62960
64117
|
let truncated = false;
|
|
62961
64118
|
try {
|
|
62962
|
-
if (includeContext && status !== "added" &&
|
|
64119
|
+
if (includeContext && status !== "added" && fs26.existsSync(filePath)) {
|
|
62963
64120
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
62964
64121
|
if (diff) {
|
|
62965
64122
|
const result = this.truncatePatch(diff, filename);
|
|
@@ -62969,7 +64126,7 @@ ${file.patch}`).join("\n\n");
|
|
|
62969
64126
|
additions = lines.filter((line) => line.startsWith("+")).length;
|
|
62970
64127
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
62971
64128
|
}
|
|
62972
|
-
} else if (status !== "added" &&
|
|
64129
|
+
} else if (status !== "added" && fs26.existsSync(filePath)) {
|
|
62973
64130
|
const diff = await this.git.diff(["--", filename]).catch(() => "");
|
|
62974
64131
|
if (diff) {
|
|
62975
64132
|
const lines = diff.split("\n");
|
|
@@ -62977,17 +64134,17 @@ ${file.patch}`).join("\n\n");
|
|
|
62977
64134
|
deletions = lines.filter((line) => line.startsWith("-")).length;
|
|
62978
64135
|
}
|
|
62979
64136
|
}
|
|
62980
|
-
if (status === "added" &&
|
|
64137
|
+
if (status === "added" && fs26.existsSync(filePath)) {
|
|
62981
64138
|
try {
|
|
62982
|
-
const stats =
|
|
64139
|
+
const stats = fs26.statSync(filePath);
|
|
62983
64140
|
if (stats.isFile() && stats.size < 1024 * 1024) {
|
|
62984
64141
|
if (includeContext) {
|
|
62985
|
-
content =
|
|
64142
|
+
content = fs26.readFileSync(filePath, "utf8");
|
|
62986
64143
|
const result = this.truncatePatch(content, filename);
|
|
62987
64144
|
patch = result.patch;
|
|
62988
64145
|
truncated = result.truncated;
|
|
62989
64146
|
}
|
|
62990
|
-
const fileContent = includeContext ? content :
|
|
64147
|
+
const fileContent = includeContext ? content : fs26.readFileSync(filePath, "utf8");
|
|
62991
64148
|
additions = fileContent.split("\n").length;
|
|
62992
64149
|
}
|
|
62993
64150
|
} catch {
|
|
@@ -63078,10 +64235,11 @@ function shellEscape(str) {
|
|
|
63078
64235
|
function sanitizePathComponent(name) {
|
|
63079
64236
|
return name.replace(/\.\./g, "").replace(/[\/\\]/g, "-").replace(/^\.+/, "").trim() || "unnamed";
|
|
63080
64237
|
}
|
|
63081
|
-
var fsp2, path29, WorkspaceManager;
|
|
64238
|
+
var fs27, fsp2, path29, WorkspaceManager;
|
|
63082
64239
|
var init_workspace_manager = __esm({
|
|
63083
64240
|
"src/utils/workspace-manager.ts"() {
|
|
63084
64241
|
"use strict";
|
|
64242
|
+
fs27 = __toESM(require("fs"));
|
|
63085
64243
|
fsp2 = __toESM(require("fs/promises"));
|
|
63086
64244
|
path29 = __toESM(require("path"));
|
|
63087
64245
|
init_command_executor();
|
|
@@ -63284,12 +64442,7 @@ var init_workspace_manager = __esm({
|
|
|
63284
64442
|
}
|
|
63285
64443
|
}
|
|
63286
64444
|
try {
|
|
63287
|
-
|
|
63288
|
-
for (const entry of entries) {
|
|
63289
|
-
if (entry.name !== mainProjectName) {
|
|
63290
|
-
this.usedNames.add(entry.name);
|
|
63291
|
-
}
|
|
63292
|
-
}
|
|
64445
|
+
await this.loadExistingProjects(mainProjectName);
|
|
63293
64446
|
} catch {
|
|
63294
64447
|
}
|
|
63295
64448
|
this.registerCleanupHandlers();
|
|
@@ -63434,6 +64587,41 @@ var init_workspace_manager = __esm({
|
|
|
63434
64587
|
return false;
|
|
63435
64588
|
}
|
|
63436
64589
|
}
|
|
64590
|
+
/**
|
|
64591
|
+
* Rehydrate existing symlinked projects in a persisted workspace so future
|
|
64592
|
+
* runs update them in place instead of allocating project-2, project-3, etc.
|
|
64593
|
+
*/
|
|
64594
|
+
async loadExistingProjects(mainProjectName) {
|
|
64595
|
+
const entries = await fsp2.readdir(this.workspacePath, { withFileTypes: true });
|
|
64596
|
+
for (const entry of entries) {
|
|
64597
|
+
if (entry.name === mainProjectName) {
|
|
64598
|
+
continue;
|
|
64599
|
+
}
|
|
64600
|
+
const entryPath = path29.join(this.workspacePath, entry.name);
|
|
64601
|
+
this.usedNames.add(entry.name);
|
|
64602
|
+
if (!entry.isSymbolicLink()) {
|
|
64603
|
+
continue;
|
|
64604
|
+
}
|
|
64605
|
+
try {
|
|
64606
|
+
const targetPath = await fsp2.realpath(entryPath);
|
|
64607
|
+
const metadataPath = `${targetPath.replace(/\/?$/, "")}.metadata.json`;
|
|
64608
|
+
if (!fs27.existsSync(metadataPath)) {
|
|
64609
|
+
continue;
|
|
64610
|
+
}
|
|
64611
|
+
const metadata = JSON.parse(await fsp2.readFile(metadataPath, "utf8"));
|
|
64612
|
+
if (!metadata.repository) {
|
|
64613
|
+
continue;
|
|
64614
|
+
}
|
|
64615
|
+
this.projects.set(entry.name, {
|
|
64616
|
+
name: entry.name,
|
|
64617
|
+
path: entryPath,
|
|
64618
|
+
worktreePath: metadata.worktree_path || targetPath,
|
|
64619
|
+
repository: metadata.repository
|
|
64620
|
+
});
|
|
64621
|
+
} catch {
|
|
64622
|
+
}
|
|
64623
|
+
}
|
|
64624
|
+
}
|
|
63437
64625
|
/**
|
|
63438
64626
|
* Clean up stale workspace directories older than maxAge.
|
|
63439
64627
|
* Call periodically (e.g. at socket-runner startup) to prevent disk bloat.
|
|
@@ -63449,6 +64637,17 @@ var init_workspace_manager = __esm({
|
|
|
63449
64637
|
try {
|
|
63450
64638
|
const stat2 = await fsp2.stat(dirPath);
|
|
63451
64639
|
if (now - stat2.mtimeMs > maxAgeMs) {
|
|
64640
|
+
const topGitPath = path29.join(dirPath, ".git");
|
|
64641
|
+
try {
|
|
64642
|
+
const topGitStat = await fsp2.stat(topGitPath);
|
|
64643
|
+
if (topGitStat.isDirectory()) {
|
|
64644
|
+
logger.warn(
|
|
64645
|
+
`[Workspace] Skipping ${dirPath} \u2014 it is a real git repository, not a visor workspace`
|
|
64646
|
+
);
|
|
64647
|
+
continue;
|
|
64648
|
+
}
|
|
64649
|
+
} catch {
|
|
64650
|
+
}
|
|
63452
64651
|
try {
|
|
63453
64652
|
const subdirs = await fsp2.readdir(dirPath, { withFileTypes: true });
|
|
63454
64653
|
for (const sub of subdirs) {
|
|
@@ -63460,7 +64659,19 @@ var init_workspace_manager = __esm({
|
|
|
63460
64659
|
const match = gitContent.match(/gitdir:\s*(.+)/);
|
|
63461
64660
|
if (match) {
|
|
63462
64661
|
const worktreeGitDir = match[1].trim();
|
|
64662
|
+
if (!worktreeGitDir.includes("/.git/worktrees/")) {
|
|
64663
|
+
logger.warn(
|
|
64664
|
+
`[Workspace] Skipping worktree removal for ${subPath} \u2014 gitdir does not point to a worktree`
|
|
64665
|
+
);
|
|
64666
|
+
continue;
|
|
64667
|
+
}
|
|
63463
64668
|
const repoGitDir = worktreeGitDir.replace(/\/\.git\/worktrees\/.*$/, "");
|
|
64669
|
+
if (!repoGitDir.startsWith(basePath)) {
|
|
64670
|
+
logger.warn(
|
|
64671
|
+
`[Workspace] Skipping worktree removal for ${subPath} \u2014 parent repo ${repoGitDir} is outside workspace`
|
|
64672
|
+
);
|
|
64673
|
+
continue;
|
|
64674
|
+
}
|
|
63464
64675
|
await commandExecutor.execute(
|
|
63465
64676
|
`git -C ${shellEscape(repoGitDir)} worktree remove ${shellEscape(subPath)} --force`,
|
|
63466
64677
|
{ timeout: 1e4 }
|
|
@@ -65792,8 +67003,11 @@ var init_client = __esm({
|
|
|
65792
67003
|
console.warn("Slack auth.test failed (non-fatal); bot user id unavailable");
|
|
65793
67004
|
return "UNKNOWN_BOT";
|
|
65794
67005
|
}
|
|
67006
|
+
if (resp.bot_id) this._botId = String(resp.bot_id);
|
|
65795
67007
|
return String(resp.user_id);
|
|
65796
67008
|
}
|
|
67009
|
+
/** The bot's bot_id (from auth.test), used to detect own bot_message events */
|
|
67010
|
+
_botId;
|
|
65797
67011
|
/**
|
|
65798
67012
|
* Fetch user info from Slack API.
|
|
65799
67013
|
* Returns user profile including guest status flags, email, display name, and timezone.
|
|
@@ -65829,6 +67043,31 @@ var init_client = __esm({
|
|
|
65829
67043
|
* Open a DM channel with a user.
|
|
65830
67044
|
* Returns the DM channel ID.
|
|
65831
67045
|
*/
|
|
67046
|
+
/**
|
|
67047
|
+
* Resolve a channel name (e.g. "function-cve-alerts") to a channel ID.
|
|
67048
|
+
* Uses conversations.list with pagination. Results are cached for the process lifetime.
|
|
67049
|
+
*/
|
|
67050
|
+
channelNameCache = /* @__PURE__ */ new Map();
|
|
67051
|
+
async resolveChannelName(name) {
|
|
67052
|
+
const normalized = name.startsWith("#") ? name.slice(1) : name;
|
|
67053
|
+
if (this.channelNameCache.has(normalized)) return this.channelNameCache.get(normalized);
|
|
67054
|
+
let cursor;
|
|
67055
|
+
do {
|
|
67056
|
+
const params = {
|
|
67057
|
+
types: "public_channel,private_channel",
|
|
67058
|
+
limit: 200
|
|
67059
|
+
};
|
|
67060
|
+
if (cursor) params.cursor = cursor;
|
|
67061
|
+
const resp = await this.api("conversations.list", params);
|
|
67062
|
+
if (!resp?.ok) break;
|
|
67063
|
+
for (const ch of resp.channels || []) {
|
|
67064
|
+
this.channelNameCache.set(ch.name, ch.id);
|
|
67065
|
+
if (ch.name === normalized) return ch.id;
|
|
67066
|
+
}
|
|
67067
|
+
cursor = resp.response_metadata?.next_cursor;
|
|
67068
|
+
} while (cursor);
|
|
67069
|
+
return void 0;
|
|
67070
|
+
}
|
|
65832
67071
|
async openDM(userId) {
|
|
65833
67072
|
try {
|
|
65834
67073
|
const resp = await this.api("conversations.open", { users: userId });
|
|
@@ -66007,6 +67246,7 @@ var init_slack_frontend = __esm({
|
|
|
66007
67246
|
init_lazy_otel();
|
|
66008
67247
|
init_task_live_updates();
|
|
66009
67248
|
init_logger();
|
|
67249
|
+
init_user_facing_error();
|
|
66010
67250
|
SlackFrontend = class {
|
|
66011
67251
|
name = "slack";
|
|
66012
67252
|
subs = [];
|
|
@@ -66256,7 +67496,7 @@ var init_slack_frontend = __esm({
|
|
|
66256
67496
|
if (checkId) text += `
|
|
66257
67497
|
Check: ${checkId}`;
|
|
66258
67498
|
if (message) text += `
|
|
66259
|
-
${message}`;
|
|
67499
|
+
${formatUserFacingExecutionMessage(message)}`;
|
|
66260
67500
|
if (this.isTelemetryEnabled(ctx)) {
|
|
66261
67501
|
const suffix = this.getExecutionReferenceSuffix();
|
|
66262
67502
|
if (suffix) {
|
|
@@ -66443,17 +67683,13 @@ ${suffix}`;
|
|
|
66443
67683
|
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
66444
67684
|
}
|
|
66445
67685
|
if (!text) {
|
|
66446
|
-
const
|
|
66447
|
-
|
|
66448
|
-
(i) => i.severity === "error" && (i.ruleId?.startsWith("system/") || i.ruleId?.endsWith("/error"))
|
|
66449
|
-
);
|
|
66450
|
-
if (errorIssues.length > 0) {
|
|
66451
|
-
const errorMessages = errorIssues.map((i) => i.message).join("\n");
|
|
67686
|
+
const errorMessages = summarizeUserFacingIssues(result?.issues || []);
|
|
67687
|
+
if (errorMessages) {
|
|
66452
67688
|
text = `:warning: Something went wrong while processing your request:
|
|
66453
67689
|
${errorMessages}`;
|
|
66454
67690
|
this.errorNotified = true;
|
|
66455
67691
|
ctx.logger.warn(
|
|
66456
|
-
`[slack-frontend] posting error fallback for ${checkId}:
|
|
67692
|
+
`[slack-frontend] posting error fallback for ${checkId}: issue-derived error message`
|
|
66457
67693
|
);
|
|
66458
67694
|
}
|
|
66459
67695
|
}
|
|
@@ -67222,6 +68458,7 @@ var init_telegram_frontend = __esm({
|
|
|
67222
68458
|
init_client2();
|
|
67223
68459
|
init_markdown2();
|
|
67224
68460
|
init_task_live_updates();
|
|
68461
|
+
init_user_facing_error();
|
|
67225
68462
|
TelegramFrontend = class {
|
|
67226
68463
|
name = "telegram";
|
|
67227
68464
|
subs = [];
|
|
@@ -67385,7 +68622,7 @@ var init_telegram_frontend = __esm({
|
|
|
67385
68622
|
if (checkId) text += `
|
|
67386
68623
|
Check: ${checkId}`;
|
|
67387
68624
|
if (message) text += `
|
|
67388
|
-
${message}`;
|
|
68625
|
+
${formatUserFacingExecutionMessage(message)}`;
|
|
67389
68626
|
await telegram.sendMessage({
|
|
67390
68627
|
chat_id: chatId,
|
|
67391
68628
|
text,
|
|
@@ -67437,6 +68674,13 @@ ${message}`;
|
|
|
67437
68674
|
if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
|
|
67438
68675
|
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
67439
68676
|
}
|
|
68677
|
+
if (!text) {
|
|
68678
|
+
const issueSummary = summarizeUserFacingIssues(result?.issues || []);
|
|
68679
|
+
if (issueSummary) {
|
|
68680
|
+
text = issueSummary;
|
|
68681
|
+
this.errorNotified = true;
|
|
68682
|
+
}
|
|
68683
|
+
}
|
|
67440
68684
|
if (!text) {
|
|
67441
68685
|
ctx.logger.info(
|
|
67442
68686
|
`[telegram-frontend] skip posting reply for ${checkId}: no renderable text`
|
|
@@ -75950,6 +77194,7 @@ var init_email_frontend = __esm({
|
|
|
75950
77194
|
"use strict";
|
|
75951
77195
|
init_client3();
|
|
75952
77196
|
init_markdown3();
|
|
77197
|
+
init_user_facing_error();
|
|
75953
77198
|
EmailFrontend = class {
|
|
75954
77199
|
name = "email";
|
|
75955
77200
|
subs = [];
|
|
@@ -76028,12 +77273,14 @@ var init_email_frontend = __esm({
|
|
|
76028
77273
|
if (checkId) text += `
|
|
76029
77274
|
Check: ${checkId}`;
|
|
76030
77275
|
if (message) text += `
|
|
76031
|
-
${message}`;
|
|
77276
|
+
${formatUserFacingExecutionMessage(message)}`;
|
|
76032
77277
|
const sendOpts = {
|
|
76033
77278
|
to: ev.from,
|
|
76034
77279
|
subject: ev.subject ? addRePrefix(ev.subject) : `Visor: ${title}`,
|
|
76035
77280
|
text,
|
|
76036
|
-
html: wrapInEmailTemplate(
|
|
77281
|
+
html: wrapInEmailTemplate(
|
|
77282
|
+
`<p><strong>${title}</strong></p><p>${formatUserFacingExecutionMessage(message)}</p>`
|
|
77283
|
+
)
|
|
76037
77284
|
};
|
|
76038
77285
|
if (ev.messageId) {
|
|
76039
77286
|
sendOpts.inReplyTo = ev.messageId;
|
|
@@ -76083,6 +77330,13 @@ ${message}`;
|
|
|
76083
77330
|
if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
|
|
76084
77331
|
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
76085
77332
|
}
|
|
77333
|
+
if (!text) {
|
|
77334
|
+
const issueSummary = summarizeUserFacingIssues(result?.issues || []);
|
|
77335
|
+
if (issueSummary) {
|
|
77336
|
+
text = issueSummary;
|
|
77337
|
+
this.errorNotified = true;
|
|
77338
|
+
}
|
|
77339
|
+
}
|
|
76086
77340
|
if (!text) {
|
|
76087
77341
|
ctx.logger.info(`[email-frontend] skip posting reply for ${checkId}: no renderable text`);
|
|
76088
77342
|
return;
|
|
@@ -76392,6 +77646,7 @@ var init_whatsapp_frontend = __esm({
|
|
|
76392
77646
|
"use strict";
|
|
76393
77647
|
init_client4();
|
|
76394
77648
|
init_markdown4();
|
|
77649
|
+
init_user_facing_error();
|
|
76395
77650
|
WhatsAppFrontend = class {
|
|
76396
77651
|
name = "whatsapp";
|
|
76397
77652
|
subs = [];
|
|
@@ -76470,7 +77725,7 @@ var init_whatsapp_frontend = __esm({
|
|
|
76470
77725
|
if (checkId) text += `
|
|
76471
77726
|
Check: ${checkId}`;
|
|
76472
77727
|
if (message) text += `
|
|
76473
|
-
${message}`;
|
|
77728
|
+
${formatUserFacingExecutionMessage(message)}`;
|
|
76474
77729
|
await whatsapp.sendMessage({
|
|
76475
77730
|
to: from,
|
|
76476
77731
|
text,
|
|
@@ -76522,6 +77777,13 @@ ${message}`;
|
|
|
76522
77777
|
if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
|
|
76523
77778
|
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
76524
77779
|
}
|
|
77780
|
+
if (!text) {
|
|
77781
|
+
const issueSummary = summarizeUserFacingIssues(result?.issues || []);
|
|
77782
|
+
if (issueSummary) {
|
|
77783
|
+
text = issueSummary;
|
|
77784
|
+
this.errorNotified = true;
|
|
77785
|
+
}
|
|
77786
|
+
}
|
|
76525
77787
|
if (!text) {
|
|
76526
77788
|
ctx.logger.info(
|
|
76527
77789
|
`[whatsapp-frontend] skip posting reply for ${checkId}: no renderable text`
|
|
@@ -76720,6 +77982,7 @@ var init_teams_frontend = __esm({
|
|
|
76720
77982
|
init_client5();
|
|
76721
77983
|
init_markdown5();
|
|
76722
77984
|
init_task_live_updates();
|
|
77985
|
+
init_user_facing_error();
|
|
76723
77986
|
TeamsFrontend = class {
|
|
76724
77987
|
name = "teams";
|
|
76725
77988
|
subs = [];
|
|
@@ -76799,7 +78062,7 @@ var init_teams_frontend = __esm({
|
|
|
76799
78062
|
if (_checkId) text += `
|
|
76800
78063
|
Check: ${_checkId}`;
|
|
76801
78064
|
if (message) text += `
|
|
76802
|
-
${message}`;
|
|
78065
|
+
${formatUserFacingExecutionMessage(message)}`;
|
|
76803
78066
|
await teams.sendMessage({
|
|
76804
78067
|
conversationReference: conversationRef,
|
|
76805
78068
|
text,
|
|
@@ -76852,6 +78115,13 @@ ${message}`;
|
|
|
76852
78115
|
if (out && typeof out._rawOutput === "string" && out._rawOutput.trim().length > 0) {
|
|
76853
78116
|
text = (text || "") + "\n\n" + out._rawOutput.trim();
|
|
76854
78117
|
}
|
|
78118
|
+
if (!text) {
|
|
78119
|
+
const issueSummary = summarizeUserFacingIssues(result?.issues || []);
|
|
78120
|
+
if (issueSummary) {
|
|
78121
|
+
text = issueSummary;
|
|
78122
|
+
this.errorNotified = true;
|
|
78123
|
+
}
|
|
78124
|
+
}
|
|
76855
78125
|
if (!text) {
|
|
76856
78126
|
ctx.logger.info(`[teams-frontend] skip posting reply for ${checkId}: no renderable text`);
|
|
76857
78127
|
return;
|
|
@@ -78799,7 +80069,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
78799
80069
|
const gitAnalyzer = new GitRepositoryAnalyzer2(options.workingDirectory);
|
|
78800
80070
|
logger.info("Analyzing local git repository...");
|
|
78801
80071
|
const repositoryInfo = await gitAnalyzer.analyzeRepository();
|
|
78802
|
-
const hasConversation = !!this.executionContext?.conversation;
|
|
80072
|
+
const hasConversation = !!this.executionContext?.conversation || this.webhookContextHasConversation(options?.webhookContext);
|
|
78803
80073
|
if (!repositoryInfo.isGitRepository && !hasConversation) {
|
|
78804
80074
|
return this.createErrorResult(
|
|
78805
80075
|
repositoryInfo,
|
|
@@ -79198,7 +80468,7 @@ var init_state_machine_execution_engine = __esm({
|
|
|
79198
80468
|
try {
|
|
79199
80469
|
const { SessionRegistry: SessionRegistry2 } = await Promise.resolve().then(() => (init_session_registry(), session_registry_exports));
|
|
79200
80470
|
const sessionRegistry = SessionRegistry2.getInstance();
|
|
79201
|
-
sessionRegistry.clearAllSessions();
|
|
80471
|
+
await sessionRegistry.clearAllSessions();
|
|
79202
80472
|
} catch (error) {
|
|
79203
80473
|
logger.debug(`[StateMachine] Failed to cleanup sessions: ${error}`);
|
|
79204
80474
|
}
|
|
@@ -79387,6 +80657,20 @@ var init_state_machine_execution_engine = __esm({
|
|
|
79387
80657
|
const { convertToReviewSummary: convertToReviewSummary2 } = (init_summary(), __toCommonJS(summary_exports));
|
|
79388
80658
|
return convertToReviewSummary2(groupedResults, statistics);
|
|
79389
80659
|
}
|
|
80660
|
+
webhookContextHasConversation(webhookContext) {
|
|
80661
|
+
try {
|
|
80662
|
+
const webhookData = webhookContext?.webhookData;
|
|
80663
|
+
if (!(webhookData instanceof Map)) return false;
|
|
80664
|
+
for (const payload of webhookData.values()) {
|
|
80665
|
+
const value = payload;
|
|
80666
|
+
if (value?.conversation || value?.slack_conversation || value?.mcp_conversation || value?.telegram_conversation || value?.teams_conversation || value?.whatsapp_conversation || value?.email_conversation) {
|
|
80667
|
+
return true;
|
|
80668
|
+
}
|
|
80669
|
+
}
|
|
80670
|
+
} catch {
|
|
80671
|
+
}
|
|
80672
|
+
return false;
|
|
80673
|
+
}
|
|
79390
80674
|
/**
|
|
79391
80675
|
* Evaluate failure conditions for a check result
|
|
79392
80676
|
*
|