sneakoscope 10.2.0 → 10.3.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 (43) hide show
  1. package/README.md +6 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/command-manifest-lite.js +3 -1
  5. package/dist/cli/command-registry.js +9 -1
  6. package/dist/cli/install-helpers-menubar.js +57 -0
  7. package/dist/cli/install-helpers.js +10 -1
  8. package/dist/commands/decision.js +7 -0
  9. package/dist/config/skills-manifest.json +1 -1
  10. package/dist/core/agents/native-worker-backend-router.js +7 -2
  11. package/dist/core/codex-app/menubar/constants.js +2 -0
  12. package/dist/core/decisions/cli.js +324 -0
  13. package/dist/core/decisions/config.js +109 -0
  14. package/dist/core/decisions/evaluation.js +62 -0
  15. package/dist/core/decisions/integration.js +262 -0
  16. package/dist/core/decisions/openrouter.js +270 -0
  17. package/dist/core/decisions/policy.js +393 -0
  18. package/dist/core/decisions/questions.js +215 -0
  19. package/dist/core/decisions/receipt.js +41 -0
  20. package/dist/core/decisions/recovery.js +39 -0
  21. package/dist/core/decisions/routing.js +58 -0
  22. package/dist/core/decisions/state.js +173 -0
  23. package/dist/core/decisions/types.js +69 -0
  24. package/dist/core/feature-fixtures.js +1 -0
  25. package/dist/core/feature-registry.js +1 -0
  26. package/dist/core/hooks-runtime/subagent-context.js +5 -3
  27. package/dist/core/hooks-runtime/subagent-spawn-policy.js +9 -3
  28. package/dist/core/provider/model-router.js +3 -3
  29. package/dist/core/routes.js +2 -1
  30. package/dist/core/subagents/model-policy.js +15 -0
  31. package/dist/core/subagents/official-subagent-lock.js +15 -0
  32. package/dist/core/subagents/official-subagent-preparation.js +265 -24
  33. package/dist/core/subagents/official-subagent-prompt.js +56 -13
  34. package/dist/core/update/update-migration-state/retired-local-decision.js +203 -0
  35. package/dist/core/update/update-migration-state.js +6 -0
  36. package/dist/core/version.js +1 -1
  37. package/dist/native/sks-menubar/Sources/ControlCenterWindowController.swift +6 -3
  38. package/dist/native/sks-menubar/Sources/LocalDecisionModels.swift +166 -0
  39. package/dist/native/sks-menubar/Sources/LocalDecisionViewController.swift +200 -0
  40. package/dist/native/sks-menubar/Sources/OverviewViewController.swift +4 -0
  41. package/dist/native/sks-menubar/Sources/SidebarItem.swift +2 -0
  42. package/dist/scripts/current-surface-update-e2e-check.js +1 -0
  43. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ export function percentile(values, p) {
2
+ const sorted = values.filter((value) => Number.isFinite(value)).sort((left, right) => left - right);
3
+ if (!sorted.length)
4
+ return null;
5
+ const rank = Math.min(sorted.length - 1, Math.max(0, Math.ceil((p / 100) * sorted.length) - 1));
6
+ return sorted[rank] ?? null;
7
+ }
8
+ export const EVALUATION_SCHEMA = 'sks.jev-evaluation.v1';
9
+ export function buildEvaluationReport(input) {
10
+ const unavailable = [];
11
+ const rows = [...input.rows];
12
+ if (rows.length === 0)
13
+ unavailable.push('no_labeled_tasks');
14
+ const success = rows.map((row) => row.success).filter((value) => value !== null);
15
+ const escaped = rows.map((row) => row.escapedFailure).filter((value) => value !== null);
16
+ const wall = rows.map((row) => row.wallClockMs).filter((value) => Number.isFinite(value));
17
+ const overhead = rows.map((row) => row.jevOverheadMs).filter((value) => Number.isFinite(value));
18
+ const tokenKnown = rows.every((row) => row.tokenEvidence === 'provider_response');
19
+ if (!tokenKnown)
20
+ unavailable.push('token_savings_unavailable');
21
+ const inputTokens = tokenKnown
22
+ ? sum(rows.map((row) => row.inputTokens))
23
+ : null;
24
+ const outputTokens = tokenKnown
25
+ ? sum(rows.map((row) => row.outputTokens))
26
+ : null;
27
+ const cost = rows.some((row) => row.reportedCost === null)
28
+ ? null
29
+ : sum(rows.map((row) => row.reportedCost));
30
+ if (cost === null)
31
+ unavailable.push('cost_unavailable');
32
+ const successes = rows.filter((row) => row.success === true).length;
33
+ const costPerSuccess = cost === null || successes === 0 ? null : cost / successes;
34
+ const coverage = rows.filter((row) => row.coveragePreserved).length;
35
+ return {
36
+ schema: EVALUATION_SCHEMA,
37
+ evidenceLevel: input.evidenceLevel || 'synthetic',
38
+ live: input.live === true,
39
+ arms: [...new Set(rows.map((row) => row.arm))],
40
+ taskCount: rows.length,
41
+ metrics: {
42
+ taskSuccessRate: success.length ? success.filter(Boolean).length / success.length : null,
43
+ escapedFailureRate: escaped.length ? escaped.filter(Boolean).length / escaped.length : null,
44
+ wallClockP50Ms: percentile(wall, 50),
45
+ wallClockP95Ms: percentile(wall, 95),
46
+ jevOverheadP50Ms: percentile(overhead, 50),
47
+ llmRejudgeCalls: rows.reduce((sumValue, row) => sumValue + row.llmRejudgeCalls, 0),
48
+ inputTokens,
49
+ outputTokens,
50
+ reportedCost: cost,
51
+ costPerSuccessfulTask: costPerSuccess,
52
+ coveragePreservedRate: rows.length ? coverage / rows.length : null
53
+ },
54
+ unavailableReasons: [...unavailable],
55
+ receipts: [...(input.receipts || [])]
56
+ };
57
+ }
58
+ function sum(values) {
59
+ if (values.some((value) => value === null || !Number.isFinite(value)))
60
+ return null;
61
+ return values.reduce((total, value) => total + (value || 0), 0);
62
+ }
@@ -0,0 +1,262 @@
1
+ import { sha256 } from '../fsx.js';
2
+ import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
3
+ import { jevEnabled, readDecisionConfig } from './config.js';
4
+ import { requestOpenRouterDecision } from './openrouter.js';
5
+ import { compileDecision } from './policy.js';
6
+ import { buildDecisionBundle, validatePlanCoverage } from './questions.js';
7
+ import { applyRecoveryEffect, listProductionRecoveryCandidates } from './recovery.js';
8
+ import { assembleRoutingSelection, buildRoutingCandidates } from './routing.js';
9
+ import { buildDecisionReceipt } from './receipt.js';
10
+ import { applyOptionalContextSelection, graphFileDigest, hydrateContextCandidates, sourceSnapshotDigest } from './state.js';
11
+ import { DESIGN_DEFAULTS, POLICY_REVISION, UNKNOWN_USAGE } from './types.js';
12
+ let testOverrides = null;
13
+ const appliedEffects = new Set();
14
+ export function setDecisionTestOverrides(overrides) {
15
+ testOverrides = overrides;
16
+ }
17
+ function activeOverrides() {
18
+ return process.env.SKS_JEV_DECISION_TEST_OVERRIDES === '1' ? testOverrides : null;
19
+ }
20
+ export async function decideOfficialSubagentPreparation(input) {
21
+ const started = Date.now();
22
+ try {
23
+ return await decideOfficialSubagentPreparationInner(input, started);
24
+ }
25
+ catch (error) {
26
+ if (error instanceof Error && error.name === 'AssertionError')
27
+ throw error;
28
+ return keep(input, 'transport_error', started);
29
+ }
30
+ }
31
+ async function decideOfficialSubagentPreparationInner(input, started) {
32
+ const overrides = activeOverrides();
33
+ const env = input.env || process.env;
34
+ const config = overrides?.config ?? await readDecisionConfig(env);
35
+ const baseline = keep(input, 'off', started);
36
+ if (!jevEnabled(config)) {
37
+ overrides?.observe?.({ mode: config.mode, eligible: false, compiled: baseline.compiled, receipt: baseline.receipt });
38
+ return baseline;
39
+ }
40
+ const resolved = await resolveOpenRouterApiKey({ env });
41
+ if (!resolved.key) {
42
+ const missing = keep(input, 'missing_key', started);
43
+ overrides?.observe?.({ mode: config.mode, eligible: false, compiled: missing.compiled, receipt: missing.receipt });
44
+ return missing;
45
+ }
46
+ const contextCandidates = config.capabilities.context.ready
47
+ ? await hydrateContextCandidates(input.root, input.attention, input.changedPaths || [])
48
+ : [];
49
+ const planCandidates = config.capabilities.plan.ready && input.requestedSource === 'automatic'
50
+ ? buildAutomaticPlanCandidates(input)
51
+ : [];
52
+ const routingCandidates = input.requestedSource === 'automatic'
53
+ ? buildRoutingCandidates({ roles: input.routingRoles || [] })
54
+ : [];
55
+ const recoveryCandidates = config.capabilities.recovery.ready
56
+ ? [...listProductionRecoveryCandidates()]
57
+ : [];
58
+ const optionalContext = contextCandidates.filter((row) => !row.pinned && row.excerpt);
59
+ const eligible = planCandidates.length > 1 || routingCandidates.length > 0 || optionalContext.length > 0 || recoveryCandidates.length > 0;
60
+ if (!eligible) {
61
+ const compiled = keep(input, 'no_alternative', started);
62
+ overrides?.observe?.({ mode: config.mode, eligible: false, compiled: compiled.compiled, receipt: compiled.receipt });
63
+ return compiled;
64
+ }
65
+ const sourceDigest = await sourceSnapshotDigest(input.root);
66
+ const graphDigest = input.attention?.snapshot_hash || await graphFileDigest(input.root);
67
+ const bundle = buildDecisionBundle({
68
+ projectId: sha256(input.root).slice(0, 32),
69
+ workflowRunId: input.workflowRunId,
70
+ workflowRevision: input.workflowRevision,
71
+ sourceDigest,
72
+ graphDigest,
73
+ goal: input.goal,
74
+ planCandidates,
75
+ contextCandidates,
76
+ recoveryCandidates,
77
+ routingCandidates,
78
+ baselinePlanId: planCandidates[0]?.id ?? null,
79
+ requiredSliceIds: planCandidates[0]?.sliceIds ?? input.slices.map((slice) => slice.id),
80
+ requiredVerificationIds: ['review_coverage']
81
+ });
82
+ const fetchImpl = input.fetchImpl || overrides?.fetchImpl;
83
+ const transport = await requestOpenRouterDecision(bundle, {
84
+ env,
85
+ deadlineMs: input.deadlineMs ?? DESIGN_DEFAULTS.deadlineMs,
86
+ ...(fetchImpl ? { fetchImpl } : {}),
87
+ ...(input.signal ? { signal: input.signal } : {})
88
+ });
89
+ const compiled = transport.ok
90
+ ? compileDecision(bundle, transport.response)
91
+ : {
92
+ kind: 'keep_baseline',
93
+ binding: bundle.binding,
94
+ reason: transport.reason,
95
+ usage: transport.usage
96
+ };
97
+ const applied = compiled.kind === 'apply'
98
+ ? applyEffects(input.attention, contextCandidates, planCandidates, compiled)
99
+ : {
100
+ attention: input.attention,
101
+ selectedPlan: null,
102
+ selectedRouting: null,
103
+ selectedContextIds: null,
104
+ consumptionEvidence: null
105
+ };
106
+ const receipt = buildDecisionReceipt({
107
+ binding: bundle.binding,
108
+ compiled,
109
+ result: compiled.kind === 'apply' ? 'applied' : 'kept_baseline',
110
+ reason: compiled.kind === 'apply' ? compiled.effects.map((effect) => effect.kind).join(',') : compiled.reason,
111
+ elapsedMs: Date.now() - started,
112
+ cacheHit: transport.ok && transport.cacheHit,
113
+ resolvedModel: transport.ok ? transport.resolvedModel : null,
114
+ responseId: transport.ok ? transport.responseId : null,
115
+ consumptionEvidence: applied.consumptionEvidence,
116
+ env
117
+ });
118
+ const result = {
119
+ compiled,
120
+ receipt,
121
+ attention: applied.attention,
122
+ selectedPlan: applied.selectedPlan,
123
+ selectedRouting: applied.selectedRouting,
124
+ selectedContextIds: applied.selectedContextIds,
125
+ llmRejudgeCalls: 0,
126
+ bundle
127
+ };
128
+ overrides?.observe?.({ mode: config.mode, eligible: true, compiled, receipt });
129
+ return result;
130
+ }
131
+ export function effectAlreadyConsumed(identity) {
132
+ return appliedEffects.has(identity);
133
+ }
134
+ export function markEffectConsumed(identity) {
135
+ if (appliedEffects.has(identity))
136
+ return false;
137
+ if (appliedEffects.size >= 256) {
138
+ const oldest = appliedEffects.values().next().value;
139
+ if (oldest !== undefined)
140
+ appliedEffects.delete(oldest);
141
+ }
142
+ appliedEffects.add(identity);
143
+ return true;
144
+ }
145
+ export async function dispatchBoundRecovery(input) {
146
+ if (!markEffectConsumed(input.consumptionIdentity)) {
147
+ return { invoked: false, reason: 'duplicate_delivery', consumptionEvidence: null };
148
+ }
149
+ const result = await applyRecoveryEffect(input);
150
+ if (!result.ok)
151
+ return { invoked: false, reason: result.reason, consumptionEvidence: null };
152
+ return { invoked: true, reason: 'applied', consumptionEvidence: result.consumptionEvidence };
153
+ }
154
+ function applyEffects(attention, contextCandidates, planCandidates, compiled) {
155
+ let nextAttention = attention;
156
+ let selectedPlan = null;
157
+ let selectedContextIds = null;
158
+ const evidence = [];
159
+ const routingEffects = [];
160
+ for (const effect of compiled.effects) {
161
+ if (effect.kind === 'select_optional_context') {
162
+ nextAttention = applyOptionalContextSelection(attention, contextCandidates, effect.keepIds);
163
+ selectedContextIds = effect.keepIds;
164
+ evidence.push(`context:${effect.keepIds.join(',')}`);
165
+ }
166
+ if (effect.kind === 'select_plan') {
167
+ selectedPlan = planCandidates.find((row) => row.id === effect.planId) || null;
168
+ if (selectedPlan)
169
+ evidence.push(`plan:${selectedPlan.id}`);
170
+ }
171
+ if (effect.kind === 'select_routing') {
172
+ routingEffects.push({ roleId: effect.roleId, model: effect.model });
173
+ }
174
+ }
175
+ const selectedRouting = assembleRoutingSelection(routingEffects);
176
+ if (selectedRouting) {
177
+ evidence.push(...Object.entries(selectedRouting.models).map(([roleId, model]) => `routing:${roleId}=${model}`));
178
+ }
179
+ return {
180
+ attention: nextAttention,
181
+ selectedPlan,
182
+ selectedRouting,
183
+ selectedContextIds,
184
+ consumptionEvidence: evidence.length ? evidence.join('|') : null
185
+ };
186
+ }
187
+ function buildAutomaticPlanCandidates(input) {
188
+ const slices = input.slices.filter((slice) => slice.id);
189
+ const sliceIds = slices.map((slice) => slice.id);
190
+ const workerCount = Math.max(1, input.requestedSubagents);
191
+ if (workerCount < 2)
192
+ return [];
193
+ const verification = ['review_coverage'];
194
+ const covered = sliceIds.length > 0 ? sliceIds : ['parent_owned_decomposition'];
195
+ const baselineGroups = slices.length > 0
196
+ ? slices.map((slice, index) => ({
197
+ id: `G${index + 1}`,
198
+ sliceIds: [slice.id],
199
+ role: slice.agent || (slice.kind === 'expert' ? 'expert' : 'worker'),
200
+ effort: slice.kind === 'expert' ? 'max' : 'low'
201
+ }))
202
+ : [{ id: 'G1', sliceIds: covered, role: 'expert', effort: 'max' }];
203
+ const baseline = {
204
+ id: 'baseline',
205
+ summary: sliceIds.length > 0
206
+ ? `Execute every required slice with ${workerCount} permitted workers.`
207
+ : `Execute the automatic parent-owned work with ${workerCount} permitted workers. The parent still decomposes slices.`,
208
+ sliceIds: covered,
209
+ groups: baselineGroups,
210
+ workerCount,
211
+ requiredVerificationIds: verification
212
+ };
213
+ const grouped = {
214
+ id: 'grouped',
215
+ summary: sliceIds.length > 0
216
+ ? 'Execute every required slice in one permitted worker group, preserving full coverage.'
217
+ : 'Execute the automatic parent-owned work in one permitted worker group. The parent still decomposes slices.',
218
+ sliceIds: covered,
219
+ groups: [{ id: 'G1', sliceIds: covered, role: 'expert', effort: 'max' }],
220
+ workerCount: 1,
221
+ requiredVerificationIds: verification
222
+ };
223
+ const candidates = [baseline, grouped].filter((candidate) => validatePlanCoverage(covered, candidate).length === 0);
224
+ const workerCounts = new Set(candidates.map((candidate) => candidate.workerCount));
225
+ if (candidates.length < 2 || workerCounts.size < 2)
226
+ return [];
227
+ return candidates;
228
+ }
229
+ function keep(input, reason, started) {
230
+ const compiled = {
231
+ kind: 'keep_baseline',
232
+ binding: {
233
+ projectId: sha256(input.root).slice(0, 32),
234
+ workflowRunId: input.workflowRunId,
235
+ workflowRevision: input.workflowRevision,
236
+ sourceDigest: 'uncomputed',
237
+ graphDigest: input.attention?.snapshot_hash ?? null,
238
+ candidateDigest: 'none',
239
+ questionDigest: 'none',
240
+ policyRevision: POLICY_REVISION,
241
+ requestedModel: DESIGN_DEFAULTS.model
242
+ },
243
+ reason,
244
+ usage: { ...UNKNOWN_USAGE }
245
+ };
246
+ return {
247
+ compiled,
248
+ receipt: buildDecisionReceipt({
249
+ binding: compiled.binding,
250
+ compiled,
251
+ result: 'kept_baseline',
252
+ reason,
253
+ elapsedMs: Date.now() - started
254
+ }),
255
+ attention: input.attention,
256
+ selectedPlan: null,
257
+ selectedRouting: null,
258
+ selectedContextIds: null,
259
+ llmRejudgeCalls: 0,
260
+ bundle: null
261
+ };
262
+ }
@@ -0,0 +1,270 @@
1
+ import { officialSubagentLifecycleLockHeld } from '../subagents/official-subagent-lock.js';
2
+ import { resolveOpenRouterApiKey } from '../providers/openrouter/openrouter-secret-store.js';
3
+ import { redactOpenRouterSecrets, redactOpenRouterString } from '../security/redact-secrets.js';
4
+ import { decodeUsage, decodeWireResponse } from './policy.js';
5
+ import { DESIGN_DEFAULTS, UNKNOWN_USAGE } from './types.js';
6
+ export const OPENROUTER_DECISIONS_ENDPOINT = DESIGN_DEFAULTS.endpoint;
7
+ export const OPENROUTER_DECISIONS_MODEL = DESIGN_DEFAULTS.model;
8
+ const inflight = { count: 0 };
9
+ const circuit = {
10
+ failures: 0,
11
+ openedAt: 0,
12
+ halfOpen: false
13
+ };
14
+ const memo = new Map();
15
+ export function resetDecisionTransportState() {
16
+ inflight.count = 0;
17
+ circuit.failures = 0;
18
+ circuit.openedAt = 0;
19
+ circuit.halfOpen = false;
20
+ memo.clear();
21
+ }
22
+ export async function requestOpenRouterDecision(bundle, options = {}) {
23
+ if (officialSubagentLifecycleLockHeld()) {
24
+ return fail('transport_error', 'fetch_while_lifecycle_lock_held', null);
25
+ }
26
+ const request = bundle.request;
27
+ const encoded = encodeRequest(request);
28
+ if (!encoded.ok)
29
+ return fail(encoded.reason, encoded.detail, null);
30
+ if (inflight.count >= DESIGN_DEFAULTS.maxInFlight) {
31
+ return fail('busy', 'max_in_flight', null);
32
+ }
33
+ const now = options.now || Date.now;
34
+ const circuitState = inspectCircuit(now());
35
+ if (circuitState === 'open')
36
+ return fail('circuit_open', 'circuit_open', null);
37
+ const memoKey = encoded.body;
38
+ const cached = memo.get(memoKey);
39
+ if (cached) {
40
+ const decoded = decodeWireResponse(bundle, cached.response);
41
+ if (decoded.ok) {
42
+ return {
43
+ ok: true,
44
+ response: decoded.response,
45
+ usage: {
46
+ inputTokens: decoded.response.usage.input_tokens,
47
+ outputTokens: decoded.response.usage.output_tokens,
48
+ reportedCost: decoded.response.usage.cost === undefined ? null : decoded.response.usage.cost,
49
+ evidence: 'provider_response'
50
+ },
51
+ resolvedModel: decoded.response.model,
52
+ responseId: decoded.response.id || null,
53
+ cacheHit: true,
54
+ status: 200
55
+ };
56
+ }
57
+ }
58
+ const resolved = await resolveOpenRouterApiKey({ env: options.env || process.env });
59
+ if (!resolved.key)
60
+ return fail('missing_key', resolved.blockers.join(',') || 'missing_openrouter_key', null);
61
+ const controller = new AbortController();
62
+ const parent = options.signal;
63
+ const onAbort = () => controller.abort(parent?.reason);
64
+ if (parent) {
65
+ if (parent.aborted)
66
+ controller.abort(parent.reason);
67
+ else
68
+ parent.addEventListener('abort', onAbort, { once: true });
69
+ }
70
+ const deadlineMs = options.deadlineMs ?? DESIGN_DEFAULTS.deadlineMs;
71
+ const timer = setTimeout(() => controller.abort(Object.assign(new Error('deadline'), { name: 'TimeoutError' })), deadlineMs);
72
+ inflight.count += 1;
73
+ try {
74
+ const fetchImpl = options.fetchImpl || globalThis.fetch;
75
+ const response = await fetchImpl(OPENROUTER_DECISIONS_ENDPOINT, {
76
+ method: 'POST',
77
+ redirect: 'error',
78
+ headers: {
79
+ Authorization: `Bearer ${resolved.key}`,
80
+ 'Content-Type': 'application/json',
81
+ Accept: 'application/json'
82
+ },
83
+ body: encoded.body,
84
+ signal: controller.signal
85
+ });
86
+ const status = response.status;
87
+ const raw = await readBoundedBody(response, DESIGN_DEFAULTS.maxResponseBytes, controller.signal);
88
+ if (!raw.ok)
89
+ return recordFailure(status, raw.reason, raw.detail, now());
90
+ if (status === 401)
91
+ return recordAuthFailure('unauthorized', status, raw.text);
92
+ if (status === 402)
93
+ return recordAuthFailure('payment_required', status, raw.text);
94
+ if (status === 403)
95
+ return recordAuthFailure(privacyUnavailable(raw.text) ? 'privacy_unavailable' : 'unauthorized', status, raw.text);
96
+ if (status === 404)
97
+ return recordAuthFailure('capability_unavailable', status, raw.text);
98
+ if (status === 413)
99
+ return recordAuthFailure('budget_exceeded', status, raw.text);
100
+ if (status === 429)
101
+ return recordFailure(status, 'rate_limited', raw.text, now());
102
+ if (status >= 500)
103
+ return recordFailure(status, 'transport_error', raw.text, now());
104
+ if (status !== 200)
105
+ return recordFailure(status, 'transport_error', raw.text, now());
106
+ let parsed;
107
+ try {
108
+ parsed = JSON.parse(raw.text);
109
+ }
110
+ catch {
111
+ return recordFailure(status, 'invalid_response', 'malformed_json', now());
112
+ }
113
+ const decoded = decodeWireResponse(bundle, parsed);
114
+ if (!decoded.ok)
115
+ return recordFailure(status, decoded.reason, 'schema_rejected', now());
116
+ remember(memoKey, decoded.response);
117
+ circuit.failures = 0;
118
+ circuit.halfOpen = false;
119
+ return {
120
+ ok: true,
121
+ response: decoded.response,
122
+ usage: {
123
+ inputTokens: decoded.response.usage.input_tokens,
124
+ outputTokens: decoded.response.usage.output_tokens,
125
+ reportedCost: decoded.response.usage.cost === undefined ? null : decoded.response.usage.cost,
126
+ evidence: 'provider_response'
127
+ },
128
+ resolvedModel: decoded.response.model,
129
+ responseId: decoded.response.id || null,
130
+ cacheHit: false,
131
+ status: 200
132
+ };
133
+ }
134
+ catch (error) {
135
+ const aborted = controller.signal.aborted || (error instanceof Error && error.name === 'AbortError');
136
+ const timeout = error instanceof Error && (error.name === 'TimeoutError' || /deadline/i.test(error.message));
137
+ if (timeout)
138
+ return recordFailure(null, 'deadline', 'deadline', now(), UNKNOWN_USAGE);
139
+ if (aborted)
140
+ return fail('cancelled', 'cancelled', null, UNKNOWN_USAGE);
141
+ if (isRedirectError(error))
142
+ return fail('transport_error', 'redirect_refused', null);
143
+ return recordFailure(null, 'transport_error', error instanceof Error ? error.message : String(error), now());
144
+ }
145
+ finally {
146
+ clearTimeout(timer);
147
+ if (parent)
148
+ parent.removeEventListener('abort', onAbort);
149
+ inflight.count = Math.max(0, inflight.count - 1);
150
+ }
151
+ }
152
+ export function encodeRequest(request) {
153
+ if (request.model !== DESIGN_DEFAULTS.model) {
154
+ return { ok: false, reason: 'unknown_model', detail: request.model };
155
+ }
156
+ const questions = Object.keys(request.questions);
157
+ if (questions.length === 0)
158
+ return { ok: false, reason: 'invalid_response', detail: 'empty_questions' };
159
+ if (questions.length > DESIGN_DEFAULTS.maxQuestions) {
160
+ return { ok: false, reason: 'budget_exceeded', detail: 'max_questions' };
161
+ }
162
+ if (request.provider.zdr !== true
163
+ || request.provider.data_collection !== 'deny'
164
+ || request.provider.allow_fallbacks !== false) {
165
+ return { ok: false, reason: 'privacy_unavailable', detail: 'provider_policy' };
166
+ }
167
+ const body = JSON.stringify({
168
+ model: request.model,
169
+ state: request.state,
170
+ questions: request.questions,
171
+ provider: {
172
+ zdr: true,
173
+ data_collection: 'deny',
174
+ allow_fallbacks: false
175
+ },
176
+ ...(request.session_id ? { session_id: request.session_id } : {})
177
+ });
178
+ if (Buffer.byteLength(body, 'utf8') > DESIGN_DEFAULTS.maxRequestBytes) {
179
+ return { ok: false, reason: 'budget_exceeded', detail: 'max_request_bytes' };
180
+ }
181
+ return { ok: true, body };
182
+ }
183
+ export function redactDecisionValue(value, env = process.env) {
184
+ return redactOpenRouterSecrets(value, env);
185
+ }
186
+ export function redactDecisionText(value, env = process.env) {
187
+ return redactOpenRouterString(value, env);
188
+ }
189
+ async function readBoundedBody(response, maxBytes, signal) {
190
+ if (!response.body) {
191
+ const text = await response.text();
192
+ if (Buffer.byteLength(text, 'utf8') > maxBytes) {
193
+ return { ok: false, reason: 'budget_exceeded', detail: 'max_response_bytes' };
194
+ }
195
+ return { ok: true, text };
196
+ }
197
+ const reader = response.body.getReader();
198
+ const chunks = [];
199
+ let size = 0;
200
+ try {
201
+ while (true) {
202
+ if (signal.aborted)
203
+ return { ok: false, reason: 'cancelled', detail: 'cancelled' };
204
+ const { done, value } = await reader.read();
205
+ if (done)
206
+ break;
207
+ if (value) {
208
+ size += value.byteLength;
209
+ if (size > maxBytes) {
210
+ await reader.cancel();
211
+ return { ok: false, reason: 'budget_exceeded', detail: 'max_response_bytes' };
212
+ }
213
+ chunks.push(value);
214
+ }
215
+ }
216
+ }
217
+ catch (error) {
218
+ if (signal.aborted || (error instanceof Error && error.name === 'AbortError')) {
219
+ return { ok: false, reason: signal.reason instanceof Error && signal.reason.name === 'TimeoutError' ? 'deadline' : 'cancelled', detail: 'body_aborted' };
220
+ }
221
+ return { ok: false, reason: 'transport_error', detail: error instanceof Error ? error.message : String(error) };
222
+ }
223
+ finally {
224
+ reader.releaseLock();
225
+ }
226
+ return { ok: true, text: Buffer.concat(chunks.map((chunk) => Buffer.from(chunk))).toString('utf8') };
227
+ }
228
+ function inspectCircuit(now) {
229
+ if (circuit.failures < DESIGN_DEFAULTS.circuitFailures)
230
+ return 'closed';
231
+ if (now - circuit.openedAt < DESIGN_DEFAULTS.circuitOpenMs)
232
+ return 'open';
233
+ if (circuit.halfOpen)
234
+ return 'open';
235
+ circuit.halfOpen = true;
236
+ return 'half_open';
237
+ }
238
+ function recordFailure(status, reason, detail, now, usage = UNKNOWN_USAGE) {
239
+ circuit.failures += 1;
240
+ if (circuit.failures >= DESIGN_DEFAULTS.circuitFailures && circuit.openedAt === 0) {
241
+ circuit.openedAt = now;
242
+ }
243
+ if (circuit.halfOpen && reason !== 'circuit_open') {
244
+ circuit.openedAt = now;
245
+ circuit.halfOpen = false;
246
+ }
247
+ return fail(reason, detail, status, usage);
248
+ }
249
+ function recordAuthFailure(reason, status, detail) {
250
+ return fail(reason, detail, status);
251
+ }
252
+ function fail(reason, detail, status, usage = UNKNOWN_USAGE) {
253
+ return { ok: false, reason, usage, status, detail, cacheHit: false };
254
+ }
255
+ function remember(key, response) {
256
+ if (memo.size >= DESIGN_DEFAULTS.memoEntries) {
257
+ const oldest = memo.keys().next().value;
258
+ if (oldest !== undefined)
259
+ memo.delete(oldest);
260
+ }
261
+ memo.set(key, { response, storedAt: Date.now() });
262
+ }
263
+ function privacyUnavailable(text) {
264
+ return /zdr|data_collection|allow_fallbacks|privacy|data collection/i.test(text);
265
+ }
266
+ function isRedirectError(error) {
267
+ const message = error instanceof Error ? error.message : String(error);
268
+ return /redirect/i.test(message);
269
+ }
270
+ export { decodeUsage };