@cjhyy/code-shell-arena 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/README.md +46 -0
  2. package/dist/arena.d.ts +45 -0
  3. package/dist/arena.js +362 -0
  4. package/dist/capability.d.ts +43 -0
  5. package/dist/capability.js +453 -0
  6. package/dist/context/context-tools.d.ts +16 -0
  7. package/dist/context/context-tools.js +272 -0
  8. package/dist/context/within-root.d.ts +7 -0
  9. package/dist/context/within-root.js +15 -0
  10. package/dist/detect-mode.d.ts +20 -0
  11. package/dist/detect-mode.js +78 -0
  12. package/dist/digest-builder.d.ts +25 -0
  13. package/dist/digest-builder.js +120 -0
  14. package/dist/index.d.ts +25 -0
  15. package/dist/index.js +23 -0
  16. package/dist/index.runtime.d.ts +13 -0
  17. package/dist/index.runtime.js +11 -0
  18. package/dist/iterate/convergence.d.ts +25 -0
  19. package/dist/iterate/convergence.js +103 -0
  20. package/dist/iterate/formats/index.d.ts +22 -0
  21. package/dist/iterate/formats/index.js +283 -0
  22. package/dist/iterate/index.d.ts +11 -0
  23. package/dist/iterate/index.js +9 -0
  24. package/dist/iterate/iterative-arena.d.ts +31 -0
  25. package/dist/iterate/iterative-arena.js +252 -0
  26. package/dist/iterate/parse.d.ts +42 -0
  27. package/dist/iterate/parse.js +123 -0
  28. package/dist/iterate/phases/argue.d.ts +22 -0
  29. package/dist/iterate/phases/argue.js +165 -0
  30. package/dist/iterate/phases/revise.d.ts +16 -0
  31. package/dist/iterate/phases/revise.js +62 -0
  32. package/dist/iterate/phases/tournament.d.ts +34 -0
  33. package/dist/iterate/phases/tournament.js +113 -0
  34. package/dist/iterate/tools/web-tools.d.ts +13 -0
  35. package/dist/iterate/tools/web-tools.js +54 -0
  36. package/dist/iterate/types.d.ts +152 -0
  37. package/dist/iterate/types.js +8 -0
  38. package/dist/ledger.d.ts +47 -0
  39. package/dist/ledger.js +159 -0
  40. package/dist/lenses/architecture.d.ts +5 -0
  41. package/dist/lenses/architecture.js +22 -0
  42. package/dist/lenses/engineering.d.ts +5 -0
  43. package/dist/lenses/engineering.js +22 -0
  44. package/dist/lenses/general.d.ts +5 -0
  45. package/dist/lenses/general.js +20 -0
  46. package/dist/lenses/index.d.ts +16 -0
  47. package/dist/lenses/index.js +47 -0
  48. package/dist/lenses/product.d.ts +5 -0
  49. package/dist/lenses/product.js +22 -0
  50. package/dist/model-presets.d.ts +23 -0
  51. package/dist/model-presets.js +44 -0
  52. package/dist/phases/adjudication.d.ts +25 -0
  53. package/dist/phases/adjudication.js +142 -0
  54. package/dist/phases/build-consensus.d.ts +30 -0
  55. package/dist/phases/build-consensus.js +85 -0
  56. package/dist/phases/claim-registry.d.ts +26 -0
  57. package/dist/phases/claim-registry.js +60 -0
  58. package/dist/phases/cross-review.d.ts +47 -0
  59. package/dist/phases/cross-review.js +224 -0
  60. package/dist/phases/debate-rounds.d.ts +28 -0
  61. package/dist/phases/debate-rounds.js +161 -0
  62. package/dist/phases/participant-research.d.ts +39 -0
  63. package/dist/phases/participant-research.js +322 -0
  64. package/dist/phases/planning-detail-expansion.d.ts +39 -0
  65. package/dist/phases/planning-detail-expansion.js +120 -0
  66. package/dist/planner.d.ts +28 -0
  67. package/dist/planner.js +312 -0
  68. package/dist/providers/docs.d.ts +7 -0
  69. package/dist/providers/docs.js +111 -0
  70. package/dist/providers/git.d.ts +8 -0
  71. package/dist/providers/git.js +174 -0
  72. package/dist/providers/index.d.ts +32 -0
  73. package/dist/providers/index.js +132 -0
  74. package/dist/providers/none.d.ts +7 -0
  75. package/dist/providers/none.js +11 -0
  76. package/dist/providers/repo.d.ts +7 -0
  77. package/dist/providers/repo.js +258 -0
  78. package/dist/render/session.d.ts +17 -0
  79. package/dist/render/session.js +190 -0
  80. package/dist/render/terminal.d.ts +34 -0
  81. package/dist/render/terminal.js +286 -0
  82. package/dist/strategies/discussion.d.ts +25 -0
  83. package/dist/strategies/discussion.js +143 -0
  84. package/dist/strategies/index.d.ts +15 -0
  85. package/dist/strategies/index.js +28 -0
  86. package/dist/strategies/language-wrapper.d.ts +17 -0
  87. package/dist/strategies/language-wrapper.js +102 -0
  88. package/dist/strategies/lens-wrapper.d.ts +16 -0
  89. package/dist/strategies/lens-wrapper.js +236 -0
  90. package/dist/strategies/planning.d.ts +30 -0
  91. package/dist/strategies/planning.js +225 -0
  92. package/dist/strategies/review.d.ts +26 -0
  93. package/dist/strategies/review.js +168 -0
  94. package/dist/strategies/utils.d.ts +36 -0
  95. package/dist/strategies/utils.js +603 -0
  96. package/dist/tools/selector.d.ts +17 -0
  97. package/dist/tools/selector.js +61 -0
  98. package/dist/transitions.d.ts +53 -0
  99. package/dist/transitions.js +97 -0
  100. package/dist/types.d.ts +518 -0
  101. package/dist/types.js +27 -0
  102. package/package.json +45 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Lens registry — analysis perspectives for Arena sessions.
