@clipboard-health/tribunal 1.1.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.
@@ -0,0 +1,165 @@
1
+ import { z } from "zod";
2
+ export declare const roleSchema: z.ZodEnum<["advocate", "skeptic", "analyst"]>;
3
+ export declare const confidenceSchema: z.ZodNumber;
4
+ export declare const claimSchema: z.ZodObject<{
5
+ claim: z.ZodString;
6
+ confidence: z.ZodNumber;
7
+ reasoning: z.ZodString;
8
+ } & {
9
+ assumptions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ claim: string;
12
+ confidence: number;
13
+ reasoning: string;
14
+ assumptions: string[];
15
+ }, {
16
+ claim: string;
17
+ confidence: number;
18
+ reasoning: string;
19
+ assumptions?: string[] | undefined;
20
+ }>;
21
+ export declare const generatedPerspectiveResultSchema: z.ZodObject<{
22
+ role: z.ZodEnum<["advocate", "skeptic", "analyst"]>;
23
+ summary: z.ZodString;
24
+ claims: z.ZodArray<z.ZodObject<{
25
+ claim: z.ZodString;
26
+ confidence: z.ZodNumber;
27
+ reasoning: z.ZodString;
28
+ assumptions: z.ZodArray<z.ZodString, "many">;
29
+ }, "strip", z.ZodTypeAny, {
30
+ claim: string;
31
+ confidence: number;
32
+ reasoning: string;
33
+ assumptions: string[];
34
+ }, {
35
+ claim: string;
36
+ confidence: number;
37
+ reasoning: string;
38
+ assumptions: string[];
39
+ }>, "many">;
40
+ openQuestions: z.ZodArray<z.ZodString, "many">;
41
+ }, "strip", z.ZodTypeAny, {
42
+ role: "advocate" | "analyst" | "skeptic";
43
+ summary: string;
44
+ claims: {
45
+ claim: string;
46
+ confidence: number;
47
+ reasoning: string;
48
+ assumptions: string[];
49
+ }[];
50
+ openQuestions: string[];
51
+ }, {
52
+ role: "advocate" | "analyst" | "skeptic";
53
+ summary: string;
54
+ claims: {
55
+ claim: string;
56
+ confidence: number;
57
+ reasoning: string;
58
+ assumptions: string[];
59
+ }[];
60
+ openQuestions: string[];
61
+ }>;
62
+ export declare const perspectiveResultSchema: z.ZodObject<{
63
+ role: z.ZodEnum<["advocate", "skeptic", "analyst"]>;
64
+ summary: z.ZodString;
65
+ } & {
66
+ claims: z.ZodArray<z.ZodObject<{
67
+ claim: z.ZodString;
68
+ confidence: z.ZodNumber;
69
+ reasoning: z.ZodString;
70
+ } & {
71
+ assumptions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
72
+ }, "strip", z.ZodTypeAny, {
73
+ claim: string;
74
+ confidence: number;
75
+ reasoning: string;
76
+ assumptions: string[];
77
+ }, {
78
+ claim: string;
79
+ confidence: number;
80
+ reasoning: string;
81
+ assumptions?: string[] | undefined;
82
+ }>, "many">;
83
+ openQuestions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
84
+ }, "strip", z.ZodTypeAny, {
85
+ role: "advocate" | "analyst" | "skeptic";
86
+ summary: string;
87
+ claims: {
88
+ claim: string;
89
+ confidence: number;
90
+ reasoning: string;
91
+ assumptions: string[];
92
+ }[];
93
+ openQuestions: string[];
94
+ }, {
95
+ role: "advocate" | "analyst" | "skeptic";
96
+ summary: string;
97
+ claims: {
98
+ claim: string;
99
+ confidence: number;
100
+ reasoning: string;
101
+ assumptions?: string[] | undefined;
102
+ }[];
103
+ openQuestions?: string[] | undefined;
104
+ }>;
105
+ export declare const generatedDeliberationResultSchema: z.ZodObject<{
106
+ answer: z.ZodString;
107
+ keyTakeaways: z.ZodArray<z.ZodString, "many">;
108
+ consensus: z.ZodArray<z.ZodString, "many">;
109
+ disagreements: z.ZodArray<z.ZodString, "many">;
110
+ recommendation: z.ZodNullable<z.ZodString>;
111
+ confidence: z.ZodNumber;
112
+ caveats: z.ZodArray<z.ZodString, "many">;
113
+ openQuestions: z.ZodArray<z.ZodString, "many">;
114
+ }, "strip", z.ZodTypeAny, {
115
+ answer: string;
116
+ keyTakeaways: string[];
117
+ consensus: string[];
118
+ disagreements: string[];
119
+ recommendation: string | null;
120
+ confidence: number;
121
+ caveats: string[];
122
+ openQuestions: string[];
123
+ }, {
124
+ answer: string;
125
+ keyTakeaways: string[];
126
+ consensus: string[];
127
+ disagreements: string[];
128
+ recommendation: string | null;
129
+ confidence: number;
130
+ caveats: string[];
131
+ openQuestions: string[];
132
+ }>;
133
+ export declare const deliberationResultSchema: z.ZodObject<{
134
+ answer: z.ZodString;
135
+ keyTakeaways: z.ZodArray<z.ZodString, "many">;
136
+ recommendation: z.ZodNullable<z.ZodString>;
137
+ confidence: z.ZodNumber;
138
+ } & {
139
+ consensus: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
140
+ disagreements: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
141
+ caveats: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
142
+ openQuestions: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
143
+ }, "strip", z.ZodTypeAny, {
144
+ answer: string;
145
+ keyTakeaways: string[];
146
+ recommendation: string | null;
147
+ confidence: number;
148
+ consensus: string[];
149
+ disagreements: string[];
150
+ caveats: string[];
151
+ openQuestions: string[];
152
+ }, {
153
+ answer: string;
154
+ keyTakeaways: string[];
155
+ recommendation: string | null;
156
+ confidence: number;
157
+ consensus?: string[] | undefined;
158
+ disagreements?: string[] | undefined;
159
+ caveats?: string[] | undefined;
160
+ openQuestions?: string[] | undefined;
161
+ }>;
162
+ export type Role = z.infer<typeof roleSchema>;
163
+ export type Claim = z.infer<typeof claimSchema>;
164
+ export type PerspectiveResult = z.infer<typeof perspectiveResultSchema>;
165
+ export type DeliberationResult = z.infer<typeof deliberationResultSchema>;
package/src/schemas.js ADDED
@@ -0,0 +1,39 @@
1
+ import { z } from "zod";
2
+ export const roleSchema = z.enum(["advocate", "skeptic", "analyst"]);
3
+ export const confidenceSchema = z.number().min(0).max(1);
4
+ const generatedClaimSchema = z.object({
5
+ claim: z.string(),
6
+ confidence: confidenceSchema,
7
+ reasoning: z.string(),
8
+ assumptions: z.array(z.string()),
9
+ });
10
+ export const claimSchema = generatedClaimSchema.extend({
11
+ assumptions: z.array(z.string()).default([]),
12
+ });
13
+ export const generatedPerspectiveResultSchema = z.object({
14
+ role: roleSchema,
15
+ summary: z.string(),
16
+ claims: z.array(generatedClaimSchema).min(1).max(5),
17
+ openQuestions: z.array(z.string()),
18
+ });
19
+ export const perspectiveResultSchema = generatedPerspectiveResultSchema.extend({
20
+ claims: z.array(claimSchema).min(1).max(5),
21
+ openQuestions: z.array(z.string()).default([]),
22
+ });
23
+ export const generatedDeliberationResultSchema = z.object({
24
+ answer: z.string(),
25
+ keyTakeaways: z.array(z.string()).min(1).max(7),
26
+ consensus: z.array(z.string()),
27
+ disagreements: z.array(z.string()),
28
+ recommendation: z.string().nullable(),
29
+ confidence: confidenceSchema,
30
+ caveats: z.array(z.string()),
31
+ openQuestions: z.array(z.string()),
32
+ });
33
+ export const deliberationResultSchema = generatedDeliberationResultSchema.extend({
34
+ consensus: z.array(z.string()).default([]),
35
+ disagreements: z.array(z.string()).default([]),
36
+ caveats: z.array(z.string()).default([]),
37
+ openQuestions: z.array(z.string()).default([]),
38
+ });
39
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../../packages/tribunal/src/schemas.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import { type LanguageModelUsage } from "ai";
2
+ import type { z } from "zod";
3
+ import { type ModelRole, type ModelSpec } from "./models.ts";
4
+ import { type ReasoningLevel, type ReasoningOverrides } from "./reasoning.ts";
5
+ import { type DeliberationResult, type PerspectiveResult, type Role } from "./schemas.ts";
6
+ export type OutputFormat = "text" | "json" | "markdown";
7
+ export interface TribunalRequest {
8
+ query: string;
9
+ context?: string;
10
+ models?: Partial<Record<ModelRole, ModelSpec>>;
11
+ reasoning?: ReasoningOverrides;
12
+ showPerspectives?: boolean;
13
+ }
14
+ export interface TokenUsage {
15
+ inputTokens?: number;
16
+ outputTokens?: number;
17
+ totalTokens?: number;
18
+ }
19
+ export interface CallMetadata {
20
+ model: ModelSpec;
21
+ latencyMs: number;
22
+ usage?: TokenUsage;
23
+ }
24
+ export interface PerspectiveOutput {
25
+ role: Role;
26
+ result: PerspectiveResult;
27
+ metadata: CallMetadata;
28
+ }
29
+ export interface TribunalResponse {
30
+ result: DeliberationResult;
31
+ perspectives: PerspectiveOutput[];
32
+ metadata: {
33
+ models: Record<ModelRole, ModelSpec>;
34
+ reasoning?: ReasoningOverrides;
35
+ totalUsage?: TokenUsage;
36
+ estimatedCostUsd: number | null;
37
+ latencyMs: number;
38
+ warnings: string[];
39
+ };
40
+ }
41
+ export type TribunalProgressStatus = "started" | "completed" | "failed";
42
+ export interface TribunalProgressEvent {
43
+ role: ModelRole;
44
+ status: TribunalProgressStatus;
45
+ model: ModelSpec;
46
+ latencyMs?: number;
47
+ metadata?: CallMetadata;
48
+ output?: unknown;
49
+ errorMessage?: string;
50
+ }
51
+ export type TribunalProgressHandler = (event: TribunalProgressEvent) => void | Promise<void>;
52
+ interface StructuredOutputRunnerInput {
53
+ role: ModelRole;
54
+ reasoningLevel?: ReasoningLevel | undefined;
55
+ model: ModelSpec;
56
+ system: string;
57
+ prompt: string;
58
+ schema: z.ZodType;
59
+ }
60
+ interface StructuredOutputRunnerResult {
61
+ output: unknown;
62
+ metadata: CallMetadata;
63
+ }
64
+ export type StructuredOutputRunner = (input: StructuredOutputRunnerInput) => Promise<StructuredOutputRunnerResult>;
65
+ interface RunTribunalOptions {
66
+ environment?: Record<string, string | undefined>;
67
+ structuredOutputRunner?: StructuredOutputRunner;
68
+ onProgress?: TribunalProgressHandler;
69
+ now?: () => number;
70
+ }
71
+ export interface StructuredOutputInput<T> {
72
+ model: ModelSpec;
73
+ reasoningLevel?: ReasoningLevel | undefined;
74
+ system: string;
75
+ prompt: string;
76
+ schema: z.ZodType<T>;
77
+ }
78
+ export interface StructuredOutputResult<T> {
79
+ output: T;
80
+ metadata: CallMetadata;
81
+ }
82
+ export interface CostEstimateCall {
83
+ role: ModelRole;
84
+ metadata: CallMetadata;
85
+ }
86
+ export interface CostEstimate {
87
+ estimatedCostUsd: number | null;
88
+ warning?: string;
89
+ }
90
+ export declare function runTribunal(request: TribunalRequest, options?: RunTribunalOptions): Promise<TribunalResponse>;
91
+ export declare function runStructuredOutput<T>(input: StructuredOutputInput<T>): Promise<StructuredOutputResult<T>>;
92
+ export declare function normalizeUsage(usage: LanguageModelUsage): TokenUsage;
93
+ export declare function sumTokenUsage(usages: TokenUsage[]): TokenUsage | undefined;
94
+ export declare function estimateCostUsd(calls: CostEstimateCall[]): CostEstimate;
95
+ export {};
@@ -0,0 +1,383 @@
1
+ import { generateText, Output } from "ai";
2
+ import { formatModelSpec, resolveLanguageModel, resolveModelSet, } from "./models.js";
3
+ import { createReasoningProviderOptions, } from "./reasoning.js";
4
+ import { deliberationResultSchema, generatedDeliberationResultSchema, generatedPerspectiveResultSchema, perspectiveResultSchema, } from "./schemas.js";
5
+ const MAX_WARNINGS_TO_LIST = 3;
6
+ const DEFAULT_PERSPECTIVES = [
7
+ {
8
+ role: "advocate",
9
+ system: "Argue the strongest reasonable case FOR the proposal. Focus on benefits, upside, why it could work, and what would need to be true.",
10
+ },
11
+ {
12
+ role: "skeptic",
13
+ system: "Argue the strongest reasonable case AGAINST the proposal. Focus on risks, hidden costs, failure modes, and reasons it may not work.",
14
+ },
15
+ {
16
+ role: "analyst",
17
+ system: "Analyze the proposal neutrally. Map the decision space, tradeoffs, criteria, dependencies, and unknowns without taking a side.",
18
+ },
19
+ ];
20
+ const DELIBERATOR_SYSTEM_PROMPT = `You are a deliberator comparing multiple structured perspectives.
21
+
22
+ Evaluate claims on their merits, not based on model/provider identity.
23
+ Identify true consensus, important disagreements, assumptions, and decision criteria.
24
+ Do not simply summarize each perspective.
25
+ Produce a balanced answer with a clear recommendation when the evidence supports one.
26
+ If the answer depends on missing information, say what information would change the recommendation.`;
27
+ const MODEL_PRICING_USD_PER_1M_TOKENS = {};
28
+ export async function runTribunal(request, options = {}) {
29
+ const now = options.now ?? performance.now.bind(performance);
30
+ const start = now();
31
+ const structuredOutputRunner = options.structuredOutputRunner ?? defaultStructuredOutputRunner;
32
+ const models = resolveModelSet(createResolveModelSetInput(options.environment, request.models));
33
+ const reasoning = copyReasoningOverrides(request.reasoning);
34
+ const warnings = [];
35
+ const prompt = buildQuestionPrompt(createPromptInput(request.query, request.context));
36
+ const settledPerspectives = await Promise.allSettled(DEFAULT_PERSPECTIVES.map(async (perspective) => await runPerspective({
37
+ model: models[perspective.role],
38
+ onProgress: options.onProgress,
39
+ perspective,
40
+ prompt,
41
+ reasoningLevel: reasoning?.[perspective.role],
42
+ structuredOutputRunner,
43
+ })));
44
+ const perspectives = collectSuccessfulPerspectives(settledPerspectives, warnings);
45
+ if (perspectives.length < 2) {
46
+ throw new Error(`At least two perspectives must succeed before deliberation. Received ${perspectives.length}.`);
47
+ }
48
+ const deliberation = await runStructuredOutputWithProgress({
49
+ model: models.deliberator,
50
+ onProgress: options.onProgress,
51
+ prompt: buildDeliberatorPrompt(createDeliberatorPromptInput(request.query, request.context, perspectives)),
52
+ reasoningLevel: reasoning?.deliberator,
53
+ role: "deliberator",
54
+ schema: generatedDeliberationResultSchema,
55
+ structuredOutputRunner,
56
+ system: DELIBERATOR_SYSTEM_PROMPT,
57
+ });
58
+ const result = deliberationResultSchema.parse(deliberation.output);
59
+ const allCalls = [
60
+ ...perspectives.map((perspective) => ({
61
+ metadata: perspective.metadata,
62
+ role: perspective.role,
63
+ })),
64
+ { metadata: deliberation.metadata, role: "deliberator" },
65
+ ];
66
+ const costEstimate = estimateCostUsd(allCalls);
67
+ if (costEstimate.warning !== undefined) {
68
+ warnings.push(costEstimate.warning);
69
+ }
70
+ const totalUsage = sumTokenUsage(allCalls.map((call) => call.metadata.usage ?? {}));
71
+ const metadata = {
72
+ models,
73
+ estimatedCostUsd: costEstimate.estimatedCostUsd,
74
+ latencyMs: Math.round(now() - start),
75
+ warnings,
76
+ };
77
+ if (reasoning !== undefined) {
78
+ metadata.reasoning = reasoning;
79
+ }
80
+ if (totalUsage !== undefined) {
81
+ metadata.totalUsage = totalUsage;
82
+ }
83
+ return {
84
+ result,
85
+ perspectives,
86
+ metadata,
87
+ };
88
+ }
89
+ export async function runStructuredOutput(input) {
90
+ const { model, prompt, reasoningLevel, schema, system } = input;
91
+ const start = performance.now();
92
+ const providerOptions = createReasoningProviderOptions({ model, reasoningLevel });
93
+ const result = await generateText({
94
+ model: resolveLanguageModel(model),
95
+ output: Output.object({ schema }),
96
+ prompt,
97
+ ...(providerOptions === undefined ? {} : { providerOptions }),
98
+ system,
99
+ });
100
+ return {
101
+ output: result.output,
102
+ metadata: {
103
+ model,
104
+ latencyMs: Math.round(performance.now() - start),
105
+ usage: normalizeUsage(result.usage),
106
+ },
107
+ };
108
+ }
109
+ export function normalizeUsage(usage) {
110
+ const normalizedUsage = {};
111
+ assignTokenUsage(normalizedUsage, "inputTokens", usage.inputTokens);
112
+ assignTokenUsage(normalizedUsage, "outputTokens", usage.outputTokens);
113
+ assignTokenUsage(normalizedUsage, "totalTokens", usage.totalTokens);
114
+ return normalizedUsage;
115
+ }
116
+ export function sumTokenUsage(usages) {
117
+ let inputTokens = 0;
118
+ let outputTokens = 0;
119
+ let totalTokens = 0;
120
+ let hasInputTokens = false;
121
+ let hasOutputTokens = false;
122
+ let hasTotalTokens = false;
123
+ for (const usage of usages) {
124
+ if (usage.inputTokens !== undefined) {
125
+ inputTokens += usage.inputTokens;
126
+ hasInputTokens = true;
127
+ }
128
+ if (usage.outputTokens !== undefined) {
129
+ outputTokens += usage.outputTokens;
130
+ hasOutputTokens = true;
131
+ }
132
+ if (usage.totalTokens !== undefined) {
133
+ totalTokens += usage.totalTokens;
134
+ hasTotalTokens = true;
135
+ }
136
+ }
137
+ if (!hasInputTokens && !hasOutputTokens && !hasTotalTokens) {
138
+ return undefined;
139
+ }
140
+ return {
141
+ ...(hasInputTokens ? { inputTokens } : {}),
142
+ ...(hasOutputTokens ? { outputTokens } : {}),
143
+ ...(hasTotalTokens ? { totalTokens } : {}),
144
+ };
145
+ }
146
+ function assignTokenUsage(usage, key, value) {
147
+ if (value === undefined) {
148
+ return;
149
+ }
150
+ usage[key] = value;
151
+ }
152
+ function createResolveModelSetInput(environment, overrides) {
153
+ const input = {
154
+ environment: environment ?? getProcessEnvironment(),
155
+ };
156
+ if (overrides === undefined) {
157
+ return input;
158
+ }
159
+ input.overrides = overrides;
160
+ return input;
161
+ }
162
+ function getProcessEnvironment() {
163
+ // oxlint-disable-next-line node/no-process-env -- Centralized fallback so direct runTribunal calls can read op-run-injected model override vars.
164
+ return process.env;
165
+ }
166
+ function createPromptInput(query, context) {
167
+ if (context === undefined) {
168
+ return { query };
169
+ }
170
+ return { query, context };
171
+ }
172
+ function createDeliberatorPromptInput(query, context, perspectives) {
173
+ if (context === undefined) {
174
+ return { query, perspectives };
175
+ }
176
+ return { query, context, perspectives };
177
+ }
178
+ export function estimateCostUsd(calls) {
179
+ const modelsWithUnknownPricing = new Set();
180
+ for (const call of calls) {
181
+ const modelKey = formatModelSpec(call.metadata.model);
182
+ const pricing = MODEL_PRICING_USD_PER_1M_TOKENS[modelKey];
183
+ if (pricing === undefined) {
184
+ modelsWithUnknownPricing.add(modelKey);
185
+ }
186
+ }
187
+ if (modelsWithUnknownPricing.size > 0) {
188
+ return {
189
+ estimatedCostUsd: null,
190
+ warning: `Cost estimate unavailable because pricing is unknown for ${formatLimitedList([
191
+ ...modelsWithUnknownPricing,
192
+ ])}.`,
193
+ };
194
+ }
195
+ let estimatedCostUsd = 0;
196
+ for (const call of calls) {
197
+ const { metadata: { usage }, } = call;
198
+ if (usage?.inputTokens === undefined || usage.outputTokens === undefined) {
199
+ return {
200
+ estimatedCostUsd: null,
201
+ warning: "Cost estimate unavailable because token usage was not returned.",
202
+ };
203
+ }
204
+ const pricing = MODEL_PRICING_USD_PER_1M_TOKENS[formatModelSpec(call.metadata.model)];
205
+ if (pricing === undefined) {
206
+ return {
207
+ estimatedCostUsd: null,
208
+ warning: "Cost estimate unavailable because model pricing changed during calculation.",
209
+ };
210
+ }
211
+ estimatedCostUsd +=
212
+ (usage.inputTokens / 1_000_000) * pricing.inputUsdPerMillionTokens +
213
+ (usage.outputTokens / 1_000_000) * pricing.outputUsdPerMillionTokens;
214
+ }
215
+ return { estimatedCostUsd };
216
+ }
217
+ async function defaultStructuredOutputRunner(input) {
218
+ const { model, prompt, reasoningLevel, schema, system } = input;
219
+ return await runStructuredOutput({ model, prompt, reasoningLevel, schema, system });
220
+ }
221
+ async function runPerspective(input) {
222
+ const { model, onProgress, perspective, prompt, reasoningLevel, structuredOutputRunner } = input;
223
+ const output = await runStructuredOutputWithProgress({
224
+ model,
225
+ onProgress,
226
+ prompt,
227
+ reasoningLevel,
228
+ role: perspective.role,
229
+ schema: generatedPerspectiveResultSchema,
230
+ structuredOutputRunner,
231
+ system: perspective.system,
232
+ });
233
+ const result = perspectiveResultSchema.parse(output.output);
234
+ if (result.role !== perspective.role) {
235
+ throw new Error(`Perspective role mismatch: expected ${perspective.role}, received ${result.role}.`);
236
+ }
237
+ return {
238
+ role: perspective.role,
239
+ result,
240
+ metadata: output.metadata,
241
+ };
242
+ }
243
+ async function runStructuredOutputWithProgress(input) {
244
+ const { model, onProgress, prompt, reasoningLevel, role, schema, structuredOutputRunner, system, } = input;
245
+ const runnerInput = {
246
+ model,
247
+ prompt,
248
+ reasoningLevel,
249
+ role,
250
+ schema,
251
+ system,
252
+ };
253
+ await emitProgress(onProgress, { model, role, status: "started" });
254
+ try {
255
+ const output = await structuredOutputRunner(runnerInput);
256
+ await emitProgress(onProgress, {
257
+ latencyMs: output.metadata.latencyMs,
258
+ metadata: output.metadata,
259
+ model,
260
+ output: output.output,
261
+ role,
262
+ status: "completed",
263
+ });
264
+ return output;
265
+ }
266
+ catch (error) {
267
+ await emitProgress(onProgress, {
268
+ errorMessage: formatErrorMessage(error),
269
+ model,
270
+ role,
271
+ status: "failed",
272
+ });
273
+ throw error;
274
+ }
275
+ }
276
+ async function emitProgress(onProgress, event) {
277
+ if (onProgress === undefined) {
278
+ return;
279
+ }
280
+ try {
281
+ await onProgress(event);
282
+ }
283
+ catch {
284
+ // Progress hooks are best-effort; recorder or logger failures must not abort model calls.
285
+ }
286
+ }
287
+ function collectSuccessfulPerspectives(settledPerspectives, warnings) {
288
+ const perspectives = [];
289
+ for (const [index, settledPerspective] of settledPerspectives.entries()) {
290
+ const perspective = DEFAULT_PERSPECTIVES[index];
291
+ if (settledPerspective === undefined || perspective === undefined) {
292
+ continue;
293
+ }
294
+ if (settledPerspective.status === "fulfilled") {
295
+ perspectives.push(settledPerspective.value);
296
+ continue;
297
+ }
298
+ warnings.push(`${perspective.role} perspective failed: ${formatErrorMessage(settledPerspective.reason)}`);
299
+ }
300
+ return perspectives;
301
+ }
302
+ function buildQuestionPrompt(input) {
303
+ const { context, query } = input;
304
+ if (context === undefined || context.trim().length === 0) {
305
+ return `Question:
306
+ ${query}
307
+
308
+ Context:
309
+ No additional context provided.`;
310
+ }
311
+ return `Question:
312
+ ${query}
313
+
314
+ Context:
315
+ ${context}`;
316
+ }
317
+ function buildDeliberatorPrompt(input) {
318
+ const { context, perspectives, query } = input;
319
+ const contextText = context === undefined || context.trim().length === 0
320
+ ? "No additional context provided."
321
+ : context;
322
+ return `Question:
323
+ ${query}
324
+
325
+ Context:
326
+ ${contextText}
327
+
328
+ Perspectives:
329
+ ${perspectives.map(formatPerspectiveForDeliberator).join("\n\n")}`;
330
+ }
331
+ function formatPerspectiveForDeliberator(perspective) {
332
+ const claims = perspective.result.claims
333
+ .map((claim) => ` - ${claim.claim} (confidence ${formatConfidence(claim.confidence)}): ${claim.reasoning}`)
334
+ .join("\n");
335
+ const openQuestions = perspective.result.openQuestions.length === 0
336
+ ? " - none"
337
+ : perspective.result.openQuestions.map((question) => ` - ${question}`).join("\n");
338
+ return `- ${perspective.role}: ${perspective.result.summary}
339
+ Claims:
340
+ ${claims}
341
+ Open questions:
342
+ ${openQuestions}`;
343
+ }
344
+ function formatConfidence(confidence) {
345
+ return `${Math.round(confidence * 100)}%`;
346
+ }
347
+ function formatErrorMessage(error) {
348
+ return error instanceof Error ? error.message : String(error);
349
+ }
350
+ function formatLimitedList(items) {
351
+ const sortedItems = items.toSorted();
352
+ const displayedItems = sortedItems.slice(0, MAX_WARNINGS_TO_LIST);
353
+ const remainingCount = sortedItems.length - displayedItems.length;
354
+ if (remainingCount === 0) {
355
+ return displayedItems.join(", ");
356
+ }
357
+ return `${displayedItems.join(", ")} and ${remainingCount} more`;
358
+ }
359
+ function copyReasoningOverrides(overrides) {
360
+ if (overrides === undefined) {
361
+ return undefined;
362
+ }
363
+ const copiedOverrides = {};
364
+ copyReasoningOverride(copiedOverrides, overrides, "advocate");
365
+ copyReasoningOverride(copiedOverrides, overrides, "skeptic");
366
+ copyReasoningOverride(copiedOverrides, overrides, "analyst");
367
+ copyReasoningOverride(copiedOverrides, overrides, "deliberator");
368
+ if (copiedOverrides.advocate === undefined &&
369
+ copiedOverrides.skeptic === undefined &&
370
+ copiedOverrides.analyst === undefined &&
371
+ copiedOverrides.deliberator === undefined) {
372
+ return undefined;
373
+ }
374
+ return copiedOverrides;
375
+ }
376
+ function copyReasoningOverride(target, source, role) {
377
+ const level = source[role];
378
+ if (level === undefined) {
379
+ return;
380
+ }
381
+ target[role] = level;
382
+ }
383
+ //# sourceMappingURL=tribunal.js.map