avorelo 0.1.0 → 0.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.
- package/LICENSE +23 -16
- package/README.md +90 -51
- package/bin/avorelo.mjs +7 -0
- package/dist/avorelo.mjs +19741 -0
- package/package.json +135 -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,699 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
// ── Token Evidence v2 ─────────────────────────────────────────────────────────
|
|
4
|
-
//
|
|
5
|
-
// Upgrades token estimation with a priority-ordered evidence chain:
|
|
6
|
-
// 1. provider_usage — reads local artifact files for actual token counts
|
|
7
|
-
// 2. local_tokenizer — uses gpt-tokenizer for text estimation
|
|
8
|
-
// 3. chars_div_4 — fallback always kept
|
|
9
|
-
//
|
|
10
|
-
// No network. No secrets. No raw prompts in output.
|
|
11
|
-
// chars_div_4 never removed. Activation still shows no savings.
|
|
12
|
-
|
|
13
|
-
const fs = require("fs");
|
|
14
|
-
const path = require("path");
|
|
15
|
-
const crypto = require("crypto");
|
|
16
|
-
|
|
17
|
-
const SCHEMA_VERSION = 1;
|
|
18
|
-
const CONTRACT = "avorelo.tokenEvidence.v1";
|
|
19
|
-
const TOKEN_EVIDENCE_DIR_REL = ".claude/cco/orchestration/token-evidence";
|
|
20
|
-
const TOKEN_EVIDENCE_LATEST_REL = ".claude/cco/orchestration/token-evidence/latest-token-evidence.json";
|
|
21
|
-
|
|
22
|
-
const CHARS_PER_TOKEN = 4;
|
|
23
|
-
const RAW_TOKEN_CAP = 50000;
|
|
24
|
-
|
|
25
|
-
// Receipt paths (mirrors operating-value.js receipt reader)
|
|
26
|
-
const WORKSPACE_REGISTRY_REL = ".claude/cco/orchestration/workspace-registry/latest-registry.json";
|
|
27
|
-
const SESSION_CONTEXT_REL = ".claude/cco/orchestration/session-context/latest-context.json";
|
|
28
|
-
const BRAIN_PACK_REL = ".claude/cco/context/brain-pack/latest.json";
|
|
29
|
-
const WORK_CONTROL_REL = ".claude/cco/orchestration/work-control/latest-receipt.json";
|
|
30
|
-
|
|
31
|
-
// Provider usage artifact paths to scan (safe local paths only)
|
|
32
|
-
const PROVIDER_USAGE_SCAN_PATHS = [
|
|
33
|
-
".claude/cco/events",
|
|
34
|
-
".avorelo/events",
|
|
35
|
-
];
|
|
36
|
-
const ORCHESTRATION_SCAN_PATHS = [
|
|
37
|
-
".claude/cco/orchestration",
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
// gpt-tokenizer vendor path (installed in scripts/vendor to avoid root npm permission issues)
|
|
41
|
-
const GPT_TOKENIZER_VENDOR_PATH = path.resolve(__dirname, "../vendor/node_modules/gpt-tokenizer/cjs/main.js");
|
|
42
|
-
|
|
43
|
-
function shortId() {
|
|
44
|
-
return crypto.randomBytes(4).toString("hex");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function nowIso() {
|
|
48
|
-
return new Date().toISOString();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function ensureDirFor(filePath) {
|
|
52
|
-
const dir = path.dirname(filePath);
|
|
53
|
-
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
function safeReadJson(filePath) {
|
|
57
|
-
try {
|
|
58
|
-
if (!fs.existsSync(filePath)) return null;
|
|
59
|
-
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
60
|
-
} catch {
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function fileCharLen(filePath) {
|
|
66
|
-
try {
|
|
67
|
-
if (!fs.existsSync(filePath)) return 0;
|
|
68
|
-
return fs.statSync(filePath).size;
|
|
69
|
-
} catch {
|
|
70
|
-
return 0;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function charsToTokens(chars) {
|
|
75
|
-
return Math.round(chars / CHARS_PER_TOKEN);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// ── Provider Usage Evidence Extraction ────────────────────────────────────────
|
|
79
|
-
// Scans safe local paths for token usage fields only.
|
|
80
|
-
// Returns numeric usage fields without any raw content.
|
|
81
|
-
|
|
82
|
-
const ANTHROPIC_USAGE_FIELDS = [
|
|
83
|
-
"input_tokens", "output_tokens",
|
|
84
|
-
"cache_read_input_tokens", "cache_creation_input_tokens",
|
|
85
|
-
];
|
|
86
|
-
const OPENAI_USAGE_FIELDS = [
|
|
87
|
-
"prompt_tokens", "completion_tokens", "total_tokens",
|
|
88
|
-
];
|
|
89
|
-
const GENERIC_USAGE_FIELDS = [
|
|
90
|
-
"usage.input_tokens", "usage.output_tokens",
|
|
91
|
-
"usage.prompt_tokens", "usage.completion_tokens",
|
|
92
|
-
];
|
|
93
|
-
|
|
94
|
-
function extractUsageFromObject(obj) {
|
|
95
|
-
if (!obj || typeof obj !== "object") return null;
|
|
96
|
-
|
|
97
|
-
const result = {};
|
|
98
|
-
let found = false;
|
|
99
|
-
|
|
100
|
-
// Anthropic-style
|
|
101
|
-
for (const field of ANTHROPIC_USAGE_FIELDS) {
|
|
102
|
-
if (typeof obj[field] === "number" && Number.isFinite(obj[field])) {
|
|
103
|
-
result[field] = obj[field];
|
|
104
|
-
found = true;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// OpenAI-style
|
|
109
|
-
for (const field of OPENAI_USAGE_FIELDS) {
|
|
110
|
-
if (typeof obj[field] === "number" && Number.isFinite(obj[field])) {
|
|
111
|
-
result[field] = obj[field];
|
|
112
|
-
found = true;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Nested usage object
|
|
117
|
-
if (obj.usage && typeof obj.usage === "object") {
|
|
118
|
-
for (const field of [...ANTHROPIC_USAGE_FIELDS, ...OPENAI_USAGE_FIELDS]) {
|
|
119
|
-
if (typeof obj.usage[field] === "number" && Number.isFinite(obj.usage[field])) {
|
|
120
|
-
result[`usage.${field}`] = obj.usage[field];
|
|
121
|
-
found = true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return found ? result : null;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function parseJsonlDefensively(content) {
|
|
130
|
-
const lines = content.split("\n");
|
|
131
|
-
const results = [];
|
|
132
|
-
for (const line of lines) {
|
|
133
|
-
const trimmed = line.trim();
|
|
134
|
-
if (!trimmed) continue;
|
|
135
|
-
try {
|
|
136
|
-
const parsed = JSON.parse(trimmed);
|
|
137
|
-
results.push(parsed);
|
|
138
|
-
} catch {
|
|
139
|
-
// Skip malformed lines
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
return results;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function collectProviderUsageEvidence(cwd) {
|
|
146
|
-
const sourcePaths = [];
|
|
147
|
-
const usageRecords = [];
|
|
148
|
-
|
|
149
|
-
// Scan .claude/cco/events and .avorelo/events for JSONL files
|
|
150
|
-
for (const dirRel of PROVIDER_USAGE_SCAN_PATHS) {
|
|
151
|
-
const dirAbs = path.resolve(cwd, dirRel);
|
|
152
|
-
if (!fs.existsSync(dirAbs)) continue;
|
|
153
|
-
let entries;
|
|
154
|
-
try { entries = fs.readdirSync(dirAbs); } catch { continue; }
|
|
155
|
-
|
|
156
|
-
for (const entry of entries) {
|
|
157
|
-
if (!entry.endsWith(".jsonl")) continue;
|
|
158
|
-
const fileAbs = path.join(dirAbs, entry);
|
|
159
|
-
const fileRel = path.relative(cwd, fileAbs).replace(/\\/g, "/");
|
|
160
|
-
let content;
|
|
161
|
-
try { content = fs.readFileSync(fileAbs, "utf8"); } catch { continue; }
|
|
162
|
-
const records = parseJsonlDefensively(content);
|
|
163
|
-
let fileHadUsage = false;
|
|
164
|
-
for (const record of records) {
|
|
165
|
-
const usage = extractUsageFromObject(record);
|
|
166
|
-
if (usage) {
|
|
167
|
-
usageRecords.push(usage);
|
|
168
|
-
fileHadUsage = true;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (fileHadUsage) sourcePaths.push(fileRel);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Scan .claude/cco/orchestration for JSON files
|
|
176
|
-
for (const dirRel of ORCHESTRATION_SCAN_PATHS) {
|
|
177
|
-
const dirAbs = path.resolve(cwd, dirRel);
|
|
178
|
-
if (!fs.existsSync(dirAbs)) continue;
|
|
179
|
-
scanDirForUsageJson(dirAbs, cwd, sourcePaths, usageRecords);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return { usageRecords, sourcePaths };
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function scanDirForUsageJson(dirAbs, cwd, sourcePaths, usageRecords, depth = 0) {
|
|
186
|
-
if (depth > 3) return; // limit recursion
|
|
187
|
-
let entries;
|
|
188
|
-
try { entries = fs.readdirSync(dirAbs); } catch { return; }
|
|
189
|
-
|
|
190
|
-
for (const entry of entries) {
|
|
191
|
-
const entryAbs = path.join(dirAbs, entry);
|
|
192
|
-
let stat;
|
|
193
|
-
try { stat = fs.statSync(entryAbs); } catch { continue; }
|
|
194
|
-
if (stat.isDirectory()) {
|
|
195
|
-
scanDirForUsageJson(entryAbs, cwd, sourcePaths, usageRecords, depth + 1);
|
|
196
|
-
} else if (entry.endsWith(".json")) {
|
|
197
|
-
const data = safeReadJson(entryAbs);
|
|
198
|
-
if (!data) continue;
|
|
199
|
-
const usage = extractUsageFromObject(data);
|
|
200
|
-
if (usage) {
|
|
201
|
-
const fileRel = path.relative(cwd, entryAbs).replace(/\\/g, "/");
|
|
202
|
-
usageRecords.push(usage);
|
|
203
|
-
sourcePaths.push(fileRel);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
function aggregateUsageRecords(records) {
|
|
210
|
-
if (!records || records.length === 0) return null;
|
|
211
|
-
|
|
212
|
-
// Prefer most recent / highest-count approach: sum all found records
|
|
213
|
-
// for candidate token counts; or just take the first record that has provider fields.
|
|
214
|
-
// For provider_usage, we report the summed total from all records.
|
|
215
|
-
let inputTokens = null;
|
|
216
|
-
let outputTokens = null;
|
|
217
|
-
let cacheReadInputTokens = null;
|
|
218
|
-
let cacheCreationInputTokens = null;
|
|
219
|
-
let promptTokens = null;
|
|
220
|
-
let completionTokens = null;
|
|
221
|
-
let totalTokens = null;
|
|
222
|
-
|
|
223
|
-
for (const rec of records) {
|
|
224
|
-
const add = (cur, val) => cur === null ? (typeof val === "number" ? val : null) : (typeof val === "number" ? cur + val : cur);
|
|
225
|
-
inputTokens = add(inputTokens, rec.input_tokens);
|
|
226
|
-
outputTokens = add(outputTokens, rec.output_tokens);
|
|
227
|
-
cacheReadInputTokens = add(cacheReadInputTokens, rec.cache_read_input_tokens);
|
|
228
|
-
cacheCreationInputTokens = add(cacheCreationInputTokens, rec.cache_creation_input_tokens);
|
|
229
|
-
promptTokens = add(promptTokens, rec.prompt_tokens);
|
|
230
|
-
completionTokens = add(completionTokens, rec.completion_tokens);
|
|
231
|
-
totalTokens = add(totalTokens, rec.total_tokens || rec["usage.total_tokens"]);
|
|
232
|
-
if (rec["usage.input_tokens"] !== undefined) inputTokens = add(inputTokens, rec["usage.input_tokens"]);
|
|
233
|
-
if (rec["usage.output_tokens"] !== undefined) outputTokens = add(outputTokens, rec["usage.output_tokens"]);
|
|
234
|
-
if (rec["usage.prompt_tokens"] !== undefined) promptTokens = add(promptTokens, rec["usage.prompt_tokens"]);
|
|
235
|
-
if (rec["usage.completion_tokens"] !== undefined) completionTokens = add(completionTokens, rec["usage.completion_tokens"]);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Normalize: map OpenAI to Anthropic-style if Anthropic not present
|
|
239
|
-
const normalizedInput = inputTokens !== null ? inputTokens : promptTokens;
|
|
240
|
-
const normalizedOutput = outputTokens !== null ? outputTokens : completionTokens;
|
|
241
|
-
const normalizedCacheRead = cacheReadInputTokens;
|
|
242
|
-
const normalizedCacheWrite = cacheCreationInputTokens;
|
|
243
|
-
|
|
244
|
-
// totalProviderTokens
|
|
245
|
-
let total = totalTokens;
|
|
246
|
-
if (total === null && normalizedInput !== null && normalizedOutput !== null) {
|
|
247
|
-
total = normalizedInput + normalizedOutput;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
const hasAny = normalizedInput !== null || normalizedOutput !== null || total !== null;
|
|
251
|
-
if (!hasAny) return null;
|
|
252
|
-
|
|
253
|
-
return {
|
|
254
|
-
inputTokens: normalizedInput,
|
|
255
|
-
outputTokens: normalizedOutput,
|
|
256
|
-
cachedInputTokens: normalizedCacheRead,
|
|
257
|
-
cacheWriteTokens: normalizedCacheWrite,
|
|
258
|
-
cacheReadTokens: normalizedCacheRead,
|
|
259
|
-
totalProviderTokens: total,
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// ── Local Tokenizer ───────────────────────────────────────────────────────────
|
|
264
|
-
// Uses gpt-tokenizer (pure TS, zero external deps) for text estimation.
|
|
265
|
-
// Claude models: confidence=medium, caveat present.
|
|
266
|
-
// Falls back gracefully if import fails.
|
|
267
|
-
|
|
268
|
-
const CLAUDE_MODEL_CAVEAT = "Local tokenizer estimate may differ from Claude billing tokenizer.";
|
|
269
|
-
|
|
270
|
-
function getTokenizerName(provider, model) {
|
|
271
|
-
if (!model) return "cl100k_base";
|
|
272
|
-
const m = model.toLowerCase();
|
|
273
|
-
if (m.includes("gpt-4") || m.includes("gpt4")) return "cl100k_base";
|
|
274
|
-
if (m.includes("gpt-3.5") || m.includes("gpt3.5")) return "cl100k_base";
|
|
275
|
-
if (m.includes("claude")) return "cl100k_base"; // Best approximation for Claude
|
|
276
|
-
return "cl100k_base"; // Default
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
function countTokensWithLocalTokenizer(text, { provider, model } = {}) {
|
|
280
|
-
const tokenizerName = getTokenizerName(provider, model);
|
|
281
|
-
const isClaudeModel = (model || "").toLowerCase().includes("claude");
|
|
282
|
-
const caveat = isClaudeModel ? CLAUDE_MODEL_CAVEAT : null;
|
|
283
|
-
|
|
284
|
-
try {
|
|
285
|
-
// Try main install first, then vendor path
|
|
286
|
-
let tokenizer = null;
|
|
287
|
-
try {
|
|
288
|
-
tokenizer = require("gpt-tokenizer");
|
|
289
|
-
} catch {
|
|
290
|
-
try {
|
|
291
|
-
tokenizer = require(GPT_TOKENIZER_VENDOR_PATH);
|
|
292
|
-
} catch {
|
|
293
|
-
return {
|
|
294
|
-
method: "chars_div_4",
|
|
295
|
-
tokens: charsToTokens(text.length),
|
|
296
|
-
tokenizerName: "chars_div_4",
|
|
297
|
-
model: model || null,
|
|
298
|
-
confidence: "low",
|
|
299
|
-
caveat: "gpt-tokenizer not available; fell back to chars÷4.",
|
|
300
|
-
fallbackReason: "gpt-tokenizer import failed",
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
const encodeFunction = tokenizer.encode || (tokenizer.default && tokenizer.default.encode);
|
|
306
|
-
if (typeof encodeFunction !== "function") {
|
|
307
|
-
return {
|
|
308
|
-
method: "chars_div_4",
|
|
309
|
-
tokens: charsToTokens(text.length),
|
|
310
|
-
tokenizerName: "chars_div_4",
|
|
311
|
-
model: model || null,
|
|
312
|
-
confidence: "low",
|
|
313
|
-
caveat: "gpt-tokenizer encode function not found; fell back to chars÷4.",
|
|
314
|
-
fallbackReason: "encode function not found",
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
const encoded = encodeFunction(text);
|
|
319
|
-
const count = Array.isArray(encoded) ? encoded.length : charsToTokens(text.length);
|
|
320
|
-
|
|
321
|
-
return {
|
|
322
|
-
method: "local_tokenizer",
|
|
323
|
-
tokens: count,
|
|
324
|
-
tokenizerName,
|
|
325
|
-
model: model || null,
|
|
326
|
-
confidence: isClaudeModel ? "medium" : "medium",
|
|
327
|
-
caveat,
|
|
328
|
-
};
|
|
329
|
-
} catch (err) {
|
|
330
|
-
return {
|
|
331
|
-
method: "chars_div_4",
|
|
332
|
-
tokens: charsToTokens(text.length),
|
|
333
|
-
tokenizerName: "chars_div_4",
|
|
334
|
-
model: model || null,
|
|
335
|
-
confidence: "low",
|
|
336
|
-
caveat: `Local tokenizer error; fell back to chars÷4. (${err && err.message ? err.message : "unknown error"})`,
|
|
337
|
-
fallbackReason: err && err.message ? err.message : "unknown error",
|
|
338
|
-
};
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
// ── Estimate raw candidate context tokens ─────────────────────────────────────
|
|
343
|
-
// Mirrors operating-value.js estimateRawContextTokens — reads registry to
|
|
344
|
-
// estimate what could have been loaded without Avorelo optimization.
|
|
345
|
-
// Method: chars_div_4 (always, since we don't load actual token counts)
|
|
346
|
-
|
|
347
|
-
function estimateRawCandidateTokens(cwd) {
|
|
348
|
-
const registryPath = path.resolve(cwd, WORKSPACE_REGISTRY_REL);
|
|
349
|
-
const registry = safeReadJson(registryPath);
|
|
350
|
-
const brainPackPath = path.resolve(cwd, BRAIN_PACK_REL);
|
|
351
|
-
const workControlPath = path.resolve(cwd, WORK_CONTROL_REL);
|
|
352
|
-
|
|
353
|
-
const sources = [];
|
|
354
|
-
let totalChars = 0;
|
|
355
|
-
|
|
356
|
-
if (registry && registry.summary) {
|
|
357
|
-
const assetChars = (registry.summary.totalAssets || 0) * 150;
|
|
358
|
-
totalChars += assetChars;
|
|
359
|
-
sources.push("workspace-registry");
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
const brainPackChars = fileCharLen(brainPackPath);
|
|
363
|
-
if (brainPackChars > 0) {
|
|
364
|
-
totalChars += brainPackChars;
|
|
365
|
-
sources.push("brain-pack");
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
const workControlChars = fileCharLen(workControlPath);
|
|
369
|
-
if (workControlChars > 0) {
|
|
370
|
-
totalChars += workControlChars;
|
|
371
|
-
sources.push("work-control");
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
if (totalChars === 0) {
|
|
375
|
-
return {
|
|
376
|
-
tokens: null,
|
|
377
|
-
method: "unavailable",
|
|
378
|
-
confidence: "unavailable",
|
|
379
|
-
sources: [],
|
|
380
|
-
caveat: "No source data to estimate raw candidate context.",
|
|
381
|
-
};
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const rawTokens = Math.min(charsToTokens(totalChars), RAW_TOKEN_CAP);
|
|
385
|
-
return {
|
|
386
|
-
tokens: rawTokens,
|
|
387
|
-
method: "chars_div_4",
|
|
388
|
-
confidence: "estimated",
|
|
389
|
-
sources,
|
|
390
|
-
caveat: "Raw candidate context estimated from source file/receipt sizes via chars÷4. Not a measured model context window load.",
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
// ── Estimate compact handoff tokens ───────────────────────────────────────────
|
|
395
|
-
// Reads session-context for estimatedTokens field (preferred).
|
|
396
|
-
// Falls back to chars_div_4 of session-context file size.
|
|
397
|
-
|
|
398
|
-
function estimateCompactHandoffTokens(cwd) {
|
|
399
|
-
const scPath = path.resolve(cwd, SESSION_CONTEXT_REL);
|
|
400
|
-
const sc = safeReadJson(scPath);
|
|
401
|
-
|
|
402
|
-
if (sc && sc.estimatedTokens && typeof sc.estimatedTokens === "number") {
|
|
403
|
-
return {
|
|
404
|
-
tokens: sc.estimatedTokens,
|
|
405
|
-
method: "session_context",
|
|
406
|
-
confidence: "measured",
|
|
407
|
-
basis: "session-context.estimatedTokens field",
|
|
408
|
-
caveat: null,
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Also check tokenDiscipline.estimatedTokens
|
|
413
|
-
if (sc && sc.tokenDiscipline && typeof sc.tokenDiscipline.estimatedTokens === "number") {
|
|
414
|
-
return {
|
|
415
|
-
tokens: sc.tokenDiscipline.estimatedTokens,
|
|
416
|
-
method: "session_context",
|
|
417
|
-
confidence: "measured",
|
|
418
|
-
basis: "session-context.tokenDiscipline.estimatedTokens field",
|
|
419
|
-
caveat: null,
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const fileBytes = fileCharLen(scPath);
|
|
424
|
-
if (fileBytes > 0) {
|
|
425
|
-
return {
|
|
426
|
-
tokens: charsToTokens(fileBytes),
|
|
427
|
-
method: "chars_div_4",
|
|
428
|
-
confidence: "estimated",
|
|
429
|
-
basis: "session-context file size ÷ 4",
|
|
430
|
-
caveat: "Compact handoff token count estimated from file size via chars÷4. Session-context.estimatedTokens not available.",
|
|
431
|
-
};
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
return {
|
|
435
|
-
tokens: null,
|
|
436
|
-
method: "unavailable",
|
|
437
|
-
confidence: "unavailable",
|
|
438
|
-
basis: "session-context not available",
|
|
439
|
-
caveat: "Run `avorelo session-context --handoff --target claude` to generate compact handoff.",
|
|
440
|
-
};
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// ── Build token evidence ───────────────────────────────────────────────────────
|
|
444
|
-
|
|
445
|
-
function buildTokenEvidence(cwd, options = {}) {
|
|
446
|
-
const evidenceId = `te-${Date.now()}-${shortId()}`;
|
|
447
|
-
const createdAt = nowIso();
|
|
448
|
-
|
|
449
|
-
const rawEst = estimateRawCandidateTokens(cwd);
|
|
450
|
-
const compactEst = estimateCompactHandoffTokens(cwd);
|
|
451
|
-
|
|
452
|
-
const targetProvider = options.targetProvider || null;
|
|
453
|
-
const targetModel = options.targetModel || null;
|
|
454
|
-
|
|
455
|
-
// ── Priority 1: Provider usage from local artifacts ───────────────────────
|
|
456
|
-
let providerUsageResult = null;
|
|
457
|
-
let providerUsageSource = null;
|
|
458
|
-
let providerUsageSourcePaths = [];
|
|
459
|
-
|
|
460
|
-
try {
|
|
461
|
-
const { usageRecords, sourcePaths } = collectProviderUsageEvidence(cwd);
|
|
462
|
-
if (usageRecords.length > 0) {
|
|
463
|
-
const agg = aggregateUsageRecords(usageRecords);
|
|
464
|
-
if (agg) {
|
|
465
|
-
providerUsageResult = agg;
|
|
466
|
-
providerUsageSourcePaths = sourcePaths;
|
|
467
|
-
providerUsageSource = sourcePaths.length > 0 ? sourcePaths[0] : "local_artifact";
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
} catch {
|
|
471
|
-
// Non-blocking
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
// ── Priority 2: Local tokenizer ───────────────────────────────────────────
|
|
475
|
-
// We use local tokenizer for compact handoff text if no provider usage
|
|
476
|
-
let localTokenizerResult = null;
|
|
477
|
-
if (!providerUsageResult) {
|
|
478
|
-
try {
|
|
479
|
-
const scPath = path.resolve(cwd, SESSION_CONTEXT_REL);
|
|
480
|
-
if (fs.existsSync(scPath)) {
|
|
481
|
-
const scContent = fs.readFileSync(scPath, "utf8");
|
|
482
|
-
// Only use if session-context doesn't have estimatedTokens (avoid double-counting)
|
|
483
|
-
const sc = safeReadJson(scPath);
|
|
484
|
-
const hasEstimatedTokens = sc && (
|
|
485
|
-
(typeof sc.estimatedTokens === "number") ||
|
|
486
|
-
(sc.tokenDiscipline && typeof sc.tokenDiscipline.estimatedTokens === "number")
|
|
487
|
-
);
|
|
488
|
-
if (!hasEstimatedTokens && scContent.length > 0) {
|
|
489
|
-
localTokenizerResult = countTokensWithLocalTokenizer(scContent, {
|
|
490
|
-
provider: targetProvider,
|
|
491
|
-
model: targetModel,
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
} catch {
|
|
496
|
-
// Non-blocking
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
// ── Determine primary method ──────────────────────────────────────────────
|
|
501
|
-
let primaryMethod;
|
|
502
|
-
let primaryConfidence;
|
|
503
|
-
let tokenizerName;
|
|
504
|
-
let modelMatched = false;
|
|
505
|
-
let fallbackReason = null;
|
|
506
|
-
let localTokenizerUsed = false;
|
|
507
|
-
let fallbackUsed = false;
|
|
508
|
-
|
|
509
|
-
if (providerUsageResult) {
|
|
510
|
-
primaryMethod = "provider_usage";
|
|
511
|
-
primaryConfidence = "high";
|
|
512
|
-
tokenizerName = "provider_reported";
|
|
513
|
-
modelMatched = true;
|
|
514
|
-
} else if (localTokenizerResult && localTokenizerResult.method === "local_tokenizer") {
|
|
515
|
-
primaryMethod = "local_tokenizer";
|
|
516
|
-
primaryConfidence = "medium";
|
|
517
|
-
tokenizerName = localTokenizerResult.tokenizerName;
|
|
518
|
-
modelMatched = false;
|
|
519
|
-
localTokenizerUsed = true;
|
|
520
|
-
} else {
|
|
521
|
-
// Determine overall method for fallback
|
|
522
|
-
primaryMethod = compactEst.method === "session_context" && rawEst.method === "chars_div_4"
|
|
523
|
-
? "mixed: session_context + chars_div_4"
|
|
524
|
-
: rawEst.method === "unavailable" || compactEst.method === "unavailable"
|
|
525
|
-
? "unavailable"
|
|
526
|
-
: rawEst.method;
|
|
527
|
-
primaryConfidence = compactEst.confidence === "measured" ? "medium" : "low";
|
|
528
|
-
tokenizerName = "chars_div_4";
|
|
529
|
-
fallbackUsed = true;
|
|
530
|
-
fallbackReason = localTokenizerResult && localTokenizerResult.fallbackReason
|
|
531
|
-
? localTokenizerResult.fallbackReason
|
|
532
|
-
: "no provider usage found; local tokenizer unavailable or not applicable";
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
// ── Measurements ──────────────────────────────────────────────────────────
|
|
536
|
-
let inputTokens = null;
|
|
537
|
-
let cachedInputTokens = null;
|
|
538
|
-
let outputTokens = null;
|
|
539
|
-
let cacheWriteTokens = null;
|
|
540
|
-
let cacheReadTokens = null;
|
|
541
|
-
let totalProviderTokens = null;
|
|
542
|
-
|
|
543
|
-
if (providerUsageResult) {
|
|
544
|
-
inputTokens = providerUsageResult.inputTokens;
|
|
545
|
-
cachedInputTokens = providerUsageResult.cachedInputTokens;
|
|
546
|
-
outputTokens = providerUsageResult.outputTokens;
|
|
547
|
-
cacheWriteTokens = providerUsageResult.cacheWriteTokens;
|
|
548
|
-
cacheReadTokens = providerUsageResult.cacheReadTokens;
|
|
549
|
-
totalProviderTokens = providerUsageResult.totalProviderTokens;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
let candidateTokenReduction = null;
|
|
553
|
-
let contextCompressionRatio = null;
|
|
554
|
-
if (rawEst.tokens !== null && compactEst.tokens !== null && rawEst.tokens > 0) {
|
|
555
|
-
candidateTokenReduction = Math.max(0, rawEst.tokens - compactEst.tokens);
|
|
556
|
-
contextCompressionRatio = Math.round((compactEst.tokens / rawEst.tokens) * 100) / 100;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// ── Caveats ───────────────────────────────────────────────────────────────
|
|
560
|
-
const caveats = [];
|
|
561
|
-
caveats.push("Candidate context is estimated — not a guaranteed model behavior.");
|
|
562
|
-
if (rawEst.caveat) caveats.push(rawEst.caveat);
|
|
563
|
-
if (compactEst.caveat) caveats.push(compactEst.caveat);
|
|
564
|
-
if (localTokenizerResult && localTokenizerResult.caveat && !providerUsageResult) {
|
|
565
|
-
caveats.push(localTokenizerResult.caveat);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
// ── Evidence sources ──────────────────────────────────────────────────────
|
|
569
|
-
const evidenceSources = [
|
|
570
|
-
rawEst.sources.length ? `raw from: ${rawEst.sources.join(", ")}` : null,
|
|
571
|
-
compactEst.basis ? `compact from: ${compactEst.basis}` : null,
|
|
572
|
-
].filter(Boolean);
|
|
573
|
-
|
|
574
|
-
return {
|
|
575
|
-
schemaVersion: SCHEMA_VERSION,
|
|
576
|
-
contract: CONTRACT,
|
|
577
|
-
evidenceId,
|
|
578
|
-
createdAt,
|
|
579
|
-
cwd,
|
|
580
|
-
tokenizer: {
|
|
581
|
-
provider: targetProvider,
|
|
582
|
-
model: targetModel,
|
|
583
|
-
method: primaryMethod,
|
|
584
|
-
name: tokenizerName,
|
|
585
|
-
confidence: primaryConfidence,
|
|
586
|
-
modelMatched,
|
|
587
|
-
providerUsageSource,
|
|
588
|
-
fallbackReason,
|
|
589
|
-
},
|
|
590
|
-
measurements: {
|
|
591
|
-
rawCandidateContextTokens: rawEst.tokens,
|
|
592
|
-
compactHandoffTokens: compactEst.tokens,
|
|
593
|
-
inputTokens,
|
|
594
|
-
cachedInputTokens,
|
|
595
|
-
outputTokens,
|
|
596
|
-
cacheWriteTokens,
|
|
597
|
-
cacheReadTokens,
|
|
598
|
-
totalProviderTokens,
|
|
599
|
-
candidateTokenReduction,
|
|
600
|
-
contextCompressionRatio,
|
|
601
|
-
},
|
|
602
|
-
evidence: {
|
|
603
|
-
providerUsageEvidenceFound: providerUsageResult !== null,
|
|
604
|
-
localTokenizerUsed,
|
|
605
|
-
fallbackUsed,
|
|
606
|
-
sourcePaths: providerUsageSourcePaths,
|
|
607
|
-
},
|
|
608
|
-
basis: evidenceSources.join(". ") || "insufficient data",
|
|
609
|
-
caveats,
|
|
610
|
-
redacted: true,
|
|
611
|
-
};
|
|
612
|
-
}
|
|
613
|
-
|
|
614
|
-
// ── Write / read ───────────────────────────────────────────────────────────────
|
|
615
|
-
|
|
616
|
-
function writeTokenEvidence(cwd, evidence) {
|
|
617
|
-
const latestPath = path.resolve(cwd, TOKEN_EVIDENCE_LATEST_REL);
|
|
618
|
-
ensureDirFor(latestPath);
|
|
619
|
-
fs.writeFileSync(latestPath, JSON.stringify(evidence, null, 2), "utf8");
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
function readTokenEvidence(cwd) {
|
|
623
|
-
const latestPath = path.resolve(cwd, TOKEN_EVIDENCE_LATEST_REL);
|
|
624
|
-
return safeReadJson(latestPath);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
// ── Text format ────────────────────────────────────────────────────────────────
|
|
628
|
-
|
|
629
|
-
function formatTokenEvidenceText(evidence) {
|
|
630
|
-
if (!evidence) return "Token evidence unavailable.\n";
|
|
631
|
-
const m = evidence.measurements;
|
|
632
|
-
const ev = evidence.evidence || {};
|
|
633
|
-
const lines = [
|
|
634
|
-
"Avorelo Token Evidence",
|
|
635
|
-
"",
|
|
636
|
-
` Method: ${evidence.tokenizer.method}`,
|
|
637
|
-
` Confidence: ${evidence.tokenizer.confidence}`,
|
|
638
|
-
evidence.tokenizer.name ? ` Tokenizer: ${evidence.tokenizer.name}` : null,
|
|
639
|
-
"",
|
|
640
|
-
m.rawCandidateContextTokens !== null
|
|
641
|
-
? ` Raw candidate context: ~${m.rawCandidateContextTokens.toLocaleString()} tokens (estimated)`
|
|
642
|
-
: " Raw candidate context: unavailable",
|
|
643
|
-
m.compactHandoffTokens !== null
|
|
644
|
-
? ` Compact handoff: ~${m.compactHandoffTokens.toLocaleString()} tokens`
|
|
645
|
-
: " Compact handoff: unavailable",
|
|
646
|
-
m.candidateTokenReduction !== null
|
|
647
|
-
? ` Candidate reduction: ~${m.candidateTokenReduction.toLocaleString()} tokens not loaded`
|
|
648
|
-
: " Candidate reduction: unavailable",
|
|
649
|
-
m.contextCompressionRatio !== null
|
|
650
|
-
? ` Compression ratio: ${(m.contextCompressionRatio * 100).toFixed(0)}% of candidate loaded`
|
|
651
|
-
: null,
|
|
652
|
-
ev.providerUsageEvidenceFound && m.inputTokens !== null
|
|
653
|
-
? ` Provider input tokens: ${m.inputTokens.toLocaleString()} (from local artifacts)`
|
|
654
|
-
: null,
|
|
655
|
-
ev.providerUsageEvidenceFound && m.outputTokens !== null
|
|
656
|
-
? ` Provider output tokens: ${m.outputTokens.toLocaleString()} (from local artifacts)`
|
|
657
|
-
: null,
|
|
658
|
-
"",
|
|
659
|
-
` Basis: ${evidence.basis}`,
|
|
660
|
-
evidence.caveats.length ? ` Caveats:` : null,
|
|
661
|
-
...evidence.caveats.map((c) => ` - ${c}`),
|
|
662
|
-
"",
|
|
663
|
-
].filter((l) => l !== null);
|
|
664
|
-
return lines.join("\n");
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
function formatTokenEvidenceDebugSources(evidence) {
|
|
668
|
-
if (!evidence) return "Token evidence unavailable.\n";
|
|
669
|
-
const ev = evidence.evidence || {};
|
|
670
|
-
const lines = [
|
|
671
|
-
"Avorelo Token Evidence — Debug Sources",
|
|
672
|
-
"",
|
|
673
|
-
` Method: ${evidence.tokenizer.method}`,
|
|
674
|
-
` Provider usage found: ${ev.providerUsageEvidenceFound}`,
|
|
675
|
-
` Local tokenizer used: ${ev.localTokenizerUsed}`,
|
|
676
|
-
` Fallback used: ${ev.fallbackUsed}`,
|
|
677
|
-
` Fallback reason: ${evidence.tokenizer.fallbackReason || "none"}`,
|
|
678
|
-
` Provider source: ${evidence.tokenizer.providerUsageSource || "none"}`,
|
|
679
|
-
"",
|
|
680
|
-
` Source paths scanned (${(ev.sourcePaths || []).length}):`,
|
|
681
|
-
...(ev.sourcePaths || []).map((p) => ` - ${p}`),
|
|
682
|
-
"",
|
|
683
|
-
];
|
|
684
|
-
return lines.join("\n");
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
module.exports = {
|
|
688
|
-
CONTRACT,
|
|
689
|
-
TOKEN_EVIDENCE_LATEST_REL,
|
|
690
|
-
buildTokenEvidence,
|
|
691
|
-
writeTokenEvidence,
|
|
692
|
-
readTokenEvidence,
|
|
693
|
-
estimateRawCandidateTokens,
|
|
694
|
-
estimateCompactHandoffTokens,
|
|
695
|
-
countTokensWithLocalTokenizer,
|
|
696
|
-
collectProviderUsageEvidence,
|
|
697
|
-
formatTokenEvidenceText,
|
|
698
|
-
formatTokenEvidenceDebugSources,
|
|
699
|
-
};
|