@cjhyy/code-shell 0.1.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +109 -0
- package/LICENSE +21 -0
- package/README.md +177 -0
- package/dist/agent/coordinator.d.ts +49 -0
- package/dist/anthropic-NENYS535.js +9 -0
- package/dist/anthropic-XMTTVNSH.cjs +9 -0
- package/dist/arena/arena.d.ts +43 -0
- package/dist/arena/context/context-tools.d.ts +16 -0
- package/dist/arena/detect-mode.d.ts +20 -0
- package/dist/arena/digest-builder.d.ts +25 -0
- package/dist/arena/index.cjs +103 -0
- package/dist/arena/index.d.ts +29 -0
- package/dist/arena/index.js +103 -0
- package/dist/arena/iterate/convergence.d.ts +25 -0
- package/dist/arena/iterate/formats/index.d.ts +22 -0
- package/dist/arena/iterate/index.d.ts +11 -0
- package/dist/arena/iterate/iterative-arena.d.ts +23 -0
- package/dist/arena/iterate/parse.d.ts +42 -0
- package/dist/arena/iterate/phases/argue.d.ts +22 -0
- package/dist/arena/iterate/phases/revise.d.ts +16 -0
- package/dist/arena/iterate/phases/tournament.d.ts +34 -0
- package/dist/arena/iterate/tools/web-tools.d.ts +13 -0
- package/dist/arena/iterate/types.d.ts +152 -0
- package/dist/arena/ledger.d.ts +47 -0
- package/dist/arena/lenses/architecture.d.ts +5 -0
- package/dist/arena/lenses/engineering.d.ts +5 -0
- package/dist/arena/lenses/general.d.ts +5 -0
- package/dist/arena/lenses/index.d.ts +16 -0
- package/dist/arena/lenses/product.d.ts +5 -0
- package/dist/arena/model-presets.d.ts +14 -0
- package/dist/arena/phases/adjudication.d.ts +24 -0
- package/dist/arena/phases/build-consensus.d.ts +29 -0
- package/dist/arena/phases/claim-registry.d.ts +26 -0
- package/dist/arena/phases/cross-review.d.ts +45 -0
- package/dist/arena/phases/debate-rounds.d.ts +27 -0
- package/dist/arena/phases/participant-research.d.ts +38 -0
- package/dist/arena/phases/planning-detail-expansion.d.ts +38 -0
- package/dist/arena/planner.d.ts +27 -0
- package/dist/arena/providers/docs.d.ts +6 -0
- package/dist/arena/providers/git.d.ts +8 -0
- package/dist/arena/providers/index.d.ts +16 -0
- package/dist/arena/providers/none.d.ts +7 -0
- package/dist/arena/providers/repo.d.ts +6 -0
- package/dist/arena/render/session.d.ts +17 -0
- package/dist/arena/render/terminal.d.ts +33 -0
- package/dist/arena/strategies/discussion.d.ts +25 -0
- package/dist/arena/strategies/index.d.ts +15 -0
- package/dist/arena/strategies/language-wrapper.d.ts +17 -0
- package/dist/arena/strategies/lens-wrapper.d.ts +16 -0
- package/dist/arena/strategies/planning.d.ts +30 -0
- package/dist/arena/strategies/review.d.ts +26 -0
- package/dist/arena/strategies/utils.d.ts +34 -0
- package/dist/arena/tools/selector.d.ts +17 -0
- package/dist/arena/transitions.d.ts +48 -0
- package/dist/arena/types.d.ts +499 -0
- package/dist/arena-46QQ6BZQ.js +261 -0
- package/dist/arena-HZZZ2GJH.cjs +261 -0
- package/dist/bootstrap/setup.d.ts +14 -0
- package/dist/bootstrap/state.d.ts +160 -0
- package/dist/bridge-4ZRXVWBN.js +98 -0
- package/dist/bridge-W476QDFH.cjs +98 -0
- package/dist/chunk-3EAEC566.cjs +136 -0
- package/dist/chunk-3EUDE77E.cjs +1252 -0
- package/dist/chunk-3QR52LL5.cjs +105 -0
- package/dist/chunk-4PKZPFCD.js +136 -0
- package/dist/chunk-4RRUP4MM.js +86 -0
- package/dist/chunk-4SFPFHMG.js +132 -0
- package/dist/chunk-5LHVI777.cjs +246 -0
- package/dist/chunk-6JOOLG6F.js +104 -0
- package/dist/chunk-6ZIROYXX.js +207 -0
- package/dist/chunk-7RMAR2Y3.js +6511 -0
- package/dist/chunk-BE6IPRDM.js +224 -0
- package/dist/chunk-BHGIJ2DJ.js +41 -0
- package/dist/chunk-BMH65OVB.cjs +224 -0
- package/dist/chunk-C7JBCO5V.js +80 -0
- package/dist/chunk-CSO5EOEP.cjs +132 -0
- package/dist/chunk-DI7RDLOS.js +47 -0
- package/dist/chunk-DICG4AB7.cjs +73 -0
- package/dist/chunk-EL2RL5DG.cjs +101 -0
- package/dist/chunk-FGZGCFJX.cjs +207 -0
- package/dist/chunk-FUOKLKR2.js +101 -0
- package/dist/chunk-FVP7XJGS.cjs +4577 -0
- package/dist/chunk-HD7MT5XU.cjs +86 -0
- package/dist/chunk-IJSHQGMP.cjs +45 -0
- package/dist/chunk-IMMTBZ33.cjs +63 -0
- package/dist/chunk-J5XW6UYL.js +291 -0
- package/dist/chunk-JT2WSQSR.cjs +335 -0
- package/dist/chunk-KO2FPF7G.js +93 -0
- package/dist/chunk-LRAY5IYC.cjs +291 -0
- package/dist/chunk-NG73UR3L.cjs +285 -0
- package/dist/chunk-NZ4DQ72D.cjs +664 -0
- package/dist/chunk-O6F2EF2Z.js +73 -0
- package/dist/chunk-OG4MIQJ6.js +335 -0
- package/dist/chunk-QHDYFC5U.cjs +6511 -0
- package/dist/chunk-R32DJSJE.cjs +41 -0
- package/dist/chunk-S74VVJBF.js +285 -0
- package/dist/chunk-SK62UVS6.js +105 -0
- package/dist/chunk-SQZHPD3S.js +4577 -0
- package/dist/chunk-UK2SU23S.cjs +93 -0
- package/dist/chunk-VKGNCYRL.js +258 -0
- package/dist/chunk-WFQF3RT4.cjs +104 -0
- package/dist/chunk-WHRTARDJ.cjs +258 -0
- package/dist/chunk-WRZJUQ5Z.js +664 -0
- package/dist/chunk-X4HAERTQ.js +63 -0
- package/dist/chunk-XHC2JMEC.cjs +80 -0
- package/dist/chunk-YZ25SSWJ.js +222 -0
- package/dist/chunk-ZJV3YSY3.cjs +222 -0
- package/dist/chunk-ZRSSGYA5.js +246 -0
- package/dist/chunk-ZXYDDJEJ.js +1252 -0
- package/dist/cli/commands/arena.d.ts +29 -0
- package/dist/cli/commands/builtin/advanced-commands.d.ts +5 -0
- package/dist/cli/commands/builtin/core-commands.d.ts +5 -0
- package/dist/cli/commands/builtin/extra-commands.d.ts +6 -0
- package/dist/cli/commands/builtin/git-commands.d.ts +5 -0
- package/dist/cli/commands/builtin/more-commands.d.ts +7 -0
- package/dist/cli/commands/builtin/permissions-command.d.ts +5 -0
- package/dist/cli/commands/builtin/utility-commands.d.ts +5 -0
- package/dist/cli/commands/registry.d.ts +64 -0
- package/dist/cli/commands/repl.d.ts +21 -0
- package/dist/cli/commands/run.d.ts +21 -0
- package/dist/cli/commands/runs.d.ts +14 -0
- package/dist/cli/cost-tracker.d.ts +57 -0
- package/dist/cli/exit.d.ts +12 -0
- package/dist/cli/input/ndjson-reader.d.ts +31 -0
- package/dist/cli/input-compiler.d.ts +5 -0
- package/dist/cli/main.cjs +19674 -0
- package/dist/cli/main.d.ts +5 -0
- package/dist/cli/main.js +19674 -0
- package/dist/cli/onboarding.d.ts +26 -0
- package/dist/cli/output/renderer.d.ts +28 -0
- package/dist/cli/output/terminal-ui.d.ts +50 -0
- package/dist/cli/updater.d.ts +10 -0
- package/dist/compaction-44I55TLI.cjs +32 -0
- package/dist/compaction-CCHWA4G6.js +32 -0
- package/dist/context/compaction.d.ts +83 -0
- package/dist/context/manager.d.ts +99 -0
- package/dist/context/token-counter.d.ts +27 -0
- package/dist/coordinator-5HY7ALO2.js +7 -0
- package/dist/coordinator-INDLPVMF.cjs +7 -0
- package/dist/cost-tracker-IWYAUPXH.js +9 -0
- package/dist/cost-tracker-JXTJQQWE.cjs +9 -0
- package/dist/cron/scheduler.d.ts +34 -0
- package/dist/devtools-KIY3LWQJ.js +6 -0
- package/dist/devtools-LRP47DDV.cjs +6 -0
- package/dist/engine/cost-store.d.ts +22 -0
- package/dist/engine/engine.d.ts +101 -0
- package/dist/engine/model-facade.d.ts +24 -0
- package/dist/engine/query.d.ts +50 -0
- package/dist/engine/streaming-tool-queue.d.ts +34 -0
- package/dist/engine/token-budget.d.ts +22 -0
- package/dist/engine/tool-summary.d.ts +13 -0
- package/dist/engine/turn-loop.d.ts +62 -0
- package/dist/engine/turn-state.d.ts +14 -0
- package/dist/exceptions.d.ts +44 -0
- package/dist/file-history-HZWO2WQ5.js +7 -0
- package/dist/file-history-UQQILIY7.cjs +7 -0
- package/dist/git/utils.d.ts +35 -0
- package/dist/git/worktree.d.ts +36 -0
- package/dist/hooks/events.d.ts +35 -0
- package/dist/hooks/registry.d.ts +15 -0
- package/dist/index.cjs +336 -0
- package/dist/index.d.ts +64 -0
- package/dist/index.js +336 -0
- package/dist/ink/Ansi.d.ts +10 -0
- package/dist/ink/bidi.d.ts +15 -0
- package/dist/ink/clearTerminal.d.ts +13 -0
- package/dist/ink/colorize.d.ts +16 -0
- package/dist/ink/components/AlternateScreen.d.ts +21 -0
- package/dist/ink/components/App.d.ts +70 -0
- package/dist/ink/components/AppContext.d.ts +11 -0
- package/dist/ink/components/Box.d.ts +49 -0
- package/dist/ink/components/Button.d.ts +36 -0
- package/dist/ink/components/ClockContext.d.ts +9 -0
- package/dist/ink/components/CursorDeclarationContext.d.ts +22 -0
- package/dist/ink/components/ErrorOverview.d.ts +5 -0
- package/dist/ink/components/Link.d.ts +7 -0
- package/dist/ink/components/Newline.d.ts +12 -0
- package/dist/ink/components/NoSelect.d.ts +19 -0
- package/dist/ink/components/RawAnsi.d.ts +16 -0
- package/dist/ink/components/ScrollBox.d.ts +78 -0
- package/dist/ink/components/Spacer.d.ts +5 -0
- package/dist/ink/components/StdinContext.d.ts +27 -0
- package/dist/ink/components/TerminalFocusContext.d.ts +10 -0
- package/dist/ink/components/TerminalSizeContext.d.ts +5 -0
- package/dist/ink/components/Text.d.ts +55 -0
- package/dist/ink/constants.d.ts +1 -0
- package/dist/ink/cursor.d.ts +8 -0
- package/dist/ink/devtools.d.ts +5 -0
- package/dist/ink/dom.d.ts +76 -0
- package/dist/ink/events/click-event.d.ts +31 -0
- package/dist/ink/events/dispatcher.d.ts +40 -0
- package/dist/ink/events/emitter.d.ts +5 -0
- package/dist/ink/events/event-handlers.d.ts +46 -0
- package/dist/ink/events/event.d.ts +5 -0
- package/dist/ink/events/focus-event.d.ts +13 -0
- package/dist/ink/events/input-event.d.ts +30 -0
- package/dist/ink/events/keyboard-event.d.ts +19 -0
- package/dist/ink/events/paste-event.d.ts +8 -0
- package/dist/ink/events/resize-event.d.ts +9 -0
- package/dist/ink/events/terminal-event.d.ts +54 -0
- package/dist/ink/events/terminal-focus-event.d.ts +13 -0
- package/dist/ink/focus.d.ts +43 -0
- package/dist/ink/frame.d.ts +94 -0
- package/dist/ink/get-max-width.d.ts +17 -0
- package/dist/ink/hit-test.d.ts +33 -0
- package/dist/ink/hooks/use-animation-frame.d.ts +26 -0
- package/dist/ink/hooks/use-app.d.ts +5 -0
- package/dist/ink/hooks/use-declared-cursor.d.ts +26 -0
- package/dist/ink/hooks/use-input.d.ts +37 -0
- package/dist/ink/hooks/use-interval.d.ts +18 -0
- package/dist/ink/hooks/use-search-highlight.d.ts +30 -0
- package/dist/ink/hooks/use-selection.d.ts +39 -0
- package/dist/ink/hooks/use-stdin.d.ts +5 -0
- package/dist/ink/hooks/use-tab-status.d.ts +13 -0
- package/dist/ink/hooks/use-terminal-focus.d.ts +10 -0
- package/dist/ink/hooks/use-terminal-title.d.ts +12 -0
- package/dist/ink/hooks/use-terminal-viewport.d.ts +29 -0
- package/dist/ink/index.d.ts +20 -0
- package/dist/ink/ink.d.ts +369 -0
- package/dist/ink/instances.d.ts +3 -0
- package/dist/ink/layout/engine.d.ts +2 -0
- package/dist/ink/layout/geometry.d.ts +30 -0
- package/dist/ink/layout/node.d.ts +125 -0
- package/dist/ink/layout/yoga.d.ts +56 -0
- package/dist/ink/line-width-cache.d.ts +1 -0
- package/dist/ink/log-update.d.ts +17 -0
- package/dist/ink/measure-element.d.ts +16 -0
- package/dist/ink/measure-text.d.ts +6 -0
- package/dist/ink/node-cache.d.ts +20 -0
- package/dist/ink/optimizer.d.ts +15 -0
- package/dist/ink/output.d.ts +119 -0
- package/dist/ink/parse-keypress.d.ts +111 -0
- package/dist/ink/reconciler.d.ts +11 -0
- package/dist/ink/render-border.d.ts +24 -0
- package/dist/ink/render-node-to-output.d.ts +45 -0
- package/dist/ink/render-to-screen.d.ts +46 -0
- package/dist/ink/renderer.d.ts +14 -0
- package/dist/ink/root.d.ts +74 -0
- package/dist/ink/screen.d.ts +269 -0
- package/dist/ink/searchHighlight.d.ts +20 -0
- package/dist/ink/selection.d.ts +229 -0
- package/dist/ink/squash-text-nodes.d.ts +22 -0
- package/dist/ink/stringWidth.d.ts +1 -0
- package/dist/ink/styles.d.ts +306 -0
- package/dist/ink/supports-hyperlinks.d.ts +13 -0
- package/dist/ink/tabstops.d.ts +1 -0
- package/dist/ink/terminal-focus-state.d.ts +6 -0
- package/dist/ink/terminal-querier.d.ts +129 -0
- package/dist/ink/terminal.d.ts +46 -0
- package/dist/ink/termio/ansi.d.ts +65 -0
- package/dist/ink/termio/csi.d.ts +165 -0
- package/dist/ink/termio/dec.d.ts +37 -0
- package/dist/ink/termio/esc.d.ts +12 -0
- package/dist/ink/termio/osc.d.ts +173 -0
- package/dist/ink/termio/parser.d.ts +35 -0
- package/dist/ink/termio/sgr.d.ts +8 -0
- package/dist/ink/termio/tokenize.d.ts +45 -0
- package/dist/ink/termio/types.d.ts +190 -0
- package/dist/ink/termio.d.ts +24 -0
- package/dist/ink/useTerminalNotification.d.ts +28 -0
- package/dist/ink/warn.d.ts +1 -0
- package/dist/ink/widest-line.d.ts +1 -0
- package/dist/ink/wrap-text.d.ts +2 -0
- package/dist/ink/wrapAnsi.d.ts +7 -0
- package/dist/llm/client-base.d.ts +22 -0
- package/dist/llm/client-factory.d.ts +10 -0
- package/dist/llm/model-pool.d.ts +60 -0
- package/dist/llm/providers/anthropic.d.ts +19 -0
- package/dist/llm/providers/openai.d.ts +19 -0
- package/dist/llm/types.d.ts +21 -0
- package/dist/loader-DXFSUB4R.cjs +91 -0
- package/dist/loader-J5UID6CV.js +91 -0
- package/dist/logging/logger.d.ts +19 -0
- package/dist/lsp/client.d.ts +41 -0
- package/dist/lsp/manager.d.ts +40 -0
- package/dist/lsp/servers.d.ts +16 -0
- package/dist/manager-ETWDOCHN.cjs +9 -0
- package/dist/manager-HGMQNEJB.cjs +11 -0
- package/dist/manager-HUAQOORS.js +9 -0
- package/dist/manager-VJIZ2CQC.js +11 -0
- package/dist/mcp-manager-G4G2QASE.cjs +8 -0
- package/dist/mcp-manager-OKXHHWYR.js +8 -0
- package/dist/memory-AP747G5H.js +7 -0
- package/dist/memory-BH3BYUN5.cjs +7 -0
- package/dist/native-ts/yoga-layout/enums.d.ts +117 -0
- package/dist/native-ts/yoga-layout/index.d.ts +270 -0
- package/dist/onboarding-6WEPV7C2.js +11 -0
- package/dist/onboarding-QM5DY2TU.cjs +11 -0
- package/dist/openai-5HQGBL4L.js +9 -0
- package/dist/openai-656VWUQW.cjs +9 -0
- package/dist/plugins/loader.d.ts +72 -0
- package/dist/plugins/types.d.ts +4 -0
- package/dist/preset/index.d.ts +60 -0
- package/dist/product/define.d.ts +60 -0
- package/dist/product/index.cjs +23 -0
- package/dist/product/index.d.ts +5 -0
- package/dist/product/index.js +23 -0
- package/dist/product/types.d.ts +116 -0
- package/dist/prompt/composer.d.ts +42 -0
- package/dist/prompt/instruction-scanner.d.ts +54 -0
- package/dist/prompt/section-cache.d.ts +15 -0
- package/dist/prompt/section-loader.d.ts +26 -0
- package/dist/protocol/client.d.ts +64 -0
- package/dist/protocol/index.d.ts +4 -0
- package/dist/protocol/server.d.ts +49 -0
- package/dist/protocol/transport.d.ts +36 -0
- package/dist/protocol/types.d.ts +150 -0
- package/dist/react-compiler-runtime-shim.d.ts +1 -0
- package/dist/remote/bridge.d.ts +37 -0
- package/dist/run/ArtifactTracker.d.ts +38 -0
- package/dist/run/CheckpointWriter.d.ts +53 -0
- package/dist/run/EngineRunner.d.ts +70 -0
- package/dist/run/Evaluator.d.ts +56 -0
- package/dist/run/FileRunStore.d.ts +39 -0
- package/dist/run/Heartbeat.d.ts +54 -0
- package/dist/run/RunApprovalBackend.d.ts +53 -0
- package/dist/run/RunLock.d.ts +40 -0
- package/dist/run/RunManager.d.ts +76 -0
- package/dist/run/RunQueue.d.ts +27 -0
- package/dist/run/RunStore.d.ts +25 -0
- package/dist/run/factory.d.ts +57 -0
- package/dist/run/index.cjs +50 -0
- package/dist/run/index.d.ts +17 -0
- package/dist/run/index.js +50 -0
- package/dist/run/types.d.ts +136 -0
- package/dist/services/analytics.d.ts +29 -0
- package/dist/services/auto-dream.d.ts +35 -0
- package/dist/services/compact.d.ts +56 -0
- package/dist/services/diagnostics.d.ts +41 -0
- package/dist/services/extract-memories.d.ts +27 -0
- package/dist/services/index.d.ts +11 -0
- package/dist/services/notifier.d.ts +25 -0
- package/dist/services/oauth.d.ts +27 -0
- package/dist/services/session-memory.d.ts +37 -0
- package/dist/session/file-history.d.ts +41 -0
- package/dist/session/memory.d.ts +45 -0
- package/dist/session/session-manager.d.ts +23 -0
- package/dist/session/transcript.d.ts +39 -0
- package/dist/session-manager-JIIBCFTC.js +8 -0
- package/dist/session-manager-UQKB564H.cjs +8 -0
- package/dist/settings/manager.d.ts +27 -0
- package/dist/settings/schema.d.ts +676 -0
- package/dist/skills/index.d.ts +7 -0
- package/dist/skills/matcher.d.ts +21 -0
- package/dist/skills/scanner.d.ts +30 -0
- package/dist/skills-63RBYQRX.js +13 -0
- package/dist/skills-V3BPLW3D.cjs +13 -0
- package/dist/token-counter-CHAXW5O7.cjs +13 -0
- package/dist/token-counter-DSOZGZ5Q.js +13 -0
- package/dist/tool-summary-5P2GKRA4.cjs +31 -0
- package/dist/tool-summary-J2ICNMWP.js +31 -0
- package/dist/tool-system/builtin/agent.d.ts +35 -0
- package/dist/tool-system/builtin/arena.d.ts +18 -0
- package/dist/tool-system/builtin/ask-user.d.ts +12 -0
- package/dist/tool-system/builtin/bash.d.ts +6 -0
- package/dist/tool-system/builtin/brief.d.ts +6 -0
- package/dist/tool-system/builtin/config.d.ts +6 -0
- package/dist/tool-system/builtin/cron.d.ts +10 -0
- package/dist/tool-system/builtin/edit.d.ts +6 -0
- package/dist/tool-system/builtin/file-cache.d.ts +11 -0
- package/dist/tool-system/builtin/glob.d.ts +6 -0
- package/dist/tool-system/builtin/grep.d.ts +6 -0
- package/dist/tool-system/builtin/index.d.ts +10 -0
- package/dist/tool-system/builtin/lsp.d.ts +6 -0
- package/dist/tool-system/builtin/mcp-tools.d.ts +10 -0
- package/dist/tool-system/builtin/notebook-edit.d.ts +6 -0
- package/dist/tool-system/builtin/plan.d.ts +18 -0
- package/dist/tool-system/builtin/powershell.d.ts +6 -0
- package/dist/tool-system/builtin/read.d.ts +6 -0
- package/dist/tool-system/builtin/remote-trigger.d.ts +6 -0
- package/dist/tool-system/builtin/repl.d.ts +6 -0
- package/dist/tool-system/builtin/send-message.d.ts +6 -0
- package/dist/tool-system/builtin/skill.d.ts +6 -0
- package/dist/tool-system/builtin/sleep.d.ts +6 -0
- package/dist/tool-system/builtin/task.d.ts +51 -0
- package/dist/tool-system/builtin/tool-search.d.ts +12 -0
- package/dist/tool-system/builtin/web-fetch.d.ts +6 -0
- package/dist/tool-system/builtin/web-search.d.ts +6 -0
- package/dist/tool-system/builtin/worktree.d.ts +10 -0
- package/dist/tool-system/builtin/write.d.ts +6 -0
- package/dist/tool-system/executor.d.ts +25 -0
- package/dist/tool-system/mcp-manager.d.ts +50 -0
- package/dist/tool-system/permission.d.ts +69 -0
- package/dist/tool-system/registry.d.ts +24 -0
- package/dist/tool-system/validation.d.ts +5 -0
- package/dist/types.d.ts +260 -0
- package/dist/ui/App.d.ts +14 -0
- package/dist/ui/components/AgentBlock.d.ts +32 -0
- package/dist/ui/components/BabyHand.d.ts +1 -0
- package/dist/ui/components/Banner.d.ts +8 -0
- package/dist/ui/components/CodeBlock.d.ts +7 -0
- package/dist/ui/components/CommandInput.d.ts +13 -0
- package/dist/ui/components/ContextUsageBar.d.ts +7 -0
- package/dist/ui/components/DiffView.d.ts +6 -0
- package/dist/ui/components/DogLogo.d.ts +1 -0
- package/dist/ui/components/FullscreenLayout.d.ts +51 -0
- package/dist/ui/components/KunHead.d.ts +1 -0
- package/dist/ui/components/KunLogo.d.ts +1 -0
- package/dist/ui/components/MessageContent.d.ts +22 -0
- package/dist/ui/components/PermissionDialog.d.ts +8 -0
- package/dist/ui/components/PermissionPrompt.d.ts +8 -0
- package/dist/ui/components/ScrollView.d.ts +34 -0
- package/dist/ui/components/Spinner.d.ts +8 -0
- package/dist/ui/components/SpinnerWithVerb.d.ts +13 -0
- package/dist/ui/components/StatusLine.d.ts +22 -0
- package/dist/ui/components/StructuredDiff.d.ts +8 -0
- package/dist/ui/components/TaskList.d.ts +6 -0
- package/dist/ui/components/TextInput.d.ts +9 -0
- package/dist/ui/components/ThemedBox.d.ts +19 -0
- package/dist/ui/components/ThemedText.d.ts +26 -0
- package/dist/ui/components/ToolCall.d.ts +21 -0
- package/dist/ui/components/VirtualMessageList.d.ts +24 -0
- package/dist/ui/hooks/useCostSummary.d.ts +1 -0
- package/dist/ui/index.d.ts +12 -0
- package/dist/ui/input-history.d.ts +61 -0
- package/dist/ui/store.d.ts +82 -0
- package/dist/ui/terminal-renderer.d.ts +45 -0
- package/dist/ui/theme.d.ts +36 -0
- package/dist/ui/vim-mode.d.ts +25 -0
- package/dist/updater-AVD4LKTQ.js +183 -0
- package/dist/updater-URVNPDPW.cjs +183 -0
- package/dist/utils/debug.d.ts +57 -0
- package/dist/utils/earlyInput.d.ts +43 -0
- package/dist/utils/env.d.ts +24 -0
- package/dist/utils/envUtils.d.ts +51 -0
- package/dist/utils/execFileNoThrow.d.ts +39 -0
- package/dist/utils/format.d.ts +44 -0
- package/dist/utils/fullscreen.d.ts +47 -0
- package/dist/utils/intl.d.ts +22 -0
- package/dist/utils/lockfile.d.ts +15 -0
- package/dist/utils/log.d.ts +56 -0
- package/dist/utils/semver.d.ts +5 -0
- package/dist/utils/sliceAnsi.d.ts +12 -0
- package/dist/utils/systemTheme.d.ts +40 -0
- package/dist/utils/theme.d.ts +91 -0
- package/dist/utils/toolDisplay.d.ts +31 -0
- package/dist/utils/truncate.d.ts +40 -0
- package/dist/voice/index.d.ts +35 -0
- package/dist/voice-KTZ7PAGO.js +80 -0
- package/dist/voice-KZE25GKB.cjs +80 -0
- package/package.json +130 -0
|
@@ -0,0 +1,4577 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-X4HAERTQ.js";
|
|
4
|
+
import {
|
|
5
|
+
LLMError
|
|
6
|
+
} from "./chunk-FUOKLKR2.js";
|
|
7
|
+
import {
|
|
8
|
+
init_esm_shims
|
|
9
|
+
} from "./chunk-DI7RDLOS.js";
|
|
10
|
+
|
|
11
|
+
// src/llm/client-factory.ts
|
|
12
|
+
init_esm_shims();
|
|
13
|
+
var PROVIDER_REGISTRY = /* @__PURE__ */ new Map();
|
|
14
|
+
function registerProvider(name, cls) {
|
|
15
|
+
PROVIDER_REGISTRY.set(name, cls);
|
|
16
|
+
}
|
|
17
|
+
async function createLLMClient(config) {
|
|
18
|
+
let Cls = PROVIDER_REGISTRY.get(config.provider);
|
|
19
|
+
if (!Cls) {
|
|
20
|
+
if (config.provider === "anthropic") {
|
|
21
|
+
const { AnthropicClient } = await import("./anthropic-NENYS535.js");
|
|
22
|
+
registerProvider("anthropic", AnthropicClient);
|
|
23
|
+
Cls = AnthropicClient;
|
|
24
|
+
} else if (config.provider === "openai") {
|
|
25
|
+
const { OpenAIClient } = await import("./openai-5HQGBL4L.js");
|
|
26
|
+
registerProvider("openai", OpenAIClient);
|
|
27
|
+
Cls = OpenAIClient;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (!Cls) {
|
|
31
|
+
throw new LLMError(
|
|
32
|
+
`Unknown LLM provider: ${config.provider}. Available: ${[...PROVIDER_REGISTRY.keys()].join(", ")}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return new Cls(config);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// src/arena/types.ts
|
|
39
|
+
init_esm_shims();
|
|
40
|
+
var ARENA_MODE_DEFAULTS = {
|
|
41
|
+
review: { maxDiscussionRounds: 3, convergenceThreshold: 200 },
|
|
42
|
+
discussion: { maxDiscussionRounds: 4, convergenceThreshold: 300 },
|
|
43
|
+
planning: { maxDiscussionRounds: 5, convergenceThreshold: 400 }
|
|
44
|
+
};
|
|
45
|
+
var DEFAULT_EXECUTION_LIMITS = {
|
|
46
|
+
maxClaimsForReview: 12,
|
|
47
|
+
maxContestedClaimsForDebate: 5,
|
|
48
|
+
maxRequestedChecksPerClaimPerRound: 2,
|
|
49
|
+
maxReviewersPerClaim: 2,
|
|
50
|
+
maxRoadmapPhases: 6,
|
|
51
|
+
maxExpandedPhasesPerRun: 6
|
|
52
|
+
};
|
|
53
|
+
function isStrategyPlanning(s) {
|
|
54
|
+
return typeof s.detailExpansionUserPrompt === "function";
|
|
55
|
+
}
|
|
56
|
+
function isStrategyV2(s) {
|
|
57
|
+
return typeof s.verificationReviewUserPrompt === "function";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/arena/strategies/review.ts
|
|
61
|
+
init_esm_shims();
|
|
62
|
+
|
|
63
|
+
// src/arena/strategies/utils.ts
|
|
64
|
+
init_esm_shims();
|
|
65
|
+
function asString(v) {
|
|
66
|
+
if (typeof v === "string") return v;
|
|
67
|
+
if (v == null) return "";
|
|
68
|
+
if (typeof v === "number" || typeof v === "boolean") return String(v);
|
|
69
|
+
return "";
|
|
70
|
+
}
|
|
71
|
+
function sanitizeForPrompt(text) {
|
|
72
|
+
if (!text) return "";
|
|
73
|
+
return text.replace(/<\/?(?:system|assistant|user|system-reminder)[^>]*>/gi, "").replace(/^(?:IGNORE|DISREGARD|FORGET|OVERRIDE|SYSTEM|INSTRUCTION)[:\s].*/gim, "").slice(0, 3e3);
|
|
74
|
+
}
|
|
75
|
+
function extractJSON(text) {
|
|
76
|
+
const fenced = text.match(/```(?:json)?\s*\n([\s\S]*)\n\s*```/);
|
|
77
|
+
if (fenced) return fenced[1].trim();
|
|
78
|
+
const start = text.indexOf("{");
|
|
79
|
+
if (start !== -1) {
|
|
80
|
+
let depth = 0;
|
|
81
|
+
let inString = false;
|
|
82
|
+
let escape = false;
|
|
83
|
+
for (let i = start; i < text.length; i++) {
|
|
84
|
+
const ch = text[i];
|
|
85
|
+
if (escape) {
|
|
86
|
+
escape = false;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (ch === "\\") {
|
|
90
|
+
escape = true;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (ch === '"') {
|
|
94
|
+
inString = !inString;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (inString) continue;
|
|
98
|
+
if (ch === "{") depth++;
|
|
99
|
+
else if (ch === "}") {
|
|
100
|
+
depth--;
|
|
101
|
+
if (depth === 0) return text.slice(start, i + 1);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return text.slice(start);
|
|
105
|
+
}
|
|
106
|
+
return text;
|
|
107
|
+
}
|
|
108
|
+
function extractJSONArray(text) {
|
|
109
|
+
const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
110
|
+
if (fenced) return fenced[1].trim();
|
|
111
|
+
const arrayMatch = text.match(/\[[\s\S]*\]/);
|
|
112
|
+
if (arrayMatch) return arrayMatch[0];
|
|
113
|
+
return text;
|
|
114
|
+
}
|
|
115
|
+
function formatBaseContext(ctx) {
|
|
116
|
+
const sections = [];
|
|
117
|
+
if (ctx.quickFacts.length > 0) {
|
|
118
|
+
sections.push(`## Quick Facts
|
|
119
|
+
${ctx.quickFacts.map((f) => `- ${f.label}: ${f.value}`).join("\n")}`);
|
|
120
|
+
}
|
|
121
|
+
for (const artifact of ctx.artifacts) {
|
|
122
|
+
const header = artifact.ref ? `${artifact.title} (${artifact.ref})` : artifact.title;
|
|
123
|
+
sections.push(`## ${artifact.kind.toUpperCase()}: ${header}
|
|
124
|
+
\`\`\`
|
|
125
|
+
${artifact.preview}
|
|
126
|
+
\`\`\``);
|
|
127
|
+
}
|
|
128
|
+
return sections.join("\n\n");
|
|
129
|
+
}
|
|
130
|
+
function formatReports(reports) {
|
|
131
|
+
return reports.map((r) => {
|
|
132
|
+
const findingsList = r.findings.map(
|
|
133
|
+
(f) => ` - [${f.id}] (${f.kind}${f.severity ? `, ${f.severity}` : ""}) ${f.title}
|
|
134
|
+
${f.summary}
|
|
135
|
+
Evidence: ${f.evidence.map((e) => `${e.type}:${e.ref}`).join(", ")}
|
|
136
|
+
Confidence: ${f.confidence}` + (f.suggestedChange ? `
|
|
137
|
+
Suggested: ${f.suggestedChange}` : "")
|
|
138
|
+
).join("\n");
|
|
139
|
+
return `### ${r.participant}
|
|
140
|
+
${r.contextSummary}
|
|
141
|
+
|
|
142
|
+
Findings:
|
|
143
|
+
${findingsList}`;
|
|
144
|
+
}).join("\n\n");
|
|
145
|
+
}
|
|
146
|
+
function formatFindingReviews(reviews) {
|
|
147
|
+
if (reviews.length === 0) return "No peer reviews.";
|
|
148
|
+
return reviews.map(
|
|
149
|
+
(r) => `[${r.reviewer}] on ${r.findingId}: ${r.verdict}${r.reason ? ` \u2014 ${r.reason}` : ""}`
|
|
150
|
+
).join("\n");
|
|
151
|
+
}
|
|
152
|
+
function parseReport(participant, text) {
|
|
153
|
+
if (!text || text.trim().length === 0) {
|
|
154
|
+
logger.warn("arena.parse_report_empty", { participant });
|
|
155
|
+
return {
|
|
156
|
+
participant,
|
|
157
|
+
contextSummary: "(no response)",
|
|
158
|
+
findings: []
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const json = extractJSON(text);
|
|
163
|
+
const parsed = JSON.parse(json);
|
|
164
|
+
let findings = parsed.findings;
|
|
165
|
+
if (!Array.isArray(findings) && parsed.contextSummary === void 0) {
|
|
166
|
+
if (parsed.kind && parsed.title) {
|
|
167
|
+
findings = [parsed];
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const report = {
|
|
171
|
+
participant,
|
|
172
|
+
contextSummary: parsed.contextSummary ?? parsed.context_summary ?? "",
|
|
173
|
+
findings: Array.isArray(findings) ? findings.map(parseFinding) : []
|
|
174
|
+
};
|
|
175
|
+
if (report.findings.length === 0) {
|
|
176
|
+
logger.warn("arena.parse_report_no_findings", {
|
|
177
|
+
participant,
|
|
178
|
+
textPreview: text.slice(0, 200)
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
return report;
|
|
182
|
+
} catch (err) {
|
|
183
|
+
logger.warn("arena.parse_report_json_fail", {
|
|
184
|
+
participant,
|
|
185
|
+
error: err.message,
|
|
186
|
+
textLength: text.length,
|
|
187
|
+
text: text.slice(0, 2e3)
|
|
188
|
+
});
|
|
189
|
+
return extractFindingsFromFreeText(participant, text);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function parseFinding(raw, index) {
|
|
193
|
+
return {
|
|
194
|
+
id: asString(raw.id) || `f${index + 1}`,
|
|
195
|
+
kind: validateFindingKind(raw.kind),
|
|
196
|
+
title: asString(raw.title) || "Untitled",
|
|
197
|
+
summary: asString(raw.summary),
|
|
198
|
+
severity: raw.severity || void 0,
|
|
199
|
+
confidence: typeof raw.confidence === "number" ? raw.confidence : 0.5,
|
|
200
|
+
evidence: Array.isArray(raw.evidence) ? raw.evidence.map((e) => ({
|
|
201
|
+
type: e.type ?? "doc",
|
|
202
|
+
ref: e.ref ?? "",
|
|
203
|
+
note: e.note ?? ""
|
|
204
|
+
})) : [],
|
|
205
|
+
affectedFiles: Array.isArray(raw.affectedFiles) ? raw.affectedFiles : [],
|
|
206
|
+
suggestedChange: raw.suggestedChange || void 0
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function validateFindingKind(v) {
|
|
210
|
+
if (v === "strength" || v === "improvement" || v === "risk" || v === "question") return v;
|
|
211
|
+
return "improvement";
|
|
212
|
+
}
|
|
213
|
+
function extractFindingsFromFreeText(participant, text) {
|
|
214
|
+
const sections = text.split(/(?:^|\n)(?:#{1,3}\s+|\d+\.\s+|\*\*[^*]+\*\*\s*\n|---+\s*\n)/m).map((s) => s.trim()).filter((s) => s.length > 20);
|
|
215
|
+
if (sections.length === 0) {
|
|
216
|
+
return {
|
|
217
|
+
participant,
|
|
218
|
+
contextSummary: text.slice(0, 200),
|
|
219
|
+
findings: [{
|
|
220
|
+
id: `${participant}-f1`,
|
|
221
|
+
kind: "improvement",
|
|
222
|
+
title: "Analysis",
|
|
223
|
+
summary: text.slice(0, 3e3),
|
|
224
|
+
confidence: 0.5,
|
|
225
|
+
evidence: [],
|
|
226
|
+
affectedFiles: []
|
|
227
|
+
}]
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
const findings = sections.slice(0, 10).map((section, i) => {
|
|
231
|
+
const firstLine = section.split("\n")[0].slice(0, 100);
|
|
232
|
+
const body = section.length > firstLine.length ? section.slice(firstLine.length).trim() : section;
|
|
233
|
+
return {
|
|
234
|
+
id: `${participant}-f${i + 1}`,
|
|
235
|
+
kind: classifyFindingKind(section),
|
|
236
|
+
title: firstLine.replace(/^[-*•]\s*/, "").replace(/\*\*/g, ""),
|
|
237
|
+
summary: body.slice(0, 1e3),
|
|
238
|
+
confidence: 0.5,
|
|
239
|
+
evidence: [],
|
|
240
|
+
affectedFiles: extractFilePaths(section)
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
participant,
|
|
245
|
+
contextSummary: text.slice(0, 200),
|
|
246
|
+
findings
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
function classifyFindingKind(text) {
|
|
250
|
+
const lower = text.toLowerCase();
|
|
251
|
+
if (/\b(risk|danger|vulnerab|security|breaking|regression|critical)\b/.test(lower)) return "risk";
|
|
252
|
+
if (/\b(good|well|strength|clean|solid|nice|excellent|properly)\b/.test(lower)) return "strength";
|
|
253
|
+
if (/\b(question|unclear|confirm|clarif|why|how come|wonder)\b/.test(lower)) return "question";
|
|
254
|
+
return "improvement";
|
|
255
|
+
}
|
|
256
|
+
function extractFilePaths(text) {
|
|
257
|
+
const matches = text.match(/(?:^|\s)((?:src|lib|packages|app)\/[\w\-/.]+\.\w+)/gm);
|
|
258
|
+
return matches ? [...new Set(matches.map((m) => m.trim()))] : [];
|
|
259
|
+
}
|
|
260
|
+
function parseReviews(reviewer, text) {
|
|
261
|
+
try {
|
|
262
|
+
const json = extractJSONArray(text);
|
|
263
|
+
const parsed = JSON.parse(json);
|
|
264
|
+
if (!Array.isArray(parsed)) {
|
|
265
|
+
const objJson = extractJSON(text);
|
|
266
|
+
const obj = JSON.parse(objJson);
|
|
267
|
+
if (Array.isArray(obj.reviews)) {
|
|
268
|
+
return obj.reviews.map((r) => parseReview(reviewer, r));
|
|
269
|
+
}
|
|
270
|
+
return [];
|
|
271
|
+
}
|
|
272
|
+
return parsed.map((r) => parseReview(reviewer, r));
|
|
273
|
+
} catch {
|
|
274
|
+
return [];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
function parseReview(reviewer, raw) {
|
|
278
|
+
return {
|
|
279
|
+
reviewer,
|
|
280
|
+
findingId: raw.findingId ?? raw.finding_id ?? "",
|
|
281
|
+
verdict: validateVerdict(raw.verdict),
|
|
282
|
+
reason: raw.reason ?? "",
|
|
283
|
+
extraEvidence: Array.isArray(raw.extraEvidence) ? raw.extraEvidence : void 0
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function validateVerdict(v) {
|
|
287
|
+
if (v === "agree" || v === "refine" || v === "disagree" || v === "needs_evidence") return v;
|
|
288
|
+
return "agree";
|
|
289
|
+
}
|
|
290
|
+
function parseConsensus(text) {
|
|
291
|
+
try {
|
|
292
|
+
const json = extractJSON(text);
|
|
293
|
+
const parsed = JSON.parse(json);
|
|
294
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
295
|
+
throw new Error("parsed JSON is not an object");
|
|
296
|
+
}
|
|
297
|
+
return {
|
|
298
|
+
summary: asString(parsed.summary),
|
|
299
|
+
subjectSummary: asString(parsed.subjectSummary || parsed.changeSummary) || void 0,
|
|
300
|
+
strengths: parseConsensusItems(parsed.strengths),
|
|
301
|
+
improvements: parseConsensusItems(parsed.improvements),
|
|
302
|
+
risks: parseConsensusItems(parsed.risks),
|
|
303
|
+
openQuestions: parseConsensusItems(parsed.openQuestions),
|
|
304
|
+
roadmap: parseRoadmapPhases(parsed.roadmap),
|
|
305
|
+
nextActions: Array.isArray(parsed.nextActions) ? parsed.nextActions.map((a) => ({
|
|
306
|
+
title: a.title ?? "",
|
|
307
|
+
priority: validatePriority(a.priority),
|
|
308
|
+
rationale: a.rationale ?? "",
|
|
309
|
+
relatedFindings: Array.isArray(a.relatedFindings) ? a.relatedFindings : []
|
|
310
|
+
})) : []
|
|
311
|
+
};
|
|
312
|
+
} catch {
|
|
313
|
+
return {
|
|
314
|
+
summary: text,
|
|
315
|
+
strengths: [],
|
|
316
|
+
improvements: [],
|
|
317
|
+
risks: [],
|
|
318
|
+
openQuestions: [],
|
|
319
|
+
roadmap: [],
|
|
320
|
+
nextActions: []
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
function parseConsensusItems(arr) {
|
|
325
|
+
if (!Array.isArray(arr)) return [];
|
|
326
|
+
return arr.map((item) => ({
|
|
327
|
+
title: asString(item.title),
|
|
328
|
+
summary: item.summary ?? "",
|
|
329
|
+
support: Array.isArray(item.support) ? item.support : [],
|
|
330
|
+
challenge: Array.isArray(item.challenge) ? item.challenge : [],
|
|
331
|
+
confidence: typeof item.confidence === "number" ? item.confidence : 0.5,
|
|
332
|
+
evidenceRefs: Array.isArray(item.evidenceRefs) ? item.evidenceRefs : []
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
function parseRoadmapPhases(arr) {
|
|
336
|
+
if (!Array.isArray(arr)) return [];
|
|
337
|
+
return arr.map((phase) => ({
|
|
338
|
+
title: phase.title ?? "",
|
|
339
|
+
priority: validatePriority(phase.priority),
|
|
340
|
+
goal: phase.goal ?? phase.description ?? "",
|
|
341
|
+
scope: toStringArray(phase.scope),
|
|
342
|
+
deliverables: toStringArray(phase.deliverables),
|
|
343
|
+
dependencies: toStringArray(phase.dependencies),
|
|
344
|
+
risks: toStringArray(phase.risks),
|
|
345
|
+
successCriteria: toStringArray(phase.successCriteria),
|
|
346
|
+
relatedFindings: toStringArray(phase.relatedFindings)
|
|
347
|
+
}));
|
|
348
|
+
}
|
|
349
|
+
function validatePriority(value) {
|
|
350
|
+
if (value === "high" || value === "medium" || value === "low") return value;
|
|
351
|
+
return "medium";
|
|
352
|
+
}
|
|
353
|
+
function toStringArray(value) {
|
|
354
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
355
|
+
}
|
|
356
|
+
function formatClaimsForReview(claims) {
|
|
357
|
+
if (claims.length === 0) return "No claims to review.";
|
|
358
|
+
return claims.map((c) => {
|
|
359
|
+
const f = c.finding;
|
|
360
|
+
const evidence = c.evidenceRefs.length > 0 ? `
|
|
361
|
+
Evidence: ${c.evidenceRefs.join(", ")}` : "";
|
|
362
|
+
const packets = c.evidencePacketIds.length > 0 ? `
|
|
363
|
+
Evidence packets: ${c.evidencePacketIds.join(", ")}` : "";
|
|
364
|
+
return `- [${c.claimId}] (${f.kind}${f.severity ? `, ${f.severity}` : ""}, status: ${c.status})
|
|
365
|
+
${sanitizeForPrompt(f.title)}
|
|
366
|
+
${sanitizeForPrompt(f.summary)}` + evidence + packets + `
|
|
367
|
+
Confidence: ${f.confidence}`;
|
|
368
|
+
}).join("\n\n");
|
|
369
|
+
}
|
|
370
|
+
function formatDebateHistory(turns) {
|
|
371
|
+
if (turns.length === 0) return "No prior debate turns.";
|
|
372
|
+
return turns.map(
|
|
373
|
+
(t) => `[${t.participant}] stance: ${t.stance}
|
|
374
|
+
${t.summary}` + (t.newEvidenceRefs?.length ? `
|
|
375
|
+
New evidence: ${t.newEvidenceRefs.join(", ")}` : "")
|
|
376
|
+
).join("\n");
|
|
377
|
+
}
|
|
378
|
+
function formatClaimSummaryForConsensus(summary) {
|
|
379
|
+
const sections = [];
|
|
380
|
+
if (summary.verified.length > 0) {
|
|
381
|
+
sections.push(
|
|
382
|
+
`### Verified Claims (${summary.verified.length})
|
|
383
|
+
` + summary.verified.map(
|
|
384
|
+
(c) => `- [${c.claimId}] ${sanitizeForPrompt(c.finding.title)} (${c.finding.kind}, confidence: ${c.finding.confidence})`
|
|
385
|
+
).join("\n")
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
if (summary.unresolved.length > 0) {
|
|
389
|
+
sections.push(
|
|
390
|
+
`### Unresolved Claims (${summary.unresolved.length})
|
|
391
|
+
` + summary.unresolved.map(
|
|
392
|
+
(c) => `- [${c.claimId}] ${sanitizeForPrompt(c.finding.title)} \u2014 ${c.challenges.length} challenge(s), no consensus reached`
|
|
393
|
+
).join("\n")
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
if (summary.contested.length > 0) {
|
|
397
|
+
sections.push(
|
|
398
|
+
`### Still Contested (${summary.contested.length})
|
|
399
|
+
` + summary.contested.map(
|
|
400
|
+
(c) => `- [${c.claimId}] ${sanitizeForPrompt(c.finding.title)} \u2014 ${c.challenges.length} challenge(s)`
|
|
401
|
+
).join("\n")
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
if (summary.rejected.length > 0) {
|
|
405
|
+
sections.push(
|
|
406
|
+
`### Rejected Claims (${summary.rejected.length})
|
|
407
|
+
` + summary.rejected.map(
|
|
408
|
+
(c) => `- [${c.claimId}] ${sanitizeForPrompt(c.finding.title)} \u2014 rejected: ${sanitizeForPrompt(c.adjudication?.rationale ?? "no rationale")}`
|
|
409
|
+
).join("\n")
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
return sections.length > 0 ? sections.join("\n\n") : "No claims processed.";
|
|
413
|
+
}
|
|
414
|
+
function formatDigestForPrompt(digest) {
|
|
415
|
+
const sections = [];
|
|
416
|
+
sections.push(`Claims under review: ${digest.relevantClaimIds.join(", ")}`);
|
|
417
|
+
if (digest.evidencePackets.length > 0) {
|
|
418
|
+
sections.push("\nEvidence:");
|
|
419
|
+
for (const p of digest.evidencePackets) {
|
|
420
|
+
sections.push(` [${p.packetId}] ${sanitizeForPrompt(p.title)} (${p.source}): ${sanitizeForPrompt(p.summary)}`);
|
|
421
|
+
for (const e of p.excerpts.slice(0, 2)) {
|
|
422
|
+
sections.push(` > ${e.ref}: ${sanitizeForPrompt(e.snippet)}`);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
if (digest.recentChallenges.length > 0) {
|
|
427
|
+
sections.push("\nPrior challenges:");
|
|
428
|
+
for (const c of digest.recentChallenges) {
|
|
429
|
+
sections.push(` [${c.reviewer}] on ${c.claimId}: ${c.verdict} \u2014 ${sanitizeForPrompt(c.reason)}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return sections.join("\n");
|
|
433
|
+
}
|
|
434
|
+
function parseChallenges(reviewer, text) {
|
|
435
|
+
try {
|
|
436
|
+
const json = extractJSONArray(text);
|
|
437
|
+
const parsed = JSON.parse(json);
|
|
438
|
+
if (!Array.isArray(parsed)) {
|
|
439
|
+
const objJson = extractJSON(text);
|
|
440
|
+
const obj = JSON.parse(objJson);
|
|
441
|
+
if (Array.isArray(obj.challenges)) {
|
|
442
|
+
return obj.challenges.map((r) => parseChallenge(reviewer, r));
|
|
443
|
+
}
|
|
444
|
+
return [];
|
|
445
|
+
}
|
|
446
|
+
return parsed.map((r) => parseChallenge(reviewer, r));
|
|
447
|
+
} catch {
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
function parseChallenge(reviewer, raw) {
|
|
452
|
+
return {
|
|
453
|
+
reviewer,
|
|
454
|
+
claimId: asString(raw.claimId ?? raw.claim_id),
|
|
455
|
+
verdict: validateVerdict(raw.verdict),
|
|
456
|
+
reason: asString(raw.reason),
|
|
457
|
+
supportingEvidenceRefs: Array.isArray(raw.supportingEvidenceRefs) ? raw.supportingEvidenceRefs : void 0,
|
|
458
|
+
requestedChecks: Array.isArray(raw.requestedChecks) ? raw.requestedChecks.map((rc) => ({
|
|
459
|
+
requestId: rc.requestId ?? `req-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`,
|
|
460
|
+
claimId: raw.claimId ?? raw.claim_id ?? "",
|
|
461
|
+
requester: reviewer,
|
|
462
|
+
description: rc.description ?? "",
|
|
463
|
+
refs: Array.isArray(rc.refs) ? rc.refs : void 0,
|
|
464
|
+
priority: validateCheckPriority(rc.priority)
|
|
465
|
+
})) : void 0
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
function validateCheckPriority(v) {
|
|
469
|
+
if (v === "high" || v === "medium" || v === "low") return v;
|
|
470
|
+
return void 0;
|
|
471
|
+
}
|
|
472
|
+
function parseDebateTurn(participant, text) {
|
|
473
|
+
try {
|
|
474
|
+
const json = extractJSON(text);
|
|
475
|
+
const parsed = JSON.parse(json);
|
|
476
|
+
return {
|
|
477
|
+
participant,
|
|
478
|
+
stance: validateStance(parsed.stance),
|
|
479
|
+
summary: parsed.summary ?? parsed.argument ?? "",
|
|
480
|
+
newEvidenceRefs: Array.isArray(parsed.newEvidenceRefs) ? parsed.newEvidenceRefs : void 0
|
|
481
|
+
};
|
|
482
|
+
} catch {
|
|
483
|
+
return {
|
|
484
|
+
participant,
|
|
485
|
+
stance: "uncertain",
|
|
486
|
+
summary: text.slice(0, 1e3)
|
|
487
|
+
};
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
function validateStance(v) {
|
|
491
|
+
if (v === "support" || v === "oppose" || v === "narrow" || v === "uncertain") return v;
|
|
492
|
+
return "uncertain";
|
|
493
|
+
}
|
|
494
|
+
function parseAdjudication(claimId, text) {
|
|
495
|
+
try {
|
|
496
|
+
const json = extractJSON(text);
|
|
497
|
+
const parsed = JSON.parse(json);
|
|
498
|
+
return {
|
|
499
|
+
claimId,
|
|
500
|
+
outcome: validateAdjudicationOutcome(parsed.outcome),
|
|
501
|
+
rationale: asString(parsed.rationale),
|
|
502
|
+
finalSummary: asString(parsed.finalSummary ?? parsed.summary),
|
|
503
|
+
supportingEvidenceRefs: Array.isArray(parsed.supportingEvidenceRefs) ? parsed.supportingEvidenceRefs : []
|
|
504
|
+
};
|
|
505
|
+
} catch {
|
|
506
|
+
return {
|
|
507
|
+
claimId,
|
|
508
|
+
outcome: "unresolved",
|
|
509
|
+
rationale: "Failed to parse adjudication response",
|
|
510
|
+
finalSummary: text.slice(0, 500),
|
|
511
|
+
supportingEvidenceRefs: []
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
function validateAdjudicationOutcome(v) {
|
|
516
|
+
if (v === "accepted" || v === "accepted_with_revision" || v === "rejected" || v === "unresolved") return v;
|
|
517
|
+
return "unresolved";
|
|
518
|
+
}
|
|
519
|
+
function parseDetailExpansion(text) {
|
|
520
|
+
try {
|
|
521
|
+
const json = extractJSON(text);
|
|
522
|
+
const parsed = JSON.parse(json);
|
|
523
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
524
|
+
throw new Error("parsed JSON is not an object");
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
phaseTitle: asString(parsed.phaseTitle),
|
|
528
|
+
objective: asString(parsed.objective),
|
|
529
|
+
targetFiles: toStringArray(parsed.targetFiles),
|
|
530
|
+
codeChanges: toStringArray(parsed.codeChanges),
|
|
531
|
+
interfaces: toStringArray(parsed.interfaces),
|
|
532
|
+
migrationSteps: toStringArray(parsed.migrationSteps),
|
|
533
|
+
validation: toStringArray(parsed.validation),
|
|
534
|
+
effort: validateEffort(parsed.effort),
|
|
535
|
+
blockers: toStringArray(parsed.blockers),
|
|
536
|
+
evidenceRefs: toStringArray(parsed.evidenceRefs)
|
|
537
|
+
};
|
|
538
|
+
} catch {
|
|
539
|
+
return {
|
|
540
|
+
phaseTitle: "",
|
|
541
|
+
objective: text.slice(0, 500),
|
|
542
|
+
targetFiles: [],
|
|
543
|
+
codeChanges: [],
|
|
544
|
+
interfaces: [],
|
|
545
|
+
migrationSteps: [],
|
|
546
|
+
validation: [],
|
|
547
|
+
effort: "medium",
|
|
548
|
+
blockers: [],
|
|
549
|
+
evidenceRefs: []
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
function validateEffort(v) {
|
|
554
|
+
if (v === "small" || v === "medium" || v === "large") return v;
|
|
555
|
+
return "medium";
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// src/arena/strategies/review.ts
|
|
559
|
+
var ReviewStrategy = class {
|
|
560
|
+
// ─── Research Phase ──────────────────────────────────────────
|
|
561
|
+
researchSystemPrompt(name) {
|
|
562
|
+
return `You are ${name}, a reviewer in a multi-model review arena.
|
|
563
|
+
|
|
564
|
+
You may have access to read-only tools to fetch additional context. The base context is intentionally lean \u2014 use tools to inspect details as needed.
|
|
565
|
+
|
|
566
|
+
IMPORTANT RULES:
|
|
567
|
+
- Limit yourself to 3-5 tool rounds. Do NOT exhaustively read every file.
|
|
568
|
+
- Focus on the HIGHEST-IMPACT findings. Quality over quantity.
|
|
569
|
+
- Output as many findings as the topic warrants \u2014 typically 5-15 for a non-trivial subject. Each finding's "summary" should be 80+ words with concrete evidence and rationale, not a one-liner. Rank by confidence.
|
|
570
|
+
- Prioritize: risks > improvements > questions. Strengths are optional.
|
|
571
|
+
|
|
572
|
+
When ready, respond ONLY with JSON (no markdown fences):
|
|
573
|
+
{"contextSummary": "brief summary of what you investigated", "findings": [{"id": "unique-id", "kind": "risk|improvement|question|strength", "title": "short title", "summary": "detailed explanation", "severity": "high|medium|low", "confidence": 0.0-1.0, "evidence": [{"type": "file|diff|grep|git|doc", "ref": "path", "note": "what it shows"}], "affectedFiles": ["paths"], "suggestedChange": "optional"}]}`;
|
|
574
|
+
}
|
|
575
|
+
researchUserPrompt(topic, baseContext) {
|
|
576
|
+
return `## Review Topic
|
|
577
|
+
${topic}
|
|
578
|
+
|
|
579
|
+
${formatBaseContext(baseContext)}
|
|
580
|
+
|
|
581
|
+
Use tools to read specific files from the diff. Then output 3-6 highest-confidence findings as JSON.`;
|
|
582
|
+
}
|
|
583
|
+
parseResearchResponse(participant, text) {
|
|
584
|
+
return parseReport(participant, text);
|
|
585
|
+
}
|
|
586
|
+
// ─── Cross Review Phase ──────────────────────────────────────
|
|
587
|
+
crossReviewSystemPrompt(reviewerName) {
|
|
588
|
+
return `You are ${reviewerName}, reviewing other participants' review findings.
|
|
589
|
+
|
|
590
|
+
For each finding, provide a verdict:
|
|
591
|
+
- "agree": you confirm this finding
|
|
592
|
+
- "refine": mostly agree but with refinements
|
|
593
|
+
- "disagree": you believe this finding is incorrect
|
|
594
|
+
- "needs_evidence": the finding lacks sufficient evidence
|
|
595
|
+
|
|
596
|
+
Focus on HIGH VALUE findings: high severity, high risk, or conflicting conclusions.
|
|
597
|
+
You don't need to review every single finding \u2014 prioritize the important ones.
|
|
598
|
+
|
|
599
|
+
Respond ONLY with a JSON array (no markdown fences):
|
|
600
|
+
[{"findingId": "...", "verdict": "agree|refine|disagree|needs_evidence", "reason": "...", "extraEvidence": ["optional"]}]`;
|
|
601
|
+
}
|
|
602
|
+
crossReviewUserPrompt(topic, myReport, otherReports) {
|
|
603
|
+
return `## Topic: ${topic}
|
|
604
|
+
|
|
605
|
+
## Your Findings
|
|
606
|
+
${formatReports([myReport])}
|
|
607
|
+
|
|
608
|
+
## Other Reviewers' Findings
|
|
609
|
+
${formatReports(otherReports)}
|
|
610
|
+
|
|
611
|
+
Review the other participants' findings. Focus on high-priority items.`;
|
|
612
|
+
}
|
|
613
|
+
parseCrossReviewResponse(reviewer, text) {
|
|
614
|
+
return parseReviews(reviewer, text);
|
|
615
|
+
}
|
|
616
|
+
// ─── Consensus Phase ─────────────────────────────────────────
|
|
617
|
+
consensusSystemPrompt() {
|
|
618
|
+
return `You are a neutral moderator synthesizing a multi-model review into a structured consensus.
|
|
619
|
+
|
|
620
|
+
You MUST faithfully reflect the aggregated findings and peer reviews. Do NOT add new conclusions that have no source in the findings.
|
|
621
|
+
|
|
622
|
+
IMPORTANT: Start with a "subjectSummary" \u2014 a factual overview of WHAT is being reviewed (scope, key areas, high-level description). This comes BEFORE any judgment.
|
|
623
|
+
|
|
624
|
+
Respond ONLY with JSON (no markdown fences):
|
|
625
|
+
{
|
|
626
|
+
"summary": "one-paragraph overall assessment",
|
|
627
|
+
"subjectSummary": "factual overview of what is being reviewed \u2014 scope, key areas, subject description",
|
|
628
|
+
"strengths": [{"title": "...", "summary": "...", "support": ["participant names"], "challenge": [], "confidence": 0.0-1.0, "evidenceRefs": ["finding IDs"]}],
|
|
629
|
+
"improvements": [same structure],
|
|
630
|
+
"risks": [same structure],
|
|
631
|
+
"openQuestions": [same structure],
|
|
632
|
+
"nextActions": [{"title": "...", "priority": "high|medium|low", "rationale": "...", "relatedFindings": ["finding IDs"]}]
|
|
633
|
+
}`;
|
|
634
|
+
}
|
|
635
|
+
consensusUserPrompt(topic, reports, reviews) {
|
|
636
|
+
return `## Topic
|
|
637
|
+
${topic}
|
|
638
|
+
|
|
639
|
+
## Participant Reports
|
|
640
|
+
${formatReports(reports)}
|
|
641
|
+
|
|
642
|
+
## Peer Reviews
|
|
643
|
+
${formatFindingReviews(reviews)}
|
|
644
|
+
|
|
645
|
+
Synthesize a structured consensus. Group findings by category, note agreement/disagreement, and propose next actions.`;
|
|
646
|
+
}
|
|
647
|
+
parseConsensusResponse(text) {
|
|
648
|
+
return parseConsensus(text);
|
|
649
|
+
}
|
|
650
|
+
preferredFindingKinds() {
|
|
651
|
+
return ["strength", "improvement", "risk", "question"];
|
|
652
|
+
}
|
|
653
|
+
// ─── V2: Verification Review ────────────────────────────────────
|
|
654
|
+
verificationReviewUserPrompt(topic, myReport, claimsToReview, digest) {
|
|
655
|
+
return `## Topic: ${topic}
|
|
656
|
+
|
|
657
|
+
## Your Findings
|
|
658
|
+
${formatReports([myReport])}
|
|
659
|
+
|
|
660
|
+
## Claims to Verify
|
|
661
|
+
${formatClaimsForReview(claimsToReview)}
|
|
662
|
+
|
|
663
|
+
## Evidence Digest
|
|
664
|
+
${formatDigestForPrompt(digest)}
|
|
665
|
+
|
|
666
|
+
Verify each claim against the evidence. For claims lacking evidence, you may request specific checks.
|
|
667
|
+
Respond ONLY with a JSON array:
|
|
668
|
+
[{"claimId": "...", "verdict": "agree|refine|disagree|needs_evidence", "reason": "...", "supportingEvidenceRefs": ["optional"], "requestedChecks": [{"description": "what to check", "priority": "high|medium|low"}]}]`;
|
|
669
|
+
}
|
|
670
|
+
parseVerificationReviewResponse(reviewer, text) {
|
|
671
|
+
return parseChallenges(reviewer, text);
|
|
672
|
+
}
|
|
673
|
+
// ─── V2: Debate ─────────────────────────────────────────────────
|
|
674
|
+
debateTurnUserPrompt(topic, claim, priorTurns, digest) {
|
|
675
|
+
return `## Topic: ${topic}
|
|
676
|
+
|
|
677
|
+
## Contested Claim
|
|
678
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
679
|
+
${claim.finding.summary}
|
|
680
|
+
Evidence: ${claim.evidenceRefs.join(", ") || "none"}
|
|
681
|
+
|
|
682
|
+
## Prior Debate
|
|
683
|
+
${formatDebateHistory(priorTurns)}
|
|
684
|
+
|
|
685
|
+
## Evidence Digest
|
|
686
|
+
${formatDigestForPrompt(digest)}
|
|
687
|
+
|
|
688
|
+
State your position on this claim. Build on prior turns: cite specific evidence, acknowledge counter-arguments, and explain your reasoning in 150-300 words. Brevity here means missed nuance \u2014 depth wins.
|
|
689
|
+
Respond ONLY with JSON:
|
|
690
|
+
{"stance": "support|oppose|narrow|uncertain", "summary": "your argument", "newEvidenceRefs": ["optional"]}`;
|
|
691
|
+
}
|
|
692
|
+
parseDebateTurnResponse(participant, text) {
|
|
693
|
+
return parseDebateTurn(participant, text);
|
|
694
|
+
}
|
|
695
|
+
// ─── V2: Adjudication ──────────────────────────────────────────
|
|
696
|
+
adjudicationUserPrompt(topic, claim, debateRounds, digest) {
|
|
697
|
+
const debateSummary = debateRounds.length > 0 ? debateRounds.map(
|
|
698
|
+
(r) => `Round ${r.round}:
|
|
699
|
+
${formatDebateHistory(r.participants)}
|
|
700
|
+
${r.resolved ? "\u2192 Resolved" : "\u2192 Unresolved"}`
|
|
701
|
+
).join("\n\n") : "No debate rounds occurred.";
|
|
702
|
+
return `## Topic: ${topic}
|
|
703
|
+
|
|
704
|
+
## Claim Under Adjudication
|
|
705
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
706
|
+
${claim.finding.summary}
|
|
707
|
+
Owner: ${claim.owner} | Status: ${claim.status}
|
|
708
|
+
Evidence: ${claim.evidenceRefs.join(", ") || "none"}
|
|
709
|
+
|
|
710
|
+
## Challenges
|
|
711
|
+
` + claim.challenges.map((c) => `[${c.reviewer}] ${c.verdict}: ${c.reason}`).join("\n") + `
|
|
712
|
+
|
|
713
|
+
## Debate
|
|
714
|
+
${debateSummary}
|
|
715
|
+
|
|
716
|
+
## Evidence Digest
|
|
717
|
+
${formatDigestForPrompt(digest)}
|
|
718
|
+
|
|
719
|
+
As moderator, adjudicate this claim based on all available evidence.
|
|
720
|
+
Respond ONLY with JSON:
|
|
721
|
+
{"outcome": "accepted|accepted_with_revision|rejected|unresolved", "rationale": "...", "finalSummary": "revised claim summary if needed", "supportingEvidenceRefs": ["..."]}`;
|
|
722
|
+
}
|
|
723
|
+
parseAdjudicationResponse(text) {
|
|
724
|
+
return parseAdjudication("", text);
|
|
725
|
+
}
|
|
726
|
+
// ─── V2: Claim-Aware Consensus ─────────────────────────────────
|
|
727
|
+
claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary) {
|
|
728
|
+
return `## Topic
|
|
729
|
+
${topic}
|
|
730
|
+
|
|
731
|
+
## Claim Verification Status
|
|
732
|
+
${formatClaimSummaryForConsensus(claimSummary)}
|
|
733
|
+
|
|
734
|
+
## Participant Reports
|
|
735
|
+
${formatReports(reports)}
|
|
736
|
+
|
|
737
|
+
## Peer Reviews
|
|
738
|
+
${formatFindingReviews(reviews)}
|
|
739
|
+
|
|
740
|
+
Synthesize a structured consensus. Verified claims should appear as high-confidence items. Unresolved claims should appear as open questions. Rejected claims should be excluded or noted as dismissed.`;
|
|
741
|
+
}
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
// src/arena/strategies/discussion.ts
|
|
745
|
+
init_esm_shims();
|
|
746
|
+
var DiscussionStrategy = class {
|
|
747
|
+
researchSystemPrompt(name) {
|
|
748
|
+
return `You are ${name}, participating in a multi-model discussion arena.
|
|
749
|
+
|
|
750
|
+
You may have access to read-only tools to gather evidence. The base context is intentionally lean \u2014 use tools as needed.
|
|
751
|
+
|
|
752
|
+
IMPORTANT RULES:
|
|
753
|
+
- Limit yourself to 3-5 tool rounds. Do NOT exhaustively read every file.
|
|
754
|
+
- Output as many findings as the topic warrants \u2014 typically 5-15 for a non-trivial subject. Each finding's "summary" should be 80+ words with concrete evidence and rationale, not a one-liner. Rank by confidence.
|
|
755
|
+
- Focus on trade-offs, risks, and open questions. Strengths are optional.
|
|
756
|
+
|
|
757
|
+
Respond ONLY with JSON (no markdown fences):
|
|
758
|
+
{"contextSummary": "what you investigated and your overall take", "findings": [{"id": "unique-id", "kind": "risk|improvement|question|strength", "title": "short title", "summary": "detailed analysis with evidence", "severity": "high|medium|low", "confidence": 0.0-1.0, "evidence": [{"type": "file|diff|grep|git|doc", "ref": "path", "note": "what it shows"}], "affectedFiles": ["paths"], "suggestedChange": "optional"}]}`;
|
|
759
|
+
}
|
|
760
|
+
researchUserPrompt(topic, baseContext) {
|
|
761
|
+
return `## Discussion Topic
|
|
762
|
+
${topic}
|
|
763
|
+
|
|
764
|
+
${formatBaseContext(baseContext)}
|
|
765
|
+
|
|
766
|
+
Use tools to investigate, then output 3-6 highest-confidence findings as JSON.`;
|
|
767
|
+
}
|
|
768
|
+
parseResearchResponse(participant, text) {
|
|
769
|
+
return parseReport(participant, text);
|
|
770
|
+
}
|
|
771
|
+
crossReviewSystemPrompt(reviewerName) {
|
|
772
|
+
return `You are ${reviewerName}, reviewing other participants' discussion findings.
|
|
773
|
+
|
|
774
|
+
Engage thoughtfully: challenge weak arguments, acknowledge strong ones.
|
|
775
|
+
Focus on finding the best answer, not winning.
|
|
776
|
+
|
|
777
|
+
For each finding you want to address, provide a verdict:
|
|
778
|
+
- "agree": you confirm this perspective
|
|
779
|
+
- "refine": mostly agree but with nuances
|
|
780
|
+
- "disagree": you have a different view with evidence
|
|
781
|
+
- "needs_evidence": the claim lacks supporting evidence
|
|
782
|
+
|
|
783
|
+
Respond ONLY with a JSON array:
|
|
784
|
+
[{"findingId": "...", "verdict": "...", "reason": "...", "extraEvidence": ["optional"]}]`;
|
|
785
|
+
}
|
|
786
|
+
crossReviewUserPrompt(topic, myReport, otherReports) {
|
|
787
|
+
return `## Topic: ${topic}
|
|
788
|
+
|
|
789
|
+
## Your Analysis
|
|
790
|
+
${formatReports([myReport])}
|
|
791
|
+
|
|
792
|
+
## Other Participants' Analysis
|
|
793
|
+
${formatReports(otherReports)}
|
|
794
|
+
|
|
795
|
+
Review the others' findings. Where do you agree or disagree? What nuances are missing?`;
|
|
796
|
+
}
|
|
797
|
+
parseCrossReviewResponse(reviewer, text) {
|
|
798
|
+
return parseReviews(reviewer, text);
|
|
799
|
+
}
|
|
800
|
+
consensusSystemPrompt() {
|
|
801
|
+
return `You are a neutral moderator synthesizing a multi-model discussion into a balanced conclusion.
|
|
802
|
+
|
|
803
|
+
Capture key insights, agreements, and remaining disagreements. Do NOT suppress minority viewpoints \u2014 preserve them as open questions.
|
|
804
|
+
|
|
805
|
+
IMPORTANT: Start with a "subjectSummary" \u2014 a factual framing of the topic, current scope, or assumptions under discussion before moving into conclusions.
|
|
806
|
+
|
|
807
|
+
Respond ONLY with JSON (no markdown fences):
|
|
808
|
+
{
|
|
809
|
+
"summary": "balanced synthesis",
|
|
810
|
+
"subjectSummary": "factual problem framing or current scope of the discussion",
|
|
811
|
+
"strengths": [{"title": "...", "summary": "...", "support": [], "challenge": [], "confidence": 0.0-1.0, "evidenceRefs": []}],
|
|
812
|
+
"improvements": [same structure],
|
|
813
|
+
"risks": [same structure],
|
|
814
|
+
"openQuestions": [same structure \u2014 include still-debated points],
|
|
815
|
+
"nextActions": [{"title": "...", "priority": "high|medium|low", "rationale": "...", "relatedFindings": []}]
|
|
816
|
+
}`;
|
|
817
|
+
}
|
|
818
|
+
consensusUserPrompt(topic, reports, reviews) {
|
|
819
|
+
return `## Topic
|
|
820
|
+
${topic}
|
|
821
|
+
|
|
822
|
+
## Participant Analyses
|
|
823
|
+
${formatReports(reports)}
|
|
824
|
+
|
|
825
|
+
## Peer Reviews
|
|
826
|
+
${formatFindingReviews(reviews)}
|
|
827
|
+
|
|
828
|
+
Synthesize a balanced conclusion. Start with a factual subjectSummary, then preserve disagreements as open questions.`;
|
|
829
|
+
}
|
|
830
|
+
parseConsensusResponse(text) {
|
|
831
|
+
return parseConsensus(text);
|
|
832
|
+
}
|
|
833
|
+
preferredFindingKinds() {
|
|
834
|
+
return ["strength", "risk", "question"];
|
|
835
|
+
}
|
|
836
|
+
// ─── V2: Verification Review ────────────────────────────────────
|
|
837
|
+
verificationReviewUserPrompt(topic, myReport, claimsToReview, digest) {
|
|
838
|
+
return `## Topic: ${topic}
|
|
839
|
+
|
|
840
|
+
## Your Analysis
|
|
841
|
+
${formatReports([myReport])}
|
|
842
|
+
|
|
843
|
+
## Claims to Verify
|
|
844
|
+
${formatClaimsForReview(claimsToReview)}
|
|
845
|
+
|
|
846
|
+
## Evidence Digest
|
|
847
|
+
${formatDigestForPrompt(digest)}
|
|
848
|
+
|
|
849
|
+
Engage critically with each claim. Challenge weak arguments, acknowledge strong ones. Focus on finding the best answer, not winning.
|
|
850
|
+
Respond ONLY with a JSON array:
|
|
851
|
+
[{"claimId": "...", "verdict": "agree|refine|disagree|needs_evidence", "reason": "...", "supportingEvidenceRefs": ["optional"], "requestedChecks": [{"description": "what to check", "priority": "high|medium|low"}]}]`;
|
|
852
|
+
}
|
|
853
|
+
parseVerificationReviewResponse(reviewer, text) {
|
|
854
|
+
return parseChallenges(reviewer, text);
|
|
855
|
+
}
|
|
856
|
+
// ─── V2: Debate ─────────────────────────────────────────────────
|
|
857
|
+
debateTurnUserPrompt(topic, claim, priorTurns, digest) {
|
|
858
|
+
return `## Topic: ${topic}
|
|
859
|
+
|
|
860
|
+
## Contested Claim
|
|
861
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
862
|
+
${claim.finding.summary}
|
|
863
|
+
|
|
864
|
+
## Prior Debate
|
|
865
|
+
${formatDebateHistory(priorTurns)}
|
|
866
|
+
|
|
867
|
+
## Evidence Digest
|
|
868
|
+
${formatDigestForPrompt(digest)}
|
|
869
|
+
|
|
870
|
+
Engage thoughtfully. If the evidence supports the claim, narrow toward agreement. If not, explain why with evidence.
|
|
871
|
+
Respond ONLY with JSON:
|
|
872
|
+
{"stance": "support|oppose|narrow|uncertain", "summary": "your argument", "newEvidenceRefs": ["optional"]}`;
|
|
873
|
+
}
|
|
874
|
+
parseDebateTurnResponse(participant, text) {
|
|
875
|
+
return parseDebateTurn(participant, text);
|
|
876
|
+
}
|
|
877
|
+
// ─── V2: Adjudication ──────────────────────────────────────────
|
|
878
|
+
adjudicationUserPrompt(topic, claim, debateRounds, digest) {
|
|
879
|
+
const debateSummary = debateRounds.length > 0 ? debateRounds.map(
|
|
880
|
+
(r) => `Round ${r.round}:
|
|
881
|
+
${formatDebateHistory(r.participants)}
|
|
882
|
+
${r.resolved ? "\u2192 Resolved" : "\u2192 Unresolved"}`
|
|
883
|
+
).join("\n\n") : "No debate rounds occurred.";
|
|
884
|
+
return `## Topic: ${topic}
|
|
885
|
+
|
|
886
|
+
## Contested Claim
|
|
887
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
888
|
+
${claim.finding.summary}
|
|
889
|
+
Owner: ${claim.owner}
|
|
890
|
+
|
|
891
|
+
## Challenges
|
|
892
|
+
` + claim.challenges.map((c) => `[${c.reviewer}] ${c.verdict}: ${c.reason}`).join("\n") + `
|
|
893
|
+
|
|
894
|
+
## Debate
|
|
895
|
+
${debateSummary}
|
|
896
|
+
|
|
897
|
+
## Evidence Digest
|
|
898
|
+
${formatDigestForPrompt(digest)}
|
|
899
|
+
|
|
900
|
+
As moderator, synthesize the debate. Preserve minority viewpoints where the evidence is genuinely ambiguous.
|
|
901
|
+
Respond ONLY with JSON:
|
|
902
|
+
{"outcome": "accepted|accepted_with_revision|rejected|unresolved", "rationale": "...", "finalSummary": "revised claim summary", "supportingEvidenceRefs": ["..."]}`;
|
|
903
|
+
}
|
|
904
|
+
parseAdjudicationResponse(text) {
|
|
905
|
+
return parseAdjudication("", text);
|
|
906
|
+
}
|
|
907
|
+
// ─── V2: Claim-Aware Consensus ─────────────────────────────────
|
|
908
|
+
claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary) {
|
|
909
|
+
return `## Topic
|
|
910
|
+
${topic}
|
|
911
|
+
|
|
912
|
+
## Claim Verification Status
|
|
913
|
+
${formatClaimSummaryForConsensus(claimSummary)}
|
|
914
|
+
|
|
915
|
+
## Participant Analyses
|
|
916
|
+
${formatReports(reports)}
|
|
917
|
+
|
|
918
|
+
## Peer Reviews
|
|
919
|
+
${formatFindingReviews(reviews)}
|
|
920
|
+
|
|
921
|
+
Synthesize a balanced conclusion. Verified claims are high-confidence. Unresolved claims should be preserved as open questions \u2014 do NOT suppress minority viewpoints.`;
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
|
|
925
|
+
// src/arena/strategies/planning.ts
|
|
926
|
+
init_esm_shims();
|
|
927
|
+
var PlanningStrategy = class {
|
|
928
|
+
researchSystemPrompt(name) {
|
|
929
|
+
return `You are ${name}, a planner in a multi-model planning session.
|
|
930
|
+
|
|
931
|
+
You may have access to read-only tools to investigate the subject. The base context is intentionally lean \u2014 use tools as needed.
|
|
932
|
+
|
|
933
|
+
IMPORTANT RULES:
|
|
934
|
+
- Limit yourself to 3-5 tool rounds. Do NOT exhaustively read every file.
|
|
935
|
+
- Output as many findings as the topic warrants \u2014 typically 5-15 for a non-trivial subject. Each finding's "summary" should be 80+ words with concrete evidence and rationale, not a one-liner. Rank by confidence.
|
|
936
|
+
- Focus on: implementation phases (improvement), risks/blockers, decisions needed (question).
|
|
937
|
+
- Strengths are optional.
|
|
938
|
+
|
|
939
|
+
Respond ONLY with JSON (no markdown fences):
|
|
940
|
+
{"contextSummary": "what you investigated and your overall approach", "findings": [{"id": "unique-id", "kind": "improvement|risk|question|strength", "title": "short title", "summary": "detailed analysis", "severity": "high|medium|low", "confidence": 0.0-1.0, "evidence": [{"type": "file|diff|grep|git|doc", "ref": "path", "note": "what it shows"}], "affectedFiles": ["paths"], "suggestedChange": "optional"}]}`;
|
|
941
|
+
}
|
|
942
|
+
researchUserPrompt(topic, baseContext) {
|
|
943
|
+
return `## Planning Topic
|
|
944
|
+
${topic}
|
|
945
|
+
|
|
946
|
+
${formatBaseContext(baseContext)}
|
|
947
|
+
|
|
948
|
+
Use tools to investigate, then output 3-6 highest-confidence findings as JSON.`;
|
|
949
|
+
}
|
|
950
|
+
parseResearchResponse(participant, text) {
|
|
951
|
+
return parseReport(participant, text);
|
|
952
|
+
}
|
|
953
|
+
crossReviewSystemPrompt(reviewerName) {
|
|
954
|
+
return `You are ${reviewerName}, reviewing other architects' planning proposals.
|
|
955
|
+
|
|
956
|
+
Evaluate critically: identify gaps, suggest improvements, propose better alternatives.
|
|
957
|
+
|
|
958
|
+
For each finding you want to address:
|
|
959
|
+
- "agree": the proposed phase/risk is valid
|
|
960
|
+
- "refine": adjust scope, priority, or dependencies
|
|
961
|
+
- "disagree": propose a different approach
|
|
962
|
+
- "needs_evidence": more investigation needed
|
|
963
|
+
|
|
964
|
+
Respond ONLY with a JSON array:
|
|
965
|
+
[{"findingId": "...", "verdict": "...", "reason": "...", "extraEvidence": ["optional"]}]`;
|
|
966
|
+
}
|
|
967
|
+
crossReviewUserPrompt(topic, myReport, otherReports) {
|
|
968
|
+
return `## Topic: ${topic}
|
|
969
|
+
|
|
970
|
+
## Your Proposed Plan
|
|
971
|
+
${formatReports([myReport])}
|
|
972
|
+
|
|
973
|
+
## Other Architects' Plans
|
|
974
|
+
${formatReports(otherReports)}
|
|
975
|
+
|
|
976
|
+
Review the other proposals. What are the strengths and gaps? Can elements be combined?`;
|
|
977
|
+
}
|
|
978
|
+
parseCrossReviewResponse(reviewer, text) {
|
|
979
|
+
return parseReviews(reviewer, text);
|
|
980
|
+
}
|
|
981
|
+
consensusSystemPrompt() {
|
|
982
|
+
return `You are a neutral moderator synthesizing a multi-model planning session into a concrete roadmap.
|
|
983
|
+
|
|
984
|
+
Combine the best ideas into a unified plan with clear sequencing, priorities, and delivery phases.
|
|
985
|
+
IMPORTANT: the roadmap field is the primary output. Use it to describe 3 to 6 implementation phases. The nextActions field is only for the immediate 3 to 5 actions that should happen next.
|
|
986
|
+
|
|
987
|
+
Respond ONLY with JSON (no markdown fences):
|
|
988
|
+
{
|
|
989
|
+
"summary": "unified plan overview",
|
|
990
|
+
"subjectSummary": "factual overview of the current repo/problem scope before proposing changes",
|
|
991
|
+
"strengths": [{"title": "...", "summary": "...", "support": [], "challenge": [], "confidence": 0.0-1.0, "evidenceRefs": []}],
|
|
992
|
+
"improvements": [implementation phases as consensus items],
|
|
993
|
+
"risks": [identified risks],
|
|
994
|
+
"openQuestions": [decisions needed],
|
|
995
|
+
"roadmap": [{"title": "...", "priority": "high|medium|low", "goal": "...", "scope": ["..."], "deliverables": ["..."], "dependencies": ["..."], "risks": ["..."], "successCriteria": ["..."], "relatedFindings": []}],
|
|
996
|
+
"nextActions": [{"title": "...", "priority": "high|medium|low", "rationale": "...", "relatedFindings": []}]
|
|
997
|
+
}`;
|
|
998
|
+
}
|
|
999
|
+
consensusUserPrompt(topic, reports, reviews) {
|
|
1000
|
+
return `## Topic
|
|
1001
|
+
${topic}
|
|
1002
|
+
|
|
1003
|
+
## Proposed Plans
|
|
1004
|
+
${formatReports(reports)}
|
|
1005
|
+
|
|
1006
|
+
## Peer Reviews
|
|
1007
|
+
${formatFindingReviews(reviews)}
|
|
1008
|
+
|
|
1009
|
+
Synthesize a final implementation roadmap.
|
|
1010
|
+
Requirements:
|
|
1011
|
+
- Start with a factual subjectSummary of the current scope.
|
|
1012
|
+
- Produce 3 to 6 roadmap phases covering sequencing, dependencies, deliverables, and success criteria.
|
|
1013
|
+
- Use nextActions only for the immediate follow-up work, not the entire roadmap.`;
|
|
1014
|
+
}
|
|
1015
|
+
parseConsensusResponse(text) {
|
|
1016
|
+
return parseConsensus(text);
|
|
1017
|
+
}
|
|
1018
|
+
preferredFindingKinds() {
|
|
1019
|
+
return ["improvement", "risk", "question"];
|
|
1020
|
+
}
|
|
1021
|
+
// ─── V2: Verification Review ────────────────────────────────────
|
|
1022
|
+
verificationReviewUserPrompt(topic, myReport, claimsToReview, digest) {
|
|
1023
|
+
return `## Topic: ${topic}
|
|
1024
|
+
|
|
1025
|
+
## Your Proposed Plan
|
|
1026
|
+
${formatReports([myReport])}
|
|
1027
|
+
|
|
1028
|
+
## Claims to Verify
|
|
1029
|
+
${formatClaimsForReview(claimsToReview)}
|
|
1030
|
+
|
|
1031
|
+
## Evidence Digest
|
|
1032
|
+
${formatDigestForPrompt(digest)}
|
|
1033
|
+
|
|
1034
|
+
Evaluate each claim for feasibility, sequencing, and dependencies. Challenge proposals that lack evidence.
|
|
1035
|
+
Respond ONLY with a JSON array:
|
|
1036
|
+
[{"claimId": "...", "verdict": "agree|refine|disagree|needs_evidence", "reason": "...", "supportingEvidenceRefs": ["optional"], "requestedChecks": [{"description": "what to check", "priority": "high|medium|low"}]}]`;
|
|
1037
|
+
}
|
|
1038
|
+
parseVerificationReviewResponse(reviewer, text) {
|
|
1039
|
+
return parseChallenges(reviewer, text);
|
|
1040
|
+
}
|
|
1041
|
+
// ─── V2: Debate ─────────────────────────────────────────────────
|
|
1042
|
+
debateTurnUserPrompt(topic, claim, priorTurns, digest) {
|
|
1043
|
+
return `## Topic: ${topic}
|
|
1044
|
+
|
|
1045
|
+
## Contested Planning Proposal
|
|
1046
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
1047
|
+
${claim.finding.summary}
|
|
1048
|
+
|
|
1049
|
+
## Prior Debate
|
|
1050
|
+
${formatDebateHistory(priorTurns)}
|
|
1051
|
+
|
|
1052
|
+
## Evidence Digest
|
|
1053
|
+
${formatDigestForPrompt(digest)}
|
|
1054
|
+
|
|
1055
|
+
Argue for or against this proposal. Focus on feasibility, risks, and better alternatives.
|
|
1056
|
+
Respond ONLY with JSON:
|
|
1057
|
+
{"stance": "support|oppose|narrow|uncertain", "summary": "your argument", "newEvidenceRefs": ["optional"]}`;
|
|
1058
|
+
}
|
|
1059
|
+
parseDebateTurnResponse(participant, text) {
|
|
1060
|
+
return parseDebateTurn(participant, text);
|
|
1061
|
+
}
|
|
1062
|
+
// ─── V2: Adjudication ──────────────────────────────────────────
|
|
1063
|
+
adjudicationUserPrompt(topic, claim, debateRounds, digest) {
|
|
1064
|
+
const debateSummary = debateRounds.length > 0 ? debateRounds.map(
|
|
1065
|
+
(r) => `Round ${r.round}:
|
|
1066
|
+
${formatDebateHistory(r.participants)}
|
|
1067
|
+
${r.resolved ? "\u2192 Resolved" : "\u2192 Unresolved"}`
|
|
1068
|
+
).join("\n\n") : "No debate rounds occurred.";
|
|
1069
|
+
return `## Topic: ${topic}
|
|
1070
|
+
|
|
1071
|
+
## Contested Proposal
|
|
1072
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
1073
|
+
${claim.finding.summary}
|
|
1074
|
+
Owner: ${claim.owner}
|
|
1075
|
+
|
|
1076
|
+
## Challenges
|
|
1077
|
+
` + claim.challenges.map((c) => `[${c.reviewer}] ${c.verdict}: ${c.reason}`).join("\n") + `
|
|
1078
|
+
|
|
1079
|
+
## Debate
|
|
1080
|
+
${debateSummary}
|
|
1081
|
+
|
|
1082
|
+
## Evidence Digest
|
|
1083
|
+
${formatDigestForPrompt(digest)}
|
|
1084
|
+
|
|
1085
|
+
As moderator, decide whether this proposal should be included in the final plan.
|
|
1086
|
+
Respond ONLY with JSON:
|
|
1087
|
+
{"outcome": "accepted|accepted_with_revision|rejected|unresolved", "rationale": "...", "finalSummary": "revised proposal if needed", "supportingEvidenceRefs": ["..."]}`;
|
|
1088
|
+
}
|
|
1089
|
+
parseAdjudicationResponse(text) {
|
|
1090
|
+
return parseAdjudication("", text);
|
|
1091
|
+
}
|
|
1092
|
+
// ─── Planning: Merge-Oriented Review ────────────────────────────
|
|
1093
|
+
mergeReviewUserPrompt(topic, myReport, claimsToReview, digest) {
|
|
1094
|
+
return `## Topic: ${topic}
|
|
1095
|
+
|
|
1096
|
+
## Your Proposed Plan
|
|
1097
|
+
${formatReports([myReport])}
|
|
1098
|
+
|
|
1099
|
+
## Candidate Claims (Proposed Phases & Findings)
|
|
1100
|
+
${formatClaimsForReview(claimsToReview)}
|
|
1101
|
+
|
|
1102
|
+
## Evidence Digest
|
|
1103
|
+
${formatDigestForPrompt(digest)}
|
|
1104
|
+
|
|
1105
|
+
You are reviewing planning proposals for MERGE and CONVERGENCE, not for correctness judging.
|
|
1106
|
+
|
|
1107
|
+
For each claim, decide how it should be incorporated into the final roadmap:
|
|
1108
|
+
- "agree": the proposed phase/finding is valid as-is
|
|
1109
|
+
- "refine": merge with another phase, adjust priority/scope/dependencies, or narrow scope
|
|
1110
|
+
- "disagree": only for proposals that directly contradict repo evidence
|
|
1111
|
+
- "needs_evidence": mark as open question for further investigation
|
|
1112
|
+
|
|
1113
|
+
In your reason, use these semantic tags where applicable:
|
|
1114
|
+
[merge] \u2014 combine with another phase
|
|
1115
|
+
[reprioritize] \u2014 change priority ordering
|
|
1116
|
+
[split_phase] \u2014 break into smaller phases
|
|
1117
|
+
[combine_phase] \u2014 merge multiple phases into one
|
|
1118
|
+
[dependency_risk] \u2014 flag missing dependency
|
|
1119
|
+
[needs_detail] \u2014 needs more implementation specifics
|
|
1120
|
+
[open_question] \u2014 convert to open question
|
|
1121
|
+
|
|
1122
|
+
Respond ONLY with a JSON array:
|
|
1123
|
+
[{"claimId": "...", "verdict": "agree|refine|disagree|needs_evidence", "reason": "[tag] explanation", "supportingEvidenceRefs": ["optional"], "requestedChecks": [{"description": "what to check", "priority": "high|medium|low"}]}]`;
|
|
1124
|
+
}
|
|
1125
|
+
parseMergeReviewResponse(reviewer, text) {
|
|
1126
|
+
return parseChallenges(reviewer, text);
|
|
1127
|
+
}
|
|
1128
|
+
// ─── Planning: Detail Expansion ────────────────────────────────
|
|
1129
|
+
detailExpansionSystemPrompt() {
|
|
1130
|
+
return `You are a senior architect expanding a high-level roadmap phase into a concrete, repo-level implementation plan.
|
|
1131
|
+
|
|
1132
|
+
You may have access to read-only tools to investigate the codebase. Use them to verify file paths, interfaces, and module boundaries.
|
|
1133
|
+
|
|
1134
|
+
IMPORTANT RULES:
|
|
1135
|
+
- Be specific: name actual files, modules, interfaces, and functions.
|
|
1136
|
+
- If you're uncertain about a target, write "likely: <path>" and note the uncertainty.
|
|
1137
|
+
- Do not produce vague platitudes like "refactor the module" \u2014 say what changes.
|
|
1138
|
+
- Limit tool usage to 2-3 rounds for verification.
|
|
1139
|
+
|
|
1140
|
+
Respond ONLY with JSON (no markdown fences):
|
|
1141
|
+
{"phaseTitle": "...", "objective": "...", "targetFiles": ["..."], "codeChanges": ["specific change descriptions"], "interfaces": ["new or modified interfaces"], "migrationSteps": ["ordered steps"], "validation": ["how to verify"], "effort": "small|medium|large", "blockers": ["blocking issues"], "evidenceRefs": ["refs to supporting evidence"]}`;
|
|
1142
|
+
}
|
|
1143
|
+
detailExpansionUserPrompt(topic, phase, digest) {
|
|
1144
|
+
const phaseContext = [
|
|
1145
|
+
`Title: ${phase.title}`,
|
|
1146
|
+
`Priority: ${phase.priority}`,
|
|
1147
|
+
`Goal: ${phase.goal}`,
|
|
1148
|
+
phase.scope.length > 0 ? `Scope: ${phase.scope.join("; ")}` : "",
|
|
1149
|
+
phase.deliverables.length > 0 ? `Deliverables: ${phase.deliverables.join("; ")}` : "",
|
|
1150
|
+
phase.dependencies.length > 0 ? `Dependencies: ${phase.dependencies.join("; ")}` : "",
|
|
1151
|
+
phase.risks.length > 0 ? `Risks: ${phase.risks.join("; ")}` : "",
|
|
1152
|
+
phase.successCriteria.length > 0 ? `Success Criteria: ${phase.successCriteria.join("; ")}` : "",
|
|
1153
|
+
phase.relatedFindings.length > 0 ? `Related Findings: ${phase.relatedFindings.join(", ")}` : ""
|
|
1154
|
+
].filter(Boolean).join("\n");
|
|
1155
|
+
return `## Overall Topic
|
|
1156
|
+
${topic}
|
|
1157
|
+
|
|
1158
|
+
## Roadmap Phase to Expand
|
|
1159
|
+
${phaseContext}
|
|
1160
|
+
|
|
1161
|
+
## Evidence Digest
|
|
1162
|
+
${formatDigestForPrompt(digest)}
|
|
1163
|
+
|
|
1164
|
+
Expand this phase into a repo-level implementation plan.
|
|
1165
|
+
Use tools to verify file paths and interfaces if available.
|
|
1166
|
+
Answer these questions:
|
|
1167
|
+
1. Which files/modules need to change?
|
|
1168
|
+
2. What are the specific code changes?
|
|
1169
|
+
3. What interfaces need to be added or modified?
|
|
1170
|
+
4. What are the migration steps (in order)?
|
|
1171
|
+
5. How do we validate the changes?
|
|
1172
|
+
6. What is the effort estimate?
|
|
1173
|
+
7. What are the blockers?`;
|
|
1174
|
+
}
|
|
1175
|
+
parseDetailExpansionResponse(text) {
|
|
1176
|
+
return parseDetailExpansion(text);
|
|
1177
|
+
}
|
|
1178
|
+
// ─── V2: Claim-Aware Consensus ─────────────────────────────────
|
|
1179
|
+
claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary) {
|
|
1180
|
+
return `## Topic
|
|
1181
|
+
${topic}
|
|
1182
|
+
|
|
1183
|
+
## Claim Verification Status
|
|
1184
|
+
${formatClaimSummaryForConsensus(claimSummary)}
|
|
1185
|
+
|
|
1186
|
+
## Proposed Plans
|
|
1187
|
+
${formatReports(reports)}
|
|
1188
|
+
|
|
1189
|
+
## Peer Reviews
|
|
1190
|
+
${formatFindingReviews(reviews)}
|
|
1191
|
+
|
|
1192
|
+
Synthesize a final implementation roadmap.
|
|
1193
|
+
Requirements:
|
|
1194
|
+
- Verified claims should form the backbone of the roadmap.
|
|
1195
|
+
- Unresolved claims should appear as open questions or conditional phases.
|
|
1196
|
+
- Produce 3 to 6 roadmap phases with sequencing, dependencies, deliverables, and success criteria.
|
|
1197
|
+
- Use nextActions only for immediate follow-up work.`;
|
|
1198
|
+
}
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
// src/arena/lenses/index.ts
|
|
1202
|
+
init_esm_shims();
|
|
1203
|
+
|
|
1204
|
+
// src/arena/lenses/engineering.ts
|
|
1205
|
+
init_esm_shims();
|
|
1206
|
+
var engineeringLens = {
|
|
1207
|
+
name: "engineering",
|
|
1208
|
+
label: "Engineering",
|
|
1209
|
+
participantRole: "a software engineer focused on code quality, correctness, and maintainability",
|
|
1210
|
+
reviewerRole: "an engineering peer reviewer evaluating technical rigor and code health",
|
|
1211
|
+
moderatorRole: "a tech lead synthesizing engineering perspectives into actionable conclusions",
|
|
1212
|
+
summaryLabel: "Engineering Assessment",
|
|
1213
|
+
criteria: [
|
|
1214
|
+
"Code correctness and edge case handling",
|
|
1215
|
+
"Error handling and resilience",
|
|
1216
|
+
"Performance and scalability implications",
|
|
1217
|
+
"API design and interface clarity",
|
|
1218
|
+
"Test coverage and testability",
|
|
1219
|
+
"Maintainability and readability",
|
|
1220
|
+
"Security considerations",
|
|
1221
|
+
"Backward compatibility"
|
|
1222
|
+
],
|
|
1223
|
+
preferredFindingKinds: ["risk", "improvement", "strength", "question"]
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
// src/arena/lenses/product.ts
|
|
1227
|
+
init_esm_shims();
|
|
1228
|
+
var productLens = {
|
|
1229
|
+
name: "product",
|
|
1230
|
+
label: "Product",
|
|
1231
|
+
participantRole: "a product analyst focused on user value, completeness, and edge cases",
|
|
1232
|
+
reviewerRole: "a product reviewer evaluating requirements coverage and user impact",
|
|
1233
|
+
moderatorRole: "a product lead synthesizing product perspectives into prioritized insights",
|
|
1234
|
+
summaryLabel: "Product Assessment",
|
|
1235
|
+
criteria: [
|
|
1236
|
+
"Requirements completeness and coverage",
|
|
1237
|
+
"User experience and usability",
|
|
1238
|
+
"Edge cases and boundary conditions",
|
|
1239
|
+
"Acceptance criteria clarity",
|
|
1240
|
+
"Feature scope \u2014 too broad or too narrow",
|
|
1241
|
+
"User journey coherence",
|
|
1242
|
+
"Metric and success criteria definition",
|
|
1243
|
+
"Stakeholder alignment"
|
|
1244
|
+
],
|
|
1245
|
+
preferredFindingKinds: ["question", "improvement", "risk", "strength"]
|
|
1246
|
+
};
|
|
1247
|
+
|
|
1248
|
+
// src/arena/lenses/architecture.ts
|
|
1249
|
+
init_esm_shims();
|
|
1250
|
+
var architectureLens = {
|
|
1251
|
+
name: "architecture",
|
|
1252
|
+
label: "Architecture",
|
|
1253
|
+
participantRole: "a system architect focused on boundaries, modularity, and evolution paths",
|
|
1254
|
+
reviewerRole: "an architecture reviewer evaluating structural decisions and trade-offs",
|
|
1255
|
+
moderatorRole: "a chief architect synthesizing architectural perspectives into design guidance",
|
|
1256
|
+
summaryLabel: "Architecture Assessment",
|
|
1257
|
+
criteria: [
|
|
1258
|
+
"Module boundaries and responsibility separation",
|
|
1259
|
+
"Coupling and cohesion",
|
|
1260
|
+
"Extensibility and evolution path",
|
|
1261
|
+
"Dependency management",
|
|
1262
|
+
"Data flow and state management",
|
|
1263
|
+
"API surface area and abstraction levels",
|
|
1264
|
+
"Migration and backward compatibility strategy",
|
|
1265
|
+
"Operational concerns (monitoring, debugging, deployment)"
|
|
1266
|
+
],
|
|
1267
|
+
preferredFindingKinds: ["improvement", "risk", "question", "strength"]
|
|
1268
|
+
};
|
|
1269
|
+
|
|
1270
|
+
// src/arena/lenses/general.ts
|
|
1271
|
+
init_esm_shims();
|
|
1272
|
+
var generalLens = {
|
|
1273
|
+
name: "general",
|
|
1274
|
+
label: "General",
|
|
1275
|
+
participantRole: "an analyst providing a broad, balanced perspective",
|
|
1276
|
+
reviewerRole: "a peer reviewer evaluating clarity, logic, and completeness",
|
|
1277
|
+
moderatorRole: "a neutral moderator synthesizing diverse perspectives into balanced conclusions",
|
|
1278
|
+
summaryLabel: "General Assessment",
|
|
1279
|
+
criteria: [
|
|
1280
|
+
"Logical coherence and consistency",
|
|
1281
|
+
"Completeness of analysis",
|
|
1282
|
+
"Trade-off identification",
|
|
1283
|
+
"Assumption clarity",
|
|
1284
|
+
"Evidence quality",
|
|
1285
|
+
"Actionability of recommendations"
|
|
1286
|
+
],
|
|
1287
|
+
preferredFindingKinds: ["strength", "risk", "question", "improvement"]
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
// src/arena/lenses/index.ts
|
|
1291
|
+
var LENS_MAP = {
|
|
1292
|
+
engineering: engineeringLens,
|
|
1293
|
+
product: productLens,
|
|
1294
|
+
architecture: architectureLens,
|
|
1295
|
+
general: generalLens
|
|
1296
|
+
};
|
|
1297
|
+
function getLens(name) {
|
|
1298
|
+
return LENS_MAP[name];
|
|
1299
|
+
}
|
|
1300
|
+
function resolveLenses(refs) {
|
|
1301
|
+
return refs.map((ref) => LENS_MAP[ref.name]);
|
|
1302
|
+
}
|
|
1303
|
+
function buildLensPrompt(lenses, phase) {
|
|
1304
|
+
if (lenses.length === 0) return "";
|
|
1305
|
+
const roleKey = phase === "participant" ? "participantRole" : phase === "reviewer" ? "reviewerRole" : "moderatorRole";
|
|
1306
|
+
const roles = lenses.map((l) => l[roleKey]);
|
|
1307
|
+
const allCriteria = lenses.flatMap((l) => l.criteria);
|
|
1308
|
+
const uniqueCriteria = [...new Set(allCriteria)];
|
|
1309
|
+
const roleDesc = lenses.length === 1 ? `You are ${roles[0]}.` : `You combine multiple perspectives: ${roles.join("; ")}.`;
|
|
1310
|
+
return [
|
|
1311
|
+
roleDesc,
|
|
1312
|
+
"",
|
|
1313
|
+
"Evaluation criteria (prioritized):",
|
|
1314
|
+
...uniqueCriteria.map((c) => `- ${c}`)
|
|
1315
|
+
].join("\n");
|
|
1316
|
+
}
|
|
1317
|
+
var LENS_NAMES = ["engineering", "product", "architecture", "general"];
|
|
1318
|
+
|
|
1319
|
+
// src/arena/strategies/index.ts
|
|
1320
|
+
init_esm_shims();
|
|
1321
|
+
|
|
1322
|
+
// src/arena/strategies/lens-wrapper.ts
|
|
1323
|
+
init_esm_shims();
|
|
1324
|
+
function withLens(strategy, plan) {
|
|
1325
|
+
const lenses = resolveLenses(plan.lenses);
|
|
1326
|
+
if (lenses.length === 0) return strategy;
|
|
1327
|
+
const participantLensPrompt = buildLensPrompt(lenses, "participant");
|
|
1328
|
+
const reviewerLensPrompt = buildLensPrompt(lenses, "reviewer");
|
|
1329
|
+
const moderatorLensPrompt = buildLensPrompt(lenses, "moderator");
|
|
1330
|
+
const participantPlanPrompt = buildPlanPrompt(plan, "participant");
|
|
1331
|
+
const reviewerPlanPrompt = buildPlanPrompt(plan, "reviewer");
|
|
1332
|
+
const moderatorPlanPrompt = buildPlanPrompt(plan, "moderator");
|
|
1333
|
+
return {
|
|
1334
|
+
researchSystemPrompt(name) {
|
|
1335
|
+
return [
|
|
1336
|
+
strategy.researchSystemPrompt(name),
|
|
1337
|
+
"",
|
|
1338
|
+
"\u2500\u2500 Analysis Perspective \u2500\u2500",
|
|
1339
|
+
participantLensPrompt,
|
|
1340
|
+
"",
|
|
1341
|
+
"\u2500\u2500 Scenario Support \u2500\u2500",
|
|
1342
|
+
participantPlanPrompt
|
|
1343
|
+
].join("\n");
|
|
1344
|
+
},
|
|
1345
|
+
researchUserPrompt(topic, ctx) {
|
|
1346
|
+
return [formatPlanBrief(plan), "", strategy.researchUserPrompt(topic, ctx)].join("\n");
|
|
1347
|
+
},
|
|
1348
|
+
parseResearchResponse(participant, text) {
|
|
1349
|
+
return strategy.parseResearchResponse(participant, text);
|
|
1350
|
+
},
|
|
1351
|
+
crossReviewSystemPrompt(reviewerName) {
|
|
1352
|
+
return [
|
|
1353
|
+
strategy.crossReviewSystemPrompt(reviewerName),
|
|
1354
|
+
"",
|
|
1355
|
+
"\u2500\u2500 Review Perspective \u2500\u2500",
|
|
1356
|
+
reviewerLensPrompt,
|
|
1357
|
+
"",
|
|
1358
|
+
"\u2500\u2500 Scenario Support \u2500\u2500",
|
|
1359
|
+
reviewerPlanPrompt
|
|
1360
|
+
].join("\n");
|
|
1361
|
+
},
|
|
1362
|
+
crossReviewUserPrompt(topic, my, others) {
|
|
1363
|
+
return [formatPlanBrief(plan), "", strategy.crossReviewUserPrompt(topic, my, others)].join("\n");
|
|
1364
|
+
},
|
|
1365
|
+
parseCrossReviewResponse(reviewer, text) {
|
|
1366
|
+
return strategy.parseCrossReviewResponse(reviewer, text);
|
|
1367
|
+
},
|
|
1368
|
+
consensusSystemPrompt() {
|
|
1369
|
+
return [
|
|
1370
|
+
strategy.consensusSystemPrompt(),
|
|
1371
|
+
"",
|
|
1372
|
+
"\u2500\u2500 Moderator Perspective \u2500\u2500",
|
|
1373
|
+
moderatorLensPrompt,
|
|
1374
|
+
"",
|
|
1375
|
+
"\u2500\u2500 Scenario Support \u2500\u2500",
|
|
1376
|
+
moderatorPlanPrompt
|
|
1377
|
+
].join("\n");
|
|
1378
|
+
},
|
|
1379
|
+
consensusUserPrompt(topic, reports, reviews) {
|
|
1380
|
+
return [formatPlanBrief(plan), "", strategy.consensusUserPrompt(topic, reports, reviews)].join("\n");
|
|
1381
|
+
},
|
|
1382
|
+
parseConsensusResponse(text) {
|
|
1383
|
+
return strategy.parseConsensusResponse(text);
|
|
1384
|
+
},
|
|
1385
|
+
preferredFindingKinds() {
|
|
1386
|
+
const modeKinds = strategy.preferredFindingKinds();
|
|
1387
|
+
const lensKinds = lenses.flatMap((l) => l.preferredFindingKinds);
|
|
1388
|
+
const planKinds = plan.outputShape.emphasize;
|
|
1389
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1390
|
+
const result = [];
|
|
1391
|
+
for (const k of [...planKinds, ...modeKinds, ...lensKinds]) {
|
|
1392
|
+
if (!seen.has(k)) {
|
|
1393
|
+
seen.add(k);
|
|
1394
|
+
result.push(k);
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
return result;
|
|
1398
|
+
},
|
|
1399
|
+
// ─── V2 forwarding (conditional) ─────────────────────────────
|
|
1400
|
+
...isStrategyV2(strategy) ? {
|
|
1401
|
+
verificationReviewUserPrompt(topic, myReport, claims, digest) {
|
|
1402
|
+
return [formatPlanBrief(plan), "", strategy.verificationReviewUserPrompt(topic, myReport, claims, digest)].join("\n");
|
|
1403
|
+
},
|
|
1404
|
+
parseVerificationReviewResponse(reviewer, text) {
|
|
1405
|
+
return strategy.parseVerificationReviewResponse(reviewer, text);
|
|
1406
|
+
},
|
|
1407
|
+
debateTurnUserPrompt(topic, claim, priorTurns, digest) {
|
|
1408
|
+
return [formatPlanBrief(plan), "", strategy.debateTurnUserPrompt(topic, claim, priorTurns, digest)].join("\n");
|
|
1409
|
+
},
|
|
1410
|
+
parseDebateTurnResponse(participant, text) {
|
|
1411
|
+
return strategy.parseDebateTurnResponse(participant, text);
|
|
1412
|
+
},
|
|
1413
|
+
adjudicationUserPrompt(topic, claim, rounds, digest) {
|
|
1414
|
+
return [formatPlanBrief(plan), "", strategy.adjudicationUserPrompt(topic, claim, rounds, digest)].join("\n");
|
|
1415
|
+
},
|
|
1416
|
+
parseAdjudicationResponse(text) {
|
|
1417
|
+
return strategy.parseAdjudicationResponse(text);
|
|
1418
|
+
},
|
|
1419
|
+
claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary) {
|
|
1420
|
+
return [formatPlanBrief(plan), "", strategy.claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary)].join("\n");
|
|
1421
|
+
}
|
|
1422
|
+
} : {},
|
|
1423
|
+
// ─── Planning forwarding (conditional) ──────────────────────
|
|
1424
|
+
...isStrategyPlanning(strategy) ? {
|
|
1425
|
+
mergeReviewUserPrompt(topic, myReport, claims, digest) {
|
|
1426
|
+
return [formatPlanBrief(plan), "", strategy.mergeReviewUserPrompt(topic, myReport, claims, digest)].join("\n");
|
|
1427
|
+
},
|
|
1428
|
+
parseMergeReviewResponse(reviewer, text) {
|
|
1429
|
+
return strategy.parseMergeReviewResponse(reviewer, text);
|
|
1430
|
+
},
|
|
1431
|
+
detailExpansionSystemPrompt() {
|
|
1432
|
+
return [
|
|
1433
|
+
strategy.detailExpansionSystemPrompt(),
|
|
1434
|
+
"",
|
|
1435
|
+
"\u2500\u2500 Moderator Perspective \u2500\u2500",
|
|
1436
|
+
moderatorLensPrompt
|
|
1437
|
+
].join("\n");
|
|
1438
|
+
},
|
|
1439
|
+
detailExpansionUserPrompt(topic, phase, digest) {
|
|
1440
|
+
return [formatPlanBrief(plan), "", strategy.detailExpansionUserPrompt(topic, phase, digest)].join("\n");
|
|
1441
|
+
},
|
|
1442
|
+
parseDetailExpansionResponse(text) {
|
|
1443
|
+
return strategy.parseDetailExpansionResponse(text);
|
|
1444
|
+
}
|
|
1445
|
+
} : {}
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
function buildPlanPrompt(plan, phase) {
|
|
1449
|
+
const sources = plan.sources.map((s) => s.kind).join(", ");
|
|
1450
|
+
const targets = formatTargets(plan.subject.targets);
|
|
1451
|
+
const emphasis = formatEmphasis(plan.outputShape.emphasize);
|
|
1452
|
+
const notes = [
|
|
1453
|
+
`Subject: ${plan.subject.kind} \u2014 ${plan.subject.label}${targets ? ` (${targets})` : ""}`,
|
|
1454
|
+
`Evidence sources: ${sources || "none"}`,
|
|
1455
|
+
`Overview label: ${plan.outputShape.overviewLabel}`,
|
|
1456
|
+
`Emphasize these finding types first: ${emphasis}`,
|
|
1457
|
+
"Only draw conclusions from available evidence. When evidence is thin, narrow the claim or call it out explicitly.",
|
|
1458
|
+
...buildScenarioNotes(plan, phase)
|
|
1459
|
+
];
|
|
1460
|
+
return notes.map((line) => `- ${line}`).join("\n");
|
|
1461
|
+
}
|
|
1462
|
+
function formatPlanBrief(plan) {
|
|
1463
|
+
const targets = formatTargets(plan.subject.targets);
|
|
1464
|
+
const lenses = plan.lenses.map((l) => l.name).join(", ");
|
|
1465
|
+
const sources = plan.sources.map((s) => s.kind).join(", ");
|
|
1466
|
+
return [
|
|
1467
|
+
"## Arena Plan",
|
|
1468
|
+
`- Mode: ${plan.mode}`,
|
|
1469
|
+
`- Subject: ${plan.subject.kind} \u2014 ${plan.subject.label}${targets ? ` (${targets})` : ""}`,
|
|
1470
|
+
`- Lenses: ${lenses || "general"}`,
|
|
1471
|
+
`- Sources: ${sources || "none"}`,
|
|
1472
|
+
`- Output focus: ${formatEmphasis(plan.outputShape.emphasize)}`,
|
|
1473
|
+
`- Overview label: ${plan.outputShape.overviewLabel}`
|
|
1474
|
+
].join("\n");
|
|
1475
|
+
}
|
|
1476
|
+
function buildScenarioNotes(plan, phase) {
|
|
1477
|
+
const notes = [];
|
|
1478
|
+
switch (plan.subject.kind) {
|
|
1479
|
+
case "changes":
|
|
1480
|
+
notes.push(
|
|
1481
|
+
phase === "participant" ? "Treat this as a change-focused analysis: inspect behavioral impact, regressions, interfaces, and compatibility." : phase === "reviewer" ? "Challenge findings that are not grounded in changed behavior, affected files, or nearby code paths." : "Summarize the concrete impact of the changes before giving judgment."
|
|
1482
|
+
);
|
|
1483
|
+
break;
|
|
1484
|
+
case "files":
|
|
1485
|
+
notes.push(
|
|
1486
|
+
phase === "participant" ? "Stay anchored to the named files/modules and their immediate call sites instead of drifting into unrelated areas." : phase === "reviewer" ? "Prioritize corrections to claims that overgeneralize beyond the named files/modules." : "Keep the conclusion scoped to the named files/modules and their direct implications."
|
|
1487
|
+
);
|
|
1488
|
+
break;
|
|
1489
|
+
case "docs":
|
|
1490
|
+
notes.push(
|
|
1491
|
+
phase === "participant" ? "Treat this as a document-centric scene: look for ambiguity, missing acceptance criteria, edge cases, and implementation gaps." : phase === "reviewer" ? "Challenge findings that skip over unclear requirements, contradictory wording, or unsupported feasibility assumptions." : "Synthesize the document review around completeness, feasibility, and decision-ready gaps."
|
|
1492
|
+
);
|
|
1493
|
+
break;
|
|
1494
|
+
case "topic":
|
|
1495
|
+
notes.push(
|
|
1496
|
+
phase === "participant" ? "Treat this as a topic discussion: surface assumptions, trade-offs, and decision criteria instead of pretending there is hard repo evidence." : phase === "reviewer" ? "Push back on overconfident claims and preserve unresolved trade-offs as open questions." : "Preserve disagreement where needed and make assumptions explicit."
|
|
1497
|
+
);
|
|
1498
|
+
break;
|
|
1499
|
+
case "mixed":
|
|
1500
|
+
notes.push(
|
|
1501
|
+
phase === "participant" ? "This is a mixed scene: reconcile code, docs, and topic-level evidence instead of analyzing each source in isolation." : phase === "reviewer" ? "Check whether claims actually connect the different evidence sources, rather than citing only one side." : "Unify the conclusion across repo facts, documents, and higher-level reasoning."
|
|
1502
|
+
);
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
const sourceKinds = new Set(plan.sources.map((s) => s.kind));
|
|
1506
|
+
if (sourceKinds.has("docs") && sourceKinds.has("repo")) {
|
|
1507
|
+
notes.push(
|
|
1508
|
+
phase === "moderator" ? "If documents and repo evidence diverge, call out the mismatch explicitly instead of collapsing it into a single conclusion." : "Cross-check document claims against the current repo structure when possible."
|
|
1509
|
+
);
|
|
1510
|
+
} else if (sourceKinds.has("web")) {
|
|
1511
|
+
notes.push(
|
|
1512
|
+
phase === "moderator" ? "Separate external references from local repo facts in the synthesis." : "Keep external research distinct from local evidence and label it clearly."
|
|
1513
|
+
);
|
|
1514
|
+
} else if (sourceKinds.has("none")) {
|
|
1515
|
+
notes.push("No external evidence is expected here; reason from the prompt and make assumptions visible.");
|
|
1516
|
+
}
|
|
1517
|
+
if (plan.mode === "planning") {
|
|
1518
|
+
notes.push(
|
|
1519
|
+
phase === "moderator" ? "When enough evidence exists, turn the conclusion into a staged roadmap with sequencing, dependencies, and success criteria." : "Prefer findings that help phase work, sequence dependencies, or expose migration/blocker risks."
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
return notes;
|
|
1523
|
+
}
|
|
1524
|
+
function formatEmphasis(kinds) {
|
|
1525
|
+
return kinds.length > 0 ? kinds.join(", ") : "risk, improvement";
|
|
1526
|
+
}
|
|
1527
|
+
function formatTargets(targets) {
|
|
1528
|
+
return Array.isArray(targets) && targets.length > 0 ? targets.join(", ") : "";
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// src/arena/strategies/index.ts
|
|
1532
|
+
var STRATEGY_MAP = {
|
|
1533
|
+
review: () => new ReviewStrategy(),
|
|
1534
|
+
discussion: () => new DiscussionStrategy(),
|
|
1535
|
+
planning: () => new PlanningStrategy()
|
|
1536
|
+
};
|
|
1537
|
+
function getStrategy(mode) {
|
|
1538
|
+
return STRATEGY_MAP[mode]();
|
|
1539
|
+
}
|
|
1540
|
+
function getStrategyForPlan(plan) {
|
|
1541
|
+
const base = STRATEGY_MAP[plan.mode]();
|
|
1542
|
+
return withLens(base, plan);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// src/arena/planner.ts
|
|
1546
|
+
init_esm_shims();
|
|
1547
|
+
var PLANNER_SYSTEM_PROMPT = `You are the planner for a multi-model collaborative analysis tool called Arena.
|
|
1548
|
+
|
|
1549
|
+
Given a user's natural language request, produce an execution plan. You must determine:
|
|
1550
|
+
|
|
1551
|
+
1. **mode** \u2014 how to collaborate:
|
|
1552
|
+
- "review": find issues, verify quality, give a structured verdict
|
|
1553
|
+
- "discussion": explore trade-offs, compare viewpoints, preserve disagreements
|
|
1554
|
+
- "planning": build a roadmap, identify phases, dependencies, and risks
|
|
1555
|
+
|
|
1556
|
+
2. **lenses** \u2014 from what analytical perspectives (can be multiple):
|
|
1557
|
+
- "engineering": code quality, correctness, performance, maintainability
|
|
1558
|
+
- "product": user value, requirements completeness, acceptance criteria, UX
|
|
1559
|
+
- "architecture": system boundaries, modularity, coupling, evolution paths
|
|
1560
|
+
- "general": broad analysis, logic, trade-offs (use as fallback)
|
|
1561
|
+
|
|
1562
|
+
3. **sources** \u2014 where to gather evidence (can be multiple):
|
|
1563
|
+
- "git": diffs, commit history, branch comparison, changed files
|
|
1564
|
+
- "repo": directory structure, source code files, symbol search
|
|
1565
|
+
- "docs": markdown/text documents, PRDs, design docs
|
|
1566
|
+
- "web": external information, standards, competitor analysis
|
|
1567
|
+
- "none": pure topic discussion, no external evidence needed
|
|
1568
|
+
|
|
1569
|
+
4. **subject** \u2014 what is being analyzed:
|
|
1570
|
+
- kind: "changes" (code changes), "files" (specific files), "docs" (documents), "topic" (abstract topic), "mixed"
|
|
1571
|
+
- label: human-readable description
|
|
1572
|
+
- targets: specific file paths, branch names, or doc names if applicable
|
|
1573
|
+
|
|
1574
|
+
5. **outputShape** \u2014 what to emphasize in the output
|
|
1575
|
+
|
|
1576
|
+
Respond ONLY with JSON, no markdown fences:
|
|
1577
|
+
{
|
|
1578
|
+
"mode": "review|discussion|planning",
|
|
1579
|
+
"lenses": [{"name": "engineering|product|architecture|general", "weight": 1.0}],
|
|
1580
|
+
"sources": [{"kind": "git|repo|docs|web|none", "targets": ["optional specific targets"]}],
|
|
1581
|
+
"subject": {"kind": "changes|files|docs|topic|mixed", "label": "description", "targets": ["optional"]},
|
|
1582
|
+
"outputShape": {"overviewLabel": "e.g. What Changed / Current Scope / Problem Framing", "emphasize": ["risk", "improvement"]},
|
|
1583
|
+
"confidence": "high|medium|low",
|
|
1584
|
+
"followUpQuestion": "question if confidence is low, or null"
|
|
1585
|
+
}`;
|
|
1586
|
+
async function planArena(topic, llmConfig, flags, signal) {
|
|
1587
|
+
if (flags?.mode && flags?.base) {
|
|
1588
|
+
return buildExplicitPlan(topic, flags);
|
|
1589
|
+
}
|
|
1590
|
+
try {
|
|
1591
|
+
signal?.throwIfAborted();
|
|
1592
|
+
const client = await createLLMClient({
|
|
1593
|
+
...llmConfig,
|
|
1594
|
+
enableStreaming: false,
|
|
1595
|
+
maxTokens: 1024
|
|
1596
|
+
});
|
|
1597
|
+
const response = await client.createMessage({
|
|
1598
|
+
systemPrompt: PLANNER_SYSTEM_PROMPT,
|
|
1599
|
+
messages: [{ role: "user", content: `<user_request>
|
|
1600
|
+
${topic}
|
|
1601
|
+
</user_request>
|
|
1602
|
+
|
|
1603
|
+
Analyze the user request above and output the arena plan as JSON.` }],
|
|
1604
|
+
maxTokens: 1024,
|
|
1605
|
+
signal
|
|
1606
|
+
});
|
|
1607
|
+
const plan = parsePlanResponse(response.text, topic);
|
|
1608
|
+
if (flags?.mode) plan.mode = flags.mode;
|
|
1609
|
+
if (flags?.base) {
|
|
1610
|
+
plan.sources = [{ kind: "git", targets: [flags.base, flags.head ?? "HEAD"] }];
|
|
1611
|
+
plan.subject = { kind: "changes", label: `${flags.base}...${flags.head ?? "HEAD"}`, targets: [flags.base, flags.head ?? "HEAD"] };
|
|
1612
|
+
}
|
|
1613
|
+
logger.info("arena.planner", { plan });
|
|
1614
|
+
return plan;
|
|
1615
|
+
} catch (err) {
|
|
1616
|
+
logger.warn("arena.planner_fallback", { error: err.message });
|
|
1617
|
+
return buildFallbackPlan(topic, flags);
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
function parsePlanResponse(text, topic) {
|
|
1621
|
+
const fenced = text.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
1622
|
+
const jsonStr = fenced ? fenced[1].trim() : text.match(/\{[\s\S]*\}/)?.[0] ?? text;
|
|
1623
|
+
try {
|
|
1624
|
+
const parsed = JSON.parse(jsonStr);
|
|
1625
|
+
return {
|
|
1626
|
+
mode: validateMode(parsed.mode),
|
|
1627
|
+
lenses: parseLenses(parsed.lenses),
|
|
1628
|
+
sources: parseSources(parsed.sources),
|
|
1629
|
+
subject: parseSubject(parsed.subject, topic),
|
|
1630
|
+
outputShape: parseOutputShape(parsed.outputShape, parsed.mode),
|
|
1631
|
+
confidence: validateConfidence(parsed.confidence),
|
|
1632
|
+
followUpQuestion: parsed.followUpQuestion || void 0
|
|
1633
|
+
};
|
|
1634
|
+
} catch {
|
|
1635
|
+
return buildFallbackPlan(topic);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
function buildExplicitPlan(topic, flags) {
|
|
1639
|
+
return {
|
|
1640
|
+
mode: flags.mode,
|
|
1641
|
+
lenses: [{ name: "engineering", weight: 1 }],
|
|
1642
|
+
sources: [
|
|
1643
|
+
{ kind: "git", targets: [flags.base, flags.head ?? "HEAD"] },
|
|
1644
|
+
{ kind: "repo" }
|
|
1645
|
+
],
|
|
1646
|
+
subject: {
|
|
1647
|
+
kind: "changes",
|
|
1648
|
+
label: `${flags.base}...${flags.head ?? "HEAD"}`,
|
|
1649
|
+
targets: [flags.base, flags.head ?? "HEAD"]
|
|
1650
|
+
},
|
|
1651
|
+
outputShape: {
|
|
1652
|
+
overviewLabel: "What Changed",
|
|
1653
|
+
emphasize: ["risk", "improvement"]
|
|
1654
|
+
},
|
|
1655
|
+
confidence: "high"
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
function buildFallbackPlan(topic, flags) {
|
|
1659
|
+
if (flags?.base) {
|
|
1660
|
+
return {
|
|
1661
|
+
mode: flags.mode ?? "review",
|
|
1662
|
+
lenses: [{ name: "engineering", weight: 1 }],
|
|
1663
|
+
sources: [{ kind: "git" }, { kind: "repo" }],
|
|
1664
|
+
subject: { kind: "changes", label: topic, targets: [flags.base, flags.head ?? "HEAD"] },
|
|
1665
|
+
outputShape: {
|
|
1666
|
+
overviewLabel: "What Changed",
|
|
1667
|
+
emphasize: ["risk", "improvement"]
|
|
1668
|
+
},
|
|
1669
|
+
confidence: "low"
|
|
1670
|
+
};
|
|
1671
|
+
}
|
|
1672
|
+
const lower = topic.toLowerCase();
|
|
1673
|
+
const hasDoc = /\b(prd|doc|docs|spec|rfc|design doc|requirement|proposal)\b/i.test(topic) || /(文档|需求|方案)/.test(topic);
|
|
1674
|
+
const hasRoadmap = /\b(roadmap|plan|planning|strategy|migration|refactor)\b/i.test(topic) || /(演进|路线图|规划|重构)/.test(topic);
|
|
1675
|
+
const hasFeasibility = /\b(feasibility|feasible|can we|should we|trade-?off)\b/i.test(topic) || /(可行|能不能|值不值得|取舍)/.test(topic);
|
|
1676
|
+
const hasReview = /\b(review|pr|diff|patch|change)\b/i.test(topic) || /(改动|评审|审查)/.test(topic);
|
|
1677
|
+
const hasRepo = /\b(repo|repository|codebase|module|architecture)\b/i.test(topic) || /(代码库|仓库|模块|架构)/.test(topic);
|
|
1678
|
+
if (hasRoadmap) {
|
|
1679
|
+
return {
|
|
1680
|
+
mode: "planning",
|
|
1681
|
+
lenses: hasDoc ? [{ name: "architecture", weight: 1 }, { name: "product", weight: 0.7 }] : [{ name: "architecture", weight: 1 }, { name: "engineering", weight: 0.8 }],
|
|
1682
|
+
sources: hasDoc ? [{ kind: "docs" }, ...hasRepo ? [{ kind: "repo" }] : []] : [{ kind: "repo" }, { kind: "docs" }],
|
|
1683
|
+
subject: { kind: hasDoc ? "mixed" : "topic", label: topic },
|
|
1684
|
+
outputShape: {
|
|
1685
|
+
overviewLabel: "Current Scope",
|
|
1686
|
+
emphasize: ["improvement", "risk", "question"]
|
|
1687
|
+
},
|
|
1688
|
+
confidence: "low"
|
|
1689
|
+
};
|
|
1690
|
+
}
|
|
1691
|
+
if (hasDoc && hasFeasibility) {
|
|
1692
|
+
return {
|
|
1693
|
+
mode: "discussion",
|
|
1694
|
+
lenses: [{ name: "product", weight: 1 }, { name: "engineering", weight: 0.8 }],
|
|
1695
|
+
sources: hasRepo ? [{ kind: "docs" }, { kind: "repo" }] : [{ kind: "docs" }],
|
|
1696
|
+
subject: { kind: hasRepo ? "mixed" : "docs", label: topic },
|
|
1697
|
+
outputShape: {
|
|
1698
|
+
overviewLabel: "Problem Framing",
|
|
1699
|
+
emphasize: ["question", "risk", "improvement"]
|
|
1700
|
+
},
|
|
1701
|
+
confidence: "low"
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
if (hasDoc) {
|
|
1705
|
+
return {
|
|
1706
|
+
mode: hasReview ? "review" : "discussion",
|
|
1707
|
+
lenses: [{ name: "product", weight: 1 }, { name: "architecture", weight: 0.6 }],
|
|
1708
|
+
sources: hasRepo ? [{ kind: "docs" }, { kind: "repo" }] : [{ kind: "docs" }],
|
|
1709
|
+
subject: { kind: hasRepo ? "mixed" : "docs", label: topic },
|
|
1710
|
+
outputShape: {
|
|
1711
|
+
overviewLabel: hasReview ? "Current Scope" : "Problem Framing",
|
|
1712
|
+
emphasize: ["question", "improvement", "risk"]
|
|
1713
|
+
},
|
|
1714
|
+
confidence: "low"
|
|
1715
|
+
};
|
|
1716
|
+
}
|
|
1717
|
+
if (hasFeasibility) {
|
|
1718
|
+
return {
|
|
1719
|
+
mode: "discussion",
|
|
1720
|
+
lenses: hasRepo ? [{ name: "product", weight: 1 }, { name: "engineering", weight: 0.8 }] : [{ name: "general", weight: 1 }, { name: "product", weight: 0.6 }],
|
|
1721
|
+
sources: hasRepo ? [{ kind: "repo" }, { kind: "docs" }] : [{ kind: "none" }],
|
|
1722
|
+
subject: { kind: hasRepo ? "topic" : "topic", label: topic },
|
|
1723
|
+
outputShape: {
|
|
1724
|
+
overviewLabel: "Problem Framing",
|
|
1725
|
+
emphasize: ["question", "risk", "improvement"]
|
|
1726
|
+
},
|
|
1727
|
+
confidence: "low"
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
if (hasReview || /\b(commit|branch|diff|patch|bug|fix|回归)\b/i.test(lower)) {
|
|
1731
|
+
return {
|
|
1732
|
+
mode: "review",
|
|
1733
|
+
lenses: [{ name: "engineering", weight: 1 }],
|
|
1734
|
+
sources: [{ kind: "git" }, { kind: "repo" }],
|
|
1735
|
+
subject: { kind: "changes", label: topic },
|
|
1736
|
+
outputShape: {
|
|
1737
|
+
overviewLabel: "What Changed",
|
|
1738
|
+
emphasize: ["risk", "improvement", "question"]
|
|
1739
|
+
},
|
|
1740
|
+
confidence: "low"
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
return {
|
|
1744
|
+
mode: flags?.mode ?? "review",
|
|
1745
|
+
lenses: [{ name: hasRepo ? "architecture" : "general", weight: 1 }],
|
|
1746
|
+
sources: hasRepo ? [{ kind: "repo" }, { kind: "docs" }] : [{ kind: "none" }],
|
|
1747
|
+
subject: { kind: "topic", label: topic },
|
|
1748
|
+
outputShape: {
|
|
1749
|
+
overviewLabel: hasRepo ? "Current Scope" : "Problem Framing",
|
|
1750
|
+
emphasize: hasRepo ? ["improvement", "risk", "question"] : ["question", "risk", "improvement"]
|
|
1751
|
+
},
|
|
1752
|
+
confidence: "low"
|
|
1753
|
+
};
|
|
1754
|
+
}
|
|
1755
|
+
function validateMode(v) {
|
|
1756
|
+
if (v === "review" || v === "discussion" || v === "planning") return v;
|
|
1757
|
+
return "review";
|
|
1758
|
+
}
|
|
1759
|
+
function validateConfidence(v) {
|
|
1760
|
+
if (v === "high" || v === "medium" || v === "low") return v;
|
|
1761
|
+
return "medium";
|
|
1762
|
+
}
|
|
1763
|
+
function parseLenses(v) {
|
|
1764
|
+
if (!Array.isArray(v) || v.length === 0) {
|
|
1765
|
+
return [{ name: "general", weight: 1 }];
|
|
1766
|
+
}
|
|
1767
|
+
const result = v.filter((item) => LENS_NAMES.includes(item?.name)).map((item) => ({
|
|
1768
|
+
name: item.name,
|
|
1769
|
+
weight: typeof item.weight === "number" ? item.weight : 1
|
|
1770
|
+
}));
|
|
1771
|
+
return result.length > 0 ? result : [{ name: "general", weight: 1 }];
|
|
1772
|
+
}
|
|
1773
|
+
function parseSources(v) {
|
|
1774
|
+
if (!Array.isArray(v) || v.length === 0) {
|
|
1775
|
+
return [{ kind: "none" }];
|
|
1776
|
+
}
|
|
1777
|
+
const validKinds = ["git", "repo", "docs", "web", "none"];
|
|
1778
|
+
const result = v.filter((item) => validKinds.includes(item?.kind)).map((item) => ({
|
|
1779
|
+
kind: item.kind,
|
|
1780
|
+
targets: Array.isArray(item.targets) ? item.targets : void 0
|
|
1781
|
+
}));
|
|
1782
|
+
return result.length > 0 ? result : [{ kind: "none" }];
|
|
1783
|
+
}
|
|
1784
|
+
function parseSubject(v, topic) {
|
|
1785
|
+
if (!v || typeof v !== "object") {
|
|
1786
|
+
return { kind: "topic", label: topic };
|
|
1787
|
+
}
|
|
1788
|
+
const obj = v;
|
|
1789
|
+
const validKinds = ["changes", "files", "docs", "topic", "mixed"];
|
|
1790
|
+
return {
|
|
1791
|
+
kind: validKinds.includes(obj.kind) ? obj.kind : "topic",
|
|
1792
|
+
label: obj.label ?? topic,
|
|
1793
|
+
targets: Array.isArray(obj.targets) ? obj.targets : void 0
|
|
1794
|
+
};
|
|
1795
|
+
}
|
|
1796
|
+
var VALID_EMPHASIZE = /* @__PURE__ */ new Set(["strength", "improvement", "risk", "question"]);
|
|
1797
|
+
function parseOutputShape(v, mode) {
|
|
1798
|
+
if (v && typeof v === "object") {
|
|
1799
|
+
const obj = v;
|
|
1800
|
+
return {
|
|
1801
|
+
overviewLabel: obj.overviewLabel ?? "Overview",
|
|
1802
|
+
emphasize: Array.isArray(obj.emphasize) ? obj.emphasize.filter((k) => VALID_EMPHASIZE.has(k)) : ["risk", "improvement"]
|
|
1803
|
+
};
|
|
1804
|
+
}
|
|
1805
|
+
switch (mode) {
|
|
1806
|
+
case "review":
|
|
1807
|
+
return { overviewLabel: "What Changed", emphasize: ["risk", "improvement"] };
|
|
1808
|
+
case "discussion":
|
|
1809
|
+
return { overviewLabel: "Problem Framing", emphasize: ["strength", "risk", "question"] };
|
|
1810
|
+
case "planning":
|
|
1811
|
+
return { overviewLabel: "Current Scope", emphasize: ["improvement", "risk"] };
|
|
1812
|
+
default:
|
|
1813
|
+
return { overviewLabel: "Overview", emphasize: ["risk", "improvement"] };
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
// src/arena/providers/index.ts
|
|
1818
|
+
init_esm_shims();
|
|
1819
|
+
|
|
1820
|
+
// src/arena/providers/git.ts
|
|
1821
|
+
init_esm_shims();
|
|
1822
|
+
import { execFileSync } from "child_process";
|
|
1823
|
+
var MAX_DIFF_CHARS = 2e4;
|
|
1824
|
+
var MAX_CHANGED_FILES = 30;
|
|
1825
|
+
var gitProvider = {
|
|
1826
|
+
kind: "git",
|
|
1827
|
+
collect(plan, _topic) {
|
|
1828
|
+
const artifacts = [];
|
|
1829
|
+
const targets = plan.sources.find((s) => s.kind === "git")?.targets;
|
|
1830
|
+
const baseRef = sanitizeRef(targets?.[0]);
|
|
1831
|
+
const headRef = sanitizeRef(targets?.[1]) ?? "HEAD";
|
|
1832
|
+
const isCompare = !!baseRef;
|
|
1833
|
+
const currentBranch = git(["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
1834
|
+
if (currentBranch) {
|
|
1835
|
+
artifacts.push({
|
|
1836
|
+
id: "git-branch",
|
|
1837
|
+
kind: "doc",
|
|
1838
|
+
source: "git",
|
|
1839
|
+
title: "Current Branch",
|
|
1840
|
+
preview: currentBranch
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
const logArgs = isCompare ? ["log", "--oneline", `${baseRef}..${headRef}`, "--max-count=20"] : ["log", "--oneline", "-10"];
|
|
1844
|
+
const log = git(logArgs);
|
|
1845
|
+
if (log) {
|
|
1846
|
+
artifacts.push({
|
|
1847
|
+
id: "git-log",
|
|
1848
|
+
kind: "doc",
|
|
1849
|
+
source: "git",
|
|
1850
|
+
title: "Commit Log",
|
|
1851
|
+
preview: log
|
|
1852
|
+
});
|
|
1853
|
+
}
|
|
1854
|
+
const statArgs = isCompare ? ["diff", "--stat", `${baseRef}...${headRef}`] : ["diff", "--stat", "HEAD"];
|
|
1855
|
+
let stat = git(statArgs);
|
|
1856
|
+
if (!stat && !isCompare) stat = git(["diff", "--stat", "--cached"]);
|
|
1857
|
+
if (stat) {
|
|
1858
|
+
artifacts.push({
|
|
1859
|
+
id: "git-diffstat",
|
|
1860
|
+
kind: "doc",
|
|
1861
|
+
source: "git",
|
|
1862
|
+
title: "Diff Stat",
|
|
1863
|
+
preview: stat
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
const filesArgs = isCompare ? ["diff", "--name-status", `${baseRef}...${headRef}`] : ["diff", "--name-status", "HEAD"];
|
|
1867
|
+
let changedFiles = git(filesArgs);
|
|
1868
|
+
if (!changedFiles && !isCompare) changedFiles = git(["diff", "--name-status", "--cached"]);
|
|
1869
|
+
if (changedFiles) {
|
|
1870
|
+
const allChanged = changedFiles.split("\n").filter(Boolean);
|
|
1871
|
+
const limited = allChanged.slice(0, MAX_CHANGED_FILES);
|
|
1872
|
+
if (allChanged.length > MAX_CHANGED_FILES) {
|
|
1873
|
+
limited.push(`... and ${allChanged.length - MAX_CHANGED_FILES} more files`);
|
|
1874
|
+
}
|
|
1875
|
+
artifacts.push({
|
|
1876
|
+
id: "git-changed-files",
|
|
1877
|
+
kind: "doc",
|
|
1878
|
+
source: "git",
|
|
1879
|
+
title: "Changed Files",
|
|
1880
|
+
preview: limited.join("\n"),
|
|
1881
|
+
metadata: { totalCount: allChanged.length }
|
|
1882
|
+
});
|
|
1883
|
+
const paths = allChanged.map((line) => line.split(" ").slice(1).join(" ")).filter(Boolean);
|
|
1884
|
+
const dirCounts = clusterByDirectory(paths);
|
|
1885
|
+
if (dirCounts.length > 0) {
|
|
1886
|
+
artifacts.push({
|
|
1887
|
+
id: "git-dir-clusters",
|
|
1888
|
+
kind: "doc",
|
|
1889
|
+
source: "git",
|
|
1890
|
+
title: "Changes by Directory",
|
|
1891
|
+
preview: dirCounts.slice(0, 15).map(([dir, count]) => ` ${dir}/ (${count} files)`).join("\n")
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
}
|
|
1895
|
+
const diffArgs = isCompare ? ["diff", `${baseRef}...${headRef}`] : ["diff", "HEAD"];
|
|
1896
|
+
let diff = git(diffArgs);
|
|
1897
|
+
if (!diff && !isCompare) diff = git(["diff", "--cached"]);
|
|
1898
|
+
if (diff) {
|
|
1899
|
+
const truncated = diff.length > MAX_DIFF_CHARS;
|
|
1900
|
+
const preview = truncated ? diff.slice(0, MAX_DIFF_CHARS).slice(0, diff.slice(0, MAX_DIFF_CHARS).lastIndexOf("\n")) : diff;
|
|
1901
|
+
artifacts.push({
|
|
1902
|
+
id: "git-diff",
|
|
1903
|
+
kind: "diff",
|
|
1904
|
+
source: "git",
|
|
1905
|
+
title: "Diff",
|
|
1906
|
+
preview: preview + (truncated ? `
|
|
1907
|
+
|
|
1908
|
+
... TRUNCATED (${diff.length} chars total). Use read_file to inspect specific files.` : ""),
|
|
1909
|
+
metadata: { totalChars: diff.length, truncated }
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
if (!diff && !stat) {
|
|
1913
|
+
const status = git(["status", "--short"]);
|
|
1914
|
+
if (status) {
|
|
1915
|
+
artifacts.push({
|
|
1916
|
+
id: "git-status",
|
|
1917
|
+
kind: "doc",
|
|
1918
|
+
source: "git",
|
|
1919
|
+
title: "Git Status",
|
|
1920
|
+
preview: status
|
|
1921
|
+
});
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
logger.info("arena.provider.git", {
|
|
1925
|
+
artifactCount: artifacts.length,
|
|
1926
|
+
hasCompare: isCompare
|
|
1927
|
+
});
|
|
1928
|
+
return artifacts;
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
function git(args) {
|
|
1932
|
+
try {
|
|
1933
|
+
return execFileSync("git", args, {
|
|
1934
|
+
encoding: "utf-8",
|
|
1935
|
+
maxBuffer: 1024 * 1024,
|
|
1936
|
+
timeout: 1e4
|
|
1937
|
+
}).trim();
|
|
1938
|
+
} catch {
|
|
1939
|
+
return "";
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
function sanitizeRef(ref) {
|
|
1943
|
+
if (!ref) return void 0;
|
|
1944
|
+
let cleaned = ref.replace(/\.{2,}/g, "");
|
|
1945
|
+
cleaned = cleaned.replace(/[^a-zA-Z0-9/_\-~^.]/g, "");
|
|
1946
|
+
return cleaned || void 0;
|
|
1947
|
+
}
|
|
1948
|
+
function clusterByDirectory(paths) {
|
|
1949
|
+
const counts = /* @__PURE__ */ new Map();
|
|
1950
|
+
for (const p of paths) {
|
|
1951
|
+
const parts = p.split("/");
|
|
1952
|
+
const dir = parts.length > 2 ? parts.slice(0, 2).join("/") : parts[0];
|
|
1953
|
+
counts.set(dir, (counts.get(dir) ?? 0) + 1);
|
|
1954
|
+
}
|
|
1955
|
+
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
// src/arena/providers/repo.ts
|
|
1959
|
+
init_esm_shims();
|
|
1960
|
+
import { existsSync, readdirSync, statSync, readFileSync } from "fs";
|
|
1961
|
+
import { join } from "path";
|
|
1962
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
1963
|
+
var MAX_FILE_CHARS = 8e3;
|
|
1964
|
+
var repoProvider = {
|
|
1965
|
+
kind: "repo",
|
|
1966
|
+
collect(plan, topic) {
|
|
1967
|
+
const artifacts = [];
|
|
1968
|
+
const targets = plan.sources.find((s) => s.kind === "repo")?.targets ?? [];
|
|
1969
|
+
const tree = buildTree(".", 2);
|
|
1970
|
+
if (tree) {
|
|
1971
|
+
artifacts.push({
|
|
1972
|
+
id: "repo-tree",
|
|
1973
|
+
kind: "tree",
|
|
1974
|
+
source: "repo",
|
|
1975
|
+
title: "Project Structure",
|
|
1976
|
+
preview: tree
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
for (const target of targets) {
|
|
1980
|
+
if (!existsSync(target)) continue;
|
|
1981
|
+
const st = statSync(target);
|
|
1982
|
+
if (st.isDirectory()) {
|
|
1983
|
+
const dirTree = buildTree(target, 3);
|
|
1984
|
+
if (dirTree) {
|
|
1985
|
+
artifacts.push({
|
|
1986
|
+
id: `repo-tree-${target}`,
|
|
1987
|
+
kind: "tree",
|
|
1988
|
+
source: "repo",
|
|
1989
|
+
title: `Directory: ${target}`,
|
|
1990
|
+
preview: dirTree
|
|
1991
|
+
});
|
|
1992
|
+
}
|
|
1993
|
+
for (const file of collectEntryFiles(target).slice(0, 3)) {
|
|
1994
|
+
const content = safeReadFile(file);
|
|
1995
|
+
if (content) {
|
|
1996
|
+
artifacts.push({
|
|
1997
|
+
id: `repo-file-${file}`,
|
|
1998
|
+
kind: "file",
|
|
1999
|
+
source: "repo",
|
|
2000
|
+
title: file,
|
|
2001
|
+
ref: file,
|
|
2002
|
+
preview: truncate(content)
|
|
2003
|
+
});
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
} else {
|
|
2007
|
+
const content = safeReadFile(target);
|
|
2008
|
+
if (content) {
|
|
2009
|
+
artifacts.push({
|
|
2010
|
+
id: `repo-file-${target}`,
|
|
2011
|
+
kind: "file",
|
|
2012
|
+
source: "repo",
|
|
2013
|
+
title: target,
|
|
2014
|
+
ref: target,
|
|
2015
|
+
preview: truncate(content)
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
if (targets.length === 0) {
|
|
2021
|
+
const hints = extractSearchHints(topic);
|
|
2022
|
+
for (const hint of hints.slice(0, 5)) {
|
|
2023
|
+
const grepResult = safeGrep(hint);
|
|
2024
|
+
if (grepResult) {
|
|
2025
|
+
const files = grepResult.split("\n").filter(Boolean).slice(0, 10);
|
|
2026
|
+
for (const file of files) {
|
|
2027
|
+
if (!artifacts.some((a) => a.ref === file)) {
|
|
2028
|
+
artifacts.push({
|
|
2029
|
+
id: `repo-grep-${hint}-${file}`,
|
|
2030
|
+
kind: "grep",
|
|
2031
|
+
source: "repo",
|
|
2032
|
+
title: `Match: ${hint} \u2192 ${file}`,
|
|
2033
|
+
ref: file,
|
|
2034
|
+
preview: file
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
}
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
const hasGitSource = plan.sources.some((s) => s.kind === "git");
|
|
2042
|
+
if (!hasGitSource) {
|
|
2043
|
+
const recentLog = gitLog();
|
|
2044
|
+
if (recentLog) {
|
|
2045
|
+
artifacts.push({
|
|
2046
|
+
id: "repo-recent-activity",
|
|
2047
|
+
kind: "doc",
|
|
2048
|
+
source: "repo",
|
|
2049
|
+
title: "Recent Git Activity",
|
|
2050
|
+
preview: recentLog
|
|
2051
|
+
});
|
|
2052
|
+
}
|
|
2053
|
+
}
|
|
2054
|
+
logger.info("arena.provider.repo", { artifactCount: artifacts.length });
|
|
2055
|
+
return artifacts;
|
|
2056
|
+
}
|
|
2057
|
+
};
|
|
2058
|
+
function buildTree(dir, maxDepth, depth = 0, prefix = "") {
|
|
2059
|
+
if (depth >= maxDepth) return "";
|
|
2060
|
+
try {
|
|
2061
|
+
const entries = readdirSync(dir).filter(
|
|
2062
|
+
(e) => !e.startsWith(".") && e !== "node_modules" && e !== "dist" && e !== "__pycache__"
|
|
2063
|
+
);
|
|
2064
|
+
const lines = [];
|
|
2065
|
+
for (const entry of entries.slice(0, 30)) {
|
|
2066
|
+
const fullPath = join(dir, entry);
|
|
2067
|
+
const isDir = statSync(fullPath).isDirectory();
|
|
2068
|
+
lines.push(`${prefix}${isDir ? "/" : ""} ${entry}`);
|
|
2069
|
+
if (isDir && depth < maxDepth - 1) {
|
|
2070
|
+
lines.push(buildTree(fullPath, maxDepth, depth + 1, prefix + " "));
|
|
2071
|
+
}
|
|
2072
|
+
}
|
|
2073
|
+
return lines.filter(Boolean).join("\n");
|
|
2074
|
+
} catch {
|
|
2075
|
+
return "";
|
|
2076
|
+
}
|
|
2077
|
+
}
|
|
2078
|
+
function collectEntryFiles(dir) {
|
|
2079
|
+
const files = [];
|
|
2080
|
+
try {
|
|
2081
|
+
const entries = readdirSync(dir);
|
|
2082
|
+
const priority = ["index.ts", "index.js", "main.ts", "main.js", "mod.ts", "__init__.py"];
|
|
2083
|
+
for (const p of priority) {
|
|
2084
|
+
if (entries.includes(p)) files.push(join(dir, p));
|
|
2085
|
+
}
|
|
2086
|
+
for (const entry of entries) {
|
|
2087
|
+
const full = join(dir, entry);
|
|
2088
|
+
if (/\.(ts|js|tsx|jsx|py)$/.test(entry) && !files.includes(full)) {
|
|
2089
|
+
files.push(full);
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
} catch {
|
|
2093
|
+
}
|
|
2094
|
+
return files;
|
|
2095
|
+
}
|
|
2096
|
+
function safeReadFile(filePath) {
|
|
2097
|
+
try {
|
|
2098
|
+
if (!existsSync(filePath)) return null;
|
|
2099
|
+
const st = statSync(filePath);
|
|
2100
|
+
if (st.size > 5e5) return null;
|
|
2101
|
+
return readFileSync(filePath, "utf-8");
|
|
2102
|
+
} catch {
|
|
2103
|
+
return null;
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
function truncate(content) {
|
|
2107
|
+
if (content.length <= MAX_FILE_CHARS) return content;
|
|
2108
|
+
const t = content.slice(0, MAX_FILE_CHARS);
|
|
2109
|
+
const lastNl = t.lastIndexOf("\n");
|
|
2110
|
+
return t.slice(0, lastNl) + `
|
|
2111
|
+
... (truncated, ${content.length} chars total)`;
|
|
2112
|
+
}
|
|
2113
|
+
function safeGrep(pattern) {
|
|
2114
|
+
try {
|
|
2115
|
+
const result = execFileSync2("grep", [
|
|
2116
|
+
"-rl",
|
|
2117
|
+
"--include=*.ts",
|
|
2118
|
+
"--include=*.js",
|
|
2119
|
+
"--include=*.py",
|
|
2120
|
+
"--include=*.md",
|
|
2121
|
+
"-i",
|
|
2122
|
+
pattern,
|
|
2123
|
+
"."
|
|
2124
|
+
], { encoding: "utf-8", maxBuffer: 1024 * 1024, timeout: 1e4 });
|
|
2125
|
+
return result.trim();
|
|
2126
|
+
} catch {
|
|
2127
|
+
return "";
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
function gitLog() {
|
|
2131
|
+
try {
|
|
2132
|
+
return execFileSync2("git", ["log", "--oneline", "-10"], {
|
|
2133
|
+
encoding: "utf-8",
|
|
2134
|
+
maxBuffer: 1024 * 1024,
|
|
2135
|
+
timeout: 1e4
|
|
2136
|
+
}).trim();
|
|
2137
|
+
} catch {
|
|
2138
|
+
return "";
|
|
2139
|
+
}
|
|
2140
|
+
}
|
|
2141
|
+
function extractSearchHints(topic) {
|
|
2142
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
2143
|
+
"the",
|
|
2144
|
+
"a",
|
|
2145
|
+
"an",
|
|
2146
|
+
"is",
|
|
2147
|
+
"are",
|
|
2148
|
+
"was",
|
|
2149
|
+
"were",
|
|
2150
|
+
"be",
|
|
2151
|
+
"been",
|
|
2152
|
+
"have",
|
|
2153
|
+
"has",
|
|
2154
|
+
"do",
|
|
2155
|
+
"does",
|
|
2156
|
+
"did",
|
|
2157
|
+
"will",
|
|
2158
|
+
"would",
|
|
2159
|
+
"could",
|
|
2160
|
+
"should",
|
|
2161
|
+
"may",
|
|
2162
|
+
"might",
|
|
2163
|
+
"to",
|
|
2164
|
+
"of",
|
|
2165
|
+
"in",
|
|
2166
|
+
"for",
|
|
2167
|
+
"on",
|
|
2168
|
+
"with",
|
|
2169
|
+
"at",
|
|
2170
|
+
"by",
|
|
2171
|
+
"from",
|
|
2172
|
+
"about",
|
|
2173
|
+
"and",
|
|
2174
|
+
"but",
|
|
2175
|
+
"or",
|
|
2176
|
+
"not",
|
|
2177
|
+
"this",
|
|
2178
|
+
"that",
|
|
2179
|
+
"my",
|
|
2180
|
+
"your",
|
|
2181
|
+
"what",
|
|
2182
|
+
"how",
|
|
2183
|
+
"review",
|
|
2184
|
+
"discuss",
|
|
2185
|
+
"plan",
|
|
2186
|
+
"help",
|
|
2187
|
+
"please",
|
|
2188
|
+
"look",
|
|
2189
|
+
"check",
|
|
2190
|
+
"\u4E00\u4E0B",
|
|
2191
|
+
"\u770B\u770B",
|
|
2192
|
+
"\u5E2E",
|
|
2193
|
+
"\u6211",
|
|
2194
|
+
"\u7684",
|
|
2195
|
+
"\u662F\u5426",
|
|
2196
|
+
"\u600E\u4E48",
|
|
2197
|
+
"\u5982\u4F55",
|
|
2198
|
+
"\u8BA8\u8BBA",
|
|
2199
|
+
"\u89C4\u5212",
|
|
2200
|
+
"\u5BA1\u67E5"
|
|
2201
|
+
]);
|
|
2202
|
+
return topic.split(/[\s,;:]+/).map((w) => w.toLowerCase().replace(/[^a-z0-9_\-/]/g, "")).filter((w) => w.length > 2 && !stopWords.has(w));
|
|
2203
|
+
}
|
|
2204
|
+
|
|
2205
|
+
// src/arena/providers/docs.ts
|
|
2206
|
+
init_esm_shims();
|
|
2207
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
2208
|
+
import { join as join2, extname } from "path";
|
|
2209
|
+
var MAX_DOC_CHARS = 1e4;
|
|
2210
|
+
var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".txt", ".rst", ".adoc", ".org"]);
|
|
2211
|
+
var docsProvider = {
|
|
2212
|
+
kind: "docs",
|
|
2213
|
+
collect(plan, topic) {
|
|
2214
|
+
const artifacts = [];
|
|
2215
|
+
const sourceSpec = plan.sources.find((s) => s.kind === "docs");
|
|
2216
|
+
const targets = sourceSpec?.targets ?? [];
|
|
2217
|
+
for (const target of targets) {
|
|
2218
|
+
const content = safeReadDoc(target);
|
|
2219
|
+
if (content) {
|
|
2220
|
+
artifacts.push({
|
|
2221
|
+
id: `doc-${target}`,
|
|
2222
|
+
kind: "doc",
|
|
2223
|
+
source: "docs",
|
|
2224
|
+
title: target,
|
|
2225
|
+
ref: target,
|
|
2226
|
+
preview: truncate2(content)
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
if (targets.length === 0) {
|
|
2231
|
+
const docDirs = ["docs", "doc", "design", "specs", "proposals", "."];
|
|
2232
|
+
const found = /* @__PURE__ */ new Set();
|
|
2233
|
+
for (const dir of docDirs) {
|
|
2234
|
+
if (!existsSync2(dir)) continue;
|
|
2235
|
+
const docs = findDocFiles(dir, 2);
|
|
2236
|
+
for (const docPath of docs) {
|
|
2237
|
+
if (found.has(docPath)) continue;
|
|
2238
|
+
found.add(docPath);
|
|
2239
|
+
const name = docPath.toLowerCase();
|
|
2240
|
+
const topicLower = topic.toLowerCase();
|
|
2241
|
+
const keywords = topicLower.split(/\s+/).filter((w) => w.length > 2);
|
|
2242
|
+
const isRelevant = keywords.some((kw) => name.includes(kw));
|
|
2243
|
+
if (isRelevant || found.size <= 5) {
|
|
2244
|
+
const content = safeReadDoc(docPath);
|
|
2245
|
+
if (content) {
|
|
2246
|
+
artifacts.push({
|
|
2247
|
+
id: `doc-${docPath}`,
|
|
2248
|
+
kind: "doc",
|
|
2249
|
+
source: "docs",
|
|
2250
|
+
title: docPath,
|
|
2251
|
+
ref: docPath,
|
|
2252
|
+
preview: truncate2(content)
|
|
2253
|
+
});
|
|
2254
|
+
}
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
}
|
|
2259
|
+
logger.info("arena.provider.docs", { artifactCount: artifacts.length });
|
|
2260
|
+
return artifacts;
|
|
2261
|
+
}
|
|
2262
|
+
};
|
|
2263
|
+
function findDocFiles(dir, maxDepth, depth = 0) {
|
|
2264
|
+
if (depth >= maxDepth) return [];
|
|
2265
|
+
const files = [];
|
|
2266
|
+
try {
|
|
2267
|
+
const entries = readdirSync2(dir);
|
|
2268
|
+
for (const entry of entries) {
|
|
2269
|
+
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
2270
|
+
const full = join2(dir, entry);
|
|
2271
|
+
const st = statSync2(full);
|
|
2272
|
+
if (st.isFile() && DOC_EXTENSIONS.has(extname(entry).toLowerCase())) {
|
|
2273
|
+
files.push(full);
|
|
2274
|
+
} else if (st.isDirectory() && depth < maxDepth - 1) {
|
|
2275
|
+
files.push(...findDocFiles(full, maxDepth, depth + 1));
|
|
2276
|
+
}
|
|
2277
|
+
}
|
|
2278
|
+
} catch {
|
|
2279
|
+
}
|
|
2280
|
+
return files;
|
|
2281
|
+
}
|
|
2282
|
+
function safeReadDoc(filePath) {
|
|
2283
|
+
try {
|
|
2284
|
+
if (!existsSync2(filePath)) return null;
|
|
2285
|
+
const st = statSync2(filePath);
|
|
2286
|
+
if (st.size > 5e5) return null;
|
|
2287
|
+
return readFileSync2(filePath, "utf-8");
|
|
2288
|
+
} catch {
|
|
2289
|
+
return null;
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2292
|
+
function truncate2(content) {
|
|
2293
|
+
if (content.length <= MAX_DOC_CHARS) return content;
|
|
2294
|
+
const t = content.slice(0, MAX_DOC_CHARS);
|
|
2295
|
+
const lastNl = t.lastIndexOf("\n");
|
|
2296
|
+
return t.slice(0, lastNl) + `
|
|
2297
|
+
... (truncated, ${content.length} chars total)`;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
// src/arena/providers/none.ts
|
|
2301
|
+
init_esm_shims();
|
|
2302
|
+
var noneProvider = {
|
|
2303
|
+
kind: "none",
|
|
2304
|
+
collect(_plan, _topic) {
|
|
2305
|
+
return [];
|
|
2306
|
+
}
|
|
2307
|
+
};
|
|
2308
|
+
|
|
2309
|
+
// src/arena/providers/index.ts
|
|
2310
|
+
var PROVIDER_MAP = {
|
|
2311
|
+
git: gitProvider,
|
|
2312
|
+
repo: repoProvider,
|
|
2313
|
+
docs: docsProvider,
|
|
2314
|
+
web: noneProvider,
|
|
2315
|
+
// web deferred to Phase 4 — falls back to none for now
|
|
2316
|
+
none: noneProvider
|
|
2317
|
+
};
|
|
2318
|
+
function collectEvidence(plan, topic) {
|
|
2319
|
+
const artifacts = [];
|
|
2320
|
+
const quickFacts = [];
|
|
2321
|
+
const seenSources = /* @__PURE__ */ new Set();
|
|
2322
|
+
for (const sourceSpec of plan.sources) {
|
|
2323
|
+
if (seenSources.has(sourceSpec.kind)) continue;
|
|
2324
|
+
seenSources.add(sourceSpec.kind);
|
|
2325
|
+
const provider = PROVIDER_MAP[sourceSpec.kind];
|
|
2326
|
+
if (!provider) continue;
|
|
2327
|
+
const collected = provider.collect(plan, topic);
|
|
2328
|
+
artifacts.push(...collected);
|
|
2329
|
+
logger.info("arena.evidence_collected", {
|
|
2330
|
+
source: sourceSpec.kind,
|
|
2331
|
+
count: collected.length
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
const branchArtifact = artifacts.find((a) => a.id === "git-branch");
|
|
2335
|
+
if (branchArtifact) quickFacts.push({ label: "Branch", value: branchArtifact.preview });
|
|
2336
|
+
const changedFilesArtifact = artifacts.find((a) => a.id === "git-changed-files");
|
|
2337
|
+
if (changedFilesArtifact?.metadata?.totalCount) {
|
|
2338
|
+
quickFacts.push({ label: "Changed Files", value: String(changedFilesArtifact.metadata.totalCount) });
|
|
2339
|
+
}
|
|
2340
|
+
const docCount = artifacts.filter((a) => a.source === "docs").length;
|
|
2341
|
+
if (docCount > 0) quickFacts.push({ label: "Documents", value: String(docCount) });
|
|
2342
|
+
quickFacts.push({ label: "Sources", value: [...seenSources].join(", ") });
|
|
2343
|
+
quickFacts.push({ label: "Lenses", value: plan.lenses.map((l) => l.name).join(", ") });
|
|
2344
|
+
return { artifacts, quickFacts };
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
// src/arena/tools/selector.ts
|
|
2348
|
+
init_esm_shims();
|
|
2349
|
+
|
|
2350
|
+
// src/arena/context/context-tools.ts
|
|
2351
|
+
init_esm_shims();
|
|
2352
|
+
import { readFileSync as readFileSync3, existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync3 } from "fs";
|
|
2353
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
2354
|
+
import { resolve, join as join3 } from "path";
|
|
2355
|
+
var MAX_TOOL_RESULT = 15e3;
|
|
2356
|
+
var MAX_TOOL_ROUNDS = 5;
|
|
2357
|
+
var REPO_ROOT = resolve(".");
|
|
2358
|
+
var CONTEXT_TOOLS = [
|
|
2359
|
+
{
|
|
2360
|
+
name: "read_file",
|
|
2361
|
+
description: "Read a file from the repository. Use this when you need to see the full source of a file referenced in the context, or to check surrounding code (callers, type definitions, etc.).",
|
|
2362
|
+
inputSchema: {
|
|
2363
|
+
type: "object",
|
|
2364
|
+
properties: {
|
|
2365
|
+
path: { type: "string", description: "Relative file path from repo root" },
|
|
2366
|
+
offset: { type: "number", description: "Start line (1-based, optional)" },
|
|
2367
|
+
limit: { type: "number", description: "Max lines to read (optional, default 200)" }
|
|
2368
|
+
},
|
|
2369
|
+
required: ["path"]
|
|
2370
|
+
}
|
|
2371
|
+
},
|
|
2372
|
+
{
|
|
2373
|
+
name: "grep_code",
|
|
2374
|
+
description: "Search for a pattern across the codebase. Use this to find callers, references, type definitions, or usages of symbols.",
|
|
2375
|
+
inputSchema: {
|
|
2376
|
+
type: "object",
|
|
2377
|
+
properties: {
|
|
2378
|
+
pattern: { type: "string", description: "Regex pattern to search for" },
|
|
2379
|
+
glob: { type: "string", description: "File glob filter, e.g. '*.ts' (optional)" }
|
|
2380
|
+
},
|
|
2381
|
+
required: ["pattern"]
|
|
2382
|
+
}
|
|
2383
|
+
},
|
|
2384
|
+
{
|
|
2385
|
+
name: "list_files",
|
|
2386
|
+
description: "List files in a directory. Use this to understand project structure around relevant files.",
|
|
2387
|
+
inputSchema: {
|
|
2388
|
+
type: "object",
|
|
2389
|
+
properties: {
|
|
2390
|
+
path: { type: "string", description: "Directory path relative to repo root (default: '.')" }
|
|
2391
|
+
},
|
|
2392
|
+
required: []
|
|
2393
|
+
}
|
|
2394
|
+
},
|
|
2395
|
+
{
|
|
2396
|
+
name: "git_show",
|
|
2397
|
+
description: "Show a git object (commit, file at a ref, etc.). Use for inspecting specific commits or viewing a file at a different branch/commit.",
|
|
2398
|
+
inputSchema: {
|
|
2399
|
+
type: "object",
|
|
2400
|
+
properties: {
|
|
2401
|
+
ref: { type: "string", description: "Git ref, e.g. 'HEAD~3', 'main:src/foo.ts', commit hash" }
|
|
2402
|
+
},
|
|
2403
|
+
required: ["ref"]
|
|
2404
|
+
}
|
|
2405
|
+
},
|
|
2406
|
+
{
|
|
2407
|
+
name: "git_blame",
|
|
2408
|
+
description: "Show git blame for a file. Use to understand who changed what and when.",
|
|
2409
|
+
inputSchema: {
|
|
2410
|
+
type: "object",
|
|
2411
|
+
properties: {
|
|
2412
|
+
path: { type: "string", description: "File path" },
|
|
2413
|
+
lines: { type: "string", description: "Line range, e.g. '10,20' (optional)" }
|
|
2414
|
+
},
|
|
2415
|
+
required: ["path"]
|
|
2416
|
+
}
|
|
2417
|
+
}
|
|
2418
|
+
];
|
|
2419
|
+
function executeContextTool(tc) {
|
|
2420
|
+
try {
|
|
2421
|
+
switch (tc.toolName) {
|
|
2422
|
+
case "read_file":
|
|
2423
|
+
return executeReadFile(tc.args);
|
|
2424
|
+
case "grep_code":
|
|
2425
|
+
return executeGrepCode(tc.args);
|
|
2426
|
+
case "list_files":
|
|
2427
|
+
return executeListFiles(tc.args);
|
|
2428
|
+
case "git_show":
|
|
2429
|
+
return executeGitShow(tc.args);
|
|
2430
|
+
case "git_blame":
|
|
2431
|
+
return executeGitBlame(tc.args);
|
|
2432
|
+
default:
|
|
2433
|
+
return `Unknown tool: ${tc.toolName}`;
|
|
2434
|
+
}
|
|
2435
|
+
} catch (err) {
|
|
2436
|
+
return `Tool error: ${err.message}`;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
function validatePath(filePath) {
|
|
2440
|
+
const resolved = resolve(filePath);
|
|
2441
|
+
if (resolved === REPO_ROOT || resolved.startsWith(REPO_ROOT + "/")) {
|
|
2442
|
+
return resolved;
|
|
2443
|
+
}
|
|
2444
|
+
return null;
|
|
2445
|
+
}
|
|
2446
|
+
function executeReadFile(args) {
|
|
2447
|
+
const filePath = args.path;
|
|
2448
|
+
if (!validatePath(filePath)) return `Error: path outside repository: ${filePath}`;
|
|
2449
|
+
if (!existsSync3(filePath)) return `Error: file not found: ${filePath}`;
|
|
2450
|
+
const raw = readFileSync3(filePath, "utf-8");
|
|
2451
|
+
const lines = raw.split("\n");
|
|
2452
|
+
const offset = Math.max(0, (args.offset ?? 1) - 1);
|
|
2453
|
+
const limit = args.limit ?? 200;
|
|
2454
|
+
const slice = lines.slice(offset, offset + limit);
|
|
2455
|
+
const numbered = slice.map((l, i) => `${offset + i + 1} ${l}`).join("\n");
|
|
2456
|
+
return truncateResult(numbered);
|
|
2457
|
+
}
|
|
2458
|
+
function executeGrepCode(args) {
|
|
2459
|
+
const pattern = args.pattern;
|
|
2460
|
+
const glob = args.glob;
|
|
2461
|
+
const grepArgs = ["-rn", "-E", "--"];
|
|
2462
|
+
if (glob) {
|
|
2463
|
+
grepArgs.splice(1, 0, `--include=${glob}`);
|
|
2464
|
+
}
|
|
2465
|
+
grepArgs.push(pattern, ".");
|
|
2466
|
+
const result = execFileSafe("grep", grepArgs, 80);
|
|
2467
|
+
return result || "No matches found.";
|
|
2468
|
+
}
|
|
2469
|
+
function executeListFiles(args) {
|
|
2470
|
+
const dir = args.path || ".";
|
|
2471
|
+
if (!validatePath(dir)) return `Error: path outside repository: ${dir}`;
|
|
2472
|
+
try {
|
|
2473
|
+
const entries = readdirSync3(dir);
|
|
2474
|
+
const lines = [];
|
|
2475
|
+
for (const entry of entries.slice(0, 50)) {
|
|
2476
|
+
const fullPath = join3(dir, entry);
|
|
2477
|
+
try {
|
|
2478
|
+
const st = statSync3(fullPath);
|
|
2479
|
+
const type = st.isDirectory() ? "d" : "-";
|
|
2480
|
+
const size = st.isDirectory() ? "" : ` ${st.size}`;
|
|
2481
|
+
lines.push(`${type} ${entry}${size}`);
|
|
2482
|
+
} catch {
|
|
2483
|
+
lines.push(`? ${entry}`);
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2486
|
+
return lines.join("\n") || `Empty directory: ${dir}`;
|
|
2487
|
+
} catch {
|
|
2488
|
+
return `Directory not found: ${dir}`;
|
|
2489
|
+
}
|
|
2490
|
+
}
|
|
2491
|
+
function executeGitShow(args) {
|
|
2492
|
+
const ref = sanitizeGitRef(args.ref);
|
|
2493
|
+
if (!ref) return "Error: invalid git ref";
|
|
2494
|
+
const result = execFileSafe("git", ["show", "--", ref], 200);
|
|
2495
|
+
return truncateResult(result || `Could not resolve: ${ref}`);
|
|
2496
|
+
}
|
|
2497
|
+
function executeGitBlame(args) {
|
|
2498
|
+
const filePath = args.path;
|
|
2499
|
+
if (!validatePath(filePath)) return `Error: path outside repository: ${filePath}`;
|
|
2500
|
+
const lines = args.lines;
|
|
2501
|
+
const gitArgs = ["blame"];
|
|
2502
|
+
if (lines && /^\d+,\d+$/.test(lines)) {
|
|
2503
|
+
gitArgs.push(`-L`, lines);
|
|
2504
|
+
}
|
|
2505
|
+
gitArgs.push("--", filePath);
|
|
2506
|
+
const result = execFileSafe("git", gitArgs, 50);
|
|
2507
|
+
return result || `Could not blame: ${filePath}`;
|
|
2508
|
+
}
|
|
2509
|
+
function sanitizeGitRef(ref) {
|
|
2510
|
+
if (!ref || ref.length > 200) return null;
|
|
2511
|
+
const cleaned = ref.replace(/[^a-zA-Z0-9/_\-~^.:]/g, "");
|
|
2512
|
+
if (/\.\.\/|;\s|&&|\|\||`|\$\(/.test(cleaned)) return null;
|
|
2513
|
+
return cleaned || null;
|
|
2514
|
+
}
|
|
2515
|
+
function execFileSafe(cmd, args, maxLines) {
|
|
2516
|
+
try {
|
|
2517
|
+
const raw = execFileSync3(cmd, args, {
|
|
2518
|
+
encoding: "utf-8",
|
|
2519
|
+
maxBuffer: 512 * 1024,
|
|
2520
|
+
timeout: 1e4
|
|
2521
|
+
}).trim();
|
|
2522
|
+
if (maxLines) {
|
|
2523
|
+
const lines = raw.split("\n");
|
|
2524
|
+
if (lines.length > maxLines) {
|
|
2525
|
+
return lines.slice(0, maxLines).join("\n") + `
|
|
2526
|
+
... (${lines.length - maxLines} more lines)`;
|
|
2527
|
+
}
|
|
2528
|
+
}
|
|
2529
|
+
return raw;
|
|
2530
|
+
} catch {
|
|
2531
|
+
return "";
|
|
2532
|
+
}
|
|
2533
|
+
}
|
|
2534
|
+
function truncateResult(text) {
|
|
2535
|
+
if (text.length <= MAX_TOOL_RESULT) return text;
|
|
2536
|
+
return text.slice(0, MAX_TOOL_RESULT) + "\n... (truncated)";
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
// src/arena/tools/selector.ts
|
|
2540
|
+
var TOOL_PACKS = {
|
|
2541
|
+
repo_readonly: {
|
|
2542
|
+
name: "repo_readonly",
|
|
2543
|
+
toolNames: ["read_file", "grep_code", "list_files"]
|
|
2544
|
+
},
|
|
2545
|
+
git: {
|
|
2546
|
+
name: "git",
|
|
2547
|
+
toolNames: ["read_file", "grep_code", "list_files", "git_show", "git_blame"]
|
|
2548
|
+
},
|
|
2549
|
+
docs: {
|
|
2550
|
+
name: "docs",
|
|
2551
|
+
toolNames: ["read_file", "list_files"]
|
|
2552
|
+
},
|
|
2553
|
+
no_tools: {
|
|
2554
|
+
name: "no_tools",
|
|
2555
|
+
toolNames: []
|
|
2556
|
+
}
|
|
2557
|
+
};
|
|
2558
|
+
var SOURCE_TO_PACK = {
|
|
2559
|
+
git: "git",
|
|
2560
|
+
repo: "repo_readonly",
|
|
2561
|
+
docs: "docs",
|
|
2562
|
+
web: "no_tools",
|
|
2563
|
+
// web tools not yet available in arena context
|
|
2564
|
+
none: "no_tools"
|
|
2565
|
+
};
|
|
2566
|
+
function selectTools(plan) {
|
|
2567
|
+
const toolNames = /* @__PURE__ */ new Set();
|
|
2568
|
+
for (const source of plan.sources) {
|
|
2569
|
+
const packName = source.toolPack ?? SOURCE_TO_PACK[source.kind] ?? "no_tools";
|
|
2570
|
+
const pack = TOOL_PACKS[packName] ?? TOOL_PACKS.no_tools;
|
|
2571
|
+
for (const name of pack.toolNames) {
|
|
2572
|
+
toolNames.add(name);
|
|
2573
|
+
}
|
|
2574
|
+
}
|
|
2575
|
+
if (toolNames.size === 0) return [];
|
|
2576
|
+
return CONTEXT_TOOLS.filter((t) => toolNames.has(t.name));
|
|
2577
|
+
}
|
|
2578
|
+
function hasTools(plan) {
|
|
2579
|
+
return plan.sources.some((s) => {
|
|
2580
|
+
const packName = SOURCE_TO_PACK[s.kind];
|
|
2581
|
+
const pack = TOOL_PACKS[packName];
|
|
2582
|
+
return pack && pack.toolNames.length > 0;
|
|
2583
|
+
});
|
|
2584
|
+
}
|
|
2585
|
+
|
|
2586
|
+
// src/arena/phases/participant-research.ts
|
|
2587
|
+
init_esm_shims();
|
|
2588
|
+
import { createHash } from "crypto";
|
|
2589
|
+
async function runParticipantResearch(options) {
|
|
2590
|
+
const results = await runParticipantResearchWithDossiers(options);
|
|
2591
|
+
return results.map((r) => r.report);
|
|
2592
|
+
}
|
|
2593
|
+
async function runParticipantResearchWithDossiers(options) {
|
|
2594
|
+
const { participants, strategy, topic, baseContext, enableContextTools, contextTools, signal, onProgress } = options;
|
|
2595
|
+
const tools = enableContextTools ? contextTools ?? CONTEXT_TOOLS : void 0;
|
|
2596
|
+
const tasks = participants.map(async (p) => {
|
|
2597
|
+
onProgress?.({ type: "research_start", participant: p.name });
|
|
2598
|
+
const client = await createLLMClient({
|
|
2599
|
+
...p.llm,
|
|
2600
|
+
enableStreaming: false
|
|
2601
|
+
});
|
|
2602
|
+
const messages = [
|
|
2603
|
+
{
|
|
2604
|
+
role: "user",
|
|
2605
|
+
content: strategy.researchUserPrompt(topic, baseContext)
|
|
2606
|
+
}
|
|
2607
|
+
];
|
|
2608
|
+
let finalText = "";
|
|
2609
|
+
let totalRounds = 0;
|
|
2610
|
+
const toolTraces = [];
|
|
2611
|
+
const evidencePackets = [];
|
|
2612
|
+
const MAX_MESSAGES = 30;
|
|
2613
|
+
for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
|
|
2614
|
+
totalRounds = round;
|
|
2615
|
+
if (messages.length >= MAX_MESSAGES) {
|
|
2616
|
+
logger.warn("arena.research_message_limit", {
|
|
2617
|
+
participant: p.name,
|
|
2618
|
+
messageCount: messages.length,
|
|
2619
|
+
round
|
|
2620
|
+
});
|
|
2621
|
+
break;
|
|
2622
|
+
}
|
|
2623
|
+
const response = await client.createMessage({
|
|
2624
|
+
systemPrompt: strategy.researchSystemPrompt(p.name),
|
|
2625
|
+
messages,
|
|
2626
|
+
tools,
|
|
2627
|
+
signal
|
|
2628
|
+
});
|
|
2629
|
+
const toolNames = (response.toolCalls ?? []).map((tc) => tc.toolName);
|
|
2630
|
+
const hasTools2 = response.toolCalls && response.toolCalls.length > 0;
|
|
2631
|
+
logger.info("arena.research_round", {
|
|
2632
|
+
participant: p.name,
|
|
2633
|
+
round,
|
|
2634
|
+
toolCount: response.toolCalls?.length ?? 0,
|
|
2635
|
+
toolNames,
|
|
2636
|
+
textLen: response.text?.length ?? 0,
|
|
2637
|
+
stopReason: response.stopReason ?? "unknown"
|
|
2638
|
+
});
|
|
2639
|
+
if (!hasTools2) {
|
|
2640
|
+
finalText = response.text;
|
|
2641
|
+
break;
|
|
2642
|
+
}
|
|
2643
|
+
onProgress?.({
|
|
2644
|
+
type: "context_lookup",
|
|
2645
|
+
participant: p.name,
|
|
2646
|
+
tools: response.toolCalls.map((tc) => `${tc.toolName}(${JSON.stringify(tc.args)})`)
|
|
2647
|
+
});
|
|
2648
|
+
const assistantBlocks = [];
|
|
2649
|
+
if (response.text) {
|
|
2650
|
+
assistantBlocks.push({ type: "text", text: response.text });
|
|
2651
|
+
}
|
|
2652
|
+
for (const tc of response.toolCalls) {
|
|
2653
|
+
assistantBlocks.push({
|
|
2654
|
+
type: "tool_use",
|
|
2655
|
+
id: tc.id,
|
|
2656
|
+
name: tc.toolName,
|
|
2657
|
+
input: tc.args
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
messages.push({ role: "assistant", content: assistantBlocks });
|
|
2661
|
+
const resultBlocks = [];
|
|
2662
|
+
for (const tc of response.toolCalls) {
|
|
2663
|
+
const result = executeContextTool(tc);
|
|
2664
|
+
const trace = {
|
|
2665
|
+
round,
|
|
2666
|
+
toolName: tc.toolName,
|
|
2667
|
+
args: tc.args,
|
|
2668
|
+
resultRef: buildResultRef(tc),
|
|
2669
|
+
keptAsEvidence: result.length > 50 && !result.startsWith("Error:") && !result.startsWith("Tool error:") && !result.startsWith("Unknown tool:")
|
|
2670
|
+
};
|
|
2671
|
+
toolTraces.push(trace);
|
|
2672
|
+
if (trace.keptAsEvidence) {
|
|
2673
|
+
const packet = buildEvidencePacketFromTool(p.name, tc, result, inferSourceKind(tc.toolName));
|
|
2674
|
+
evidencePackets.push(packet);
|
|
2675
|
+
}
|
|
2676
|
+
resultBlocks.push({
|
|
2677
|
+
type: "tool_result",
|
|
2678
|
+
tool_use_id: tc.id,
|
|
2679
|
+
content: result
|
|
2680
|
+
});
|
|
2681
|
+
}
|
|
2682
|
+
messages.push({ role: "user", content: resultBlocks });
|
|
2683
|
+
}
|
|
2684
|
+
if (!finalText) {
|
|
2685
|
+
logger.warn("arena.research_force_conclude", {
|
|
2686
|
+
participant: p.name,
|
|
2687
|
+
totalRounds,
|
|
2688
|
+
messageCount: messages.length
|
|
2689
|
+
});
|
|
2690
|
+
const forceResponse = await client.createMessage({
|
|
2691
|
+
systemPrompt: strategy.researchSystemPrompt(p.name),
|
|
2692
|
+
messages: [
|
|
2693
|
+
...messages,
|
|
2694
|
+
{
|
|
2695
|
+
role: "user",
|
|
2696
|
+
content: "You have gathered enough context. Based on ALL the tool results above, output your findings NOW.\n\nRespond ONLY with JSON \u2014 output your highest-confidence findings (typically 5-15 for a substantive topic; each finding's `summary` should be 80+ words with concrete evidence). Do NOT request any more tools. Just output the JSON."
|
|
2697
|
+
}
|
|
2698
|
+
]
|
|
2699
|
+
});
|
|
2700
|
+
logger.info("arena.research_force_conclude_response", {
|
|
2701
|
+
participant: p.name,
|
|
2702
|
+
textLen: forceResponse.text?.length ?? 0,
|
|
2703
|
+
stopReason: forceResponse.stopReason ?? "unknown"
|
|
2704
|
+
});
|
|
2705
|
+
finalText = forceResponse.text;
|
|
2706
|
+
}
|
|
2707
|
+
logger.info("arena.research_raw_response", {
|
|
2708
|
+
participant: p.name,
|
|
2709
|
+
textLength: finalText.length,
|
|
2710
|
+
text: finalText
|
|
2711
|
+
});
|
|
2712
|
+
if (!finalText || finalText.trim().length === 0) {
|
|
2713
|
+
logger.warn("arena.research_failed", {
|
|
2714
|
+
participant: p.name,
|
|
2715
|
+
totalRounds,
|
|
2716
|
+
messageCount: messages.length
|
|
2717
|
+
});
|
|
2718
|
+
const failedReport = {
|
|
2719
|
+
participant: p.name,
|
|
2720
|
+
contextSummary: `(research failed: model returned empty response after ${totalRounds} tool rounds)`,
|
|
2721
|
+
findings: []
|
|
2722
|
+
};
|
|
2723
|
+
const failedDossier = {
|
|
2724
|
+
participant: p.name,
|
|
2725
|
+
contextSummary: failedReport.contextSummary,
|
|
2726
|
+
findings: [],
|
|
2727
|
+
toolTrace: toolTraces,
|
|
2728
|
+
evidencePackets,
|
|
2729
|
+
findingEvidenceLinks: []
|
|
2730
|
+
};
|
|
2731
|
+
onProgress?.({ type: "research_done", participant: p.name, report: failedReport });
|
|
2732
|
+
return { report: failedReport, dossier: failedDossier };
|
|
2733
|
+
}
|
|
2734
|
+
const report = strategy.parseResearchResponse(p.name, finalText);
|
|
2735
|
+
const findingEvidenceLinks = buildFindingEvidenceLinks(report, evidencePackets);
|
|
2736
|
+
const dossier = {
|
|
2737
|
+
participant: p.name,
|
|
2738
|
+
contextSummary: report.contextSummary,
|
|
2739
|
+
findings: report.findings,
|
|
2740
|
+
toolTrace: toolTraces,
|
|
2741
|
+
evidencePackets,
|
|
2742
|
+
findingEvidenceLinks
|
|
2743
|
+
};
|
|
2744
|
+
onProgress?.({ type: "research_done", participant: p.name, report });
|
|
2745
|
+
return { report, dossier };
|
|
2746
|
+
});
|
|
2747
|
+
return Promise.all(tasks);
|
|
2748
|
+
}
|
|
2749
|
+
function buildResultRef(tc) {
|
|
2750
|
+
if (tc.toolName === "read_file" && tc.args.path) return `file:${tc.args.path}`;
|
|
2751
|
+
if (tc.toolName === "grep_code" && tc.args.pattern) return `grep:${tc.args.pattern}`;
|
|
2752
|
+
if (tc.toolName === "list_files" && tc.args.path) return `dir:${tc.args.path}`;
|
|
2753
|
+
if (tc.toolName === "git_show" && tc.args.ref) return `git:${tc.args.ref}`;
|
|
2754
|
+
if (tc.toolName === "git_blame" && tc.args.path) return `blame:${tc.args.path}`;
|
|
2755
|
+
return `${tc.toolName}:${JSON.stringify(tc.args).slice(0, 60)}`;
|
|
2756
|
+
}
|
|
2757
|
+
function generatePacketId(participant, source, ref, snippet) {
|
|
2758
|
+
const hash = createHash("sha256").update(`${participant}:${source}:${ref}:${snippet.slice(0, 200)}`).digest("hex").slice(0, 12);
|
|
2759
|
+
return `pkt-${hash}`;
|
|
2760
|
+
}
|
|
2761
|
+
function inferSourceKind(toolName) {
|
|
2762
|
+
switch (toolName) {
|
|
2763
|
+
case "git_show":
|
|
2764
|
+
case "git_blame":
|
|
2765
|
+
return "git";
|
|
2766
|
+
case "read_file":
|
|
2767
|
+
case "grep_code":
|
|
2768
|
+
case "list_files":
|
|
2769
|
+
return "repo";
|
|
2770
|
+
default:
|
|
2771
|
+
return "repo";
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
function buildEvidencePacketFromTool(participant, tc, result, source) {
|
|
2775
|
+
const ref = buildResultRef(tc);
|
|
2776
|
+
const snippet = result.slice(0, 500);
|
|
2777
|
+
const packetId = generatePacketId(participant, source, ref, snippet);
|
|
2778
|
+
return {
|
|
2779
|
+
packetId,
|
|
2780
|
+
participant,
|
|
2781
|
+
source,
|
|
2782
|
+
title: `${tc.toolName}: ${formatToolArgs(tc.args)}`,
|
|
2783
|
+
refs: [ref],
|
|
2784
|
+
summary: result.slice(0, 200),
|
|
2785
|
+
excerpts: [{
|
|
2786
|
+
ref,
|
|
2787
|
+
snippet,
|
|
2788
|
+
note: `Result of ${tc.toolName} call`
|
|
2789
|
+
}]
|
|
2790
|
+
};
|
|
2791
|
+
}
|
|
2792
|
+
function formatToolArgs(args) {
|
|
2793
|
+
const parts = [];
|
|
2794
|
+
for (const [key, value] of Object.entries(args)) {
|
|
2795
|
+
if (typeof value === "string") parts.push(value.slice(0, 60));
|
|
2796
|
+
else parts.push(`${key}=${JSON.stringify(value)}`);
|
|
2797
|
+
}
|
|
2798
|
+
return parts.join(", ").slice(0, 100);
|
|
2799
|
+
}
|
|
2800
|
+
function buildFindingEvidenceLinks(report, packets) {
|
|
2801
|
+
const links = [];
|
|
2802
|
+
for (const finding of report.findings) {
|
|
2803
|
+
const matchedPacketIds = [];
|
|
2804
|
+
for (const evidence of finding.evidence) {
|
|
2805
|
+
const evidenceRef = `${evidence.type}:${evidence.ref}`;
|
|
2806
|
+
for (const packet of packets) {
|
|
2807
|
+
const matches = packet.refs.some((pRef) => {
|
|
2808
|
+
if (pRef === evidenceRef) return true;
|
|
2809
|
+
if (evidence.ref && pRef.includes(evidence.ref)) return true;
|
|
2810
|
+
return false;
|
|
2811
|
+
});
|
|
2812
|
+
if (matches && !matchedPacketIds.includes(packet.packetId)) {
|
|
2813
|
+
matchedPacketIds.push(packet.packetId);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
if (matchedPacketIds.length > 0) {
|
|
2818
|
+
links.push({
|
|
2819
|
+
findingId: finding.id,
|
|
2820
|
+
evidencePacketIds: matchedPacketIds
|
|
2821
|
+
});
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
return links;
|
|
2825
|
+
}
|
|
2826
|
+
|
|
2827
|
+
// src/arena/phases/claim-registry.ts
|
|
2828
|
+
init_esm_shims();
|
|
2829
|
+
function registerClaims(options) {
|
|
2830
|
+
const { dossiers, ledger, onProgress } = options;
|
|
2831
|
+
const claims = [];
|
|
2832
|
+
for (const dossier of dossiers) {
|
|
2833
|
+
const linkMap = /* @__PURE__ */ new Map();
|
|
2834
|
+
for (const link of dossier.findingEvidenceLinks) {
|
|
2835
|
+
linkMap.set(link.findingId, link.evidencePacketIds);
|
|
2836
|
+
}
|
|
2837
|
+
for (const finding of dossier.findings) {
|
|
2838
|
+
const claimId = `${dossier.participant}:${finding.id}`;
|
|
2839
|
+
const packetIds = linkMap.get(finding.id) ?? [];
|
|
2840
|
+
const evidenceRefs = finding.evidence.map((e) => `${e.type}:${e.ref}`);
|
|
2841
|
+
const claim = {
|
|
2842
|
+
claimId,
|
|
2843
|
+
owner: dossier.participant,
|
|
2844
|
+
finding,
|
|
2845
|
+
evidenceRefs,
|
|
2846
|
+
evidencePacketIds: packetIds,
|
|
2847
|
+
status: "proposed",
|
|
2848
|
+
challenges: [],
|
|
2849
|
+
debateRounds: []
|
|
2850
|
+
};
|
|
2851
|
+
claims.push(claim);
|
|
2852
|
+
ledger.appendClaim(claim);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
onProgress?.({ type: "claims_registered", claimCount: claims.length });
|
|
2856
|
+
return claims;
|
|
2857
|
+
}
|
|
2858
|
+
function selectClaimsForReview(claims, maxClaims) {
|
|
2859
|
+
const severityOrder = { high: 0, medium: 1, low: 2 };
|
|
2860
|
+
const sorted = [...claims].sort((a, b) => {
|
|
2861
|
+
const sevA = severityOrder[a.finding.severity ?? "medium"] ?? 1;
|
|
2862
|
+
const sevB = severityOrder[b.finding.severity ?? "medium"] ?? 1;
|
|
2863
|
+
if (sevA !== sevB) return sevA - sevB;
|
|
2864
|
+
return b.finding.confidence - a.finding.confidence;
|
|
2865
|
+
});
|
|
2866
|
+
return sorted.slice(0, maxClaims);
|
|
2867
|
+
}
|
|
2868
|
+
|
|
2869
|
+
// src/arena/digest-builder.ts
|
|
2870
|
+
init_esm_shims();
|
|
2871
|
+
function buildDigest(ledger, options) {
|
|
2872
|
+
const { round, relevantClaimIds } = options;
|
|
2873
|
+
const claimIdSet = new Set(relevantClaimIds);
|
|
2874
|
+
const snapshot = ledger.getSnapshot();
|
|
2875
|
+
const packetIds = /* @__PURE__ */ new Set();
|
|
2876
|
+
for (const claimId of relevantClaimIds) {
|
|
2877
|
+
const claim = ledger.getClaimById(claimId);
|
|
2878
|
+
if (claim) {
|
|
2879
|
+
for (const pid of claim.evidencePacketIds) {
|
|
2880
|
+
packetIds.add(pid);
|
|
2881
|
+
}
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
const evidencePackets = [];
|
|
2885
|
+
for (const pid of packetIds) {
|
|
2886
|
+
const packet = ledger.getPacketById(pid);
|
|
2887
|
+
if (packet) evidencePackets.push(packet);
|
|
2888
|
+
}
|
|
2889
|
+
const toolTraceSummary = [];
|
|
2890
|
+
for (const dossier of snapshot.dossiers) {
|
|
2891
|
+
for (const trace of dossier.toolTrace) {
|
|
2892
|
+
if (trace.keptAsEvidence) {
|
|
2893
|
+
toolTraceSummary.push({
|
|
2894
|
+
participant: dossier.participant,
|
|
2895
|
+
toolName: trace.toolName,
|
|
2896
|
+
ref: trace.resultRef
|
|
2897
|
+
});
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
const recentChallenges = snapshot.challenges.filter((c) => claimIdSet.has(c.claimId));
|
|
2902
|
+
const requestedChecks = snapshot.requestedChecks.filter((c) => claimIdSet.has(c.claimId));
|
|
2903
|
+
const priorAdjudications = snapshot.adjudications.filter((a) => claimIdSet.has(a.claimId));
|
|
2904
|
+
return {
|
|
2905
|
+
round,
|
|
2906
|
+
relevantClaimIds,
|
|
2907
|
+
evidencePackets,
|
|
2908
|
+
toolTraceSummary,
|
|
2909
|
+
recentChallenges,
|
|
2910
|
+
requestedChecks,
|
|
2911
|
+
priorAdjudications
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
function sanitize(text) {
|
|
2915
|
+
if (!text) return "";
|
|
2916
|
+
return text.replace(/<\/?(?:system|assistant|user|system-reminder)[^>]*>/gi, "").replace(/^(?:IGNORE|DISREGARD|FORGET|OVERRIDE|SYSTEM|INSTRUCTION)[:\s].*/gim, "").replace(/\n{3,}/g, "\n\n").slice(0, 2e3);
|
|
2917
|
+
}
|
|
2918
|
+
function formatDigest(digest) {
|
|
2919
|
+
const sections = [];
|
|
2920
|
+
sections.push(`## Round ${digest.round} Research Digest`);
|
|
2921
|
+
sections.push(`Claims under consideration: ${digest.relevantClaimIds.join(", ")}`);
|
|
2922
|
+
if (digest.evidencePackets.length > 0) {
|
|
2923
|
+
sections.push("\n### Evidence");
|
|
2924
|
+
for (const packet of digest.evidencePackets) {
|
|
2925
|
+
sections.push(`- [${packet.packetId}] ${sanitize(packet.title)} (${packet.source})`);
|
|
2926
|
+
sections.push(` ${sanitize(packet.summary)}`);
|
|
2927
|
+
for (const excerpt of packet.excerpts.slice(0, 3)) {
|
|
2928
|
+
sections.push(` > ${excerpt.ref}: ${sanitize(excerpt.snippet)}`);
|
|
2929
|
+
if (excerpt.note) sections.push(` Note: ${sanitize(excerpt.note)}`);
|
|
2930
|
+
}
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
if (digest.recentChallenges.length > 0) {
|
|
2934
|
+
sections.push("\n### Challenges");
|
|
2935
|
+
for (const c of digest.recentChallenges) {
|
|
2936
|
+
sections.push(`- [${c.reviewer}] on ${c.claimId}: ${c.verdict} \u2014 ${sanitize(c.reason)}`);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
if (digest.requestedChecks.length > 0) {
|
|
2940
|
+
sections.push("\n### Requested Checks");
|
|
2941
|
+
for (const check of digest.requestedChecks) {
|
|
2942
|
+
const pri = check.priority ? ` (${check.priority})` : "";
|
|
2943
|
+
sections.push(`- [${check.requestId}]${pri}: ${sanitize(check.description)}`);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2946
|
+
if (digest.priorAdjudications.length > 0) {
|
|
2947
|
+
sections.push("\n### Prior Adjudications");
|
|
2948
|
+
for (const adj of digest.priorAdjudications) {
|
|
2949
|
+
sections.push(`- ${adj.claimId}: ${adj.outcome} \u2014 ${sanitize(adj.rationale)}`);
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
return sections.join("\n");
|
|
2953
|
+
}
|
|
2954
|
+
|
|
2955
|
+
// src/arena/transitions.ts
|
|
2956
|
+
init_esm_shims();
|
|
2957
|
+
var VALID_TRANSITIONS = {
|
|
2958
|
+
proposed: ["under_review"],
|
|
2959
|
+
under_review: ["verified", "contested", "rejected"],
|
|
2960
|
+
contested: ["under_review", "unresolved", "verified", "rejected"],
|
|
2961
|
+
verified: [],
|
|
2962
|
+
rejected: [],
|
|
2963
|
+
unresolved: []
|
|
2964
|
+
};
|
|
2965
|
+
function transitionClaim(claim, to) {
|
|
2966
|
+
const allowed = VALID_TRANSITIONS[claim.status];
|
|
2967
|
+
if (!allowed.includes(to)) {
|
|
2968
|
+
return false;
|
|
2969
|
+
}
|
|
2970
|
+
claim.status = to;
|
|
2971
|
+
return true;
|
|
2972
|
+
}
|
|
2973
|
+
function resolveClaimStatus(claim, challenges, hasPendingChecks) {
|
|
2974
|
+
if (challenges.length === 0) return claim.status;
|
|
2975
|
+
const verdicts = challenges.map((c) => c.verdict);
|
|
2976
|
+
if (verdicts.includes("disagree") || verdicts.includes("needs_evidence")) {
|
|
2977
|
+
return "contested";
|
|
2978
|
+
}
|
|
2979
|
+
if (hasPendingChecks) {
|
|
2980
|
+
return "contested";
|
|
2981
|
+
}
|
|
2982
|
+
if (verdicts.includes("agree")) {
|
|
2983
|
+
return "verified";
|
|
2984
|
+
}
|
|
2985
|
+
return claim.status;
|
|
2986
|
+
}
|
|
2987
|
+
function markUnderReview(claim) {
|
|
2988
|
+
return transitionClaim(claim, "under_review");
|
|
2989
|
+
}
|
|
2990
|
+
function applyReviewResult(claim, challenges, hasPendingChecks) {
|
|
2991
|
+
const nextStatus = resolveClaimStatus(claim, challenges, hasPendingChecks);
|
|
2992
|
+
transitionClaim(claim, nextStatus);
|
|
2993
|
+
}
|
|
2994
|
+
function markUnresolved(claim) {
|
|
2995
|
+
return transitionClaim(claim, "unresolved");
|
|
2996
|
+
}
|
|
2997
|
+
function isTerminal(claim) {
|
|
2998
|
+
return VALID_TRANSITIONS[claim.status].length === 0;
|
|
2999
|
+
}
|
|
3000
|
+
function validTransitions(claim) {
|
|
3001
|
+
return VALID_TRANSITIONS[claim.status];
|
|
3002
|
+
}
|
|
3003
|
+
|
|
3004
|
+
// src/arena/phases/cross-review.ts
|
|
3005
|
+
init_esm_shims();
|
|
3006
|
+
async function runCrossReview(options) {
|
|
3007
|
+
const { participants, strategy, topic, reports, signal, onProgress } = options;
|
|
3008
|
+
onProgress?.({ type: "cross_review_start", round: 1 });
|
|
3009
|
+
const allReviews = [];
|
|
3010
|
+
const tasks = participants.map(async (p) => {
|
|
3011
|
+
const myReport = reports.find((r) => r.participant === p.name);
|
|
3012
|
+
const otherReports = reports.filter((r) => r.participant !== p.name);
|
|
3013
|
+
if (otherReports.length === 0) return [];
|
|
3014
|
+
if (!myReport) return [];
|
|
3015
|
+
const client = await createLLMClient({
|
|
3016
|
+
...p.llm,
|
|
3017
|
+
enableStreaming: false
|
|
3018
|
+
});
|
|
3019
|
+
const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
|
|
3020
|
+
const userContent = strategy.crossReviewUserPrompt(topic, myReport, otherReports);
|
|
3021
|
+
let response = await client.createMessage({
|
|
3022
|
+
systemPrompt,
|
|
3023
|
+
messages: [{ role: "user", content: userContent }],
|
|
3024
|
+
signal
|
|
3025
|
+
});
|
|
3026
|
+
logger.info("arena.cross_review_raw_response", {
|
|
3027
|
+
participant: p.name,
|
|
3028
|
+
text: response.text,
|
|
3029
|
+
stopReason: response.stopReason
|
|
3030
|
+
});
|
|
3031
|
+
if (response.stopReason === "length") {
|
|
3032
|
+
logger.warn("arena.cross_review_truncated", { participant: p.name });
|
|
3033
|
+
const retryResponse = await client.createMessage({
|
|
3034
|
+
systemPrompt,
|
|
3035
|
+
messages: [
|
|
3036
|
+
{ role: "user", content: userContent },
|
|
3037
|
+
{ role: "assistant", content: response.text },
|
|
3038
|
+
{
|
|
3039
|
+
role: "user",
|
|
3040
|
+
content: "Your previous response was truncated. Please output the COMPLETE review JSON, using shorter comments. Respond ONLY with JSON."
|
|
3041
|
+
}
|
|
3042
|
+
],
|
|
3043
|
+
signal
|
|
3044
|
+
});
|
|
3045
|
+
logger.info("arena.cross_review_retry", {
|
|
3046
|
+
participant: p.name,
|
|
3047
|
+
stopReason: retryResponse.stopReason
|
|
3048
|
+
});
|
|
3049
|
+
response = retryResponse;
|
|
3050
|
+
}
|
|
3051
|
+
return strategy.parseCrossReviewResponse(p.name, response.text);
|
|
3052
|
+
});
|
|
3053
|
+
const results = await Promise.all(tasks);
|
|
3054
|
+
for (const reviews of results) {
|
|
3055
|
+
allReviews.push(...reviews);
|
|
3056
|
+
}
|
|
3057
|
+
onProgress?.({ type: "cross_review_done", reviews: allReviews });
|
|
3058
|
+
return allReviews;
|
|
3059
|
+
}
|
|
3060
|
+
async function runVerificationReview(options) {
|
|
3061
|
+
const { participants, strategy, topic, reports, ledger, limits, mode, signal, onProgress } = options;
|
|
3062
|
+
const allClaims = ledger.getAllClaims();
|
|
3063
|
+
const claimsToReview = selectClaimsForReview(allClaims, limits.maxClaimsForReview);
|
|
3064
|
+
for (const claim of claimsToReview) {
|
|
3065
|
+
markUnderReview(claim);
|
|
3066
|
+
}
|
|
3067
|
+
const relevantClaimIds = claimsToReview.map((c) => c.claimId);
|
|
3068
|
+
const digest = buildDigest(ledger, { round: 1, relevantClaimIds });
|
|
3069
|
+
const isPlanningMerge = mode === "planning" && isStrategyPlanning(strategy);
|
|
3070
|
+
if (isPlanningMerge) {
|
|
3071
|
+
onProgress?.({ type: "planning_merge_review_start" });
|
|
3072
|
+
} else {
|
|
3073
|
+
onProgress?.({ type: "verification_start" });
|
|
3074
|
+
}
|
|
3075
|
+
const v2 = isStrategyV2(strategy);
|
|
3076
|
+
const allReviews = [];
|
|
3077
|
+
const allChallenges = [];
|
|
3078
|
+
const tasks = participants.map(async (p) => {
|
|
3079
|
+
const myReport = reports.find((r) => r.participant === p.name);
|
|
3080
|
+
const otherReports = reports.filter((r) => r.participant !== p.name);
|
|
3081
|
+
if (!myReport || otherReports.length === 0) return;
|
|
3082
|
+
const myClaims = claimsToReview.filter((c) => c.owner !== p.name);
|
|
3083
|
+
if (myClaims.length === 0) return;
|
|
3084
|
+
const client = await createLLMClient({
|
|
3085
|
+
...p.llm,
|
|
3086
|
+
enableStreaming: false
|
|
3087
|
+
});
|
|
3088
|
+
const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
|
|
3089
|
+
if (v2) {
|
|
3090
|
+
const userContent = isPlanningMerge ? strategy.mergeReviewUserPrompt(topic, myReport, myClaims, digest) : strategy.verificationReviewUserPrompt(topic, myReport, myClaims, digest);
|
|
3091
|
+
let response = await client.createMessage({
|
|
3092
|
+
systemPrompt,
|
|
3093
|
+
messages: [{ role: "user", content: userContent }],
|
|
3094
|
+
signal
|
|
3095
|
+
});
|
|
3096
|
+
logger.info("arena.verification_review_raw", {
|
|
3097
|
+
participant: p.name,
|
|
3098
|
+
stopReason: response.stopReason
|
|
3099
|
+
});
|
|
3100
|
+
if (response.stopReason === "length") {
|
|
3101
|
+
const retryResponse = await client.createMessage({
|
|
3102
|
+
systemPrompt,
|
|
3103
|
+
messages: [
|
|
3104
|
+
{ role: "user", content: userContent },
|
|
3105
|
+
{ role: "assistant", content: response.text },
|
|
3106
|
+
{ role: "user", content: "Truncated. Output the COMPLETE JSON array, shorter comments. JSON only." }
|
|
3107
|
+
],
|
|
3108
|
+
signal
|
|
3109
|
+
});
|
|
3110
|
+
response = retryResponse;
|
|
3111
|
+
}
|
|
3112
|
+
const challenges = isPlanningMerge ? strategy.parseMergeReviewResponse(p.name, response.text) : strategy.parseVerificationReviewResponse(p.name, response.text);
|
|
3113
|
+
for (const challenge of challenges) {
|
|
3114
|
+
allChallenges.push(challenge);
|
|
3115
|
+
ledger.appendChallenge(challenge);
|
|
3116
|
+
}
|
|
3117
|
+
for (const ch of challenges) {
|
|
3118
|
+
allReviews.push({
|
|
3119
|
+
reviewer: ch.reviewer,
|
|
3120
|
+
findingId: ch.claimId,
|
|
3121
|
+
verdict: ch.verdict,
|
|
3122
|
+
reason: ch.reason,
|
|
3123
|
+
extraEvidence: ch.supportingEvidenceRefs
|
|
3124
|
+
});
|
|
3125
|
+
}
|
|
3126
|
+
} else {
|
|
3127
|
+
const userContent = strategy.crossReviewUserPrompt(topic, myReport, otherReports);
|
|
3128
|
+
let response = await client.createMessage({
|
|
3129
|
+
systemPrompt,
|
|
3130
|
+
messages: [{ role: "user", content: userContent }],
|
|
3131
|
+
signal
|
|
3132
|
+
});
|
|
3133
|
+
if (response.stopReason === "length") {
|
|
3134
|
+
const retryResponse = await client.createMessage({
|
|
3135
|
+
systemPrompt,
|
|
3136
|
+
messages: [
|
|
3137
|
+
{ role: "user", content: userContent },
|
|
3138
|
+
{ role: "assistant", content: response.text },
|
|
3139
|
+
{ role: "user", content: "Truncated. Output COMPLETE review JSON. Shorter comments. JSON only." }
|
|
3140
|
+
],
|
|
3141
|
+
signal
|
|
3142
|
+
});
|
|
3143
|
+
response = retryResponse;
|
|
3144
|
+
}
|
|
3145
|
+
const reviews = strategy.parseCrossReviewResponse(p.name, response.text);
|
|
3146
|
+
allReviews.push(...reviews);
|
|
3147
|
+
for (const review of reviews) {
|
|
3148
|
+
const claim = claimsToReview.find(
|
|
3149
|
+
(c) => c.finding.id === review.findingId || c.claimId === review.findingId
|
|
3150
|
+
);
|
|
3151
|
+
if (claim) {
|
|
3152
|
+
const challenge = {
|
|
3153
|
+
reviewer: review.reviewer,
|
|
3154
|
+
claimId: claim.claimId,
|
|
3155
|
+
verdict: review.verdict,
|
|
3156
|
+
reason: review.reason,
|
|
3157
|
+
supportingEvidenceRefs: review.extraEvidence
|
|
3158
|
+
};
|
|
3159
|
+
allChallenges.push(challenge);
|
|
3160
|
+
ledger.appendChallenge(challenge);
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
}
|
|
3164
|
+
});
|
|
3165
|
+
await Promise.all(tasks);
|
|
3166
|
+
for (const claim of claimsToReview) {
|
|
3167
|
+
const claimChallenges = ledger.getChallengesForClaim(claim.claimId);
|
|
3168
|
+
const hasPendingChecks = ledger.getPendingChecksForClaim(claim.claimId).length > 0;
|
|
3169
|
+
applyReviewResult(claim, claimChallenges, hasPendingChecks);
|
|
3170
|
+
}
|
|
3171
|
+
logger.info("arena.verification_done", {
|
|
3172
|
+
reviewCount: allReviews.length,
|
|
3173
|
+
challengeCount: allChallenges.length,
|
|
3174
|
+
claimStatuses: Object.fromEntries(
|
|
3175
|
+
claimsToReview.map((c) => [c.claimId, c.status])
|
|
3176
|
+
)
|
|
3177
|
+
});
|
|
3178
|
+
if (isPlanningMerge) {
|
|
3179
|
+
onProgress?.({ type: "planning_merge_review_done", mergeCount: allChallenges.length });
|
|
3180
|
+
} else {
|
|
3181
|
+
onProgress?.({ type: "verification_done", challengeCount: allChallenges.length });
|
|
3182
|
+
}
|
|
3183
|
+
onProgress?.({ type: "cross_review_done", reviews: allReviews });
|
|
3184
|
+
return { reviews: allReviews, challenges: allChallenges };
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
// src/arena/phases/debate-rounds.ts
|
|
3188
|
+
init_esm_shims();
|
|
3189
|
+
async function runDebateRounds(options) {
|
|
3190
|
+
const { participants, strategy, topic, ledger, limits, maxRounds, signal, onProgress } = options;
|
|
3191
|
+
const contested = ledger.getClaimsByStatus("contested").slice(0, limits.maxContestedClaimsForDebate);
|
|
3192
|
+
if (contested.length === 0) {
|
|
3193
|
+
logger.info("arena.debate_skip", { reason: "no contested claims" });
|
|
3194
|
+
return [];
|
|
3195
|
+
}
|
|
3196
|
+
logger.info("arena.debate_start", {
|
|
3197
|
+
contestedCount: contested.length,
|
|
3198
|
+
maxRounds
|
|
3199
|
+
});
|
|
3200
|
+
const allDebateRounds = [];
|
|
3201
|
+
const v2 = isStrategyV2(strategy);
|
|
3202
|
+
for (const claim of contested) {
|
|
3203
|
+
signal?.throwIfAborted();
|
|
3204
|
+
const claimDebateRounds = await debateClaim({
|
|
3205
|
+
claim,
|
|
3206
|
+
participants,
|
|
3207
|
+
strategy,
|
|
3208
|
+
v2,
|
|
3209
|
+
topic,
|
|
3210
|
+
ledger,
|
|
3211
|
+
maxRounds,
|
|
3212
|
+
signal,
|
|
3213
|
+
onProgress
|
|
3214
|
+
});
|
|
3215
|
+
allDebateRounds.push(...claimDebateRounds);
|
|
3216
|
+
claim.debateRounds.push(...claimDebateRounds);
|
|
3217
|
+
}
|
|
3218
|
+
return allDebateRounds;
|
|
3219
|
+
}
|
|
3220
|
+
async function debateClaim(options) {
|
|
3221
|
+
const { claim, participants, strategy, v2, topic, ledger, maxRounds, signal, onProgress } = options;
|
|
3222
|
+
const owner = participants.find((p) => p.name === claim.owner);
|
|
3223
|
+
const primaryChallenger = findPrimaryChallenger(claim, participants);
|
|
3224
|
+
if (!owner || !primaryChallenger) {
|
|
3225
|
+
logger.warn("arena.debate_skip_claim", {
|
|
3226
|
+
claimId: claim.claimId,
|
|
3227
|
+
reason: "cannot identify debaters"
|
|
3228
|
+
});
|
|
3229
|
+
return [];
|
|
3230
|
+
}
|
|
3231
|
+
const debaters = [owner, primaryChallenger];
|
|
3232
|
+
const rounds = [];
|
|
3233
|
+
const allTurns = [];
|
|
3234
|
+
for (let round = 1; round <= maxRounds; round++) {
|
|
3235
|
+
signal?.throwIfAborted();
|
|
3236
|
+
onProgress?.({ type: "debate_round_start", round, claims: [claim.claimId] });
|
|
3237
|
+
const digest = buildDigest(ledger, {
|
|
3238
|
+
round,
|
|
3239
|
+
relevantClaimIds: [claim.claimId]
|
|
3240
|
+
});
|
|
3241
|
+
const turnPromises = debaters.map(async (p) => {
|
|
3242
|
+
const client = await createLLMClient({
|
|
3243
|
+
...p.llm,
|
|
3244
|
+
enableStreaming: false
|
|
3245
|
+
});
|
|
3246
|
+
const systemPrompt = strategy.crossReviewSystemPrompt(p.name);
|
|
3247
|
+
let userContent;
|
|
3248
|
+
if (v2) {
|
|
3249
|
+
userContent = strategy.debateTurnUserPrompt(topic, claim, allTurns, digest);
|
|
3250
|
+
} else {
|
|
3251
|
+
userContent = buildFallbackDebatePrompt(topic, claim, allTurns);
|
|
3252
|
+
}
|
|
3253
|
+
const response = await client.createMessage({
|
|
3254
|
+
systemPrompt,
|
|
3255
|
+
messages: [{ role: "user", content: userContent }],
|
|
3256
|
+
signal
|
|
3257
|
+
});
|
|
3258
|
+
logger.info("arena.debate_turn", {
|
|
3259
|
+
participant: p.name,
|
|
3260
|
+
claimId: claim.claimId,
|
|
3261
|
+
round,
|
|
3262
|
+
stopReason: response.stopReason
|
|
3263
|
+
});
|
|
3264
|
+
if (v2) {
|
|
3265
|
+
return strategy.parseDebateTurnResponse(p.name, response.text);
|
|
3266
|
+
}
|
|
3267
|
+
return parseDebateTurn(p.name, response.text);
|
|
3268
|
+
});
|
|
3269
|
+
const turns = await Promise.all(turnPromises);
|
|
3270
|
+
allTurns.push(...turns);
|
|
3271
|
+
const resolved = turns.every((t) => t.stance === "support" || t.stance === "narrow");
|
|
3272
|
+
const resolutionNote = resolved ? `Converged in round ${round}: ${turns.map((t) => `${t.participant}=${t.stance}`).join(", ")}` : void 0;
|
|
3273
|
+
const debateRound = {
|
|
3274
|
+
round,
|
|
3275
|
+
claimId: claim.claimId,
|
|
3276
|
+
participants: turns,
|
|
3277
|
+
resolved,
|
|
3278
|
+
resolutionNote
|
|
3279
|
+
};
|
|
3280
|
+
rounds.push(debateRound);
|
|
3281
|
+
onProgress?.({ type: "debate_round_done", round, resolved: resolved ? 1 : 0 });
|
|
3282
|
+
if (resolved) {
|
|
3283
|
+
logger.info("arena.debate_converged", {
|
|
3284
|
+
claimId: claim.claimId,
|
|
3285
|
+
round
|
|
3286
|
+
});
|
|
3287
|
+
break;
|
|
3288
|
+
}
|
|
3289
|
+
}
|
|
3290
|
+
return rounds;
|
|
3291
|
+
}
|
|
3292
|
+
function findPrimaryChallenger(claim, participants) {
|
|
3293
|
+
const disagreeChallenge = claim.challenges.find((c) => c.verdict === "disagree");
|
|
3294
|
+
const needsEvidenceChallenge = claim.challenges.find((c) => c.verdict === "needs_evidence");
|
|
3295
|
+
const challenge = disagreeChallenge ?? needsEvidenceChallenge ?? claim.challenges[0];
|
|
3296
|
+
if (!challenge) return void 0;
|
|
3297
|
+
return participants.find((p) => p.name === challenge.reviewer);
|
|
3298
|
+
}
|
|
3299
|
+
function buildFallbackDebatePrompt(topic, claim, priorTurns) {
|
|
3300
|
+
const turnsText = priorTurns.length > 0 ? priorTurns.map((t) => `[${t.participant}] ${t.stance}: ${t.summary}`).join("\n") : "No prior turns.";
|
|
3301
|
+
return `## Topic: ${topic}
|
|
3302
|
+
|
|
3303
|
+
## Contested Claim
|
|
3304
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
3305
|
+
${claim.finding.summary}
|
|
3306
|
+
|
|
3307
|
+
## Prior Debate
|
|
3308
|
+
${turnsText}
|
|
3309
|
+
|
|
3310
|
+
State your position on this claim. Build on prior turns: cite specific evidence, acknowledge counter-arguments, and explain your reasoning in 150-300 words. Brevity here means missed nuance \u2014 depth wins.
|
|
3311
|
+
Respond ONLY with JSON:
|
|
3312
|
+
{"stance": "support|oppose|narrow|uncertain", "summary": "your argument", "newEvidenceRefs": ["optional"]}`;
|
|
3313
|
+
}
|
|
3314
|
+
|
|
3315
|
+
// src/arena/phases/adjudication.ts
|
|
3316
|
+
init_esm_shims();
|
|
3317
|
+
async function runAdjudication(options) {
|
|
3318
|
+
const { concluder, strategy, topic, ledger, signal, onProgress } = options;
|
|
3319
|
+
const allAdjudications = [];
|
|
3320
|
+
const v2 = isStrategyV2(strategy);
|
|
3321
|
+
const contestedClaims = ledger.getClaimsByStatus("contested");
|
|
3322
|
+
const verifiedClaims = ledger.getClaimsByStatus("verified");
|
|
3323
|
+
if (contestedClaims.length === 0 && verifiedClaims.length === 0) {
|
|
3324
|
+
logger.info("arena.adjudication_skip", { reason: "no claims to adjudicate" });
|
|
3325
|
+
return [];
|
|
3326
|
+
}
|
|
3327
|
+
logger.info("arena.adjudication_start", {
|
|
3328
|
+
contested: contestedClaims.length,
|
|
3329
|
+
verified: verifiedClaims.length
|
|
3330
|
+
});
|
|
3331
|
+
const client = await createLLMClient({
|
|
3332
|
+
...concluder.llm,
|
|
3333
|
+
enableStreaming: false
|
|
3334
|
+
});
|
|
3335
|
+
for (const claim of contestedClaims) {
|
|
3336
|
+
signal?.throwIfAborted();
|
|
3337
|
+
const digest = buildDigest(ledger, {
|
|
3338
|
+
round: 0,
|
|
3339
|
+
// adjudication is post-debate
|
|
3340
|
+
relevantClaimIds: [claim.claimId]
|
|
3341
|
+
});
|
|
3342
|
+
const systemPrompt = strategy.consensusSystemPrompt();
|
|
3343
|
+
let userContent;
|
|
3344
|
+
if (v2) {
|
|
3345
|
+
userContent = strategy.adjudicationUserPrompt(topic, claim, claim.debateRounds, digest);
|
|
3346
|
+
} else {
|
|
3347
|
+
userContent = buildFallbackAdjudicationPrompt(topic, claim);
|
|
3348
|
+
}
|
|
3349
|
+
const response = await client.createMessage({
|
|
3350
|
+
systemPrompt,
|
|
3351
|
+
messages: [{ role: "user", content: userContent }],
|
|
3352
|
+
signal
|
|
3353
|
+
});
|
|
3354
|
+
logger.info("arena.adjudication_response", {
|
|
3355
|
+
claimId: claim.claimId,
|
|
3356
|
+
stopReason: response.stopReason
|
|
3357
|
+
});
|
|
3358
|
+
let adjudication;
|
|
3359
|
+
if (v2) {
|
|
3360
|
+
adjudication = strategy.parseAdjudicationResponse(response.text);
|
|
3361
|
+
adjudication.claimId = claim.claimId;
|
|
3362
|
+
} else {
|
|
3363
|
+
adjudication = parseAdjudication(claim.claimId, response.text);
|
|
3364
|
+
}
|
|
3365
|
+
ledger.appendAdjudication(adjudication);
|
|
3366
|
+
allAdjudications.push(adjudication);
|
|
3367
|
+
applyAdjudicationOutcome(claim, adjudication);
|
|
3368
|
+
}
|
|
3369
|
+
for (const claim of verifiedClaims) {
|
|
3370
|
+
const adjudication = {
|
|
3371
|
+
claimId: claim.claimId,
|
|
3372
|
+
outcome: "accepted",
|
|
3373
|
+
rationale: "Verified by peer review without contest",
|
|
3374
|
+
finalSummary: claim.finding.summary,
|
|
3375
|
+
supportingEvidenceRefs: claim.evidenceRefs
|
|
3376
|
+
};
|
|
3377
|
+
ledger.appendAdjudication(adjudication);
|
|
3378
|
+
allAdjudications.push(adjudication);
|
|
3379
|
+
}
|
|
3380
|
+
const remaining = ledger.getClaimsByStatus("proposed", "under_review");
|
|
3381
|
+
for (const claim of remaining) {
|
|
3382
|
+
markUnresolved(claim);
|
|
3383
|
+
}
|
|
3384
|
+
const accepted = allAdjudications.filter(
|
|
3385
|
+
(a) => a.outcome === "accepted" || a.outcome === "accepted_with_revision"
|
|
3386
|
+
).length;
|
|
3387
|
+
const unresolved = ledger.getClaimsByStatus("unresolved").length;
|
|
3388
|
+
logger.info("arena.adjudication_done", {
|
|
3389
|
+
total: allAdjudications.length,
|
|
3390
|
+
accepted,
|
|
3391
|
+
unresolved
|
|
3392
|
+
});
|
|
3393
|
+
onProgress?.({ type: "adjudication_done", accepted, unresolved });
|
|
3394
|
+
return allAdjudications;
|
|
3395
|
+
}
|
|
3396
|
+
function applyAdjudicationOutcome(claim, adjudication) {
|
|
3397
|
+
switch (adjudication.outcome) {
|
|
3398
|
+
case "accepted":
|
|
3399
|
+
case "accepted_with_revision":
|
|
3400
|
+
transitionClaim(claim, "verified");
|
|
3401
|
+
break;
|
|
3402
|
+
case "rejected":
|
|
3403
|
+
transitionClaim(claim, "rejected");
|
|
3404
|
+
break;
|
|
3405
|
+
case "unresolved":
|
|
3406
|
+
markUnresolved(claim);
|
|
3407
|
+
break;
|
|
3408
|
+
}
|
|
3409
|
+
}
|
|
3410
|
+
function buildFallbackAdjudicationPrompt(topic, claim) {
|
|
3411
|
+
const challengeText = claim.challenges.length > 0 ? claim.challenges.map((c) => `[${c.reviewer}] ${c.verdict}: ${c.reason}`).join("\n") : "No challenges.";
|
|
3412
|
+
const debateText = claim.debateRounds.length > 0 ? claim.debateRounds.map(
|
|
3413
|
+
(r) => `Round ${r.round}:
|
|
3414
|
+
` + r.participants.map((t) => ` [${t.participant}] ${t.stance}: ${t.summary}`).join("\n") + `
|
|
3415
|
+
${r.resolved ? "\u2192 Resolved" : "\u2192 Unresolved"}`
|
|
3416
|
+
).join("\n\n") : "No debate rounds.";
|
|
3417
|
+
return `## Topic: ${topic}
|
|
3418
|
+
|
|
3419
|
+
## Claim Under Adjudication
|
|
3420
|
+
[${claim.claimId}] ${claim.finding.title}
|
|
3421
|
+
${claim.finding.summary}
|
|
3422
|
+
Owner: ${claim.owner} | Status: ${claim.status}
|
|
3423
|
+
|
|
3424
|
+
## Challenges
|
|
3425
|
+
${challengeText}
|
|
3426
|
+
|
|
3427
|
+
## Debate
|
|
3428
|
+
${debateText}
|
|
3429
|
+
|
|
3430
|
+
As moderator, adjudicate this claim based on all available evidence.
|
|
3431
|
+
Respond ONLY with JSON:
|
|
3432
|
+
{"outcome": "accepted|accepted_with_revision|rejected|unresolved", "rationale": "...", "finalSummary": "revised claim summary if needed", "supportingEvidenceRefs": ["..."]}`;
|
|
3433
|
+
}
|
|
3434
|
+
|
|
3435
|
+
// src/arena/phases/build-consensus.ts
|
|
3436
|
+
init_esm_shims();
|
|
3437
|
+
async function buildConsensus(options) {
|
|
3438
|
+
const { concluder, strategy, topic, reports, reviews, claimSummary, signal, onProgress } = options;
|
|
3439
|
+
onProgress?.({ type: "consensus_start" });
|
|
3440
|
+
const client = await createLLMClient({
|
|
3441
|
+
...concluder.llm,
|
|
3442
|
+
enableStreaming: false
|
|
3443
|
+
});
|
|
3444
|
+
const systemPrompt = strategy.consensusSystemPrompt();
|
|
3445
|
+
let userContent;
|
|
3446
|
+
const v2 = isStrategyV2(strategy);
|
|
3447
|
+
if (v2 && claimSummary) {
|
|
3448
|
+
userContent = strategy.claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary);
|
|
3449
|
+
logger.info("arena.consensus_claim_aware", {
|
|
3450
|
+
verified: claimSummary.verified.length,
|
|
3451
|
+
unresolved: claimSummary.unresolved.length,
|
|
3452
|
+
contested: claimSummary.contested.length,
|
|
3453
|
+
rejected: claimSummary.rejected.length
|
|
3454
|
+
});
|
|
3455
|
+
} else {
|
|
3456
|
+
userContent = strategy.consensusUserPrompt(topic, reports, reviews);
|
|
3457
|
+
}
|
|
3458
|
+
const response = await client.createMessage({
|
|
3459
|
+
systemPrompt,
|
|
3460
|
+
messages: [{ role: "user", content: userContent }],
|
|
3461
|
+
signal
|
|
3462
|
+
});
|
|
3463
|
+
logger.info("arena.consensus_raw_response", {
|
|
3464
|
+
text: response.text,
|
|
3465
|
+
stopReason: response.stopReason
|
|
3466
|
+
});
|
|
3467
|
+
if (response.stopReason === "length") {
|
|
3468
|
+
logger.warn("arena.consensus_truncated", {
|
|
3469
|
+
textLen: response.text?.length ?? 0,
|
|
3470
|
+
maxTokens: concluder.llm.maxTokens
|
|
3471
|
+
});
|
|
3472
|
+
const retryResponse = await client.createMessage({
|
|
3473
|
+
systemPrompt,
|
|
3474
|
+
messages: [
|
|
3475
|
+
{ role: "user", content: userContent },
|
|
3476
|
+
{ role: "assistant", content: response.text },
|
|
3477
|
+
{
|
|
3478
|
+
role: "user",
|
|
3479
|
+
content: "Your previous response was truncated. Please output a COMPLETE but more concise version of the consensus JSON. Keep all sections but use shorter summaries. Respond ONLY with the complete JSON object."
|
|
3480
|
+
}
|
|
3481
|
+
],
|
|
3482
|
+
signal
|
|
3483
|
+
});
|
|
3484
|
+
logger.info("arena.consensus_retry_response", {
|
|
3485
|
+
textLen: retryResponse.text?.length ?? 0,
|
|
3486
|
+
stopReason: retryResponse.stopReason
|
|
3487
|
+
});
|
|
3488
|
+
if (retryResponse.stopReason === "length") {
|
|
3489
|
+
logger.warn("arena.consensus_still_truncated", {
|
|
3490
|
+
textLen: retryResponse.text?.length ?? 0
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
const consensus2 = strategy.parseConsensusResponse(retryResponse.text);
|
|
3494
|
+
onProgress?.({ type: "consensus_done", consensus: consensus2 });
|
|
3495
|
+
return consensus2;
|
|
3496
|
+
}
|
|
3497
|
+
const consensus = strategy.parseConsensusResponse(response.text);
|
|
3498
|
+
onProgress?.({ type: "consensus_done", consensus });
|
|
3499
|
+
return consensus;
|
|
3500
|
+
}
|
|
3501
|
+
|
|
3502
|
+
// src/arena/ledger.ts
|
|
3503
|
+
init_esm_shims();
|
|
3504
|
+
var WARN_CLAIMS = 50;
|
|
3505
|
+
var WARN_PACKETS = 200;
|
|
3506
|
+
var WARN_CHALLENGES = 100;
|
|
3507
|
+
var ArenaLedger = class {
|
|
3508
|
+
ledger;
|
|
3509
|
+
index;
|
|
3510
|
+
constructor() {
|
|
3511
|
+
this.ledger = {
|
|
3512
|
+
dossiers: [],
|
|
3513
|
+
evidencePackets: [],
|
|
3514
|
+
toolTraces: [],
|
|
3515
|
+
claims: [],
|
|
3516
|
+
challenges: [],
|
|
3517
|
+
requestedChecks: [],
|
|
3518
|
+
adjudications: []
|
|
3519
|
+
};
|
|
3520
|
+
this.index = {
|
|
3521
|
+
claimsById: /* @__PURE__ */ new Map(),
|
|
3522
|
+
packetsById: /* @__PURE__ */ new Map(),
|
|
3523
|
+
requestsById: /* @__PURE__ */ new Map()
|
|
3524
|
+
};
|
|
3525
|
+
}
|
|
3526
|
+
// ─── Append operations ──────────────────────────────────────
|
|
3527
|
+
/** Check if ledger is approaching memory limits and log warnings */
|
|
3528
|
+
checkGrowth() {
|
|
3529
|
+
const claims = this.ledger.claims.length;
|
|
3530
|
+
const packets = this.ledger.evidencePackets.length;
|
|
3531
|
+
const challenges = this.ledger.challenges.length;
|
|
3532
|
+
if (claims === WARN_CLAIMS || packets === WARN_PACKETS || challenges === WARN_CHALLENGES) {
|
|
3533
|
+
logger.warn("arena.ledger_growth", { claims, packets, challenges, toolTraces: this.ledger.toolTraces.length });
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
appendDossier(dossier) {
|
|
3537
|
+
this.ledger.dossiers.push(dossier);
|
|
3538
|
+
for (const packet of dossier.evidencePackets) {
|
|
3539
|
+
this.appendEvidencePacket(packet);
|
|
3540
|
+
}
|
|
3541
|
+
for (const trace of dossier.toolTrace) {
|
|
3542
|
+
this.ledger.toolTraces.push(trace);
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
appendEvidencePacket(packet) {
|
|
3546
|
+
if (!this.index.packetsById.has(packet.packetId)) {
|
|
3547
|
+
this.ledger.evidencePackets.push(packet);
|
|
3548
|
+
this.index.packetsById.set(packet.packetId, packet);
|
|
3549
|
+
}
|
|
3550
|
+
}
|
|
3551
|
+
appendClaim(claim) {
|
|
3552
|
+
this.ledger.claims.push(claim);
|
|
3553
|
+
this.index.claimsById.set(claim.claimId, claim);
|
|
3554
|
+
this.checkGrowth();
|
|
3555
|
+
}
|
|
3556
|
+
appendChallenge(challenge) {
|
|
3557
|
+
this.ledger.challenges.push(challenge);
|
|
3558
|
+
const claim = this.index.claimsById.get(challenge.claimId);
|
|
3559
|
+
if (claim) {
|
|
3560
|
+
claim.challenges.push(challenge);
|
|
3561
|
+
}
|
|
3562
|
+
this.checkGrowth();
|
|
3563
|
+
if (challenge.requestedChecks) {
|
|
3564
|
+
for (const check of challenge.requestedChecks) {
|
|
3565
|
+
this.appendRequestedCheck(check);
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
appendRequestedCheck(check) {
|
|
3570
|
+
if (!this.index.requestsById.has(check.requestId)) {
|
|
3571
|
+
this.ledger.requestedChecks.push(check);
|
|
3572
|
+
this.index.requestsById.set(check.requestId, check);
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
appendAdjudication(adjudication) {
|
|
3576
|
+
this.ledger.adjudications.push(adjudication);
|
|
3577
|
+
const claim = this.index.claimsById.get(adjudication.claimId);
|
|
3578
|
+
if (claim) {
|
|
3579
|
+
claim.adjudication = adjudication;
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
// ─── Query operations ───────────────────────────────────────
|
|
3583
|
+
/**
|
|
3584
|
+
* Return a read-only reference to the full ledger.
|
|
3585
|
+
*
|
|
3586
|
+
* Note: `Readonly<>` is shallow — inner arrays are still mutable at the
|
|
3587
|
+
* type level. Callers MUST NOT mutate the returned object; use the
|
|
3588
|
+
* append* methods instead. A deep-freeze or structural clone can be
|
|
3589
|
+
* added later if external consumers need a truly immutable snapshot.
|
|
3590
|
+
*/
|
|
3591
|
+
getSnapshot() {
|
|
3592
|
+
return this.ledger;
|
|
3593
|
+
}
|
|
3594
|
+
getClaimById(claimId) {
|
|
3595
|
+
return this.index.claimsById.get(claimId);
|
|
3596
|
+
}
|
|
3597
|
+
getPacketById(packetId) {
|
|
3598
|
+
return this.index.packetsById.get(packetId);
|
|
3599
|
+
}
|
|
3600
|
+
getAllClaims() {
|
|
3601
|
+
return this.ledger.claims;
|
|
3602
|
+
}
|
|
3603
|
+
getClaimsByStatus(...statuses) {
|
|
3604
|
+
const set = new Set(statuses);
|
|
3605
|
+
return this.ledger.claims.filter((c) => set.has(c.status));
|
|
3606
|
+
}
|
|
3607
|
+
getClaimsByOwner(owner) {
|
|
3608
|
+
return this.ledger.claims.filter((c) => c.owner === owner);
|
|
3609
|
+
}
|
|
3610
|
+
getChallengesForClaim(claimId) {
|
|
3611
|
+
return this.ledger.challenges.filter((c) => c.claimId === claimId);
|
|
3612
|
+
}
|
|
3613
|
+
getPendingChecks() {
|
|
3614
|
+
return this.ledger.requestedChecks;
|
|
3615
|
+
}
|
|
3616
|
+
getPendingChecksForClaim(claimId) {
|
|
3617
|
+
return this.ledger.requestedChecks.filter((c) => c.claimId === claimId);
|
|
3618
|
+
}
|
|
3619
|
+
getPacketsForClaim(claimId) {
|
|
3620
|
+
const claim = this.index.claimsById.get(claimId);
|
|
3621
|
+
if (!claim) return [];
|
|
3622
|
+
return claim.evidencePacketIds.map((id) => this.index.packetsById.get(id)).filter((p) => p !== void 0);
|
|
3623
|
+
}
|
|
3624
|
+
getDossiers() {
|
|
3625
|
+
return this.ledger.dossiers;
|
|
3626
|
+
}
|
|
3627
|
+
getDossierByParticipant(participant) {
|
|
3628
|
+
const dossiers = this.ledger.dossiers.filter((d) => d.participant === participant);
|
|
3629
|
+
return dossiers[dossiers.length - 1];
|
|
3630
|
+
}
|
|
3631
|
+
};
|
|
3632
|
+
|
|
3633
|
+
// src/arena/arena.ts
|
|
3634
|
+
init_esm_shims();
|
|
3635
|
+
|
|
3636
|
+
// src/arena/phases/planning-detail-expansion.ts
|
|
3637
|
+
init_esm_shims();
|
|
3638
|
+
var MAX_EXPANSION_TOOL_ROUNDS = 3;
|
|
3639
|
+
async function runDetailExpansion(options) {
|
|
3640
|
+
const { concluder, strategy, topic, phases, ledger, limits, signal, onProgress } = options;
|
|
3641
|
+
const tools = options.enableContextTools !== false ? options.contextTools ?? CONTEXT_TOOLS : void 0;
|
|
3642
|
+
const toExpand = phases.slice(0, limits.maxExpandedPhasesPerRun);
|
|
3643
|
+
onProgress?.({ type: "roadmap_expansion_start", phaseCount: toExpand.length });
|
|
3644
|
+
logger.info("arena.detail_expansion_start", { phaseCount: toExpand.length });
|
|
3645
|
+
const details = [];
|
|
3646
|
+
const client = await createLLMClient({
|
|
3647
|
+
...concluder.llm,
|
|
3648
|
+
enableStreaming: false
|
|
3649
|
+
});
|
|
3650
|
+
const systemPrompt = strategy.detailExpansionSystemPrompt();
|
|
3651
|
+
for (const phase of toExpand) {
|
|
3652
|
+
signal?.throwIfAborted();
|
|
3653
|
+
const relevantClaimIds = phase.relatedFindings ?? [];
|
|
3654
|
+
const digest = buildDigest(ledger, { round: 1, relevantClaimIds });
|
|
3655
|
+
const userContent = strategy.detailExpansionUserPrompt(topic, phase, digest);
|
|
3656
|
+
const messages = [{ role: "user", content: userContent }];
|
|
3657
|
+
let finalText = "";
|
|
3658
|
+
const maxRounds = tools ? Math.min(MAX_EXPANSION_TOOL_ROUNDS, MAX_TOOL_ROUNDS) : 0;
|
|
3659
|
+
for (let round = 0; round <= maxRounds; round++) {
|
|
3660
|
+
const response = await client.createMessage({
|
|
3661
|
+
systemPrompt,
|
|
3662
|
+
messages,
|
|
3663
|
+
tools,
|
|
3664
|
+
signal
|
|
3665
|
+
});
|
|
3666
|
+
const hasTools2 = response.toolCalls && response.toolCalls.length > 0;
|
|
3667
|
+
logger.info("arena.detail_expansion_round", {
|
|
3668
|
+
phase: phase.title,
|
|
3669
|
+
round,
|
|
3670
|
+
toolCount: response.toolCalls?.length ?? 0,
|
|
3671
|
+
stopReason: response.stopReason
|
|
3672
|
+
});
|
|
3673
|
+
if (!hasTools2) {
|
|
3674
|
+
finalText = response.text;
|
|
3675
|
+
break;
|
|
3676
|
+
}
|
|
3677
|
+
const assistantBlocks = [];
|
|
3678
|
+
if (response.text) {
|
|
3679
|
+
assistantBlocks.push({ type: "text", text: response.text });
|
|
3680
|
+
}
|
|
3681
|
+
for (const tc of response.toolCalls) {
|
|
3682
|
+
assistantBlocks.push({
|
|
3683
|
+
type: "tool_use",
|
|
3684
|
+
id: tc.id,
|
|
3685
|
+
name: tc.toolName,
|
|
3686
|
+
input: tc.args
|
|
3687
|
+
});
|
|
3688
|
+
}
|
|
3689
|
+
messages.push({ role: "assistant", content: assistantBlocks });
|
|
3690
|
+
const toolResultBlocks = [];
|
|
3691
|
+
for (const tc of response.toolCalls) {
|
|
3692
|
+
const result = executeContextTool(tc);
|
|
3693
|
+
toolResultBlocks.push({
|
|
3694
|
+
type: "tool_result",
|
|
3695
|
+
tool_use_id: tc.id,
|
|
3696
|
+
content: result.slice(0, 15e3)
|
|
3697
|
+
});
|
|
3698
|
+
}
|
|
3699
|
+
messages.push({ role: "user", content: toolResultBlocks });
|
|
3700
|
+
if (round === maxRounds) {
|
|
3701
|
+
finalText = response.text;
|
|
3702
|
+
}
|
|
3703
|
+
}
|
|
3704
|
+
if (!finalText) {
|
|
3705
|
+
finalText = "";
|
|
3706
|
+
}
|
|
3707
|
+
logger.info("arena.detail_expansion_phase", {
|
|
3708
|
+
phase: phase.title,
|
|
3709
|
+
textLen: finalText.length
|
|
3710
|
+
});
|
|
3711
|
+
const detail = strategy.parseDetailExpansionResponse(finalText);
|
|
3712
|
+
if (!detail.phaseTitle) detail.phaseTitle = phase.title;
|
|
3713
|
+
if (detail.targetFiles.length === 0 && detail.codeChanges.length === 0) {
|
|
3714
|
+
logger.warn("arena.detail_expansion_sparse", {
|
|
3715
|
+
phase: phase.title,
|
|
3716
|
+
objective: detail.objective.slice(0, 200)
|
|
3717
|
+
});
|
|
3718
|
+
}
|
|
3719
|
+
details.push(detail);
|
|
3720
|
+
}
|
|
3721
|
+
logger.info("arena.detail_expansion_done", { detailCount: details.length });
|
|
3722
|
+
onProgress?.({ type: "roadmap_expansion_done", detailCount: details.length });
|
|
3723
|
+
return details;
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
// src/arena/strategies/language-wrapper.ts
|
|
3727
|
+
init_esm_shims();
|
|
3728
|
+
function detectLanguageInstruction(topic) {
|
|
3729
|
+
const nonAscii = topic.replace(/[\x00-\x7F]/g, "");
|
|
3730
|
+
const ratio = nonAscii.length / Math.max(topic.length, 1);
|
|
3731
|
+
if (ratio < 0.15) return "";
|
|
3732
|
+
if (/[\u4e00-\u9fff\u3400-\u4dbf]/.test(topic)) {
|
|
3733
|
+
return "IMPORTANT: The user's query is in Chinese. You MUST respond in Chinese (\u4E2D\u6587). All text output \u2014 summaries, findings, titles, descriptions, rationale \u2014 must be written in Chinese. JSON field values must be in Chinese.";
|
|
3734
|
+
}
|
|
3735
|
+
if (/[\u3040-\u309f\u30a0-\u30ff]/.test(topic)) {
|
|
3736
|
+
return "IMPORTANT: The user's query is in Japanese. You MUST respond in Japanese (\u65E5\u672C\u8A9E). All text output must be in Japanese. JSON field values must be in Japanese.";
|
|
3737
|
+
}
|
|
3738
|
+
if (/[\uac00-\ud7af]/.test(topic)) {
|
|
3739
|
+
return "IMPORTANT: The user's query is in Korean. You MUST respond in Korean (\uD55C\uAD6D\uC5B4). All text output must be in Korean. JSON field values must be in Korean.";
|
|
3740
|
+
}
|
|
3741
|
+
if (/[\u0400-\u04ff]/.test(topic)) {
|
|
3742
|
+
return "IMPORTANT: The user's query is in Russian. You MUST respond in Russian (\u0440\u0443\u0441\u0441\u043A\u0438\u0439). All text output must be in Russian. JSON field values must be in Russian.";
|
|
3743
|
+
}
|
|
3744
|
+
if (/[\u0600-\u06ff]/.test(topic)) {
|
|
3745
|
+
return "IMPORTANT: The user's query is in Arabic. You MUST respond in Arabic (\u0627\u0644\u0639\u0631\u0628\u064A\u0629). All text output must be in Arabic. JSON field values must be in Arabic.";
|
|
3746
|
+
}
|
|
3747
|
+
return "IMPORTANT: Respond in the same language as the user's query. All text output \u2014 summaries, findings, titles, descriptions \u2014 must match the query language.";
|
|
3748
|
+
}
|
|
3749
|
+
function withLanguage(strategy, topic) {
|
|
3750
|
+
const langInstruction = detectLanguageInstruction(topic);
|
|
3751
|
+
if (!langInstruction) return strategy;
|
|
3752
|
+
return {
|
|
3753
|
+
researchSystemPrompt(name) {
|
|
3754
|
+
return `${langInstruction}
|
|
3755
|
+
|
|
3756
|
+
${strategy.researchSystemPrompt(name)}`;
|
|
3757
|
+
},
|
|
3758
|
+
researchUserPrompt(t, ctx) {
|
|
3759
|
+
return strategy.researchUserPrompt(t, ctx);
|
|
3760
|
+
},
|
|
3761
|
+
parseResearchResponse(participant, text) {
|
|
3762
|
+
return strategy.parseResearchResponse(participant, text);
|
|
3763
|
+
},
|
|
3764
|
+
crossReviewSystemPrompt(reviewerName) {
|
|
3765
|
+
return `${langInstruction}
|
|
3766
|
+
|
|
3767
|
+
${strategy.crossReviewSystemPrompt(reviewerName)}`;
|
|
3768
|
+
},
|
|
3769
|
+
crossReviewUserPrompt(t, my, others) {
|
|
3770
|
+
return strategy.crossReviewUserPrompt(t, my, others);
|
|
3771
|
+
},
|
|
3772
|
+
parseCrossReviewResponse(reviewer, text) {
|
|
3773
|
+
return strategy.parseCrossReviewResponse(reviewer, text);
|
|
3774
|
+
},
|
|
3775
|
+
consensusSystemPrompt() {
|
|
3776
|
+
return `${langInstruction}
|
|
3777
|
+
|
|
3778
|
+
${strategy.consensusSystemPrompt()}`;
|
|
3779
|
+
},
|
|
3780
|
+
consensusUserPrompt(t, reports, reviews) {
|
|
3781
|
+
return strategy.consensusUserPrompt(t, reports, reviews);
|
|
3782
|
+
},
|
|
3783
|
+
parseConsensusResponse(text) {
|
|
3784
|
+
return strategy.parseConsensusResponse(text);
|
|
3785
|
+
},
|
|
3786
|
+
preferredFindingKinds() {
|
|
3787
|
+
return strategy.preferredFindingKinds();
|
|
3788
|
+
},
|
|
3789
|
+
// ─── V2 forwarding (conditional) ─────────────────────────────
|
|
3790
|
+
...isStrategyV2(strategy) ? {
|
|
3791
|
+
verificationReviewUserPrompt(topic2, myReport, claims, digest) {
|
|
3792
|
+
return strategy.verificationReviewUserPrompt(topic2, myReport, claims, digest);
|
|
3793
|
+
},
|
|
3794
|
+
parseVerificationReviewResponse(reviewer, text) {
|
|
3795
|
+
return strategy.parseVerificationReviewResponse(reviewer, text);
|
|
3796
|
+
},
|
|
3797
|
+
debateTurnUserPrompt(topic2, claim, priorTurns, digest) {
|
|
3798
|
+
return strategy.debateTurnUserPrompt(topic2, claim, priorTurns, digest);
|
|
3799
|
+
},
|
|
3800
|
+
parseDebateTurnResponse(participant, text) {
|
|
3801
|
+
return strategy.parseDebateTurnResponse(participant, text);
|
|
3802
|
+
},
|
|
3803
|
+
adjudicationUserPrompt(topic2, claim, rounds, digest) {
|
|
3804
|
+
return strategy.adjudicationUserPrompt(topic2, claim, rounds, digest);
|
|
3805
|
+
},
|
|
3806
|
+
parseAdjudicationResponse(text) {
|
|
3807
|
+
return strategy.parseAdjudicationResponse(text);
|
|
3808
|
+
},
|
|
3809
|
+
claimAwareConsensusUserPrompt(topic2, reports, reviews, claimSummary) {
|
|
3810
|
+
return strategy.claimAwareConsensusUserPrompt(topic2, reports, reviews, claimSummary);
|
|
3811
|
+
}
|
|
3812
|
+
} : {}
|
|
3813
|
+
};
|
|
3814
|
+
}
|
|
3815
|
+
|
|
3816
|
+
// src/arena/arena.ts
|
|
3817
|
+
var Arena = class {
|
|
3818
|
+
config;
|
|
3819
|
+
strategy;
|
|
3820
|
+
limits;
|
|
3821
|
+
constructor(config) {
|
|
3822
|
+
if (config.participants.length < 2) {
|
|
3823
|
+
throw new Error("Arena requires at least 2 participants");
|
|
3824
|
+
}
|
|
3825
|
+
const mode = config.mode ?? "review";
|
|
3826
|
+
const defaults = ARENA_MODE_DEFAULTS[mode];
|
|
3827
|
+
this.config = {
|
|
3828
|
+
mode,
|
|
3829
|
+
maxDiscussionRounds: config.maxDiscussionRounds ?? defaults.maxDiscussionRounds,
|
|
3830
|
+
...config
|
|
3831
|
+
};
|
|
3832
|
+
this.limits = config.executionLimits ?? DEFAULT_EXECUTION_LIMITS;
|
|
3833
|
+
this.strategy = config.strategy ?? getStrategy(mode);
|
|
3834
|
+
}
|
|
3835
|
+
/**
|
|
3836
|
+
* Run a full arena session.
|
|
3837
|
+
*
|
|
3838
|
+
* @param topic - User's topic/request (natural language)
|
|
3839
|
+
* @param flags - Optional explicit overrides (mode, base, head)
|
|
3840
|
+
*/
|
|
3841
|
+
async run(topic, flags) {
|
|
3842
|
+
const participantNames = this.config.participants.map((p) => p.name);
|
|
3843
|
+
const signal = this.config.signal;
|
|
3844
|
+
logger.info("arena.start", { mode: this.config.mode, participants: participantNames });
|
|
3845
|
+
signal?.throwIfAborted();
|
|
3846
|
+
const llmConfig = this.config.participants[0].llm;
|
|
3847
|
+
const plan = await planArena(topic, llmConfig, flags, signal);
|
|
3848
|
+
this.config.onProgress?.({ type: "plan_resolved", plan });
|
|
3849
|
+
logger.info("arena.plan", {
|
|
3850
|
+
mode: plan.mode,
|
|
3851
|
+
lenses: plan.lenses.map((l) => l.name),
|
|
3852
|
+
sources: plan.sources.map((s) => s.kind),
|
|
3853
|
+
subject: plan.subject.label,
|
|
3854
|
+
confidence: plan.confidence
|
|
3855
|
+
});
|
|
3856
|
+
if (!flags?.mode && plan.mode !== this.config.mode) {
|
|
3857
|
+
this.config.mode = plan.mode;
|
|
3858
|
+
}
|
|
3859
|
+
signal?.throwIfAborted();
|
|
3860
|
+
const { artifacts, quickFacts } = collectEvidence(plan, topic);
|
|
3861
|
+
this.config.onProgress?.({ type: "evidence_collected", artifacts });
|
|
3862
|
+
logger.info("arena.evidence", {
|
|
3863
|
+
artifactCount: artifacts.length,
|
|
3864
|
+
sources: plan.sources.map((s) => s.kind)
|
|
3865
|
+
});
|
|
3866
|
+
const baseContext = { plan, artifacts, quickFacts };
|
|
3867
|
+
this.strategy = this.config.strategy ?? getStrategyForPlan(plan);
|
|
3868
|
+
this.strategy = withLanguage(this.strategy, topic);
|
|
3869
|
+
const contextTools = this.config.enableContextTools !== false ? selectTools(plan) : void 0;
|
|
3870
|
+
signal?.throwIfAborted();
|
|
3871
|
+
logger.info("arena.research_phase", { participants: participantNames });
|
|
3872
|
+
const researchResults = await runParticipantResearchWithDossiers({
|
|
3873
|
+
participants: this.config.participants,
|
|
3874
|
+
strategy: this.strategy,
|
|
3875
|
+
topic,
|
|
3876
|
+
baseContext,
|
|
3877
|
+
enableContextTools: this.config.enableContextTools ?? true,
|
|
3878
|
+
contextTools,
|
|
3879
|
+
signal,
|
|
3880
|
+
onProgress: this.config.onProgress
|
|
3881
|
+
});
|
|
3882
|
+
const reports = researchResults.map((r) => r.report);
|
|
3883
|
+
const dossiers = researchResults.map((r) => r.dossier);
|
|
3884
|
+
signal?.throwIfAborted();
|
|
3885
|
+
const ledger = new ArenaLedger();
|
|
3886
|
+
for (const dossier of dossiers) {
|
|
3887
|
+
ledger.appendDossier(dossier);
|
|
3888
|
+
}
|
|
3889
|
+
const claims = registerClaims({
|
|
3890
|
+
dossiers,
|
|
3891
|
+
ledger,
|
|
3892
|
+
onProgress: this.config.onProgress
|
|
3893
|
+
});
|
|
3894
|
+
logger.info("arena.claims_registered", {
|
|
3895
|
+
claimCount: claims.length,
|
|
3896
|
+
byOwner: Object.fromEntries(
|
|
3897
|
+
participantNames.map((name) => [
|
|
3898
|
+
name,
|
|
3899
|
+
claims.filter((c) => c.owner === name).length
|
|
3900
|
+
])
|
|
3901
|
+
)
|
|
3902
|
+
});
|
|
3903
|
+
signal?.throwIfAborted();
|
|
3904
|
+
const concluderName = this.config.concluder ?? participantNames[0];
|
|
3905
|
+
const concluderMatch = this.config.participants.find((p) => p.name === concluderName);
|
|
3906
|
+
if (!concluderMatch && this.config.concluder) {
|
|
3907
|
+
logger.warn("arena.concluder_not_found", {
|
|
3908
|
+
requested: this.config.concluder,
|
|
3909
|
+
fallback: this.config.participants[0].name
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
const concluder = concluderMatch ?? this.config.participants[0];
|
|
3913
|
+
const enableTools = this.config.enableContextTools !== false;
|
|
3914
|
+
if (this.config.mode === "planning") {
|
|
3915
|
+
return this.runPlanningPath({
|
|
3916
|
+
topic,
|
|
3917
|
+
participantNames,
|
|
3918
|
+
plan,
|
|
3919
|
+
baseContext,
|
|
3920
|
+
reports,
|
|
3921
|
+
dossiers,
|
|
3922
|
+
ledger,
|
|
3923
|
+
concluder,
|
|
3924
|
+
contextTools,
|
|
3925
|
+
enableContextTools: enableTools,
|
|
3926
|
+
signal
|
|
3927
|
+
});
|
|
3928
|
+
}
|
|
3929
|
+
return this.runReviewDiscussionPath({
|
|
3930
|
+
topic,
|
|
3931
|
+
participantNames,
|
|
3932
|
+
plan,
|
|
3933
|
+
baseContext,
|
|
3934
|
+
reports,
|
|
3935
|
+
dossiers,
|
|
3936
|
+
ledger,
|
|
3937
|
+
concluder,
|
|
3938
|
+
signal
|
|
3939
|
+
});
|
|
3940
|
+
}
|
|
3941
|
+
/**
|
|
3942
|
+
* Planning path: merge-oriented review → roadmap consensus → detail expansion.
|
|
3943
|
+
*
|
|
3944
|
+
* Planning mode does NOT enter heavy debate/adjudication. Contested findings
|
|
3945
|
+
* are converted to open questions or dependency risks in the roadmap.
|
|
3946
|
+
*/
|
|
3947
|
+
async runPlanningPath(ctx) {
|
|
3948
|
+
const {
|
|
3949
|
+
topic,
|
|
3950
|
+
participantNames,
|
|
3951
|
+
plan,
|
|
3952
|
+
baseContext,
|
|
3953
|
+
reports,
|
|
3954
|
+
dossiers,
|
|
3955
|
+
ledger,
|
|
3956
|
+
concluder,
|
|
3957
|
+
contextTools,
|
|
3958
|
+
enableContextTools,
|
|
3959
|
+
signal
|
|
3960
|
+
} = ctx;
|
|
3961
|
+
logger.info("arena.planning_merge_review_phase");
|
|
3962
|
+
const { reviews } = await runVerificationReview({
|
|
3963
|
+
participants: this.config.participants,
|
|
3964
|
+
strategy: this.strategy,
|
|
3965
|
+
topic,
|
|
3966
|
+
reports,
|
|
3967
|
+
ledger,
|
|
3968
|
+
limits: this.limits,
|
|
3969
|
+
mode: "planning",
|
|
3970
|
+
signal,
|
|
3971
|
+
onProgress: this.config.onProgress
|
|
3972
|
+
});
|
|
3973
|
+
signal?.throwIfAborted();
|
|
3974
|
+
const allClaims = ledger.getAllClaims();
|
|
3975
|
+
const claimSummary = {
|
|
3976
|
+
verified: allClaims.filter((c) => c.status === "verified"),
|
|
3977
|
+
contested: allClaims.filter((c) => c.status === "contested"),
|
|
3978
|
+
unresolved: allClaims.filter((c) => c.status === "unresolved"),
|
|
3979
|
+
rejected: allClaims.filter((c) => c.status === "rejected")
|
|
3980
|
+
};
|
|
3981
|
+
logger.info("arena.planning_consensus_phase", { concluder: concluder.name });
|
|
3982
|
+
const consensus = await buildConsensus({
|
|
3983
|
+
concluder,
|
|
3984
|
+
strategy: this.strategy,
|
|
3985
|
+
topic,
|
|
3986
|
+
reports,
|
|
3987
|
+
reviews,
|
|
3988
|
+
claimSummary,
|
|
3989
|
+
signal,
|
|
3990
|
+
onProgress: this.config.onProgress
|
|
3991
|
+
});
|
|
3992
|
+
signal?.throwIfAborted();
|
|
3993
|
+
if (consensus.roadmap.length > 0 && isStrategyPlanning(this.strategy)) {
|
|
3994
|
+
logger.info("arena.detail_expansion_phase", { phaseCount: consensus.roadmap.length });
|
|
3995
|
+
try {
|
|
3996
|
+
const details = await runDetailExpansion({
|
|
3997
|
+
concluder,
|
|
3998
|
+
strategy: this.strategy,
|
|
3999
|
+
topic,
|
|
4000
|
+
phases: consensus.roadmap,
|
|
4001
|
+
ledger,
|
|
4002
|
+
limits: this.limits,
|
|
4003
|
+
enableContextTools,
|
|
4004
|
+
contextTools,
|
|
4005
|
+
signal,
|
|
4006
|
+
onProgress: this.config.onProgress
|
|
4007
|
+
});
|
|
4008
|
+
consensus.roadmapDetails = details;
|
|
4009
|
+
} catch (err) {
|
|
4010
|
+
logger.warn("arena.detail_expansion_failed", {
|
|
4011
|
+
error: err.message,
|
|
4012
|
+
phaseCount: consensus.roadmap.length
|
|
4013
|
+
});
|
|
4014
|
+
}
|
|
4015
|
+
}
|
|
4016
|
+
return {
|
|
4017
|
+
topic,
|
|
4018
|
+
mode: this.config.mode,
|
|
4019
|
+
participants: participantNames,
|
|
4020
|
+
plan,
|
|
4021
|
+
baseContext,
|
|
4022
|
+
reports,
|
|
4023
|
+
dossiers,
|
|
4024
|
+
claims: allClaims,
|
|
4025
|
+
reviews,
|
|
4026
|
+
consensus
|
|
4027
|
+
};
|
|
4028
|
+
}
|
|
4029
|
+
/**
|
|
4030
|
+
* Review/Discussion path: verification review → debate → adjudication → consensus.
|
|
4031
|
+
*
|
|
4032
|
+
* This is the heavy trust loop for claim verification.
|
|
4033
|
+
*/
|
|
4034
|
+
async runReviewDiscussionPath(ctx) {
|
|
4035
|
+
const { topic, participantNames, plan, baseContext, reports, dossiers, ledger, concluder, signal } = ctx;
|
|
4036
|
+
logger.info("arena.verification_review_phase");
|
|
4037
|
+
const { reviews } = await runVerificationReview({
|
|
4038
|
+
participants: this.config.participants,
|
|
4039
|
+
strategy: this.strategy,
|
|
4040
|
+
topic,
|
|
4041
|
+
reports,
|
|
4042
|
+
ledger,
|
|
4043
|
+
limits: this.limits,
|
|
4044
|
+
signal,
|
|
4045
|
+
onProgress: this.config.onProgress
|
|
4046
|
+
});
|
|
4047
|
+
signal?.throwIfAborted();
|
|
4048
|
+
logger.info("arena.debate_phase");
|
|
4049
|
+
const debateRounds = await runDebateRounds({
|
|
4050
|
+
participants: this.config.participants,
|
|
4051
|
+
strategy: this.strategy,
|
|
4052
|
+
topic,
|
|
4053
|
+
ledger,
|
|
4054
|
+
limits: this.limits,
|
|
4055
|
+
maxRounds: this.config.maxDiscussionRounds,
|
|
4056
|
+
signal,
|
|
4057
|
+
onProgress: this.config.onProgress
|
|
4058
|
+
});
|
|
4059
|
+
signal?.throwIfAborted();
|
|
4060
|
+
logger.info("arena.adjudication_phase", { concluder: concluder.name });
|
|
4061
|
+
const adjudications = await runAdjudication({
|
|
4062
|
+
concluder,
|
|
4063
|
+
strategy: this.strategy,
|
|
4064
|
+
topic,
|
|
4065
|
+
ledger,
|
|
4066
|
+
signal,
|
|
4067
|
+
onProgress: this.config.onProgress
|
|
4068
|
+
});
|
|
4069
|
+
signal?.throwIfAborted();
|
|
4070
|
+
const allClaims = ledger.getAllClaims();
|
|
4071
|
+
const claimSummary = {
|
|
4072
|
+
verified: allClaims.filter((c) => c.status === "verified"),
|
|
4073
|
+
contested: allClaims.filter((c) => c.status === "contested"),
|
|
4074
|
+
unresolved: allClaims.filter((c) => c.status === "unresolved"),
|
|
4075
|
+
rejected: allClaims.filter((c) => c.status === "rejected")
|
|
4076
|
+
};
|
|
4077
|
+
logger.info("arena.consensus_phase", {
|
|
4078
|
+
concluder: concluder.name,
|
|
4079
|
+
verified: claimSummary.verified.length,
|
|
4080
|
+
unresolved: claimSummary.unresolved.length
|
|
4081
|
+
});
|
|
4082
|
+
const consensus = await buildConsensus({
|
|
4083
|
+
concluder,
|
|
4084
|
+
strategy: this.strategy,
|
|
4085
|
+
topic,
|
|
4086
|
+
reports,
|
|
4087
|
+
reviews,
|
|
4088
|
+
claimSummary,
|
|
4089
|
+
signal,
|
|
4090
|
+
onProgress: this.config.onProgress
|
|
4091
|
+
});
|
|
4092
|
+
return {
|
|
4093
|
+
topic,
|
|
4094
|
+
mode: this.config.mode,
|
|
4095
|
+
participants: participantNames,
|
|
4096
|
+
plan,
|
|
4097
|
+
baseContext,
|
|
4098
|
+
reports,
|
|
4099
|
+
dossiers,
|
|
4100
|
+
claims: allClaims,
|
|
4101
|
+
reviews,
|
|
4102
|
+
debateRounds,
|
|
4103
|
+
adjudications,
|
|
4104
|
+
consensus
|
|
4105
|
+
};
|
|
4106
|
+
}
|
|
4107
|
+
};
|
|
4108
|
+
|
|
4109
|
+
// src/arena/model-presets.ts
|
|
4110
|
+
init_esm_shims();
|
|
4111
|
+
var MODEL_PRESETS = {
|
|
4112
|
+
claude: { provider: "openai", model: "anthropic/claude-opus-4.6", maxOutputTokens: 32e3 },
|
|
4113
|
+
"claude-sonnet": { provider: "openai", model: "anthropic/claude-sonnet-4.6", maxOutputTokens: 16e3 },
|
|
4114
|
+
"claude-haiku": { provider: "openai", model: "anthropic/claude-haiku-4.5", maxOutputTokens: 8192 },
|
|
4115
|
+
gpt: { provider: "openai", model: "openai/gpt-5.4", maxOutputTokens: 32e3 },
|
|
4116
|
+
"gpt4o": { provider: "openai", model: "openai/gpt-4o", maxOutputTokens: 16384 },
|
|
4117
|
+
o4: { provider: "openai", model: "openai/o4-mini", maxOutputTokens: 1e5 },
|
|
4118
|
+
o3: { provider: "openai", model: "openai/o3", maxOutputTokens: 1e5 },
|
|
4119
|
+
deepseek: { provider: "openai", model: "deepseek/deepseek-v3.2", maxOutputTokens: 8192 },
|
|
4120
|
+
"deepseek-r1": { provider: "openai", model: "deepseek/deepseek-r1", maxOutputTokens: 8192 },
|
|
4121
|
+
gemini: { provider: "openai", model: "google/gemini-3.1-pro-preview", maxOutputTokens: 65536 },
|
|
4122
|
+
"gemini-2.5": { provider: "openai", model: "google/gemini-2.5-pro", maxOutputTokens: 65536 },
|
|
4123
|
+
"gemini-flash": { provider: "openai", model: "google/gemini-3-flash-preview", maxOutputTokens: 65536 },
|
|
4124
|
+
qwen: { provider: "openai", model: "qwen/qwen3-235b-a22b", maxOutputTokens: 8192 },
|
|
4125
|
+
"qwen-coder": { provider: "openai", model: "qwen/qwen3-coder", maxOutputTokens: 16384 },
|
|
4126
|
+
llama: { provider: "openai", model: "meta-llama/llama-4-maverick", maxOutputTokens: 32e3 },
|
|
4127
|
+
devstral: { provider: "openai", model: "mistralai/devstral-medium", maxOutputTokens: 24e3 }
|
|
4128
|
+
};
|
|
4129
|
+
function getMaxOutputTokens(model) {
|
|
4130
|
+
const byKey = MODEL_PRESETS[model];
|
|
4131
|
+
if (byKey) return byKey.maxOutputTokens;
|
|
4132
|
+
const byModel = Object.values(MODEL_PRESETS).find((p) => p.model === model);
|
|
4133
|
+
return byModel?.maxOutputTokens ?? 8192;
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
// src/arena/render/terminal.ts
|
|
4137
|
+
init_esm_shims();
|
|
4138
|
+
import chalk from "chalk";
|
|
4139
|
+
function formatArenaResult(result) {
|
|
4140
|
+
const modeLabel = result.mode.charAt(0).toUpperCase() + result.mode.slice(1);
|
|
4141
|
+
const lines = [];
|
|
4142
|
+
lines.push("");
|
|
4143
|
+
lines.push(chalk.bold(` \u2550\u2550\u2550 ${modeLabel} Conclusion \u2550\u2550\u2550`));
|
|
4144
|
+
lines.push("");
|
|
4145
|
+
if (result.consensus.summary) {
|
|
4146
|
+
lines.push(` ${result.consensus.summary}`);
|
|
4147
|
+
lines.push("");
|
|
4148
|
+
}
|
|
4149
|
+
if (result.mode === "planning") {
|
|
4150
|
+
lines.push(...formatRoadmapSection(result.consensus.roadmap));
|
|
4151
|
+
if (result.consensus.roadmapDetails && result.consensus.roadmapDetails.length > 0) {
|
|
4152
|
+
lines.push(...formatRoadmapDetailsSection(result.consensus.roadmapDetails));
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
for (const section of getOrderedConsensusSections(result)) {
|
|
4156
|
+
lines.push(...formatConsensusSection(section.title, section.items, section.color, section.icon));
|
|
4157
|
+
}
|
|
4158
|
+
if (result.mode !== "planning") {
|
|
4159
|
+
lines.push(...formatRoadmapSection(result.consensus.roadmap));
|
|
4160
|
+
}
|
|
4161
|
+
if (result.consensus.nextActions.length > 0) {
|
|
4162
|
+
lines.push(chalk.bold.white("\n Next Actions:"));
|
|
4163
|
+
for (const action of result.consensus.nextActions) {
|
|
4164
|
+
const pri = action.priority === "high" ? chalk.red("HIGH") : action.priority === "medium" ? chalk.yellow("MED") : chalk.dim("LOW");
|
|
4165
|
+
lines.push(` ${chalk.white("\u2192")} [${pri}${chalk.white("]")} ${action.title}`);
|
|
4166
|
+
if (action.rationale) {
|
|
4167
|
+
lines.push(chalk.dim(` ${action.rationale}`));
|
|
4168
|
+
}
|
|
4169
|
+
}
|
|
4170
|
+
}
|
|
4171
|
+
lines.push("");
|
|
4172
|
+
const planInfo = result.plan ? ` | Lenses: ${result.plan.lenses.map((l) => l.name).join(", ")} | Sources: ${result.plan.sources.map((s) => s.kind).join(", ")}` : "";
|
|
4173
|
+
lines.push(chalk.dim(
|
|
4174
|
+
` Mode: ${modeLabel} | Findings: ${countFindings(result)} | Models: ${result.participants.join(", ")}` + planInfo
|
|
4175
|
+
));
|
|
4176
|
+
lines.push("");
|
|
4177
|
+
return lines.join("\n");
|
|
4178
|
+
}
|
|
4179
|
+
function printArenaResult(result) {
|
|
4180
|
+
console.log(formatArenaResult(result));
|
|
4181
|
+
}
|
|
4182
|
+
function formatConsensusSection(title, items, color, icon) {
|
|
4183
|
+
if (items.length === 0) return [];
|
|
4184
|
+
const lines = [];
|
|
4185
|
+
lines.push(color(`
|
|
4186
|
+
${title}:`));
|
|
4187
|
+
for (const item of items) {
|
|
4188
|
+
lines.push(color(` ${icon} ${item.title}`));
|
|
4189
|
+
if (item.summary) {
|
|
4190
|
+
lines.push(chalk.dim(` ${item.summary}`));
|
|
4191
|
+
}
|
|
4192
|
+
if (item.challenge.length > 0) {
|
|
4193
|
+
lines.push(chalk.dim(` Challenged by: ${item.challenge.join(", ")}`));
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
return lines;
|
|
4197
|
+
}
|
|
4198
|
+
function countFindings(result) {
|
|
4199
|
+
return result.reports.reduce((sum, r) => sum + r.findings.length, 0);
|
|
4200
|
+
}
|
|
4201
|
+
function formatRoadmapSection(phases) {
|
|
4202
|
+
if (phases.length === 0) return [];
|
|
4203
|
+
const lines = [];
|
|
4204
|
+
lines.push(chalk.bold.white("\n Roadmap:"));
|
|
4205
|
+
for (const [index, phase] of phases.entries()) {
|
|
4206
|
+
const pri = phase.priority === "high" ? chalk.red("HIGH") : phase.priority === "medium" ? chalk.yellow("MED") : chalk.dim("LOW");
|
|
4207
|
+
lines.push(` ${chalk.white("\u2192")} Phase ${index + 1}: ${phase.title} [${pri}${chalk.white("]")}`);
|
|
4208
|
+
if (phase.goal) {
|
|
4209
|
+
lines.push(chalk.dim(` Goal: ${phase.goal}`));
|
|
4210
|
+
}
|
|
4211
|
+
if (phase.scope.length > 0) {
|
|
4212
|
+
lines.push(chalk.dim(` Scope: ${phase.scope.join("; ")}`));
|
|
4213
|
+
}
|
|
4214
|
+
if (phase.deliverables.length > 0) {
|
|
4215
|
+
lines.push(chalk.dim(` Deliverables: ${phase.deliverables.join("; ")}`));
|
|
4216
|
+
}
|
|
4217
|
+
if (phase.dependencies.length > 0) {
|
|
4218
|
+
lines.push(chalk.dim(` Dependencies: ${phase.dependencies.join("; ")}`));
|
|
4219
|
+
}
|
|
4220
|
+
if (phase.risks.length > 0) {
|
|
4221
|
+
lines.push(chalk.dim(` Risks: ${phase.risks.join("; ")}`));
|
|
4222
|
+
}
|
|
4223
|
+
if (phase.successCriteria.length > 0) {
|
|
4224
|
+
lines.push(chalk.dim(` Success: ${phase.successCriteria.join("; ")}`));
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
return lines;
|
|
4228
|
+
}
|
|
4229
|
+
function formatRoadmapDetailsSection(details) {
|
|
4230
|
+
const lines = [];
|
|
4231
|
+
lines.push(chalk.bold.white("\n Implementation Details:"));
|
|
4232
|
+
for (const detail of details) {
|
|
4233
|
+
const effort = detail.effort === "large" ? chalk.red("L") : detail.effort === "medium" ? chalk.yellow("M") : chalk.green("S");
|
|
4234
|
+
lines.push(`
|
|
4235
|
+
${chalk.white("\u25B8")} ${detail.phaseTitle} [${effort}]`);
|
|
4236
|
+
if (detail.objective) {
|
|
4237
|
+
lines.push(chalk.dim(` ${detail.objective}`));
|
|
4238
|
+
}
|
|
4239
|
+
if (detail.targetFiles.length > 0) {
|
|
4240
|
+
lines.push(chalk.dim(` Files: ${detail.targetFiles.join(", ")}`));
|
|
4241
|
+
}
|
|
4242
|
+
if (detail.codeChanges.length > 0) {
|
|
4243
|
+
for (const change of detail.codeChanges) {
|
|
4244
|
+
lines.push(chalk.dim(` \u2022 ${change}`));
|
|
4245
|
+
}
|
|
4246
|
+
}
|
|
4247
|
+
if (detail.interfaces.length > 0) {
|
|
4248
|
+
lines.push(chalk.dim(` Interfaces: ${detail.interfaces.join("; ")}`));
|
|
4249
|
+
}
|
|
4250
|
+
if (detail.migrationSteps.length > 0) {
|
|
4251
|
+
lines.push(chalk.dim(` Migration:`));
|
|
4252
|
+
for (const [i, step] of detail.migrationSteps.entries()) {
|
|
4253
|
+
lines.push(chalk.dim(` ${i + 1}. ${step}`));
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
if (detail.validation.length > 0) {
|
|
4257
|
+
lines.push(chalk.dim(` Validation: ${detail.validation.join("; ")}`));
|
|
4258
|
+
}
|
|
4259
|
+
if (detail.blockers.length > 0) {
|
|
4260
|
+
lines.push(chalk.red(` Blockers: ${detail.blockers.join("; ")}`));
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
return lines;
|
|
4264
|
+
}
|
|
4265
|
+
function getOrderedConsensusSections(result) {
|
|
4266
|
+
const VALID_KINDS = /* @__PURE__ */ new Set(["strength", "improvement", "risk", "question"]);
|
|
4267
|
+
const defaultOrder = ["risk", "improvement", "strength", "question"];
|
|
4268
|
+
const emphasize = (result.plan?.outputShape?.emphasize ?? []).filter((k) => VALID_KINDS.has(k));
|
|
4269
|
+
const orderedKinds = dedupeFindingKinds([...emphasize, ...defaultOrder]);
|
|
4270
|
+
return orderedKinds.map((kind) => {
|
|
4271
|
+
switch (kind) {
|
|
4272
|
+
case "strength":
|
|
4273
|
+
return { kind, title: "Strengths", items: result.consensus.strengths, color: chalk.green, icon: "\u2713" };
|
|
4274
|
+
case "improvement":
|
|
4275
|
+
return { kind, title: "Improvements", items: result.consensus.improvements, color: chalk.yellow, icon: "\u2192" };
|
|
4276
|
+
case "risk":
|
|
4277
|
+
return { kind, title: "Risks", items: result.consensus.risks, color: chalk.red, icon: "\u26A0" };
|
|
4278
|
+
case "question":
|
|
4279
|
+
return { kind, title: "Open Questions", items: result.consensus.openQuestions, color: chalk.cyan, icon: "?" };
|
|
4280
|
+
}
|
|
4281
|
+
}).filter((section) => section != null).filter((section) => section.items.length > 0);
|
|
4282
|
+
}
|
|
4283
|
+
function dedupeFindingKinds(kinds) {
|
|
4284
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4285
|
+
const result = [];
|
|
4286
|
+
for (const kind of kinds) {
|
|
4287
|
+
if (!seen.has(kind)) {
|
|
4288
|
+
seen.add(kind);
|
|
4289
|
+
result.push(kind);
|
|
4290
|
+
}
|
|
4291
|
+
}
|
|
4292
|
+
return result;
|
|
4293
|
+
}
|
|
4294
|
+
function createProgressRenderer(sink) {
|
|
4295
|
+
return (event) => {
|
|
4296
|
+
switch (event.type) {
|
|
4297
|
+
case "plan_resolved":
|
|
4298
|
+
sink(chalk.dim(
|
|
4299
|
+
`Plan: ${event.plan.mode} | lenses: ${event.plan.lenses.map((l) => l.name).join(", ")} | sources: ${event.plan.sources.map((s) => s.kind).join(", ")}` + (event.plan.confidence !== "high" ? ` (${event.plan.confidence} confidence)` : "")
|
|
4300
|
+
));
|
|
4301
|
+
break;
|
|
4302
|
+
case "evidence_collected":
|
|
4303
|
+
sink(chalk.dim(`Evidence: ${event.artifacts.length} artifacts collected`));
|
|
4304
|
+
break;
|
|
4305
|
+
case "research_start":
|
|
4306
|
+
sink(chalk.dim(`\u23F3 ${event.participant} researching...`));
|
|
4307
|
+
break;
|
|
4308
|
+
case "research_done": {
|
|
4309
|
+
const fCount = event.report.findings.length;
|
|
4310
|
+
sink(`${event.participant}: ${chalk.green("done")} (${fCount} findings)`);
|
|
4311
|
+
break;
|
|
4312
|
+
}
|
|
4313
|
+
case "context_lookup":
|
|
4314
|
+
for (const t of event.tools) {
|
|
4315
|
+
sink(chalk.dim(` \u{1F50D} ${event.participant}: ${t}`));
|
|
4316
|
+
}
|
|
4317
|
+
break;
|
|
4318
|
+
case "claims_registered":
|
|
4319
|
+
sink(chalk.dim(`${event.claimCount} claims registered`));
|
|
4320
|
+
break;
|
|
4321
|
+
case "cross_review_start":
|
|
4322
|
+
sink(chalk.dim("\u2500\u2500 Cross Review \u2500\u2500"));
|
|
4323
|
+
break;
|
|
4324
|
+
case "cross_review_done":
|
|
4325
|
+
sink(chalk.dim(`${event.reviews.length} peer reviews collected`));
|
|
4326
|
+
break;
|
|
4327
|
+
case "verification_start":
|
|
4328
|
+
sink(chalk.dim("\u2500\u2500 Verification Review \u2500\u2500"));
|
|
4329
|
+
break;
|
|
4330
|
+
case "verification_done":
|
|
4331
|
+
sink(chalk.dim(`${event.challengeCount} challenges raised`));
|
|
4332
|
+
break;
|
|
4333
|
+
case "debate_round_start":
|
|
4334
|
+
sink(chalk.dim(`\u2500\u2500 Debate Round ${event.round} (${event.claims.length} claims) \u2500\u2500`));
|
|
4335
|
+
break;
|
|
4336
|
+
case "debate_round_done":
|
|
4337
|
+
sink(chalk.dim(`Round ${event.round}: ${event.resolved} resolved`));
|
|
4338
|
+
break;
|
|
4339
|
+
case "adjudication_done":
|
|
4340
|
+
sink(chalk.dim(`Adjudication: ${event.accepted} accepted, ${event.unresolved} unresolved`));
|
|
4341
|
+
break;
|
|
4342
|
+
case "planning_merge_review_start":
|
|
4343
|
+
sink(chalk.dim("\u2500\u2500 Planning Merge Review \u2500\u2500"));
|
|
4344
|
+
break;
|
|
4345
|
+
case "planning_merge_review_done":
|
|
4346
|
+
sink(chalk.dim(`${event.mergeCount} merge suggestions collected`));
|
|
4347
|
+
break;
|
|
4348
|
+
case "roadmap_expansion_start":
|
|
4349
|
+
sink(chalk.dim(`\u2500\u2500 Expanding ${event.phaseCount} roadmap phases \u2500\u2500`));
|
|
4350
|
+
break;
|
|
4351
|
+
case "roadmap_expansion_done":
|
|
4352
|
+
sink(chalk.dim(`${event.detailCount} phase details generated`));
|
|
4353
|
+
break;
|
|
4354
|
+
case "consensus_start":
|
|
4355
|
+
sink(chalk.dim("\u23F3 Building consensus..."));
|
|
4356
|
+
break;
|
|
4357
|
+
case "consensus_done":
|
|
4358
|
+
break;
|
|
4359
|
+
}
|
|
4360
|
+
};
|
|
4361
|
+
}
|
|
4362
|
+
function renderProgress(event) {
|
|
4363
|
+
createProgressRenderer((text) => console.log(` ${text}`))(event);
|
|
4364
|
+
}
|
|
4365
|
+
|
|
4366
|
+
// src/arena/render/session.ts
|
|
4367
|
+
init_esm_shims();
|
|
4368
|
+
function formatArenaResultForSession(result) {
|
|
4369
|
+
const lines = [];
|
|
4370
|
+
const modeLabel = result.mode.charAt(0).toUpperCase() + result.mode.slice(1);
|
|
4371
|
+
lines.push(`## Arena ${modeLabel} Result`);
|
|
4372
|
+
lines.push(`**Topic:** ${result.topic}`);
|
|
4373
|
+
lines.push(`**Models:** ${result.participants.join(" vs ")}`);
|
|
4374
|
+
lines.push("");
|
|
4375
|
+
const c = result.consensus;
|
|
4376
|
+
if (c.subjectSummary) {
|
|
4377
|
+
const overviewLabel = result.plan.outputShape.overviewLabel ?? (result.mode === "review" ? "What Changed" : result.mode === "planning" ? "Current Scope" : "Problem Framing");
|
|
4378
|
+
lines.push(`### ${overviewLabel}`);
|
|
4379
|
+
lines.push(c.subjectSummary);
|
|
4380
|
+
lines.push("");
|
|
4381
|
+
}
|
|
4382
|
+
if (c.summary) {
|
|
4383
|
+
lines.push("### Overall Assessment");
|
|
4384
|
+
lines.push(c.summary);
|
|
4385
|
+
lines.push("");
|
|
4386
|
+
}
|
|
4387
|
+
if (result.mode === "planning" && c.roadmap.length > 0) {
|
|
4388
|
+
lines.push("### Roadmap");
|
|
4389
|
+
for (const [index, phase] of c.roadmap.entries()) {
|
|
4390
|
+
lines.push(...formatRoadmapPhase(index, phase));
|
|
4391
|
+
}
|
|
4392
|
+
lines.push("");
|
|
4393
|
+
if (c.roadmapDetails && c.roadmapDetails.length > 0) {
|
|
4394
|
+
lines.push("### Implementation Details");
|
|
4395
|
+
for (const detail of c.roadmapDetails) {
|
|
4396
|
+
lines.push(...formatPhaseDetail(detail));
|
|
4397
|
+
}
|
|
4398
|
+
lines.push("");
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
for (const section of getOrderedConsensusSections2(result)) {
|
|
4402
|
+
lines.push(`### ${section.title}`);
|
|
4403
|
+
for (const item of section.items) {
|
|
4404
|
+
lines.push(`- **${item.title}**: ${item.summary}`);
|
|
4405
|
+
}
|
|
4406
|
+
lines.push("");
|
|
4407
|
+
}
|
|
4408
|
+
if (result.mode !== "planning" && c.roadmap.length > 0) {
|
|
4409
|
+
lines.push("### Roadmap");
|
|
4410
|
+
for (const [index, phase] of c.roadmap.entries()) {
|
|
4411
|
+
lines.push(...formatRoadmapPhase(index, phase));
|
|
4412
|
+
}
|
|
4413
|
+
lines.push("");
|
|
4414
|
+
}
|
|
4415
|
+
if (c.nextActions.length > 0) {
|
|
4416
|
+
lines.push("### Next Actions");
|
|
4417
|
+
for (const action of c.nextActions) {
|
|
4418
|
+
const pri = action.priority === "high" ? "\u{1F534}" : action.priority === "medium" ? "\u{1F7E1}" : "\u26AA";
|
|
4419
|
+
lines.push(`- ${pri} **${action.title}**: ${action.rationale}`);
|
|
4420
|
+
}
|
|
4421
|
+
lines.push("");
|
|
4422
|
+
}
|
|
4423
|
+
if (result.reports.some((r) => r.findings.length > 0)) {
|
|
4424
|
+
lines.push("---");
|
|
4425
|
+
lines.push("### Participant Findings");
|
|
4426
|
+
for (const report of result.reports) {
|
|
4427
|
+
if (report.findings.length === 0) {
|
|
4428
|
+
lines.push(`
|
|
4429
|
+
**${report.participant}**: ${report.contextSummary || "(no findings)"}`);
|
|
4430
|
+
continue;
|
|
4431
|
+
}
|
|
4432
|
+
lines.push(`
|
|
4433
|
+
**${report.participant}** (${report.findings.length} findings)`);
|
|
4434
|
+
if (report.contextSummary) {
|
|
4435
|
+
lines.push(`> ${report.contextSummary}`);
|
|
4436
|
+
}
|
|
4437
|
+
for (const f of report.findings) {
|
|
4438
|
+
const severity = f.severity ? `/${f.severity}` : "";
|
|
4439
|
+
lines.push(`- [${f.kind}${severity}] **${f.title}**: ${f.summary}`);
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
return lines.join("\n");
|
|
4444
|
+
}
|
|
4445
|
+
function formatRoadmapPhase(index, phase) {
|
|
4446
|
+
const pri = phase.priority === "high" ? "High" : phase.priority === "medium" ? "Medium" : "Low";
|
|
4447
|
+
const lines = [`#### Phase ${index + 1}: ${phase.title} (${pri} priority)`];
|
|
4448
|
+
if (phase.goal) {
|
|
4449
|
+
lines.push(`Goal: ${phase.goal}`);
|
|
4450
|
+
}
|
|
4451
|
+
if (phase.scope.length > 0) {
|
|
4452
|
+
lines.push(`Scope: ${phase.scope.join("; ")}`);
|
|
4453
|
+
}
|
|
4454
|
+
if (phase.deliverables.length > 0) {
|
|
4455
|
+
lines.push(`Deliverables: ${phase.deliverables.join("; ")}`);
|
|
4456
|
+
}
|
|
4457
|
+
if (phase.dependencies.length > 0) {
|
|
4458
|
+
lines.push(`Dependencies: ${phase.dependencies.join("; ")}`);
|
|
4459
|
+
}
|
|
4460
|
+
if (phase.risks.length > 0) {
|
|
4461
|
+
lines.push(`Risks: ${phase.risks.join("; ")}`);
|
|
4462
|
+
}
|
|
4463
|
+
if (phase.successCriteria.length > 0) {
|
|
4464
|
+
lines.push(`Success Criteria: ${phase.successCriteria.join("; ")}`);
|
|
4465
|
+
}
|
|
4466
|
+
lines.push("");
|
|
4467
|
+
return lines;
|
|
4468
|
+
}
|
|
4469
|
+
function formatPhaseDetail(detail) {
|
|
4470
|
+
const effort = detail.effort === "large" ? "Large" : detail.effort === "small" ? "Small" : "Medium";
|
|
4471
|
+
const lines = [`#### ${detail.phaseTitle} (${effort} effort)`];
|
|
4472
|
+
if (detail.objective) {
|
|
4473
|
+
lines.push(detail.objective);
|
|
4474
|
+
}
|
|
4475
|
+
if (detail.targetFiles.length > 0) {
|
|
4476
|
+
lines.push(`**Target files:** ${detail.targetFiles.map((f) => "`" + f + "`").join(", ")}`);
|
|
4477
|
+
}
|
|
4478
|
+
if (detail.codeChanges.length > 0) {
|
|
4479
|
+
lines.push("**Code changes:**");
|
|
4480
|
+
for (const change of detail.codeChanges) {
|
|
4481
|
+
lines.push(`- ${change}`);
|
|
4482
|
+
}
|
|
4483
|
+
}
|
|
4484
|
+
if (detail.interfaces.length > 0) {
|
|
4485
|
+
lines.push(`**Interfaces:** ${detail.interfaces.join("; ")}`);
|
|
4486
|
+
}
|
|
4487
|
+
if (detail.migrationSteps.length > 0) {
|
|
4488
|
+
lines.push("**Migration steps:**");
|
|
4489
|
+
for (const [i, step] of detail.migrationSteps.entries()) {
|
|
4490
|
+
lines.push(`${i + 1}. ${step}`);
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
if (detail.validation.length > 0) {
|
|
4494
|
+
lines.push(`**Validation:** ${detail.validation.join("; ")}`);
|
|
4495
|
+
}
|
|
4496
|
+
if (detail.blockers.length > 0) {
|
|
4497
|
+
lines.push(`**Blockers:** ${detail.blockers.join("; ")}`);
|
|
4498
|
+
}
|
|
4499
|
+
lines.push("");
|
|
4500
|
+
return lines;
|
|
4501
|
+
}
|
|
4502
|
+
function getOrderedConsensusSections2(result) {
|
|
4503
|
+
const VALID_KINDS = /* @__PURE__ */ new Set(["strength", "improvement", "risk", "question"]);
|
|
4504
|
+
const defaultOrder = ["risk", "improvement", "strength", "question"];
|
|
4505
|
+
const emphasize = (result.plan?.outputShape?.emphasize ?? []).filter((k) => VALID_KINDS.has(k));
|
|
4506
|
+
const orderedKinds = dedupeFindingKinds2([...emphasize, ...defaultOrder]);
|
|
4507
|
+
const sections = orderedKinds.map((kind) => {
|
|
4508
|
+
switch (kind) {
|
|
4509
|
+
case "strength":
|
|
4510
|
+
return { kind, title: "\u2713 Strengths", items: result.consensus.strengths };
|
|
4511
|
+
case "improvement":
|
|
4512
|
+
return { kind, title: "\u2192 Improvements", items: result.consensus.improvements };
|
|
4513
|
+
case "risk":
|
|
4514
|
+
return { kind, title: "\u26A0 Risks", items: result.consensus.risks };
|
|
4515
|
+
case "question":
|
|
4516
|
+
return { kind, title: "? Open Questions", items: result.consensus.openQuestions };
|
|
4517
|
+
}
|
|
4518
|
+
});
|
|
4519
|
+
return sections.filter((section) => section != null).filter((section) => section.items.length > 0);
|
|
4520
|
+
}
|
|
4521
|
+
function dedupeFindingKinds2(kinds) {
|
|
4522
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4523
|
+
const result = [];
|
|
4524
|
+
for (const kind of kinds) {
|
|
4525
|
+
if (!seen.has(kind)) {
|
|
4526
|
+
seen.add(kind);
|
|
4527
|
+
result.push(kind);
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
return result;
|
|
4531
|
+
}
|
|
4532
|
+
|
|
4533
|
+
export {
|
|
4534
|
+
registerProvider,
|
|
4535
|
+
createLLMClient,
|
|
4536
|
+
isStrategyV2,
|
|
4537
|
+
ReviewStrategy,
|
|
4538
|
+
DiscussionStrategy,
|
|
4539
|
+
PlanningStrategy,
|
|
4540
|
+
getLens,
|
|
4541
|
+
resolveLenses,
|
|
4542
|
+
buildLensPrompt,
|
|
4543
|
+
LENS_NAMES,
|
|
4544
|
+
getStrategy,
|
|
4545
|
+
getStrategyForPlan,
|
|
4546
|
+
planArena,
|
|
4547
|
+
collectEvidence,
|
|
4548
|
+
selectTools,
|
|
4549
|
+
hasTools,
|
|
4550
|
+
runParticipantResearch,
|
|
4551
|
+
runParticipantResearchWithDossiers,
|
|
4552
|
+
registerClaims,
|
|
4553
|
+
selectClaimsForReview,
|
|
4554
|
+
buildDigest,
|
|
4555
|
+
formatDigest,
|
|
4556
|
+
transitionClaim,
|
|
4557
|
+
resolveClaimStatus,
|
|
4558
|
+
markUnderReview,
|
|
4559
|
+
applyReviewResult,
|
|
4560
|
+
markUnresolved,
|
|
4561
|
+
isTerminal,
|
|
4562
|
+
validTransitions,
|
|
4563
|
+
runCrossReview,
|
|
4564
|
+
runVerificationReview,
|
|
4565
|
+
runDebateRounds,
|
|
4566
|
+
runAdjudication,
|
|
4567
|
+
buildConsensus,
|
|
4568
|
+
ArenaLedger,
|
|
4569
|
+
Arena,
|
|
4570
|
+
MODEL_PRESETS,
|
|
4571
|
+
getMaxOutputTokens,
|
|
4572
|
+
formatArenaResult,
|
|
4573
|
+
printArenaResult,
|
|
4574
|
+
createProgressRenderer,
|
|
4575
|
+
renderProgress,
|
|
4576
|
+
formatArenaResultForSession
|
|
4577
|
+
};
|