avorelo 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -16
- package/README.md +91 -51
- package/bin/avorelo.mjs +7 -0
- package/dist/avorelo.mjs +14337 -0
- package/package.json +106 -120
- package/bin/avorelo +0 -9
- package/scripts/README.md +0 -40
- package/scripts/cco-dashboard.js +0 -252
- package/scripts/cco-status.js +0 -430
- package/scripts/lib/activation/account-state.js +0 -37
- package/scripts/lib/activation/activation-runner.js +0 -546
- package/scripts/lib/activation/activation-self-healing.js +0 -480
- package/scripts/lib/activation/activation-state.js +0 -83
- package/scripts/lib/activation/activation-summary.js +0 -191
- package/scripts/lib/activation/adapters/claude-code.js +0 -77
- package/scripts/lib/activation/adapters/codex-cli.js +0 -52
- package/scripts/lib/activation/adapters/cursor.js +0 -37
- package/scripts/lib/activation/adapters/github-agent.js +0 -39
- package/scripts/lib/activation/adapters/terminal.js +0 -42
- package/scripts/lib/activation/adapters/vscode.js +0 -39
- package/scripts/lib/activation/adapters/windsurf.js +0 -37
- package/scripts/lib/activation/ai-surface-detector.js +0 -151
- package/scripts/lib/activation/connect-account.js +0 -145
- package/scripts/lib/activation/detect-environment.js +0 -75
- package/scripts/lib/activation/detect-hosts.js +0 -62
- package/scripts/lib/activation/format-activation-output.js +0 -109
- package/scripts/lib/activation/next-action.js +0 -43
- package/scripts/lib/activation/repair-engine.js +0 -219
- package/scripts/lib/activation-distribution-readiness.js +0 -507
- package/scripts/lib/adapter-conformance.js +0 -176
- package/scripts/lib/adapter-readiness.js +0 -417
- package/scripts/lib/adapter-safety-boundaries.js +0 -335
- package/scripts/lib/adapter-technical-readiness-gate.js +0 -205
- package/scripts/lib/agent-access-governance.js +0 -455
- package/scripts/lib/agent-enforcement.js +0 -765
- package/scripts/lib/agent-policy-profile.js +0 -210
- package/scripts/lib/agent-security/action-evaluator.js +0 -507
- package/scripts/lib/agent-security/adapter-registry.js +0 -98
- package/scripts/lib/agent-security/auto-policy.js +0 -139
- package/scripts/lib/agent-security/bounded-scan.js +0 -93
- package/scripts/lib/agent-security/enforcement-adapter.js +0 -174
- package/scripts/lib/agent-security/enforcement-engine.js +0 -1129
- package/scripts/lib/agent-security/file-write-adapter.js +0 -183
- package/scripts/lib/agent-security/file-write-rules.js +0 -178
- package/scripts/lib/agent-security/index.js +0 -3342
- package/scripts/lib/agent-security/instruction-risk.js +0 -181
- package/scripts/lib/agent-security/mcp-action-adapter.js +0 -185
- package/scripts/lib/agent-security/mcp-action-rules.js +0 -184
- package/scripts/lib/agent-security/package-action-adapter.js +0 -175
- package/scripts/lib/agent-security/package-action-rules.js +0 -233
- package/scripts/lib/agent-security/performance.js +0 -148
- package/scripts/lib/agent-security/permission-minimizer.js +0 -403
- package/scripts/lib/agent-security/scan-cache.js +0 -74
- package/scripts/lib/agent-security/source-trust.js +0 -146
- package/scripts/lib/ai-install-prompt.js +0 -288
- package/scripts/lib/ai-workspace-hygiene.js +0 -1499
- package/scripts/lib/alpha-activation.js +0 -520
- package/scripts/lib/alpha-feedback.js +0 -263
- package/scripts/lib/alpha-readiness-gate.js +0 -332
- package/scripts/lib/anti-gaming.js +0 -169
- package/scripts/lib/artifact-health.js +0 -431
- package/scripts/lib/attribution.js +0 -180
- package/scripts/lib/audit.js +0 -289
- package/scripts/lib/avorelo-skill-registry.js +0 -810
- package/scripts/lib/batch-jobs.js +0 -71
- package/scripts/lib/brain-pack.js +0 -578
- package/scripts/lib/brand-boundary.js +0 -424
- package/scripts/lib/brand.js +0 -74
- package/scripts/lib/browser-capability.js +0 -1048
- package/scripts/lib/browser-proof-preflight.js +0 -321
- package/scripts/lib/cache-readiness.js +0 -187
- package/scripts/lib/canonical-reentry.js +0 -162
- package/scripts/lib/capability-packs.js +0 -314
- package/scripts/lib/capability-recommender.js +0 -512
- package/scripts/lib/capability-registry.js +0 -1059
- package/scripts/lib/carry-forward-surfacing.js +0 -194
- package/scripts/lib/ccusage-adapter.js +0 -188
- package/scripts/lib/company-loop.js +0 -1149
- package/scripts/lib/config.js +0 -637
- package/scripts/lib/context-acquisition-plan.js +0 -287
- package/scripts/lib/context-budget-guard.js +0 -170
- package/scripts/lib/context-budget-scanner.js +0 -257
- package/scripts/lib/context-optimizer.js +0 -715
- package/scripts/lib/context-reduction-plan.js +0 -178
- package/scripts/lib/context-safety.js +0 -88
- package/scripts/lib/context-savings-engine.js +0 -158
- package/scripts/lib/cost-evidence.js +0 -254
- package/scripts/lib/cross-host-install-plan.js +0 -308
- package/scripts/lib/cross-host-install-readiness.js +0 -237
- package/scripts/lib/cross-host-value-flow.js +0 -268
- package/scripts/lib/dashboard.js +0 -900
- package/scripts/lib/design-partner-feedback.js +0 -346
- package/scripts/lib/entitlements.js +0 -100
- package/scripts/lib/execution-packet.js +0 -559
- package/scripts/lib/experimentation-events.js +0 -547
- package/scripts/lib/external-capability-compliance.js +0 -107
- package/scripts/lib/external-user-simulation.js +0 -166
- package/scripts/lib/failure-recovery-readiness.js +0 -81
- package/scripts/lib/failure-recovery.js +0 -419
- package/scripts/lib/feedback-intelligence.js +0 -537
- package/scripts/lib/feedback-signals.js +0 -205
- package/scripts/lib/file-integrity.js +0 -68
- package/scripts/lib/fsx.js +0 -127
- package/scripts/lib/full-readiness-gate.js +0 -451
- package/scripts/lib/guidance-builder.js +0 -174
- package/scripts/lib/hook-apply.js +0 -1019
- package/scripts/lib/hook-baseline.js +0 -310
- package/scripts/lib/hook-config-preview.js +0 -275
- package/scripts/lib/hook-contracts.js +0 -290
- package/scripts/lib/hook-safety-boundary-readiness.js +0 -80
- package/scripts/lib/host-capability-matrix.js +0 -351
- package/scripts/lib/host-support-context.js +0 -254
- package/scripts/lib/http-hook-action.js +0 -538
- package/scripts/lib/install-ai-readiness.js +0 -84
- package/scripts/lib/install-intake-risk.js +0 -1037
- package/scripts/lib/install-journey-intelligence.js +0 -329
- package/scripts/lib/intervention-guidance.js +0 -57
- package/scripts/lib/known-limitations.js +0 -115
- package/scripts/lib/l8-path-truth.js +0 -146
- package/scripts/lib/launch-hardening-gate.js +0 -436
- package/scripts/lib/launch-readiness.js +0 -628
- package/scripts/lib/learning-memory.js +0 -686
- package/scripts/lib/lifecycle-hooks.js +0 -802
- package/scripts/lib/local-package-smoke.js +0 -423
- package/scripts/lib/local-pricing.js +0 -299
- package/scripts/lib/mcp-enforcement.js +0 -311
- package/scripts/lib/mcp-least-privilege-policy.js +0 -303
- package/scripts/lib/mcp-tool-inventory.js +0 -388
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +0 -335
- package/scripts/lib/metrics.js +0 -699
- package/scripts/lib/micro-proof.js +0 -133
- package/scripts/lib/next-run-context.js +0 -436
- package/scripts/lib/operating-value.js +0 -1648
- package/scripts/lib/optimization-v3.js +0 -122
- package/scripts/lib/orchestration/adapters/_shared.js +0 -49
- package/scripts/lib/orchestration/adapters/aider.js +0 -18
- package/scripts/lib/orchestration/adapters/claude-code.js +0 -35
- package/scripts/lib/orchestration/adapters/codex.js +0 -35
- package/scripts/lib/orchestration/adapters/gemini-cli.js +0 -18
- package/scripts/lib/orchestration/adapters/git.js +0 -25
- package/scripts/lib/orchestration/adapters/index.js +0 -31
- package/scripts/lib/orchestration/adapters/lm-studio.js +0 -18
- package/scripts/lib/orchestration/adapters/ollama.js +0 -18
- package/scripts/lib/orchestration/adapters/opencode.js +0 -18
- package/scripts/lib/orchestration/adapters/openrouter.js +0 -18
- package/scripts/lib/orchestration/adapters/test-runner.js +0 -25
- package/scripts/lib/orchestration/cli.js +0 -438
- package/scripts/lib/orchestration/execution-manager.js +0 -279
- package/scripts/lib/orchestration/handoff.js +0 -314
- package/scripts/lib/orchestration/index.js +0 -456
- package/scripts/lib/orchestration/inventory.js +0 -47
- package/scripts/lib/orchestration/model-discovery.js +0 -498
- package/scripts/lib/orchestration/model-profiler.js +0 -170
- package/scripts/lib/orchestration/model-profiles.js +0 -252
- package/scripts/lib/orchestration/model-refresh-policy.js +0 -72
- package/scripts/lib/orchestration/proof-writer.js +0 -349
- package/scripts/lib/orchestration/provider-discovery/aider.js +0 -49
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +0 -56
- package/scripts/lib/orchestration/provider-discovery/codex.js +0 -49
- package/scripts/lib/orchestration/provider-discovery/common.js +0 -186
- package/scripts/lib/orchestration/provider-discovery/gemini.js +0 -106
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +0 -118
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +0 -12
- package/scripts/lib/orchestration/provider-discovery/ollama.js +0 -100
- package/scripts/lib/orchestration/provider-discovery/opencode.js +0 -47
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +0 -44
- package/scripts/lib/orchestration/risk-classifier.js +0 -130
- package/scripts/lib/orchestration/routing-policy.js +0 -486
- package/scripts/lib/orchestration/settings.js +0 -112
- package/scripts/lib/orchestration/state.js +0 -165
- package/scripts/lib/orchestration/verification-manager.js +0 -138
- package/scripts/lib/output-profiles.js +0 -146
- package/scripts/lib/package-content-audit.js +0 -368
- package/scripts/lib/package-runtime.js +0 -278
- package/scripts/lib/plan-surface.js +0 -53
- package/scripts/lib/plans.js +0 -2318
- package/scripts/lib/policy-provider.js +0 -27
- package/scripts/lib/prelaunch-activation-readiness.js +0 -409
- package/scripts/lib/prelaunch-evidence-store.js +0 -816
- package/scripts/lib/prelaunch-intelligence.js +0 -869
- package/scripts/lib/pricing-experiment.js +0 -118
- package/scripts/lib/pro-moment-events.js +0 -77
- package/scripts/lib/pro-moment-state.js +0 -227
- package/scripts/lib/pro-moments.js +0 -1216
- package/scripts/lib/product-learning-events.js +0 -629
- package/scripts/lib/project-profile.js +0 -555
- package/scripts/lib/prompt-compiler.js +0 -280
- package/scripts/lib/prompt-lint.js +0 -32
- package/scripts/lib/prompt-suggestions.js +0 -52
- package/scripts/lib/proof-canonical.js +0 -398
- package/scripts/lib/proof-drilldown.js +0 -383
- package/scripts/lib/proof-events.js +0 -342
- package/scripts/lib/proof-history.js +0 -243
- package/scripts/lib/proof-metrics.js +0 -296
- package/scripts/lib/proof-outcome-evidence.js +0 -134
- package/scripts/lib/proof-receipt.js +0 -335
- package/scripts/lib/proof-record.js +0 -461
- package/scripts/lib/public-activation-distribution-gate.js +0 -258
- package/scripts/lib/public-cli.js +0 -3891
- package/scripts/lib/public-distribution-truth.js +0 -211
- package/scripts/lib/public-install-claim-checker.js +0 -294
- package/scripts/lib/publish-provenance-readiness.js +0 -283
- package/scripts/lib/readiness-delta.js +0 -218
- package/scripts/lib/readiness-evidence-closure.js +0 -196
- package/scripts/lib/reentry-memory-capture.js +0 -241
- package/scripts/lib/reentry-memory-retrieval.js +0 -302
- package/scripts/lib/reentry-memory-status.js +0 -146
- package/scripts/lib/reentry-memory-store.js +0 -178
- package/scripts/lib/reentry-state.js +0 -66
- package/scripts/lib/release-candidate-bundle.js +0 -166
- package/scripts/lib/remediation.js +0 -81
- package/scripts/lib/repo-map.js +0 -391
- package/scripts/lib/run-improvements-lifecycle.js +0 -330
- package/scripts/lib/run-improvements.js +0 -789
- package/scripts/lib/runtime-decision-policy.js +0 -387
- package/scripts/lib/safe-path-engine.js +0 -705
- package/scripts/lib/safe-run-controller.js +0 -887
- package/scripts/lib/score.js +0 -262
- package/scripts/lib/seamless-enforcement.js +0 -329
- package/scripts/lib/seamless-outcome.js +0 -689
- package/scripts/lib/seamless-reality-gate.js +0 -5043
- package/scripts/lib/security-risk-classifier.js +0 -511
- package/scripts/lib/security-scan.js +0 -384
- package/scripts/lib/session-context-optimizer.js +0 -1211
- package/scripts/lib/session-timing.js +0 -315
- package/scripts/lib/skill-hygiene.js +0 -805
- package/scripts/lib/skill-packs.js +0 -161
- package/scripts/lib/skills-operating-layer.js +0 -580
- package/scripts/lib/smart-work-routing.js +0 -768
- package/scripts/lib/source-catalog.js +0 -700
- package/scripts/lib/status-value-summary.js +0 -32
- package/scripts/lib/support-bundle.js +0 -578
- package/scripts/lib/task-continuation.js +0 -440
- package/scripts/lib/test-helpers.js +0 -15
- package/scripts/lib/tier.js +0 -38
- package/scripts/lib/token-context-quality-gate.js +0 -370
- package/scripts/lib/token-cost-capture.js +0 -187
- package/scripts/lib/token-cost-intelligence.js +0 -358
- package/scripts/lib/token-efficiency-evidence.js +0 -213
- package/scripts/lib/token-evidence.js +0 -699
- package/scripts/lib/tokenish.js +0 -17
- package/scripts/lib/tool-output-sandbox.js +0 -304
- package/scripts/lib/trust-audit.js +0 -136
- package/scripts/lib/unified-events.js +0 -396
- package/scripts/lib/upgrade-interruption-recovery.js +0 -407
- package/scripts/lib/usage-ledger.js +0 -201
- package/scripts/lib/value-ledger.js +0 -130
- package/scripts/lib/value-proof-calibration.js +0 -531
- package/scripts/lib/visual-qa.js +0 -231
- package/scripts/lib/voice-alpha.js +0 -29
- package/scripts/lib/work-aware-orchestration.js +0 -976
- package/scripts/lib/work-control-receipts.js +0 -577
- package/scripts/lib/work-ledger.js +0 -1123
- package/scripts/lib/work-panel-preview.js +0 -352
- package/scripts/lib/workflow-discipline.js +0 -280
- package/scripts/lib/workflow-signals.js +0 -419
- package/scripts/lib/workspace-map.js +0 -281
- package/scripts/lib/workspace-registry.js +0 -1367
- package/scripts/lib/workspace-resolver.js +0 -480
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { scanRepo, LARGE_FILE_TOKEN_THRESHOLD, LARGE_FOLDER_TOKEN_THRESHOLD } = require("./context-budget-scanner");
|
|
4
|
-
|
|
5
|
-
const SCHEMA_VERSION = "context-budget-reduction-plan.v1";
|
|
6
|
-
|
|
7
|
-
const VALID_MODES = ["let_wuz_reduce_context", "block_large_context", "allow_full_context"];
|
|
8
|
-
|
|
9
|
-
function normalizeMode(raw) {
|
|
10
|
-
return VALID_MODES.includes(raw) ? raw : "let_wuz_reduce_context";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function recommendForFile(entry, mode) {
|
|
14
|
-
const { kind, reasonCodes } = entry;
|
|
15
|
-
|
|
16
|
-
if (reasonCodes.includes("sensitive_context")) return "approval_required";
|
|
17
|
-
if (kind === "binary" || reasonCodes.includes("binary_or_unknown")) return "skip";
|
|
18
|
-
if (kind === "lockfile" || reasonCodes.includes("lockfile")) return "skip";
|
|
19
|
-
if (kind === "log" || reasonCodes.includes("log_file")) return "skip";
|
|
20
|
-
if (kind === "too_large" || reasonCodes.includes("too_large_to_read")) return "skip";
|
|
21
|
-
|
|
22
|
-
if (mode === "allow_full_context") return "read";
|
|
23
|
-
|
|
24
|
-
if (reasonCodes.includes("large_file")) return "summarize";
|
|
25
|
-
|
|
26
|
-
return "read";
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function generateReductionPlan(cwd, config = {}) {
|
|
30
|
-
const mode = normalizeMode(config.mode);
|
|
31
|
-
const profile = config.profile || "balanced";
|
|
32
|
-
const recommendedLimitTokens = Number.isFinite(Number(config.recommendedLimitTokens))
|
|
33
|
-
? Math.max(1000, Number(config.recommendedLimitTokens)) : 32000;
|
|
34
|
-
|
|
35
|
-
const scanConfig = {
|
|
36
|
-
maxFilesScanned: config.maxFilesScanned,
|
|
37
|
-
maxFileBytes: config.maxFileBytes,
|
|
38
|
-
maxDepth: config.maxDepth,
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const scanResult = scanRepo(cwd, scanConfig);
|
|
42
|
-
|
|
43
|
-
const approvalRequired = [];
|
|
44
|
-
const excludedOrReduced = [];
|
|
45
|
-
const recommendedContext = [];
|
|
46
|
-
const hotspots = [];
|
|
47
|
-
|
|
48
|
-
let estimatedTotalTokens = 0;
|
|
49
|
-
let estimatedReductionTokens = 0;
|
|
50
|
-
|
|
51
|
-
for (const entry of scanResult.fileEntries) {
|
|
52
|
-
estimatedTotalTokens += entry.estimatedTokens;
|
|
53
|
-
const recommendation = recommendForFile(entry, mode);
|
|
54
|
-
|
|
55
|
-
const planEntry = {
|
|
56
|
-
path: entry.path,
|
|
57
|
-
estimatedTokens: entry.estimatedTokens,
|
|
58
|
-
recommendation,
|
|
59
|
-
reasonCodes: entry.reasonCodes,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
if (recommendation === "approval_required") {
|
|
63
|
-
approvalRequired.push(planEntry);
|
|
64
|
-
estimatedReductionTokens += entry.estimatedTokens;
|
|
65
|
-
} else if (recommendation === "skip" || recommendation === "ignore_generated") {
|
|
66
|
-
excludedOrReduced.push(planEntry);
|
|
67
|
-
estimatedReductionTokens += entry.estimatedTokens;
|
|
68
|
-
} else if (recommendation === "summarize") {
|
|
69
|
-
excludedOrReduced.push(planEntry);
|
|
70
|
-
estimatedReductionTokens += Math.floor(entry.estimatedTokens * 0.8);
|
|
71
|
-
} else {
|
|
72
|
-
recommendedContext.push(planEntry);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (entry.estimatedTokens >= LARGE_FILE_TOKEN_THRESHOLD) {
|
|
76
|
-
hotspots.push({
|
|
77
|
-
path: entry.path,
|
|
78
|
-
type: "file",
|
|
79
|
-
estimatedTokens: entry.estimatedTokens,
|
|
80
|
-
reasonCodes: entry.reasonCodes,
|
|
81
|
-
recommendation,
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const dir of scanResult.skippedDirs) {
|
|
87
|
-
excludedOrReduced.push({
|
|
88
|
-
path: dir.path,
|
|
89
|
-
estimatedTokens: 0,
|
|
90
|
-
recommendation: dir.recommendation,
|
|
91
|
-
reasonCodes: dir.reasonCodes,
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
for (const folder of scanResult.largeFolders) {
|
|
96
|
-
hotspots.push({
|
|
97
|
-
path: folder.path,
|
|
98
|
-
type: "folder",
|
|
99
|
-
estimatedTokens: folder.estimatedTokens,
|
|
100
|
-
reasonCodes: ["large_folder"],
|
|
101
|
-
recommendation: "summarize",
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
hotspots.sort((a, b) => b.estimatedTokens - a.estimatedTokens);
|
|
106
|
-
|
|
107
|
-
const overBudget = estimatedTotalTokens > recommendedLimitTokens;
|
|
108
|
-
const estimatedReductionPercent = estimatedTotalTokens > 0
|
|
109
|
-
? Math.round((estimatedReductionTokens / estimatedTotalTokens) * 100)
|
|
110
|
-
: 0;
|
|
111
|
-
|
|
112
|
-
const overBudgetCodes = [];
|
|
113
|
-
if (overBudget) overBudgetCodes.push("context_over_budget");
|
|
114
|
-
|
|
115
|
-
const safeBecause = buildSafeBecause(scanResult, excludedOrReduced, recommendedContext, approvalRequired);
|
|
116
|
-
|
|
117
|
-
let nextBestAction = "No context reduction needed — estimated context is within budget";
|
|
118
|
-
if (overBudget && mode === "let_wuz_reduce_context") {
|
|
119
|
-
nextBestAction = "Let Wuz reduce context before the next agent run";
|
|
120
|
-
} else if (overBudget && mode === "block_large_context") {
|
|
121
|
-
nextBestAction = "Context exceeds budget — agent run is blocked until context is reduced";
|
|
122
|
-
} else if (overBudget) {
|
|
123
|
-
nextBestAction = "Context exceeds budget — consider applying the reduction plan";
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
schemaVersion: SCHEMA_VERSION,
|
|
128
|
-
cwd,
|
|
129
|
-
generatedAt: new Date().toISOString(),
|
|
130
|
-
mode,
|
|
131
|
-
profile,
|
|
132
|
-
budget: {
|
|
133
|
-
estimatedTotalTokens,
|
|
134
|
-
recommendedLimitTokens,
|
|
135
|
-
overBudget,
|
|
136
|
-
estimatedReductionTokens,
|
|
137
|
-
estimatedReductionPercent,
|
|
138
|
-
scannedFiles: scanResult.scannedFiles,
|
|
139
|
-
skippedFiles: scanResult.skippedFiles,
|
|
140
|
-
overBudgetReasonCodes: overBudgetCodes,
|
|
141
|
-
},
|
|
142
|
-
hotspots: hotspots.slice(0, 20),
|
|
143
|
-
recommendedContext: recommendedContext.sort((a, b) => b.estimatedTokens - a.estimatedTokens).slice(0, 100),
|
|
144
|
-
excludedOrReduced: excludedOrReduced.slice(0, 100),
|
|
145
|
-
approvalRequired,
|
|
146
|
-
proof: { safeBecause },
|
|
147
|
-
nextBestAction,
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function buildSafeBecause(scanResult, excludedOrReduced, recommendedContext, approvalRequired) {
|
|
152
|
-
const reasons = [];
|
|
153
|
-
|
|
154
|
-
const hasSkippedDeps = scanResult.skippedDirs.some((d) => d.reasonCodes.includes("dependency_folder"));
|
|
155
|
-
const hasSkippedGenerated = scanResult.skippedDirs.some((d) => d.reasonCodes.includes("generated_or_build_artifact"));
|
|
156
|
-
const hasSummarized = excludedOrReduced.some((e) => e.recommendation === "summarize");
|
|
157
|
-
const hasSkippedBinary = excludedOrReduced.some((e) => e.reasonCodes.includes("binary_or_unknown"));
|
|
158
|
-
const hasApprovalRequired = approvalRequired.length > 0;
|
|
159
|
-
const hasSmallSourceFiles = recommendedContext.some(
|
|
160
|
-
(e) => e.reasonCodes.some((c) => ["source_file", "config_file", "documentation_file"].includes(c))
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
if (hasSkippedDeps) reasons.push("Dependency and vendor folders were excluded");
|
|
164
|
-
if (hasSkippedGenerated) reasons.push("Generated and build artifact folders were excluded");
|
|
165
|
-
if (hasSummarized) reasons.push("Large source files were converted to summarize recommendations");
|
|
166
|
-
if (hasSkippedBinary) reasons.push("Binary and media files were excluded");
|
|
167
|
-
if (hasSmallSourceFiles) reasons.push("Small source, config, and documentation files were prioritized for full read");
|
|
168
|
-
if (hasApprovalRequired) reasons.push("Sensitive-looking files require approval before use");
|
|
169
|
-
if (reasons.length === 0) reasons.push("Scan completed — no significant context pressure detected");
|
|
170
|
-
|
|
171
|
-
return reasons;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
module.exports = {
|
|
175
|
-
generateReductionPlan,
|
|
176
|
-
normalizeMode,
|
|
177
|
-
SCHEMA_VERSION,
|
|
178
|
-
};
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const fs = require("fs");
|
|
4
|
-
const path = require("path");
|
|
5
|
-
const { redactSensitive } = require("./reentry-memory-capture");
|
|
6
|
-
|
|
7
|
-
const SECRET_BASENAMES = new Set([
|
|
8
|
-
".env", ".env.local", ".env.production", ".env.staging", ".env.development",
|
|
9
|
-
".env.server", ".env.client", ".env.test",
|
|
10
|
-
"id_rsa", "id_ed25519", "id_ecdsa", "id_dsa",
|
|
11
|
-
"credentials", "credentials.json", "service-account.json",
|
|
12
|
-
".netrc", ".pgpass",
|
|
13
|
-
]);
|
|
14
|
-
|
|
15
|
-
const SECRET_EXTENSIONS = new Set([
|
|
16
|
-
".pem", ".key", ".p12", ".pfx", ".cer", ".crt", ".der",
|
|
17
|
-
".gpg", ".asc",
|
|
18
|
-
]);
|
|
19
|
-
|
|
20
|
-
const SECRET_PATH_FRAGMENTS = [
|
|
21
|
-
/[/\\]\.aws[/\\]/,
|
|
22
|
-
/[/\\]\.ssh[/\\]/,
|
|
23
|
-
/[/\\]\.gnupg[/\\]/,
|
|
24
|
-
/[/\\]\.config[/\\]gcloud[/\\]/,
|
|
25
|
-
/[/\\]\.kube[/\\]/,
|
|
26
|
-
/[/\\]secrets[/\\]/,
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
function isSecretLikePath(filePath) {
|
|
30
|
-
if (!filePath || typeof filePath !== "string") return false;
|
|
31
|
-
const base = path.basename(filePath).toLowerCase();
|
|
32
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
33
|
-
|
|
34
|
-
if (SECRET_BASENAMES.has(base)) return true;
|
|
35
|
-
if (SECRET_EXTENSIONS.has(ext)) return true;
|
|
36
|
-
if (/^\.env(\..+)?$/.test(base)) return true;
|
|
37
|
-
|
|
38
|
-
const normalized = filePath.replace(/\\/g, "/");
|
|
39
|
-
return SECRET_PATH_FRAGMENTS.some((re) => re.test(normalized));
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function isPathSafe(filePath, rootDir) {
|
|
43
|
-
if (!filePath || !rootDir) return false;
|
|
44
|
-
const resolved = path.resolve(rootDir, filePath);
|
|
45
|
-
const root = path.resolve(rootDir);
|
|
46
|
-
return resolved.startsWith(root + path.sep) || resolved === root;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function scrubSensitiveValues(text) {
|
|
50
|
-
return redactSensitive(text);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function detectCce(cwd) {
|
|
54
|
-
try {
|
|
55
|
-
if (fs.existsSync(path.join(cwd, ".context-engine.yaml"))) return true;
|
|
56
|
-
if (fs.existsSync(path.join(cwd, ".cce"))) return true;
|
|
57
|
-
const mcpPath = path.join(cwd, ".mcp.json");
|
|
58
|
-
if (fs.existsSync(mcpPath)) {
|
|
59
|
-
try {
|
|
60
|
-
const mcp = JSON.parse(fs.readFileSync(mcpPath, "utf8"));
|
|
61
|
-
const servers = mcp.mcpServers || mcp.servers || {};
|
|
62
|
-
if (Object.keys(servers).some((k) => k.toLowerCase().includes("context-engine") || k.toLowerCase().includes("cce"))) {
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
65
|
-
} catch {}
|
|
66
|
-
}
|
|
67
|
-
} catch {}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function buildContextSafetyStatus(cwd) {
|
|
72
|
-
const cceDetected = detectCce(cwd);
|
|
73
|
-
return {
|
|
74
|
-
cceDetected,
|
|
75
|
-
statusLine: cceDetected
|
|
76
|
-
? "Context provider: external CCE detected — Wuz is operating alongside it"
|
|
77
|
-
: null,
|
|
78
|
-
showInStatus: cceDetected,
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
module.exports = {
|
|
83
|
-
isSecretLikePath,
|
|
84
|
-
isPathSafe,
|
|
85
|
-
scrubSensitiveValues,
|
|
86
|
-
detectCce,
|
|
87
|
-
buildContextSafetyStatus,
|
|
88
|
-
};
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Context savings engine — thin helpers wired to real PRE_CONTEXT control points.
|
|
4
|
-
// Only emits savings events when Wuz actually intercepts output before agent/user sees it.
|
|
5
|
-
|
|
6
|
-
const crypto = require("crypto");
|
|
7
|
-
const { summarizeLargeObject } = require("./tokenish");
|
|
8
|
-
|
|
9
|
-
const BYTES_PER_TOKEN = 4;
|
|
10
|
-
const DEFAULT_MAX_REENTRY_TOKENS = 800;
|
|
11
|
-
const DEFAULT_MAX_EVIDENCE_ITEMS = 20;
|
|
12
|
-
|
|
13
|
-
function bytesToTokens(bytes) {
|
|
14
|
-
return Math.round(bytes / BYTES_PER_TOKEN);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Intercept large tool output — real PRE_CONTEXT for MCP tools, advisory for built-ins.
|
|
18
|
-
// ledger is a ValueLedger instance or null (caller provides; engine does not instantiate).
|
|
19
|
-
// Returns { summary, ledgerEvent } where summary is from tokenish or null.
|
|
20
|
-
function summarizeLargeOutput(obj, ledger, opts = {}) {
|
|
21
|
-
const summary = summarizeLargeObject(obj);
|
|
22
|
-
if (!summary || !summary.isLarge) {
|
|
23
|
-
return { summary: null, ledgerEvent: null };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const savedBytes = summary.bytes - Buffer.byteLength(summary.preview || "", "utf8");
|
|
27
|
-
const savedTokens = bytesToTokens(Math.max(0, savedBytes));
|
|
28
|
-
|
|
29
|
-
let ledgerEvent = null;
|
|
30
|
-
if (ledger) {
|
|
31
|
-
ledgerEvent = ledger.append({
|
|
32
|
-
bucket: "large_output_referenced",
|
|
33
|
-
sessionId: opts.sessionId || null,
|
|
34
|
-
label: `Large output intercepted: ${summary.bytes} bytes → preview`,
|
|
35
|
-
savedBytes: Math.max(0, savedBytes),
|
|
36
|
-
savedTokens,
|
|
37
|
-
notes: opts.toolName ? `tool=${opts.toolName}` : "large output stored out-of-band",
|
|
38
|
-
artifactRef: opts.artifactRef || null,
|
|
39
|
-
});
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return { summary, ledgerEvent };
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Check if content was recently emitted (deduplication guard).
|
|
46
|
-
// recentHashes is a Set<string> maintained by the caller across tool calls in a session.
|
|
47
|
-
// Pure function — no ledger side effect.
|
|
48
|
-
function suppressDuplicateContext(text, recentHashes) {
|
|
49
|
-
if (!text || typeof text !== "string") return { suppressed: false, hash: null };
|
|
50
|
-
const sample = text.slice(0, 500);
|
|
51
|
-
const hash = crypto.createHash("sha1").update(sample).digest("hex");
|
|
52
|
-
if (recentHashes && recentHashes.has(hash)) {
|
|
53
|
-
return { suppressed: true, hash };
|
|
54
|
-
}
|
|
55
|
-
if (recentHashes) recentHashes.add(hash);
|
|
56
|
-
return { suppressed: false, hash };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Bound reentry memory candidates to a token budget before session-start injection.
|
|
60
|
-
// This is real PRE_CONTEXT — we control what gets included in the hook output.
|
|
61
|
-
// ledger is a ValueLedger instance or null.
|
|
62
|
-
// Returns { selected, droppedCount, savedTokens }.
|
|
63
|
-
function selectCompactReentryContext(candidates, maxTokens, ledger, opts = {}) {
|
|
64
|
-
const limit = Number.isFinite(maxTokens) && maxTokens > 0 ? maxTokens : DEFAULT_MAX_REENTRY_TOKENS;
|
|
65
|
-
const all = Array.isArray(candidates) ? candidates : [];
|
|
66
|
-
|
|
67
|
-
if (all.length === 0) {
|
|
68
|
-
return { selected: [], droppedCount: 0, savedTokens: 0 };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const selected = [];
|
|
72
|
-
let accumulated = 0;
|
|
73
|
-
let droppedTokens = 0;
|
|
74
|
-
let droppedCount = 0;
|
|
75
|
-
|
|
76
|
-
for (const c of all) {
|
|
77
|
-
const tokens = Number.isFinite(c.estimatedTokens) ? c.estimatedTokens : 0;
|
|
78
|
-
if (accumulated + tokens <= limit) {
|
|
79
|
-
selected.push(c);
|
|
80
|
-
accumulated += tokens;
|
|
81
|
-
} else {
|
|
82
|
-
droppedCount++;
|
|
83
|
-
droppedTokens += tokens;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (ledger) {
|
|
88
|
-
if (selected.length > 0) {
|
|
89
|
-
// Structural bounding event: candidates within budget were selected.
|
|
90
|
-
// savedTokens=0 because candidates are in hook JSON metadata only;
|
|
91
|
-
// they are not injected as additionalContext yet.
|
|
92
|
-
ledger.append({
|
|
93
|
-
bucket: "reentry_reused",
|
|
94
|
-
sessionId: opts.sessionId || null,
|
|
95
|
-
label: `${selected.length} reentry candidates selected (within token budget)`,
|
|
96
|
-
savedBytes: 0,
|
|
97
|
-
savedTokens: 0,
|
|
98
|
-
notes: `selected=${selected.length}, ~${accumulated} tokens, structural-bounding-only`,
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
if (droppedCount > 0) {
|
|
102
|
-
// Structural bounding event: excess candidates were dropped from hook output.
|
|
103
|
-
// savedTokens=0 because these candidates were not being injected anyway;
|
|
104
|
-
// bounding the hook JSON does not prevent token injection to Claude.
|
|
105
|
-
ledger.append({
|
|
106
|
-
bucket: "context_avoided",
|
|
107
|
-
sessionId: opts.sessionId || null,
|
|
108
|
-
label: `${droppedCount} reentry candidates dropped from hook output (over token limit)`,
|
|
109
|
-
savedBytes: 0,
|
|
110
|
-
savedTokens: 0,
|
|
111
|
-
notes: `limit=${limit}, dropped=${droppedCount}, structural-bounding-only`,
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
return { selected, droppedCount, savedTokens: droppedTokens };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Trim evidence list to maxItems — pure, no ledger side effect.
|
|
120
|
-
// Returns { items, droppedCount }.
|
|
121
|
-
function compactEvidenceList(items, maxItems) {
|
|
122
|
-
const max = Number.isFinite(maxItems) && maxItems > 0 ? maxItems : DEFAULT_MAX_EVIDENCE_ITEMS;
|
|
123
|
-
const all = Array.isArray(items) ? items : [];
|
|
124
|
-
if (all.length <= max) return { items: all, droppedCount: 0 };
|
|
125
|
-
return { items: all.slice(0, max), droppedCount: all.length - max };
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Aggregate ledger events into a savings estimate.
|
|
129
|
-
// Pure — no I/O, no throws.
|
|
130
|
-
function safeEstimateSavings(events) {
|
|
131
|
-
if (!Array.isArray(events)) return { totalSavedBytes: 0, totalSavedTokens: 0, byBucket: {} };
|
|
132
|
-
let totalSavedBytes = 0;
|
|
133
|
-
let totalSavedTokens = 0;
|
|
134
|
-
const byBucket = {};
|
|
135
|
-
for (const e of events) {
|
|
136
|
-
if (!e || !e.bucket) continue;
|
|
137
|
-
if (!byBucket[e.bucket]) byBucket[e.bucket] = { count: 0, savedBytes: 0, savedTokens: 0 };
|
|
138
|
-
const eb = Number.isFinite(e.savedBytes) ? e.savedBytes : 0;
|
|
139
|
-
const et = Number.isFinite(e.savedTokens) ? e.savedTokens : 0;
|
|
140
|
-
byBucket[e.bucket].count++;
|
|
141
|
-
byBucket[e.bucket].savedBytes += eb;
|
|
142
|
-
byBucket[e.bucket].savedTokens += et;
|
|
143
|
-
totalSavedBytes += eb;
|
|
144
|
-
totalSavedTokens += et;
|
|
145
|
-
}
|
|
146
|
-
return { totalSavedBytes, totalSavedTokens, byBucket };
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
module.exports = {
|
|
150
|
-
summarizeLargeOutput,
|
|
151
|
-
suppressDuplicateContext,
|
|
152
|
-
selectCompactReentryContext,
|
|
153
|
-
compactEvidenceList,
|
|
154
|
-
safeEstimateSavings,
|
|
155
|
-
bytesToTokens,
|
|
156
|
-
DEFAULT_MAX_REENTRY_TOKENS,
|
|
157
|
-
DEFAULT_MAX_EVIDENCE_ITEMS,
|
|
158
|
-
};
|
|
@@ -1,254 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// Canonical cost-aware preference layer for active/near-active CJS surfaces.
|
|
4
|
-
// Safe rule:
|
|
5
|
-
// - Use cost-specific totals for cost displays/comparisons.
|
|
6
|
-
// - Fall back to measured import summaries only for measured cost/token display.
|
|
7
|
-
// - Never substitute legacy mixed-unit totals for currency comparisons.
|
|
8
|
-
|
|
9
|
-
function numberOrZero(value) {
|
|
10
|
-
const number = Number(value);
|
|
11
|
-
return Number.isFinite(number) ? number : 0;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function numberOrNull(value) {
|
|
15
|
-
const number = Number(value);
|
|
16
|
-
return Number.isFinite(number) ? number : null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function buildCostEvidenceSummary(params = {}) {
|
|
20
|
-
const aggregation = params.aggregation || {};
|
|
21
|
-
const weekly = aggregation.weeklyRepoSummary || {};
|
|
22
|
-
const measuredUsage = params.measuredUsage || null;
|
|
23
|
-
|
|
24
|
-
const aggregatedMeasuredCost = numberOrZero(weekly.measuredCostTotal);
|
|
25
|
-
const aggregatedCounterfactualCost = numberOrZero(weekly.estimatedCostCounterfactualTotal);
|
|
26
|
-
const aggregatedTokenUsage = numberOrZero(weekly.tokenUsageTotal);
|
|
27
|
-
|
|
28
|
-
const importedMeasuredCost = numberOrZero(measuredUsage?.totalCost);
|
|
29
|
-
const importedTokenUsage = numberOrZero(measuredUsage?.totalTokens);
|
|
30
|
-
|
|
31
|
-
const preferredMeasuredCost = aggregatedMeasuredCost > 0 ? aggregatedMeasuredCost : importedMeasuredCost;
|
|
32
|
-
const preferredEstimatedCostCounterfactual = aggregatedCounterfactualCost > 0 ? aggregatedCounterfactualCost : null;
|
|
33
|
-
const preferredTokenUsage = aggregatedTokenUsage > 0 ? aggregatedTokenUsage : importedTokenUsage;
|
|
34
|
-
|
|
35
|
-
const comparisonAvailable = preferredMeasuredCost > 0 && preferredEstimatedCostCounterfactual !== null;
|
|
36
|
-
const source = preferredMeasuredCost > 0 ? "measured" : "unavailable";
|
|
37
|
-
const basis = comparisonAvailable ? "cost-aware" : preferredMeasuredCost > 0 ? "measured-only" : "unavailable";
|
|
38
|
-
|
|
39
|
-
return {
|
|
40
|
-
measuredCostTotal: aggregatedMeasuredCost,
|
|
41
|
-
estimatedCostCounterfactualTotal: aggregatedCounterfactualCost,
|
|
42
|
-
tokenUsageTotal: aggregatedTokenUsage,
|
|
43
|
-
preferredMeasuredCost,
|
|
44
|
-
preferredEstimatedCostCounterfactual,
|
|
45
|
-
preferredTokenUsage,
|
|
46
|
-
source,
|
|
47
|
-
basis,
|
|
48
|
-
comparisonAvailable,
|
|
49
|
-
fallback:
|
|
50
|
-
aggregatedMeasuredCost <= 0 && importedMeasuredCost > 0
|
|
51
|
-
? "usage-import-summary"
|
|
52
|
-
: "outcome-aggregation",
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function buildImportedVsInternalDelta(params = {}) {
|
|
57
|
-
const summary = buildCostEvidenceSummary(params);
|
|
58
|
-
if (!summary.comparisonAvailable) return null;
|
|
59
|
-
|
|
60
|
-
const delta = Math.round((summary.preferredMeasuredCost - summary.preferredEstimatedCostCounterfactual) * 100) / 100;
|
|
61
|
-
return {
|
|
62
|
-
measuredCost: summary.preferredMeasuredCost,
|
|
63
|
-
internalCounterfactual: summary.preferredEstimatedCostCounterfactual,
|
|
64
|
-
delta,
|
|
65
|
-
unit: "currency",
|
|
66
|
-
basis: summary.basis,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// ── Cost Evidence v1 (avorelo.costEvidence.v1) ────────────────────────────────
|
|
71
|
-
//
|
|
72
|
-
// Builds a structured cost evidence object from local pricing + token evidence.
|
|
73
|
-
// Rules:
|
|
74
|
-
// - No pricing source → all cost fields null
|
|
75
|
-
// - Token estimate only → confidence cannot be high
|
|
76
|
-
// - websiteAllowed / pricingAllowed always false for one-off estimated values
|
|
77
|
-
// - Never show $0 saved unless truly measured zero
|
|
78
|
-
// - No network fetch. No billing/account integration.
|
|
79
|
-
|
|
80
|
-
const crypto = require("crypto");
|
|
81
|
-
|
|
82
|
-
function shortId() {
|
|
83
|
-
return crypto.randomBytes(4).toString("hex");
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function nowIso() {
|
|
87
|
-
return new Date().toISOString();
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function buildCostEvidenceV1(params = {}) {
|
|
91
|
-
const {
|
|
92
|
-
pricingSource = null,
|
|
93
|
-
pricingEntry = null,
|
|
94
|
-
tokenEvidence = null,
|
|
95
|
-
provider = null,
|
|
96
|
-
model = null,
|
|
97
|
-
} = params;
|
|
98
|
-
|
|
99
|
-
const costEvidenceId = `ce-${Date.now()}-${shortId()}`;
|
|
100
|
-
const createdAt = nowIso();
|
|
101
|
-
const currency = (pricingEntry && pricingEntry.currency) || "USD";
|
|
102
|
-
|
|
103
|
-
const hasPricing = pricingSource && pricingSource.available && pricingEntry !== null;
|
|
104
|
-
const hasTokens = tokenEvidence && tokenEvidence.measurements &&
|
|
105
|
-
tokenEvidence.measurements.candidateTokenReduction !== null;
|
|
106
|
-
|
|
107
|
-
if (!hasPricing) {
|
|
108
|
-
return {
|
|
109
|
-
schemaVersion: 1,
|
|
110
|
-
contract: "avorelo.costEvidence.v1",
|
|
111
|
-
costEvidenceId,
|
|
112
|
-
createdAt,
|
|
113
|
-
pricingSource: pricingSource ? pricingSource.source : "unavailable",
|
|
114
|
-
usageSource: "unavailable",
|
|
115
|
-
provider,
|
|
116
|
-
model,
|
|
117
|
-
currency,
|
|
118
|
-
tokenInputs: null,
|
|
119
|
-
costEstimate: null,
|
|
120
|
-
costAvoidedEstimate: null,
|
|
121
|
-
formula: null,
|
|
122
|
-
confidence: "unavailable",
|
|
123
|
-
valueClass: "unavailable",
|
|
124
|
-
caveats: [
|
|
125
|
-
"Cost estimate unavailable: no local pricing source found.",
|
|
126
|
-
"Run `avorelo pricing --init` to create a local pricing config.",
|
|
127
|
-
"Token reduction is available without a pricing source.",
|
|
128
|
-
],
|
|
129
|
-
pricingAllowed: false,
|
|
130
|
-
dashboardAllowed: false,
|
|
131
|
-
websiteAllowed: false,
|
|
132
|
-
redacted: true,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!hasTokens) {
|
|
137
|
-
return {
|
|
138
|
-
schemaVersion: 1,
|
|
139
|
-
contract: "avorelo.costEvidence.v1",
|
|
140
|
-
costEvidenceId,
|
|
141
|
-
createdAt,
|
|
142
|
-
pricingSource: pricingSource.source,
|
|
143
|
-
usageSource: "unavailable",
|
|
144
|
-
provider: pricingEntry.provider,
|
|
145
|
-
model: pricingEntry.model,
|
|
146
|
-
currency,
|
|
147
|
-
tokenInputs: null,
|
|
148
|
-
costEstimate: null,
|
|
149
|
-
costAvoidedEstimate: null,
|
|
150
|
-
formula: null,
|
|
151
|
-
confidence: "unavailable",
|
|
152
|
-
valueClass: "unavailable",
|
|
153
|
-
caveats: [
|
|
154
|
-
"Pricing source available but token evidence insufficient for cost estimate.",
|
|
155
|
-
"Run `avorelo token-evidence --json` to generate token evidence.",
|
|
156
|
-
],
|
|
157
|
-
pricingAllowed: false,
|
|
158
|
-
dashboardAllowed: false,
|
|
159
|
-
websiteAllowed: false,
|
|
160
|
-
redacted: true,
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const m = tokenEvidence.measurements;
|
|
165
|
-
const avoidedTokens = m.candidateTokenReduction;
|
|
166
|
-
|
|
167
|
-
// Cost calculation: avoided tokens × input token price (per 1M)
|
|
168
|
-
// Candidate context avoided = input tokens not sent
|
|
169
|
-
const pricePerMillion = numberOrNull(pricingEntry.inputTokenPrice);
|
|
170
|
-
if (pricePerMillion === null || avoidedTokens === null) {
|
|
171
|
-
return {
|
|
172
|
-
schemaVersion: 1,
|
|
173
|
-
contract: "avorelo.costEvidence.v1",
|
|
174
|
-
costEvidenceId,
|
|
175
|
-
createdAt,
|
|
176
|
-
pricingSource: pricingSource.source,
|
|
177
|
-
usageSource: "token_evidence",
|
|
178
|
-
provider: pricingEntry.provider,
|
|
179
|
-
model: pricingEntry.model,
|
|
180
|
-
currency,
|
|
181
|
-
tokenInputs: { avoidedCandidateTokens: avoidedTokens },
|
|
182
|
-
costEstimate: null,
|
|
183
|
-
costAvoidedEstimate: null,
|
|
184
|
-
formula: "candidate_token_reduction × input_price_per_1m_tokens",
|
|
185
|
-
confidence: "unavailable",
|
|
186
|
-
valueClass: "unavailable",
|
|
187
|
-
caveats: ["Could not compute cost: missing price or token count."],
|
|
188
|
-
pricingAllowed: false,
|
|
189
|
-
dashboardAllowed: false,
|
|
190
|
-
websiteAllowed: false,
|
|
191
|
-
redacted: true,
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const costAvoidedEstimate = Math.round((avoidedTokens / 1_000_000) * pricePerMillion * 10000) / 10000;
|
|
196
|
-
|
|
197
|
-
// Confidence: depends on token method and pricing source
|
|
198
|
-
// provider_usage + local pricing → medium
|
|
199
|
-
// local_tokenizer + local pricing → medium
|
|
200
|
-
// chars_div_4 + local pricing → low (unchanged)
|
|
201
|
-
const pricingConfidence = pricingSource.confidence || "low";
|
|
202
|
-
const tokenMethod = tokenEvidence.tokenizer ? (tokenEvidence.tokenizer.method || "chars_div_4") : "chars_div_4";
|
|
203
|
-
const tokenConfidence = tokenEvidence.tokenizer ? (tokenEvidence.tokenizer.confidence || "low") : "low";
|
|
204
|
-
const tokenIsStrongerThanCharsDiv4 = tokenMethod === "provider_usage" || tokenMethod === "local_tokenizer";
|
|
205
|
-
// If token evidence is stronger than chars_div_4, cost confidence can be medium even with default_local pricing
|
|
206
|
-
const overallConfidence = tokenIsStrongerThanCharsDiv4
|
|
207
|
-
? "medium"
|
|
208
|
-
: (pricingConfidence === "low" || tokenConfidence === "low" ? "low" : "medium");
|
|
209
|
-
|
|
210
|
-
const caveats = [
|
|
211
|
-
`Pricing source: ${pricingSource.caveat || pricingSource.source}`,
|
|
212
|
-
`Token method: ${tokenEvidence.tokenizer.method} — candidate context is estimated, not a measured model load.`,
|
|
213
|
-
"Cost estimate is based on avoided candidate context tokens, not actual API spend.",
|
|
214
|
-
"Do not use for billing reconciliation or exact cost claims.",
|
|
215
|
-
pricingEntry.caveat || null,
|
|
216
|
-
].filter(Boolean);
|
|
217
|
-
|
|
218
|
-
return {
|
|
219
|
-
schemaVersion: 1,
|
|
220
|
-
contract: "avorelo.costEvidence.v1",
|
|
221
|
-
costEvidenceId,
|
|
222
|
-
createdAt,
|
|
223
|
-
pricingSource: pricingSource.source,
|
|
224
|
-
usageSource: "token_evidence",
|
|
225
|
-
provider: pricingEntry.provider,
|
|
226
|
-
model: pricingEntry.model,
|
|
227
|
-
currency,
|
|
228
|
-
tokenInputs: {
|
|
229
|
-
inputTokens: m.inputTokens,
|
|
230
|
-
cachedInputTokens: m.cachedInputTokens,
|
|
231
|
-
outputTokens: m.outputTokens,
|
|
232
|
-
cacheWriteTokens: m.cacheWriteTokens,
|
|
233
|
-
cacheReadTokens: m.cacheReadTokens,
|
|
234
|
-
avoidedCandidateTokens: avoidedTokens,
|
|
235
|
-
},
|
|
236
|
-
costEstimate: null, // actual session cost not measurable without provider_usage
|
|
237
|
-
costAvoidedEstimate,
|
|
238
|
-
formula: `${avoidedTokens.toLocaleString()} avoided tokens ÷ 1,000,000 × $${pricePerMillion}/M input tokens`,
|
|
239
|
-
confidence: overallConfidence,
|
|
240
|
-
valueClass: "estimated",
|
|
241
|
-
caveats,
|
|
242
|
-
pricingAllowed: false,
|
|
243
|
-
dashboardAllowed: true,
|
|
244
|
-
websiteAllowed: false,
|
|
245
|
-
redacted: true,
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
module.exports = {
|
|
250
|
-
buildCostEvidenceSummary,
|
|
251
|
-
buildImportedVsInternalDelta,
|
|
252
|
-
buildCostEvidenceV1,
|
|
253
|
-
numberOrNull,
|
|
254
|
-
};
|