3
+ */
4
+ import type { ArenaLens, ArenaLensName, ArenaLensRef } from "../types.js";
5
+ export { engineeringLens } from "./engineering.js";
6
+ export { productLens } from "./product.js";
7
+ export { architectureLens } from "./architecture.js";
8
+ export { generalLens } from "./general.js";
9
+ /** Get a lens by name. */
10
+ export declare function getLens(name: ArenaLensName): ArenaLens;
11
+ /** Resolve lens refs into full ArenaLens objects. */
12
+ export declare function resolveLenses(refs: ArenaLensRef[]): ArenaLens[];
13
+ /** Build a combined lens prompt fragment for participant system prompts. */
14
+ export declare function buildLensPrompt(lenses: ArenaLens[], phase: "participant" | "reviewer" | "moderator"): string;
15
+ /** All available lens names */
16
+ export declare const LENS_NAMES: ArenaLensName[];
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Lens registry — analysis perspectives for Arena sessions.
3
+ */
4
+ import { engineeringLens } from "./engineering.js";
5
+ import { productLens } from "./product.js";
6
+ import { architectureLens } from "./architecture.js";
7
+ import { generalLens } from "./general.js";
8
+ export { engineeringLens } from "./engineering.js";
9
+ export { productLens } from "./product.js";
10
+ export { architectureLens } from "./architecture.js";
11
+ export { generalLens } from "./general.js";
12
+ const LENS_MAP = {
13
+ engineering: engineeringLens,
14
+ product: productLens,
15
+ architecture: architectureLens,
16
+ general: generalLens,
17
+ };
18
+ /** Get a lens by name. */
19
+ export function getLens(name) {
20
+ return LENS_MAP[name];
21
+ }
22
+ /** Resolve lens refs into full ArenaLens objects. */
23
+ export function resolveLenses(refs) {
24
+ return refs.map((ref) => LENS_MAP[ref.name]);
25
+ }
26
+ /** Build a combined lens prompt fragment for participant system prompts. */
27
+ export function buildLensPrompt(lenses, phase) {
28
+ if (lenses.length === 0)
29
+ return "";
30
+ const roleKey = phase === "participant" ? "participantRole"
31
+ : phase === "reviewer" ? "reviewerRole"
32
+ : "moderatorRole";
33
+ const roles = lenses.map((l) => l[roleKey]);
34
+ const allCriteria = lenses.flatMap((l) => l.criteria);
35
+ const uniqueCriteria = [...new Set(allCriteria)];
36
+ const roleDesc = lenses.length === 1
37
+ ? `You are ${roles[0]}.`
38
+ : `You combine multiple perspectives: ${roles.join("; ")}.`;
39
+ return [
40
+ roleDesc,
41
+ "",
42
+ "Evaluation criteria (prioritized):",
43
+ ...uniqueCriteria.map((c) => `- ${c}`),
44
+ ].join("\n");
45
+ }
46
+ /** All available lens names */
47
+ export const LENS_NAMES = ["engineering", "product", "architecture", "general"];
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Product lens — user value, completeness, edge cases, acceptance criteria.
3
+ */
4
+ import type { ArenaLens } from "../types.js";
5
+ export declare const productLens: ArenaLens;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Product lens — user value, completeness, edge cases, acceptance criteria.
3
+ */
4
+ export const productLens = {
5
+ name: "product",
6
+ label: "Product",
7
+ participantRole: "a product analyst focused on user value, completeness, and edge cases",
8
+ reviewerRole: "a product reviewer evaluating requirements coverage and user impact",
9
+ moderatorRole: "a product lead synthesizing product perspectives into prioritized insights",
10
+ summaryLabel: "Product Assessment",
11
+ criteria: [
12
+ "Requirements completeness and coverage",
13
+ "User experience and usability",
14
+ "Edge cases and boundary conditions",
15
+ "Acceptance criteria clarity",
16
+ "Feature scope — too broad or too narrow",
17
+ "User journey coherence",
18
+ "Metric and success criteria definition",
19
+ "Stakeholder alignment",
20
+ ],
21
+ preferredFindingKinds: ["question", "improvement", "risk", "strength"],
22
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Arena model presets — known models with their max output token limits.
3
+ *
4
+ * Used by arena participant resolution to set appropriate maxTokens
5
+ * so that LLM output is not prematurely truncated.
6
+ */
7
+ export interface ModelPreset {
8
+ provider: string;
9
+ model: string;
10
+ maxOutputTokens: number;
11
+ }
12
+ export declare const MODEL_PRESETS: Record<string, ModelPreset>;
13
+ /**
14
+ * Look up max output tokens for a model string, falling back to a safe default.
15
+ *
16
+ * Lookup order:
17
+ * 1. Arena preset key (e.g. "claude", "gpt") — short aliases used in arena UX
18
+ * 2. Arena preset model path (e.g. "anthropic/claude-opus-4.6")
19
+ * 3. resolveMaxOutput — the canonical table in onboarding.ts that also covers
20
+ * direct-provider IDs (deepseek-v4-pro, …) and OpenRouter snapshot
21
+ * 4. 8192 — final safe fallback
22
+ */
23
+ export declare function getMaxOutputTokens(model: string): number;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Arena model presets — known models with their max output token limits.
3
+ *
4
+ * Used by arena participant resolution to set appropriate maxTokens
5
+ * so that LLM output is not prematurely truncated.
6
+ */
7
+ import { resolveMaxOutput } from "@cjhyy/code-shell-core/extension";
8
+ export const MODEL_PRESETS = {
9
+ claude: { provider: "openai", model: "anthropic/claude-opus-4.6", maxOutputTokens: 32000 },
10
+ "claude-sonnet": { provider: "openai", model: "anthropic/claude-sonnet-4.6", maxOutputTokens: 16000 },
11
+ "claude-haiku": { provider: "openai", model: "anthropic/claude-haiku-4.5", maxOutputTokens: 8192 },
12
+ gpt: { provider: "openai", model: "openai/gpt-5.5", maxOutputTokens: 32000 },
13
+ "gpt4o": { provider: "openai", model: "openai/gpt-4o", maxOutputTokens: 16384 },
14
+ o4: { provider: "openai", model: "openai/o4-mini", maxOutputTokens: 100000 },
15
+ o3: { provider: "openai", model: "openai/o3", maxOutputTokens: 100000 },
16
+ deepseek: { provider: "openai", model: "deepseek/deepseek-v3.2", maxOutputTokens: 8192 },
17
+ "deepseek-r1": { provider: "openai", model: "deepseek/deepseek-r1", maxOutputTokens: 8192 },
18
+ gemini: { provider: "openai", model: "google/gemini-3.1-pro-preview", maxOutputTokens: 65536 },
19
+ "gemini-2.5": { provider: "openai", model: "google/gemini-2.5-pro", maxOutputTokens: 65536 },
20
+ "gemini-flash": { provider: "openai", model: "google/gemini-3-flash-preview", maxOutputTokens: 65536 },
21
+ qwen: { provider: "openai", model: "qwen/qwen3-235b-a22b", maxOutputTokens: 8192 },
22
+ "qwen-coder": { provider: "openai", model: "qwen/qwen3-coder", maxOutputTokens: 16384 },
23
+ llama: { provider: "openai", model: "meta-llama/llama-4-maverick", maxOutputTokens: 32000 },
24
+ devstral: { provider: "openai", model: "mistralai/devstral-medium", maxOutputTokens: 24000 },
25
+ };
26
+ /**
27
+ * Look up max output tokens for a model string, falling back to a safe default.
28
+ *
29
+ * Lookup order:
30
+ * 1. Arena preset key (e.g. "claude", "gpt") — short aliases used in arena UX
31
+ * 2. Arena preset model path (e.g. "anthropic/claude-opus-4.6")
32
+ * 3. resolveMaxOutput — the canonical table in onboarding.ts that also covers
33
+ * direct-provider IDs (deepseek-v4-pro, …) and OpenRouter snapshot
34
+ * 4. 8192 — final safe fallback
35
+ */
36
+ export function getMaxOutputTokens(model) {
37
+ const byKey = MODEL_PRESETS[model];
38
+ if (byKey)
39
+ return byKey.maxOutputTokens;
40
+ const byModel = Object.values(MODEL_PRESETS).find((p) => p.model === model);
41
+ if (byModel)
42
+ return byModel.maxOutputTokens;
43
+ return resolveMaxOutput(model) ?? 8192;
44
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Adjudication — the moderator makes evidence-based rulings on contested claims.
3
+ *
4
+ * For each contested/debated claim, the moderator sees the full claim context
5
+ * (evidence, challenges, debate rounds) and produces a ClaimAdjudication.
6
+ *
7
+ * Claims that were verified without contest get synthetic adjudication records.
8
+ */
9
+ import type { ArenaParticipant, ArenaStrategy, ClaimAdjudication, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
10
+ import type { ArenaLedger } from "../ledger.js";
11
+ interface AdjudicationOptions {
12
+ concluder: ArenaParticipant;
13
+ strategy: ArenaStrategy;
14
+ topic: string;
15
+ ledger: ArenaLedger;
16
+ signal?: AbortSignal;
17
+ onProgress?: (event: ArenaProgressEvent) => void;
18
+ onUsage?: ArenaUsageRecorder;
19
+ }
20
+ /**
21
+ * Run adjudication phase. The moderator rules on contested claims.
22
+ * Verified claims get synthetic adjudications.
23
+ */
24
+ export declare function runAdjudication(options: AdjudicationOptions): Promise<ClaimAdjudication[]>;
25
+ export {};
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Adjudication — the moderator makes evidence-based rulings on contested claims.
3
+ *
4
+ * For each contested/debated claim, the moderator sees the full claim context
5
+ * (evidence, challenges, debate rounds) and produces a ClaimAdjudication.
6
+ *
7
+ * Claims that were verified without contest get synthetic adjudication records.
8
+ */
9
+ import { createLLMClient } from "@cjhyy/code-shell-core/extension";
10
+ import { isStrategyV2 } from "../types.js";
11
+ import { buildDigest } from "../digest-builder.js";
12
+ import { transitionClaim, markUnresolved } from "../transitions.js";
13
+ import { parseAdjudication as parseAdjudicationUtil } from "../strategies/utils.js";
14
+ import { logger } from "@cjhyy/code-shell-core/extension";
15
+ /**
16
+ * Run adjudication phase. The moderator rules on contested claims.
17
+ * Verified claims get synthetic adjudications.
18
+ */
19
+ export async function runAdjudication(options) {
20
+ const { concluder, strategy, topic, ledger, signal, onProgress, onUsage } = options;
21
+ const allAdjudications = [];
22
+ const v2 = isStrategyV2(strategy);
23
+ // Claims that need moderator adjudication: contested or those with debate rounds
24
+ const contestedClaims = ledger.getClaimsByStatus("contested");
25
+ // Claims that were verified cleanly — synthetic adjudication
26
+ const verifiedClaims = ledger.getClaimsByStatus("verified");
27
+ if (contestedClaims.length === 0 && verifiedClaims.length === 0) {
28
+ logger.info("arena.adjudication_skip", { reason: "no claims to adjudicate" });
29
+ return [];
30
+ }
31
+ logger.info("arena.adjudication_start", {
32
+ contested: contestedClaims.length,
33
+ verified: verifiedClaims.length,
34
+ });
35
+ // Adjudicate contested claims via LLM
36
+ const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
37
+ for (const claim of contestedClaims) {
38
+ signal?.throwIfAborted();
39
+ const digest = buildDigest(ledger, {
40
+ round: 0, // adjudication is post-debate
41
+ relevantClaimIds: [claim.claimId],
42
+ });
43
+ const systemPrompt = strategy.consensusSystemPrompt();
44
+ let userContent;
45
+ if (v2) {
46
+ userContent = strategy
47
+ .adjudicationUserPrompt(topic, claim, claim.debateRounds, digest);
48
+ }
49
+ else {
50
+ userContent = buildFallbackAdjudicationPrompt(topic, claim);
51
+ }
52
+ const response = await client.createMessage({
53
+ systemPrompt,
54
+ messages: [{ role: "user", content: userContent }],
55
+ signal,
56
+ });
57
+ onUsage?.(response.usage);
58
+ logger.info("arena.adjudication_response", {
59
+ claimId: claim.claimId,
60
+ stopReason: response.stopReason,
61
+ });
62
+ let adjudication;
63
+ if (v2) {
64
+ adjudication = strategy.parseAdjudicationResponse(response.text);
65
+ adjudication.claimId = claim.claimId;
66
+ }
67
+ else {
68
+ adjudication = parseAdjudicationUtil(claim.claimId, response.text);
69
+ }
70
+ // Record in ledger
71
+ ledger.appendAdjudication(adjudication);
72
+ allAdjudications.push(adjudication);
73
+ // Transition claim based on outcome
74
+ applyAdjudicationOutcome(claim, adjudication);
75
+ }
76
+ // Synthetic adjudications for verified claims
77
+ for (const claim of verifiedClaims) {
78
+ const adjudication = {
79
+ claimId: claim.claimId,
80
+ outcome: "accepted",
81
+ rationale: "Verified by peer review without contest",
82
+ finalSummary: claim.finding.summary,
83
+ supportingEvidenceRefs: claim.evidenceRefs,
84
+ };
85
+ ledger.appendAdjudication(adjudication);
86
+ allAdjudications.push(adjudication);
87
+ }
88
+ // Any remaining proposed/under_review claims → mark unresolved
89
+ const remaining = ledger.getClaimsByStatus("proposed", "under_review");
90
+ for (const claim of remaining) {
91
+ markUnresolved(claim);
92
+ }
93
+ const accepted = allAdjudications.filter((a) => a.outcome === "accepted" || a.outcome === "accepted_with_revision").length;
94
+ const unresolved = ledger.getClaimsByStatus("unresolved").length;
95
+ logger.info("arena.adjudication_done", {
96
+ total: allAdjudications.length,
97
+ accepted,
98
+ unresolved,
99
+ });
100
+ onProgress?.({ type: "adjudication_done", accepted, unresolved });
101
+ return allAdjudications;
102
+ }
103
+ /**
104
+ * Apply adjudication outcome to claim status.
105
+ */
106
+ function applyAdjudicationOutcome(claim, adjudication) {
107
+ switch (adjudication.outcome) {
108
+ case "accepted":
109
+ case "accepted_with_revision":
110
+ transitionClaim(claim, "verified");
111
+ break;
112
+ case "rejected":
113
+ transitionClaim(claim, "rejected");
114
+ break;
115
+ case "unresolved":
116
+ markUnresolved(claim);
117
+ break;
118
+ }
119
+ }
120
+ /**
121
+ * Build a fallback adjudication prompt for non-V2 strategies.
122
+ */
123
+ function buildFallbackAdjudicationPrompt(topic, claim) {
124
+ const challengeText = claim.challenges.length > 0
125
+ ? claim.challenges.map((c) => `[${c.reviewer}] ${c.verdict}: ${c.reason}`).join("\n")
126
+ : "No challenges.";
127
+ const debateText = claim.debateRounds.length > 0
128
+ ? claim.debateRounds.map((r) => `Round ${r.round}:\n` +
129
+ r.participants.map((t) => ` [${t.participant}] ${t.stance}: ${t.summary}`).join("\n") +
130
+ `\n ${r.resolved ? "→ Resolved" : "→ Unresolved"}`).join("\n\n")
131
+ : "No debate rounds.";
132
+ return (`## Topic: ${topic}\n\n` +
133
+ `## Claim Under Adjudication\n` +
134
+ `[${claim.claimId}] ${claim.finding.title}\n${claim.finding.summary}\n` +
135
+ `Owner: ${claim.owner} | Status: ${claim.status}\n\n` +
136
+ `## Challenges\n${challengeText}\n\n` +
137
+ `## Debate\n${debateText}\n\n` +
138
+ `As moderator, adjudicate this claim based on all available evidence.\n` +
139
+ `Respond ONLY with JSON:\n` +
140
+ `{"outcome": "accepted|accepted_with_revision|rejected|unresolved", "rationale": "...", ` +
141
+ `"finalSummary": "revised claim summary if needed", "supportingEvidenceRefs": ["..."]}`);
142
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ConsensusBuilder — aggregates findings + peer reviews into structured consensus.
3
+ *
4
+ * V1: Program aggregates finding + review data, moderator LLM organizes.
5
+ * V2: When claim data is available, uses claim-aware prompts that distinguish
6
+ * verified/unresolved/rejected claims.
7
+ *
8
+ * In both paths, the moderator must faithfully reflect aggregated results
9
+ * (no new unsourced conclusions).
10
+ */
11
+ import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ArenaConsensus, ClaimStatusSummary, ArenaProgressEvent, ArenaUsageRecorder } from "../types.js";
12
+ interface ConsensusOptions {
13
+ /** The participant that acts as moderator/concluder */
14
+ concluder: ArenaParticipant;
15
+ strategy: ArenaStrategy;
16
+ topic: string;
17
+ reports: ParticipantReport[];
18
+ reviews: FindingReview[];
19
+ /** Claim status summary — when provided, enables claim-aware consensus (V2) */
20
+ claimSummary?: ClaimStatusSummary;
21
+ signal?: AbortSignal;
22
+ onProgress?: (event: ArenaProgressEvent) => void;
23
+ onUsage?: ArenaUsageRecorder;
24
+ }
25
+ /**
26
+ * Build structured consensus from participant reports, cross-reviews,
27
+ * and optionally adjudicated claim data.
28
+ */
29
+ export declare function buildConsensus(options: ConsensusOptions): Promise<ArenaConsensus>;
30
+ export {};
@@ -0,0 +1,85 @@
1
+ /**
2
+ * ConsensusBuilder — aggregates findings + peer reviews into structured consensus.
3
+ *
4
+ * V1: Program aggregates finding + review data, moderator LLM organizes.
5
+ * V2: When claim data is available, uses claim-aware prompts that distinguish
6
+ * verified/unresolved/rejected claims.
7
+ *
8
+ * In both paths, the moderator must faithfully reflect aggregated results
9
+ * (no new unsourced conclusions).
10
+ */
11
+ import { createLLMClient } from "@cjhyy/code-shell-core/extension";
12
+ import { logger } from "@cjhyy/code-shell-core/extension";
13
+ import { isStrategyV2 } from "../types.js";
14
+ /**
15
+ * Build structured consensus from participant reports, cross-reviews,
16
+ * and optionally adjudicated claim data.
17
+ */
18
+ export async function buildConsensus(options) {
19
+ const { concluder, strategy, topic, reports, reviews, claimSummary, signal, onProgress, onUsage } = options;
20
+ onProgress?.({ type: "consensus_start" });
21
+ const client = await createLLMClient(concluder.llm, concluder.clientDefaults);
22
+ const systemPrompt = strategy.consensusSystemPrompt();
23
+ // Choose user prompt: claim-aware (V2) or standard (V1)
24
+ let userContent;
25
+ const v2 = isStrategyV2(strategy);
26
+ if (v2 && claimSummary) {
27
+ userContent = strategy
28
+ .claimAwareConsensusUserPrompt(topic, reports, reviews, claimSummary);
29
+ logger.info("arena.consensus_claim_aware", {
30
+ verified: claimSummary.verified.length,
31
+ unresolved: claimSummary.unresolved.length,
32
+ contested: claimSummary.contested.length,
33
+ rejected: claimSummary.rejected.length,
34
+ });
35
+ }
36
+ else {
37
+ userContent = strategy.consensusUserPrompt(topic, reports, reviews);
38
+ }
39
+ const response = await client.createMessage({
40
+ systemPrompt,
41
+ messages: [{ role: "user", content: userContent }],
42
+ signal,
43
+ });
44
+ onUsage?.(response.usage);
45
+ logger.info("arena.consensus_raw_response", {
46
+ text: response.text,
47
+ stopReason: response.stopReason,
48
+ });
49
+ // If output was truncated by max_tokens, retry with a condensed prompt
50
+ if (response.stopReason === "length") {
51
+ logger.warn("arena.consensus_truncated", {
52
+ textLen: response.text?.length ?? 0,
53
+ maxTokens: concluder.llm.maxTokens,
54
+ });
55
+ const retryResponse = await client.createMessage({
56
+ systemPrompt,
57
+ messages: [
58
+ { role: "user", content: userContent },
59
+ { role: "assistant", content: response.text },
60
+ {
61
+ role: "user",
62
+ content: "Your previous response was truncated. Please output a COMPLETE but more concise version of the consensus JSON. " +
63
+ "Keep all sections but use shorter summaries. Respond ONLY with the complete JSON object.",
64
+ },
65
+ ],
66
+ signal,
67
+ });
68
+ onUsage?.(retryResponse.usage);
69
+ logger.info("arena.consensus_retry_response", {
70
+ textLen: retryResponse.text?.length ?? 0,
71
+ stopReason: retryResponse.stopReason,
72
+ });
73
+ if (retryResponse.stopReason === "length") {
74
+ logger.warn("arena.consensus_still_truncated", {
75
+ textLen: retryResponse.text?.length ?? 0,
76
+ });
77
+ }
78
+ const consensus = strategy.parseConsensusResponse(retryResponse.text);
79
+ onProgress?.({ type: "consensus_done", consensus });
80
+ return consensus;
81
+ }
82
+ const consensus = strategy.parseConsensusResponse(response.text);
83
+ onProgress?.({ type: "consensus_done", consensus });
84
+ return consensus;
85
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * ClaimRegistry — converts ResearchDossier findings into ClaimRecords.
3
+ *
4
+ * Each finding is elevated to a claim with:
5
+ * - Stable global ID (owner + finding.id)
6
+ * - Evidence packet references from findingEvidenceLinks
7
+ * - Initial status: "proposed"
8
+ */
9
+ import type { ResearchDossier, ClaimRecord, ArenaProgressEvent } from "../types.js";
10
+ import type { ArenaLedger } from "../ledger.js";
11
+ interface ClaimRegistryOptions {
12
+ dossiers: ResearchDossier[];
13
+ ledger: ArenaLedger;
14
+ onProgress?: (event: ArenaProgressEvent) => void;
15
+ }
16
+ /**
17
+ * Register all findings from research dossiers as claims.
18
+ * Returns the newly created ClaimRecord array.
19
+ */
20
+ export declare function registerClaims(options: ClaimRegistryOptions): ClaimRecord[];
21
+ /**
22
+ * Select claims for review, prioritized by severity and confidence.
23
+ * Respects the maxClaimsForReview limit.
24
+ */
25
+ export declare function selectClaimsForReview(claims: ClaimRecord[], maxClaims: number): ClaimRecord[];
26
+ export {};
@@ -0,0 +1,60 @@
1
+ /**
2
+ * ClaimRegistry — converts ResearchDossier findings into ClaimRecords.
3
+ *
4
+ * Each finding is elevated to a claim with:
5
+ * - Stable global ID (owner + finding.id)
6
+ * - Evidence packet references from findingEvidenceLinks
7
+ * - Initial status: "proposed"
8
+ */
9
+ /**
10
+ * Register all findings from research dossiers as claims.
11
+ * Returns the newly created ClaimRecord array.
12
+ */
13
+ export function registerClaims(options) {
14
+ const { dossiers, ledger, onProgress } = options;
15
+ const claims = [];
16
+ for (const dossier of dossiers) {
17
+ // Build a lookup from findingId → evidencePacketIds
18
+ const linkMap = new Map();
19
+ for (const link of dossier.findingEvidenceLinks) {
20
+ linkMap.set(link.findingId, link.evidencePacketIds);
21
+ }
22
+ for (const finding of dossier.findings) {
23
+ const claimId = `${dossier.participant}:${finding.id}`;
24
+ const packetIds = linkMap.get(finding.id) ?? [];
25
+ // Build evidence refs from finding.evidence + packet refs
26
+ const evidenceRefs = finding.evidence.map((e) => `${e.type}:${e.ref}`);
27
+ const claim = {
28
+ claimId,
29
+ owner: dossier.participant,
30
+ finding,
31
+ evidenceRefs,
32
+ evidencePacketIds: packetIds,
33
+ status: "proposed",
34
+ challenges: [],
35
+ debateRounds: [],
36
+ };
37
+ claims.push(claim);
38
+ ledger.appendClaim(claim);
39
+ }
40
+ }
41
+ onProgress?.({ type: "claims_registered", claimCount: claims.length });
42
+ return claims;
43
+ }
44
+ /**
45
+ * Select claims for review, prioritized by severity and confidence.
46
+ * Respects the maxClaimsForReview limit.
47
+ */
48
+ export function selectClaimsForReview(claims, maxClaims) {
49
+ const severityOrder = { high: 0, medium: 1, low: 2 };
50
+ const sorted = [...claims].sort((a, b) => {
51
+ // Higher severity first
52
+ const sevA = severityOrder[a.finding.severity ?? "medium"] ?? 1;
53
+ const sevB = severityOrder[b.finding.severity ?? "medium"] ?? 1;
54
+ if (sevA !== sevB)
55
+ return sevA - sevB;
56
+ // Higher confidence first
57
+ return b.finding.confidence - a.finding.confidence;
58
+ });
59
+ return sorted.slice(0, maxClaims);
60
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * CrossReview / VerificationReview — participants review each other's findings.
3
+ *
4
+ * V1 (CrossReview): Each participant sees the other participants' structured findings
5
+ * and provides per-finding verdicts: agree, refine, disagree, needs_evidence.
6
+ *
7
+ * V2 (VerificationReview): Reviewers get claim + evidence packet + digest data,
8
+ * and produce ClaimChallenge records that feed into the claim state machine.
9
+ */
10
+ import type { ArenaParticipant, ArenaStrategy, ParticipantReport, FindingReview, ClaimChallenge, ArenaProgressEvent, ArenaExecutionLimits, ArenaUsageRecorder } from "../types.js";
11
+ import type { ArenaLedger } from "../ledger.js";
12
+ interface CrossReviewOptions {
13
+ participants: ArenaParticipant[];
14
+ strategy: ArenaStrategy;
15
+ topic: string;
16
+ reports: ParticipantReport[];
17
+ signal?: AbortSignal;
18
+ onProgress?: (event: ArenaProgressEvent) => void;
19
+ onUsage?: ArenaUsageRecorder;
20
+ }
21
+ /**
22
+ * Run cross-review phase (V1). Each participant reviews the others' findings.
23
+ */
24
+ export declare function runCrossReview(options: CrossReviewOptions): Promise<FindingReview[]>;
25
+ interface VerificationReviewOptions {
26
+ participants: ArenaParticipant[];
27
+ strategy: ArenaStrategy;
28
+ topic: string;
29
+ reports: ParticipantReport[];
30
+ ledger: ArenaLedger;
31
+ limits: ArenaExecutionLimits;
32
+ /** Arena mode — planning routes to merge-oriented review */
33
+ mode?: "review" | "discussion" | "planning";
34
+ signal?: AbortSignal;
35
+ onProgress?: (event: ArenaProgressEvent) => void;
36
+ onUsage?: ArenaUsageRecorder;
37
+ }
38
+ interface VerificationReviewResult {
39
+ reviews: FindingReview[];
40
+ challenges: ClaimChallenge[];
41
+ }
42
+ /**
43
+ * Run verification-review phase. Reviewers get claim + evidence + digest data.
44
+ * Falls back to V1 cross-review if strategy doesn't implement V2.
45
+ */
46
+ export declare function runVerificationReview(options: VerificationReviewOptions): Promise<VerificationReviewResult>;
47
+ export {};