@oeronteros-1/opencode-orchestra 1.0.22 → 1.0.23

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 (40) hide show
  1. package/README.md +11 -3
  2. package/dashboard-dist/assets/index-DMvE5MHc.js +124 -0
  3. package/dashboard-dist/index.html +1 -1
  4. package/dist/cache-refresh.d.ts +42 -0
  5. package/dist/cache-refresh.js +130 -0
  6. package/dist/cache-refresh.js.map +1 -0
  7. package/dist/cli.d.ts +8 -0
  8. package/dist/cli.js +13 -5
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config/schema.d.ts +9 -1
  11. package/dist/config/schema.js +24 -1
  12. package/dist/config/schema.js.map +1 -1
  13. package/dist/dashboard/server.js +2 -2
  14. package/dist/dashboard/server.js.map +1 -1
  15. package/dist/index.js +25 -7
  16. package/dist/index.js.map +1 -1
  17. package/dist/pricing/cost.d.ts +29 -0
  18. package/dist/pricing/cost.js +47 -0
  19. package/dist/pricing/cost.js.map +1 -0
  20. package/dist/pricing/model-match.d.ts +59 -0
  21. package/dist/pricing/model-match.js +134 -0
  22. package/dist/pricing/model-match.js.map +1 -0
  23. package/dist/pricing/openrouter.d.ts +60 -0
  24. package/dist/pricing/openrouter.js +150 -0
  25. package/dist/pricing/openrouter.js.map +1 -0
  26. package/dist/pricing/resolver.d.ts +50 -0
  27. package/dist/pricing/resolver.js +145 -0
  28. package/dist/pricing/resolver.js.map +1 -0
  29. package/dist/routing/pricing/estimate.d.ts +14 -1
  30. package/dist/routing/pricing/estimate.js +81 -20
  31. package/dist/routing/pricing/estimate.js.map +1 -1
  32. package/dist/telemetry/ledger.d.ts +6 -1
  33. package/dist/telemetry/ledger.js +15 -3
  34. package/dist/telemetry/ledger.js.map +1 -1
  35. package/dist/tools.d.ts +3 -0
  36. package/dist/tools.js +3 -1
  37. package/dist/tools.js.map +1 -1
  38. package/package.json +2 -2
  39. package/schema/opencode-orchestra.schema.json +21 -1
  40. package/dashboard-dist/assets/index-BZTn_d2K.js +0 -124
@@ -0,0 +1,150 @@
1
+ // OpenRouter pricing source. The models list is a public, keyless endpoint;
2
+ // prices arrive as decimal strings in USD per single token and are converted
3
+ // here to USD per 1M tokens, matching the plugin's internal unit everywhere
4
+ // else. Per-request prices (request/image/audio) stay in their own units and
5
+ // are never multiplied by token counts. Fetching is cached with a TTL and
6
+ // single-flight; failures keep the previous snapshot so offline estimation
7
+ // keeps working.
8
+ import { normalizeModelName, splitProviderId } from "./model-match.js";
9
+ function toPrice(value) {
10
+ if (typeof value !== "string" || value.trim() === "")
11
+ return undefined;
12
+ const parsed = Number(value);
13
+ return Number.isFinite(parsed) && parsed >= 0 ? parsed : undefined;
14
+ }
15
+ /** Convert a per-token string price to USD per 1M, rounded to micro-dollars. */
16
+ function toPerMillion(value) {
17
+ const parsed = toPrice(value);
18
+ if (parsed === undefined)
19
+ return undefined;
20
+ return Math.round(parsed * 1_000_000 * 1_000_000) / 1_000_000;
21
+ }
22
+ function splitVariant(id) {
23
+ const idx = id.lastIndexOf(":");
24
+ if (idx === -1)
25
+ return { base: id };
26
+ return { base: id.slice(0, idx), variant: id.slice(idx + 1) };
27
+ }
28
+ /**
29
+ * Parse an OpenRouter `/api/v1/models` response into normalized models.
30
+ * Missing or invalid price fields become undefined (never 0, never throws).
31
+ */
32
+ export function parseOpenRouterModels(text) {
33
+ let parsed;
34
+ try {
35
+ parsed = JSON.parse(text);
36
+ }
37
+ catch {
38
+ return [];
39
+ }
40
+ if (typeof parsed !== "object" || parsed === null)
41
+ return [];
42
+ const data = parsed.data;
43
+ if (!Array.isArray(data))
44
+ return [];
45
+ const models = [];
46
+ for (const item of data) {
47
+ if (typeof item !== "object" || item === null)
48
+ continue;
49
+ const raw = item;
50
+ if (typeof raw.id !== "string" || !raw.id)
51
+ continue;
52
+ const { base, variant } = splitVariant(raw.id);
53
+ const pricingRaw = (typeof raw.pricing === "object" && raw.pricing !== null ? raw.pricing : {});
54
+ const input = toPerMillion(pricingRaw.prompt);
55
+ const output = toPerMillion(pricingRaw.completion);
56
+ const cacheRead = toPerMillion(pricingRaw.input_cache_read);
57
+ const reasoning = toPerMillion(pricingRaw.internal_reasoning);
58
+ const request = toPrice(pricingRaw.request);
59
+ const image = toPrice(pricingRaw.image);
60
+ const audio = toPrice(pricingRaw.audio);
61
+ models.push({
62
+ id: base.toLowerCase(),
63
+ ...(variant ? { variant } : {}),
64
+ ...(typeof raw.name === "string" && raw.name ? { name: raw.name } : {}),
65
+ ...(typeof raw.canonical_slug === "string" && raw.canonical_slug
66
+ ? { canonicalSlug: splitProviderId(raw.canonical_slug).rest }
67
+ : {}),
68
+ ...(typeof raw.context_length === "number" ? { contextLength: raw.context_length } : {}),
69
+ isFreeVariant: variant === "free",
70
+ pricing: {
71
+ ...(input !== undefined ? { input } : {}),
72
+ ...(output !== undefined ? { output } : {}),
73
+ ...(cacheRead !== undefined ? { cacheRead } : {}),
74
+ ...(reasoning !== undefined ? { reasoning } : {}),
75
+ ...(request !== undefined ? { request } : {}),
76
+ ...(image !== undefined ? { image } : {}),
77
+ ...(audio !== undefined ? { audio } : {}),
78
+ },
79
+ });
80
+ }
81
+ return models;
82
+ }
83
+ /** Build matchModel catalog entries from a parsed OpenRouter list. */
84
+ export function toModelEntries(models) {
85
+ const entries = [];
86
+ for (const model of models) {
87
+ const { provider, rest } = splitProviderId(model.id);
88
+ const aliases = [];
89
+ if (model.name)
90
+ aliases.push(model.name);
91
+ if (model.canonicalSlug)
92
+ aliases.push(model.canonicalSlug);
93
+ entries.push({
94
+ id: normalizeModelName(rest),
95
+ ...(provider ? { provider } : {}),
96
+ ...(aliases.length ? { aliases } : {}),
97
+ });
98
+ }
99
+ return entries;
100
+ }
101
+ export const DEFAULT_OPENROUTER_ENDPOINT = "https://openrouter.ai/api/v1/models";
102
+ /**
103
+ * TTL-on-data cache over the OpenRouter models list. Concurrent callers
104
+ * share one in-flight fetch; a failed refresh keeps the previous snapshot.
105
+ */
106
+ export function createOpenRouterCache(options) {
107
+ const endpoint = options.endpoint ?? DEFAULT_OPENROUTER_ENDPOINT;
108
+ const fetchImpl = options.fetchImpl ?? fetch;
109
+ const now = options.now ?? Date.now;
110
+ let models;
111
+ let fetchedAt;
112
+ let lastError;
113
+ let inFlight;
114
+ const refresh = () => {
115
+ inFlight = (async () => {
116
+ try {
117
+ const response = await fetchImpl(endpoint);
118
+ if (!response.ok)
119
+ throw new Error(`openrouter ${response.status}`);
120
+ models = parseOpenRouterModels(await response.text());
121
+ fetchedAt = now();
122
+ lastError = undefined;
123
+ }
124
+ catch (error) {
125
+ lastError = error instanceof Error ? error.message : String(error);
126
+ }
127
+ return models ?? [];
128
+ })().finally(() => {
129
+ inFlight = undefined;
130
+ });
131
+ return inFlight;
132
+ };
133
+ const getModels = (force = false) => {
134
+ if (inFlight)
135
+ return inFlight;
136
+ if (force || models === undefined || now() - (fetchedAt ?? 0) >= options.ttlMs)
137
+ return refresh();
138
+ return Promise.resolve(models);
139
+ };
140
+ return {
141
+ getModels,
142
+ get fetchedAt() {
143
+ return fetchedAt;
144
+ },
145
+ get lastError() {
146
+ return lastError;
147
+ },
148
+ };
149
+ }
150
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/pricing/openrouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ import type { PriceSnapshot } from "../routing/pricing/prices.js";
2
+ import type { ModelCost } from "../config/schema.js";
3
+ import { type MatchMethod } from "./model-match.js";
4
+ import { type OpenRouterModel } from "./openrouter.js";
5
+ import type { PricingResolution } from "./cost.js";
6
+ export interface ModelAliasEntry {
7
+ /** Canonical model id the aliases point at, e.g. "gpt-5-6-sol". */
8
+ canonical: string;
9
+ /** Raw names that map to the canonical id. */
10
+ aliases: string[];
11
+ }
12
+ export interface OpenRouterSource {
13
+ getModels(force?: boolean): Promise<OpenRouterModel[]>;
14
+ }
15
+ export interface ResolverConfig {
16
+ snapshot: PriceSnapshot;
17
+ aliases?: ModelAliasEntry[];
18
+ openRouter?: OpenRouterSource;
19
+ }
20
+ export interface ResolverInput {
21
+ /** Raw id, optionally provider-prefixed ("CX/GPT-5.6 Sol"). */
22
+ id?: string;
23
+ providerID?: string;
24
+ modelID?: string;
25
+ /** Config-declared cost class when known (routing pools, provider catalog). */
26
+ declaredCost?: ModelCost;
27
+ /** Explicit configured USD/1M prices; beat every lookup source. */
28
+ explicitPrice?: {
29
+ input: number;
30
+ output: number;
31
+ };
32
+ }
33
+ export interface ResolvedPricing extends PricingResolution {
34
+ /** Canonical normalized model id ("gpt-5-6-sol"). */
35
+ canonicalId?: string;
36
+ /** Where the price came from. */
37
+ source?: "config" | "snapshot" | "openrouter";
38
+ /** How the model identity was matched. */
39
+ method?: MatchMethod;
40
+ familyAmbiguous?: boolean;
41
+ /** True when the async OpenRouter tier should still be tried. */
42
+ needsFallback?: boolean;
43
+ }
44
+ /** Sync tier: config declarations + user aliases + the price snapshot. */
45
+ export declare function resolvePricingSync(input: ResolverInput, config: ResolverConfig): ResolvedPricing;
46
+ /**
47
+ * Full resolution: sync tier first, then the cached OpenRouter catalog when
48
+ * the price is still unknown. Never fetches when OpenRouter is disabled.
49
+ */
50
+ export declare function resolvePricing(input: ResolverInput, config: ResolverConfig): Promise<ResolvedPricing>;
@@ -0,0 +1,145 @@
1
+ // Pricing Resolver: turns a raw model id (any spelling, any provider prefix)
2
+ // into a PricingResolution. Precedence, highest first:
3
+ //
4
+ // 1. config-declared cost class (free / subscription)
5
+ // 2. explicit configured price (priceInput/priceOutput)
6
+ // 3. ":free" variant suffix in the raw id
7
+ // 4. user-defined model aliases (they beat every external source)
8
+ // 5. built-in / operator-refreshed price snapshot (provider pricing)
9
+ // 6. OpenRouter fallback (opt-in; cached; async)
10
+ // 7. unknown — tokens still counted, cost null, never silently free
11
+ //
12
+ // Free models and subscription models are priced $0 with their status kept;
13
+ // "unknown" is a distinct state so callers can surface "no price" honestly.
14
+ import { matchModel, normalizeModelName, parseModelId, splitProviderId, } from "./model-match.js";
15
+ import { toModelEntries } from "./openrouter.js";
16
+ function snapshotCatalog(snapshot) {
17
+ const entries = [];
18
+ const keyByEntryId = new Map();
19
+ for (const key of Object.keys(snapshot.prices)) {
20
+ const { provider, rest } = splitProviderId(key);
21
+ const entryId = normalizeModelName(rest);
22
+ entries.push({
23
+ id: entryId,
24
+ ...(provider ? { provider: normalizeModelName(provider) } : {}),
25
+ });
26
+ keyByEntryId.set(entryId, key);
27
+ }
28
+ return { entries, keyByEntryId };
29
+ }
30
+ function rawIdOf(input) {
31
+ if (input.id)
32
+ return input.id;
33
+ if (input.providerID && input.modelID)
34
+ return `${input.providerID}/${input.modelID}`;
35
+ return input.modelID ?? "";
36
+ }
37
+ function resolveFromSnapshot(entryId, method, catalog, snapshot) {
38
+ const key = catalog.keyByEntryId.get(entryId);
39
+ const price = key ? snapshot.prices[key] : undefined;
40
+ if (!key || !price) {
41
+ return { status: "unknown", canonicalId: entryId, method, needsFallback: true };
42
+ }
43
+ if (price.input === 0 && price.output === 0) {
44
+ return { status: "free", canonicalId: entryId, source: "snapshot", method };
45
+ }
46
+ return {
47
+ status: "paid",
48
+ input: price.input,
49
+ output: price.output,
50
+ canonicalId: entryId,
51
+ source: "snapshot",
52
+ method,
53
+ };
54
+ }
55
+ /** Sync tier: config declarations + user aliases + the price snapshot. */
56
+ export function resolvePricingSync(input, config) {
57
+ const raw = rawIdOf(input);
58
+ const parsed = parseModelId(raw);
59
+ if (input.declaredCost === "subscription")
60
+ return { status: "subscription", canonicalId: parsed.model };
61
+ if (input.declaredCost === "free")
62
+ return { status: "free", canonicalId: parsed.model };
63
+ const explicit = input.explicitPrice;
64
+ if (explicit && explicit.input >= 0 && explicit.output >= 0) {
65
+ if (explicit.input === 0 && explicit.output === 0) {
66
+ return { status: "free", canonicalId: parsed.model, source: "config" };
67
+ }
68
+ return {
69
+ status: "paid",
70
+ input: explicit.input,
71
+ output: explicit.output,
72
+ canonicalId: parsed.model,
73
+ source: "config",
74
+ };
75
+ }
76
+ if (parsed.variant === "free")
77
+ return { status: "free", canonicalId: parsed.model };
78
+ const catalog = snapshotCatalog(config.snapshot);
79
+ for (const aliasEntry of config.aliases ?? []) {
80
+ const hit = (aliasEntry.aliases ?? []).some((alias) => normalizeModelName(alias) === parsed.model);
81
+ if (hit)
82
+ return resolveFromSnapshot(aliasEntry.canonical, "alias", catalog, config.snapshot);
83
+ }
84
+ const match = matchModel(raw, catalog.entries);
85
+ if (match.method !== "none") {
86
+ return resolveFromSnapshot(match.canonical, match.method, catalog, config.snapshot);
87
+ }
88
+ return {
89
+ status: "unknown",
90
+ canonicalId: match.canonical,
91
+ ...(match.familyAmbiguous ? { familyAmbiguous: true } : {}),
92
+ needsFallback: true,
93
+ };
94
+ }
95
+ function classifyOpenRouter(model, match) {
96
+ const pricing = model.pricing;
97
+ if (model.isFreeVariant) {
98
+ return { status: "free", canonicalId: match.canonical, source: "openrouter", method: match.method };
99
+ }
100
+ const hasMediaPrice = (pricing.request ?? 0) > 0 || (pricing.image ?? 0) > 0 || (pricing.audio ?? 0) > 0;
101
+ if (pricing.input !== undefined && pricing.output !== undefined) {
102
+ if (pricing.input === 0 && pricing.output === 0) {
103
+ // Zero token prices alone do not prove a model is free: media models
104
+ // bill per request/image instead. Keep those unknown, never silent $0.
105
+ if (hasMediaPrice) {
106
+ return { status: "unknown", canonicalId: match.canonical, source: "openrouter", method: match.method };
107
+ }
108
+ return { status: "free", canonicalId: match.canonical, source: "openrouter", method: match.method };
109
+ }
110
+ return {
111
+ status: "paid",
112
+ input: pricing.input,
113
+ output: pricing.output,
114
+ ...(pricing.cacheRead !== undefined ? { cacheRead: pricing.cacheRead } : {}),
115
+ ...(pricing.reasoning !== undefined ? { reasoning: pricing.reasoning } : {}),
116
+ canonicalId: match.canonical,
117
+ source: "openrouter",
118
+ method: match.method,
119
+ };
120
+ }
121
+ return { status: "unknown", canonicalId: match.canonical, source: "openrouter", method: match.method };
122
+ }
123
+ /**
124
+ * Full resolution: sync tier first, then the cached OpenRouter catalog when
125
+ * the price is still unknown. Never fetches when OpenRouter is disabled.
126
+ */
127
+ export async function resolvePricing(input, config) {
128
+ const sync = resolvePricingSync(input, config);
129
+ if (sync.status !== "unknown" || !sync.needsFallback || !config.openRouter)
130
+ return sync;
131
+ const models = await config.openRouter.getModels(false);
132
+ const entries = toModelEntries(models);
133
+ const match = matchModel(rawIdOf(input), entries);
134
+ if (match.method === "none")
135
+ return sync;
136
+ const modelByEntryId = new Map();
137
+ for (const model of models) {
138
+ modelByEntryId.set(normalizeModelName(splitProviderId(model.id).rest), model);
139
+ }
140
+ const model = modelByEntryId.get(match.canonical);
141
+ if (!model)
142
+ return sync;
143
+ return classifyOpenRouter(model, { canonical: match.canonical, method: match.method });
144
+ }
145
+ //# sourceMappingURL=resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/pricing/resolver.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,7 @@ import type { BudgetMode, ModelCandidateInput } from "../../config/schema.js";
2
2
  import { resolveModel } from "../model-resolver.js";
3
3
  import type { PriceSnapshot } from "./prices.js";
4
4
  import type { TaskPlan } from "../planner.js";
5
+ import { type ModelAliasEntry, type OpenRouterSource } from "../../pricing/resolver.js";
5
6
  export interface PriceResolution {
6
7
  /** Price source used for the estimate (where a paid price was found). */
7
8
  source: "snapshot" | "remote";
@@ -34,6 +35,10 @@ export interface CostEstimateInput {
34
35
  workerCapabilityOf?: (worker: string) => Parameters<typeof resolveModel>[0]["capability"];
35
36
  snapshot: PriceSnapshot;
36
37
  tokens?: TokenEstimates;
38
+ /** User-defined model aliases (config pricing.aliases). */
39
+ aliases?: ModelAliasEntry[];
40
+ /** Optional OpenRouter fallback source (config pricing.openrouter). */
41
+ openRouter?: OpenRouterSource;
37
42
  }
38
43
  export interface CostBreakdown {
39
44
  workers: number;
@@ -43,6 +48,14 @@ export interface CostBreakdown {
43
48
  subtotal: number;
44
49
  /** Conservative +20% buffer for retries / cache misses / reasoning tokens. */
45
50
  total: number;
51
+ /** Calls whose price could not be determined; excluded from all totals. */
52
+ unknownCalls: number;
53
+ /** Calls priced $0 because the model is free (tokens still counted). */
54
+ freeCalls: number;
55
+ /** Calls priced $0 because the model runs inside a subscription. */
56
+ subscriptionCalls: number;
57
+ /** Calls priced with a known USD rate. */
58
+ paidCalls: number;
46
59
  }
47
60
  export interface CostEstimate {
48
61
  budget: BudgetMode;
@@ -52,7 +65,7 @@ export interface CostEstimate {
52
65
  /** Human-readable summary line for pre-run confirmation. */
53
66
  summary: string;
54
67
  }
55
- export declare function estimateCost(input: CostEstimateInput): CostEstimate;
68
+ export declare function estimateCost(input: CostEstimateInput): Promise<CostEstimate>;
56
69
  /** Compare an estimate against a warning threshold; returns a pre-run message. */
57
70
  export declare function formatEstimateWarning(estimate: CostEstimate, thresholdUSD?: number): string | undefined;
58
71
  /** Total combined price for a model id or 0 when unknown/free. */
@@ -1,5 +1,6 @@
1
- import { normalizeCandidate, resolveModel } from "../model-resolver.js";
2
- import { lookupPrice, combinedPrice } from "./prices.js";
1
+ import { resolveModel } from "../model-resolver.js";
2
+ import { combinedPrice, lookupPrice } from "./prices.js";
3
+ import { resolvePricing } from "../../pricing/resolver.js";
3
4
  export const DEFAULT_TOKEN_ESTIMATES = {
4
5
  workerTokens: 4_000,
5
6
  leadTokens: 6_000,
@@ -13,7 +14,12 @@ function costOfTokens(price, tokens) {
13
14
  const input = tokens * 0.75;
14
15
  return (input * price.input + output * price.output) / 1_000_000;
15
16
  }
16
- function resolvePoolPriceId(pool, budget, capability, snapshot) {
17
+ /**
18
+ * Resolve the pool's chosen model for a budget/capability, then resolve its
19
+ * pricing through the full ladder (config declarations -> explicit price ->
20
+ * aliases -> provider snapshot -> OpenRouter fallback).
21
+ */
22
+ async function resolvePoolPricing(pool, budget, capability, config) {
17
23
  const resolved = resolveModel({
18
24
  pool,
19
25
  capability,
@@ -28,28 +34,78 @@ function resolvePoolPriceId(pool, budget, capability, snapshot) {
28
34
  });
29
35
  if (!resolved)
30
36
  return {};
31
- const configuredPrice = resolved.priceInput !== undefined && resolved.priceOutput !== undefined
32
- ? { input: resolved.priceInput, output: resolved.priceOutput }
33
- : undefined;
34
- const price = configuredPrice ?? lookupPrice(snapshot, resolved.id);
35
- // Only paid models carry a dollar estimate; free/subscription resolve to 0.
36
- const resolvedPrice = resolved.cost === "paid" ? price : undefined;
37
- return { id: resolved.id, ...(resolvedPrice ? { price: resolvedPrice } : {}) };
37
+ const input = {
38
+ id: resolved.id,
39
+ declaredCost: resolved.cost,
40
+ ...(resolved.priceInput !== undefined && resolved.priceOutput !== undefined
41
+ ? { explicitPrice: { input: resolved.priceInput, output: resolved.priceOutput } }
42
+ : {}),
43
+ };
44
+ const resolution = await resolvePricing(input, config);
45
+ return { id: resolved.id, resolution };
38
46
  }
39
- export function estimateCost(input) {
47
+ export async function estimateCost(input) {
40
48
  const tokens = input.tokens ?? DEFAULT_TOKEN_ESTIMATES;
41
- const lead = resolvePoolPriceId(input.leadPool, input.budget, "reasoning", input.snapshot);
42
- const judge = resolvePoolPriceId(input.judgePool, input.budget, "review", input.snapshot);
43
- const workersCost = input.plan.nodes.reduce((sum, node) => {
49
+ const config = {
50
+ snapshot: input.snapshot,
51
+ ...(input.aliases?.length ? { aliases: input.aliases } : {}),
52
+ ...(input.openRouter ? { openRouter: input.openRouter } : {}),
53
+ };
54
+ const lead = await resolvePoolPricing(input.leadPool, input.budget, "reasoning", config);
55
+ const judge = await resolvePoolPricing(input.judgePool, input.budget, "review", config);
56
+ let workersCost = 0;
57
+ let paidCalls = 0;
58
+ let freeCalls = 0;
59
+ let subscriptionCalls = 0;
60
+ let unknownCalls = 0;
61
+ for (const node of input.plan.nodes) {
44
62
  const poolKey = input.workerPoolOf(node.worker);
45
63
  const pool = input.workerPools[poolKey] ?? [];
46
64
  const capability = input.workerCapabilityOf?.(node.worker)
47
65
  ?? (poolKey === "image" ? "image" : poolKey === "vision" ? "vision" : poolKey === "research" ? "research" : poolKey === "reasoning" ? "reasoning" : "code");
48
- const { price } = resolvePoolPriceId(pool, input.budget, capability, input.snapshot);
49
- return sum + costOfTokens(price, tokens.workerTokens);
50
- }, 0);
51
- const leadCost = costOfTokens(lead.price, tokens.leadTokens);
52
- const judgeCost = costOfTokens(judge.price, tokens.judgeTokens);
66
+ const { resolution } = await resolvePoolPricing(pool, input.budget, capability, config);
67
+ if (!resolution)
68
+ continue;
69
+ if (resolution.status === "paid") {
70
+ workersCost += costOfTokens({ input: resolution.input ?? 0, output: resolution.output ?? 0 }, tokens.workerTokens);
71
+ paidCalls += 1;
72
+ }
73
+ else if (resolution.status === "free") {
74
+ freeCalls += 1;
75
+ }
76
+ else if (resolution.status === "subscription") {
77
+ subscriptionCalls += 1;
78
+ }
79
+ else {
80
+ unknownCalls += 1;
81
+ }
82
+ }
83
+ const leadCost = lead.resolution?.status === "paid"
84
+ ? costOfTokens({ input: lead.resolution.input ?? 0, output: lead.resolution.output ?? 0 }, tokens.leadTokens)
85
+ : 0;
86
+ const judgeCost = judge.resolution?.status === "paid"
87
+ ? costOfTokens({ input: judge.resolution.input ?? 0, output: judge.resolution.output ?? 0 }, tokens.judgeTokens)
88
+ : 0;
89
+ if (lead.resolution) {
90
+ if (lead.resolution.status === "paid")
91
+ paidCalls += 1;
92
+ else if (lead.resolution.status === "free")
93
+ freeCalls += 1;
94
+ else if (lead.resolution.status === "subscription")
95
+ subscriptionCalls += 1;
96
+ else
97
+ unknownCalls += 1;
98
+ }
99
+ if (judge.resolution) {
100
+ if (judge.resolution.status === "paid")
101
+ paidCalls += 1;
102
+ else if (judge.resolution.status === "free")
103
+ freeCalls += 1;
104
+ else if (judge.resolution.status === "subscription")
105
+ subscriptionCalls += 1;
106
+ else
107
+ unknownCalls += 1;
108
+ }
53
109
  const subtotal = workersCost + leadCost + judgeCost;
54
110
  const total = subtotal * 1.2;
55
111
  const breakdown = {
@@ -59,12 +115,17 @@ export function estimateCost(input) {
59
115
  judgeCost,
60
116
  subtotal,
61
117
  total,
118
+ unknownCalls,
119
+ freeCalls,
120
+ subscriptionCalls,
121
+ paidCalls,
62
122
  };
123
+ const unknownNote = unknownCalls > 0 ? `, ${unknownCalls} call(s) with unknown price excluded` : "";
63
124
  return {
64
125
  budget: input.budget,
65
126
  total,
66
127
  breakdown,
67
- summary: `This task in ${input.budget} mode will cost roughly $${total.toFixed(2)} (${breakdown.workers} worker calls, lead, ${judge.price ? "plus judge" : "no judge"} arbitration).`,
128
+ summary: `This task in ${input.budget} mode will cost roughly $${total.toFixed(2)} (${breakdown.workers} worker calls, lead, ${judge.resolution?.status === "paid" ? "plus judge" : "no judge"} arbitration${unknownNote}).`,
68
129
  };
69
130
  }
70
131
  /** Compare an estimate against a warning threshold; returns a pre-run message. */
@@ -1 +1 @@
1
- {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../../src/routing/pricing/estimate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"estimate.js","sourceRoot":"","sources":["../../../src/routing/pricing/estimate.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import type { ModelCandidateInput, ProfileName } from "../config/schema.js";
2
+ import type { PricingStatus } from "../pricing/cost.js";
2
3
  export interface TokenUsage {
3
4
  input: number;
4
5
  output: number;
@@ -17,6 +18,8 @@ export interface MessageUsage {
17
18
  completedAt?: number;
18
19
  finish?: string;
19
20
  tokens: TokenUsage;
21
+ /** How pricing was classified at record time (unknown = no rate found). */
22
+ pricingStatus?: PricingStatus;
20
23
  /** Opt-in debug text: only recorded when telemetry.storeTexts is enabled. */
21
24
  prompt?: string;
22
25
  reply?: string;
@@ -27,6 +30,7 @@ export interface SessionLedger {
27
30
  premiumEscalations: number;
28
31
  estimatedPaidUsage: number;
29
32
  freeWorkerCalls: number;
33
+ unknownPriceCalls: number;
30
34
  consensus?: number;
31
35
  messages: Record<string, MessageUsage>;
32
36
  }
@@ -57,9 +61,10 @@ export declare class Ledger {
57
61
  readonly stateFile: string;
58
62
  private readonly storeTexts;
59
63
  private readonly modelCosts;
64
+ private readonly pricingStatusOf;
60
65
  private state?;
61
66
  private queue;
62
- constructor(directory: string, telemetryDirectory: string, enabled: boolean, pools: ModelCandidateInput[][], storeTexts?: boolean);
67
+ constructor(directory: string, telemetryDirectory: string, enabled: boolean, pools: ModelCandidateInput[][], storeTexts?: boolean, pricingStatusOf?: (providerID: string | undefined, modelID: string | undefined) => PricingStatus | undefined);
63
68
  private load;
64
69
  private save;
65
70
  private mutate;