avorelo 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +56 -0
- package/bin/avorelo +9 -0
- package/package.json +135 -0
- package/scripts/README.md +40 -0
- package/scripts/cco-dashboard.js +252 -0
- package/scripts/cco-status.js +430 -0
- package/scripts/lib/activation/account-state.js +37 -0
- package/scripts/lib/activation/activation-runner.js +546 -0
- package/scripts/lib/activation/activation-self-healing.js +480 -0
- package/scripts/lib/activation/activation-state.js +83 -0
- package/scripts/lib/activation/activation-summary.js +191 -0
- package/scripts/lib/activation/adapters/claude-code.js +77 -0
- package/scripts/lib/activation/adapters/codex-cli.js +52 -0
- package/scripts/lib/activation/adapters/cursor.js +37 -0
- package/scripts/lib/activation/adapters/github-agent.js +39 -0
- package/scripts/lib/activation/adapters/terminal.js +42 -0
- package/scripts/lib/activation/adapters/vscode.js +39 -0
- package/scripts/lib/activation/adapters/windsurf.js +37 -0
- package/scripts/lib/activation/ai-surface-detector.js +151 -0
- package/scripts/lib/activation/connect-account.js +145 -0
- package/scripts/lib/activation/detect-environment.js +75 -0
- package/scripts/lib/activation/detect-hosts.js +62 -0
- package/scripts/lib/activation/format-activation-output.js +109 -0
- package/scripts/lib/activation/next-action.js +43 -0
- package/scripts/lib/activation/repair-engine.js +219 -0
- package/scripts/lib/activation-distribution-readiness.js +507 -0
- package/scripts/lib/adapter-conformance.js +176 -0
- package/scripts/lib/adapter-readiness.js +417 -0
- package/scripts/lib/adapter-safety-boundaries.js +335 -0
- package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
- package/scripts/lib/agent-access-governance.js +455 -0
- package/scripts/lib/agent-enforcement.js +765 -0
- package/scripts/lib/agent-policy-profile.js +210 -0
- package/scripts/lib/agent-security/action-evaluator.js +507 -0
- package/scripts/lib/agent-security/adapter-registry.js +98 -0
- package/scripts/lib/agent-security/auto-policy.js +139 -0
- package/scripts/lib/agent-security/bounded-scan.js +93 -0
- package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
- package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
- package/scripts/lib/agent-security/file-write-adapter.js +183 -0
- package/scripts/lib/agent-security/file-write-rules.js +178 -0
- package/scripts/lib/agent-security/index.js +3342 -0
- package/scripts/lib/agent-security/instruction-risk.js +181 -0
- package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
- package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
- package/scripts/lib/agent-security/package-action-adapter.js +175 -0
- package/scripts/lib/agent-security/package-action-rules.js +233 -0
- package/scripts/lib/agent-security/performance.js +148 -0
- package/scripts/lib/agent-security/permission-minimizer.js +403 -0
- package/scripts/lib/agent-security/scan-cache.js +74 -0
- package/scripts/lib/agent-security/source-trust.js +146 -0
- package/scripts/lib/ai-install-prompt.js +288 -0
- package/scripts/lib/ai-workspace-hygiene.js +1499 -0
- package/scripts/lib/alpha-activation.js +520 -0
- package/scripts/lib/alpha-feedback.js +263 -0
- package/scripts/lib/alpha-readiness-gate.js +332 -0
- package/scripts/lib/anti-gaming.js +169 -0
- package/scripts/lib/artifact-health.js +431 -0
- package/scripts/lib/attribution.js +180 -0
- package/scripts/lib/audit.js +289 -0
- package/scripts/lib/avorelo-skill-registry.js +810 -0
- package/scripts/lib/batch-jobs.js +71 -0
- package/scripts/lib/brain-pack.js +578 -0
- package/scripts/lib/brand-boundary.js +424 -0
- package/scripts/lib/brand.js +74 -0
- package/scripts/lib/browser-capability.js +1048 -0
- package/scripts/lib/browser-proof-preflight.js +321 -0
- package/scripts/lib/cache-readiness.js +187 -0
- package/scripts/lib/canonical-reentry.js +162 -0
- package/scripts/lib/capability-packs.js +314 -0
- package/scripts/lib/capability-recommender.js +512 -0
- package/scripts/lib/capability-registry.js +1059 -0
- package/scripts/lib/carry-forward-surfacing.js +194 -0
- package/scripts/lib/ccusage-adapter.js +188 -0
- package/scripts/lib/company-loop.js +1149 -0
- package/scripts/lib/config.js +637 -0
- package/scripts/lib/context-acquisition-plan.js +287 -0
- package/scripts/lib/context-budget-guard.js +170 -0
- package/scripts/lib/context-budget-scanner.js +257 -0
- package/scripts/lib/context-optimizer.js +715 -0
- package/scripts/lib/context-reduction-plan.js +178 -0
- package/scripts/lib/context-safety.js +88 -0
- package/scripts/lib/context-savings-engine.js +158 -0
- package/scripts/lib/cost-evidence.js +254 -0
- package/scripts/lib/cross-host-install-plan.js +308 -0
- package/scripts/lib/cross-host-install-readiness.js +237 -0
- package/scripts/lib/cross-host-value-flow.js +268 -0
- package/scripts/lib/dashboard.js +900 -0
- package/scripts/lib/design-partner-feedback.js +346 -0
- package/scripts/lib/entitlements.js +100 -0
- package/scripts/lib/execution-packet.js +559 -0
- package/scripts/lib/experimentation-events.js +547 -0
- package/scripts/lib/external-capability-compliance.js +107 -0
- package/scripts/lib/external-user-simulation.js +166 -0
- package/scripts/lib/failure-recovery-readiness.js +81 -0
- package/scripts/lib/failure-recovery.js +419 -0
- package/scripts/lib/feedback-intelligence.js +537 -0
- package/scripts/lib/feedback-signals.js +205 -0
- package/scripts/lib/file-integrity.js +68 -0
- package/scripts/lib/fsx.js +127 -0
- package/scripts/lib/full-readiness-gate.js +451 -0
- package/scripts/lib/guidance-builder.js +174 -0
- package/scripts/lib/hook-apply.js +1019 -0
- package/scripts/lib/hook-baseline.js +310 -0
- package/scripts/lib/hook-config-preview.js +275 -0
- package/scripts/lib/hook-contracts.js +290 -0
- package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
- package/scripts/lib/host-capability-matrix.js +351 -0
- package/scripts/lib/host-support-context.js +254 -0
- package/scripts/lib/http-hook-action.js +538 -0
- package/scripts/lib/install-ai-readiness.js +84 -0
- package/scripts/lib/install-intake-risk.js +1037 -0
- package/scripts/lib/install-journey-intelligence.js +329 -0
- package/scripts/lib/intervention-guidance.js +57 -0
- package/scripts/lib/known-limitations.js +115 -0
- package/scripts/lib/l8-path-truth.js +146 -0
- package/scripts/lib/launch-hardening-gate.js +436 -0
- package/scripts/lib/launch-readiness.js +628 -0
- package/scripts/lib/learning-memory.js +686 -0
- package/scripts/lib/lifecycle-hooks.js +802 -0
- package/scripts/lib/local-package-smoke.js +423 -0
- package/scripts/lib/local-pricing.js +299 -0
- package/scripts/lib/mcp-enforcement.js +311 -0
- package/scripts/lib/mcp-least-privilege-policy.js +303 -0
- package/scripts/lib/mcp-tool-inventory.js +388 -0
- package/scripts/lib/mcp-tool-risk.js +0 -0
- package/scripts/lib/memory.js +335 -0
- package/scripts/lib/metrics.js +699 -0
- package/scripts/lib/micro-proof.js +133 -0
- package/scripts/lib/next-run-context.js +436 -0
- package/scripts/lib/operating-value.js +1648 -0
- package/scripts/lib/optimization-v3.js +122 -0
- package/scripts/lib/orchestration/adapters/_shared.js +49 -0
- package/scripts/lib/orchestration/adapters/aider.js +18 -0
- package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
- package/scripts/lib/orchestration/adapters/codex.js +35 -0
- package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
- package/scripts/lib/orchestration/adapters/git.js +25 -0
- package/scripts/lib/orchestration/adapters/index.js +31 -0
- package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
- package/scripts/lib/orchestration/adapters/ollama.js +18 -0
- package/scripts/lib/orchestration/adapters/opencode.js +18 -0
- package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
- package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
- package/scripts/lib/orchestration/cli.js +438 -0
- package/scripts/lib/orchestration/execution-manager.js +279 -0
- package/scripts/lib/orchestration/handoff.js +314 -0
- package/scripts/lib/orchestration/index.js +456 -0
- package/scripts/lib/orchestration/inventory.js +47 -0
- package/scripts/lib/orchestration/model-discovery.js +498 -0
- package/scripts/lib/orchestration/model-profiler.js +170 -0
- package/scripts/lib/orchestration/model-profiles.js +252 -0
- package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
- package/scripts/lib/orchestration/proof-writer.js +349 -0
- package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
- package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
- package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
- package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
- package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
- package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
- package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
- package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
- package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
- package/scripts/lib/orchestration/risk-classifier.js +130 -0
- package/scripts/lib/orchestration/routing-policy.js +486 -0
- package/scripts/lib/orchestration/settings.js +112 -0
- package/scripts/lib/orchestration/state.js +165 -0
- package/scripts/lib/orchestration/verification-manager.js +138 -0
- package/scripts/lib/output-profiles.js +146 -0
- package/scripts/lib/package-content-audit.js +368 -0
- package/scripts/lib/package-runtime.js +278 -0
- package/scripts/lib/plan-surface.js +53 -0
- package/scripts/lib/plans.js +2318 -0
- package/scripts/lib/policy-provider.js +27 -0
- package/scripts/lib/prelaunch-activation-readiness.js +409 -0
- package/scripts/lib/prelaunch-evidence-store.js +816 -0
- package/scripts/lib/prelaunch-intelligence.js +869 -0
- package/scripts/lib/pricing-experiment.js +118 -0
- package/scripts/lib/pro-moment-events.js +77 -0
- package/scripts/lib/pro-moment-state.js +227 -0
- package/scripts/lib/pro-moments.js +1216 -0
- package/scripts/lib/product-learning-events.js +629 -0
- package/scripts/lib/project-profile.js +555 -0
- package/scripts/lib/prompt-compiler.js +280 -0
- package/scripts/lib/prompt-lint.js +32 -0
- package/scripts/lib/prompt-suggestions.js +52 -0
- package/scripts/lib/proof-canonical.js +398 -0
- package/scripts/lib/proof-drilldown.js +383 -0
- package/scripts/lib/proof-events.js +342 -0
- package/scripts/lib/proof-history.js +243 -0
- package/scripts/lib/proof-metrics.js +296 -0
- package/scripts/lib/proof-outcome-evidence.js +134 -0
- package/scripts/lib/proof-receipt.js +335 -0
- package/scripts/lib/proof-record.js +461 -0
- package/scripts/lib/public-activation-distribution-gate.js +258 -0
- package/scripts/lib/public-cli.js +3891 -0
- package/scripts/lib/public-distribution-truth.js +211 -0
- package/scripts/lib/public-install-claim-checker.js +294 -0
- package/scripts/lib/publish-provenance-readiness.js +283 -0
- package/scripts/lib/readiness-delta.js +218 -0
- package/scripts/lib/readiness-evidence-closure.js +196 -0
- package/scripts/lib/reentry-memory-capture.js +241 -0
- package/scripts/lib/reentry-memory-retrieval.js +302 -0
- package/scripts/lib/reentry-memory-status.js +146 -0
- package/scripts/lib/reentry-memory-store.js +178 -0
- package/scripts/lib/reentry-state.js +66 -0
- package/scripts/lib/release-candidate-bundle.js +166 -0
- package/scripts/lib/remediation.js +81 -0
- package/scripts/lib/repo-map.js +391 -0
- package/scripts/lib/run-improvements-lifecycle.js +330 -0
- package/scripts/lib/run-improvements.js +789 -0
- package/scripts/lib/runtime-decision-policy.js +387 -0
- package/scripts/lib/safe-path-engine.js +705 -0
- package/scripts/lib/safe-run-controller.js +887 -0
- package/scripts/lib/score.js +262 -0
- package/scripts/lib/seamless-enforcement.js +329 -0
- package/scripts/lib/seamless-outcome.js +689 -0
- package/scripts/lib/seamless-reality-gate.js +5043 -0
- package/scripts/lib/security-risk-classifier.js +511 -0
- package/scripts/lib/security-scan.js +384 -0
- package/scripts/lib/session-context-optimizer.js +1211 -0
- package/scripts/lib/session-timing.js +315 -0
- package/scripts/lib/skill-hygiene.js +805 -0
- package/scripts/lib/skill-packs.js +161 -0
- package/scripts/lib/skills-operating-layer.js +580 -0
- package/scripts/lib/smart-work-routing.js +768 -0
- package/scripts/lib/source-catalog.js +700 -0
- package/scripts/lib/status-value-summary.js +32 -0
- package/scripts/lib/support-bundle.js +578 -0
- package/scripts/lib/task-continuation.js +440 -0
- package/scripts/lib/test-helpers.js +15 -0
- package/scripts/lib/tier.js +38 -0
- package/scripts/lib/token-context-quality-gate.js +370 -0
- package/scripts/lib/token-cost-capture.js +187 -0
- package/scripts/lib/token-cost-intelligence.js +358 -0
- package/scripts/lib/token-efficiency-evidence.js +213 -0
- package/scripts/lib/token-evidence.js +699 -0
- package/scripts/lib/tokenish.js +17 -0
- package/scripts/lib/tool-output-sandbox.js +304 -0
- package/scripts/lib/trust-audit.js +136 -0
- package/scripts/lib/unified-events.js +396 -0
- package/scripts/lib/upgrade-interruption-recovery.js +407 -0
- package/scripts/lib/usage-ledger.js +201 -0
- package/scripts/lib/value-ledger.js +130 -0
- package/scripts/lib/value-proof-calibration.js +531 -0
- package/scripts/lib/visual-qa.js +231 -0
- package/scripts/lib/voice-alpha.js +29 -0
- package/scripts/lib/work-aware-orchestration.js +976 -0
- package/scripts/lib/work-control-receipts.js +577 -0
- package/scripts/lib/work-ledger.js +1123 -0
- package/scripts/lib/work-panel-preview.js +352 -0
- package/scripts/lib/workflow-discipline.js +280 -0
- package/scripts/lib/workflow-signals.js +419 -0
- package/scripts/lib/workspace-map.js +281 -0
- package/scripts/lib/workspace-registry.js +1367 -0
- package/scripts/lib/workspace-resolver.js +480 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
// ── Token Context Quality Gate ────────────────────────────────────────────────
|
|
4
|
+
//
|
|
5
|
+
// Validates that token reduction does not hurt task quality, proof, safety,
|
|
6
|
+
// debuggability, or seamless UX.
|
|
7
|
+
//
|
|
8
|
+
// Contract: avorelo.tokenContextQualityGate.v1
|
|
9
|
+
//
|
|
10
|
+
// Pass: context bounded, proof available, no raw dump, secrets excluded,
|
|
11
|
+
// fallback available, debug artifacts available.
|
|
12
|
+
// Warn: estimate unavailable but context bounded, repo map partial,
|
|
13
|
+
// cache readiness unknown, proof unavailable but next action clear.
|
|
14
|
+
// Fail: full repo dump, raw huge output injected, proof hidden by compression,
|
|
15
|
+
// support bundle leaks raw code/secrets.
|
|
16
|
+
|
|
17
|
+
const fs = require("node:fs");
|
|
18
|
+
const path = require("node:path");
|
|
19
|
+
const { nowIso } = require("./fsx");
|
|
20
|
+
const { appendProductLearningEvent } = require("./product-learning-events");
|
|
21
|
+
|
|
22
|
+
const CONTRACT = "avorelo.tokenContextQualityGate.v1";
|
|
23
|
+
const SCHEMA_VERSION = 1;
|
|
24
|
+
|
|
25
|
+
const QUALITY_GATE_DIR_REL = ".claude/cco/orchestration/token-efficiency";
|
|
26
|
+
const LATEST_GATE_REL = `${QUALITY_GATE_DIR_REL}/latest-quality-gate.json`;
|
|
27
|
+
|
|
28
|
+
// Thresholds
|
|
29
|
+
const MAX_CONTEXT_TOKENS_DEFAULT = 16384;
|
|
30
|
+
const FULL_REPO_TOKEN_THRESHOLD = 50000; // above this with no selection = dump
|
|
31
|
+
|
|
32
|
+
// ── Individual checks ─────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
function checkNoFullRepoDump(cwd, signals) {
|
|
35
|
+
const id = "no_full_repo_dump_by_default";
|
|
36
|
+
const { contextPlanStrategy, estimatedContextTokens } = signals;
|
|
37
|
+
|
|
38
|
+
if (contextPlanStrategy && !contextPlanStrategy.useFullFiles && !contextPlanStrategy.conservative) {
|
|
39
|
+
// Context plan explicitly avoids full files
|
|
40
|
+
return { id, status: "pass", detail: "Context plan does not load full repo by default." };
|
|
41
|
+
}
|
|
42
|
+
if (estimatedContextTokens && estimatedContextTokens > FULL_REPO_TOKEN_THRESHOLD) {
|
|
43
|
+
return { id, status: "fail", detail: `Estimated context ${estimatedContextTokens} tokens exceeds full-repo threshold (${FULL_REPO_TOKEN_THRESHOLD}). Full repo dump suspected.` };
|
|
44
|
+
}
|
|
45
|
+
if (!contextPlanStrategy) {
|
|
46
|
+
return { id, status: "warn", detail: "No context plan available. Cannot confirm no-full-repo-dump." };
|
|
47
|
+
}
|
|
48
|
+
return { id, status: "pass", detail: "Context tokens within expected range. No full repo dump detected." };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function checkRawToolOutputNotInjected(cwd, signals) {
|
|
52
|
+
const id = "raw_tool_output_not_injected";
|
|
53
|
+
const { toolOutputSandboxSummary } = signals;
|
|
54
|
+
|
|
55
|
+
if (!toolOutputSandboxSummary) {
|
|
56
|
+
return { id, status: "warn", detail: "No tool output sandbox summary. Cannot confirm raw output excluded from context." };
|
|
57
|
+
}
|
|
58
|
+
if (toolOutputSandboxSummary.oversized && !toolOutputSandboxSummary.artifactRef) {
|
|
59
|
+
return { id, status: "fail", detail: "Oversized tool output has no artifact ref — raw output may have entered context." };
|
|
60
|
+
}
|
|
61
|
+
return { id, status: "pass", detail: "Tool output sandbox summary available. Raw artifacts stored separately." };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function checkContextBounded(cwd, signals) {
|
|
65
|
+
const id = "selected_context_bounded";
|
|
66
|
+
const { estimatedContextTokens, contextBudget } = signals;
|
|
67
|
+
|
|
68
|
+
// estimatedContextTokens===0 with withinBudget:true means context plan ran but selected nothing —
|
|
69
|
+
// that is the smallest possible (and fully bounded) context; treat it as passing.
|
|
70
|
+
if (contextBudget && contextBudget.withinBudget === true && estimatedContextTokens != null) {
|
|
71
|
+
const max = contextBudget.maxTokens || MAX_CONTEXT_TOKENS_DEFAULT;
|
|
72
|
+
return { id, status: "pass", detail: `Context within budget: ~${estimatedContextTokens}/${max} tokens (withinBudget confirmed).` };
|
|
73
|
+
}
|
|
74
|
+
if (!estimatedContextTokens) {
|
|
75
|
+
return { id, status: "warn", detail: "Context token estimate not available. Cannot confirm bounded." };
|
|
76
|
+
}
|
|
77
|
+
const max = (contextBudget && contextBudget.maxTokens) || MAX_CONTEXT_TOKENS_DEFAULT;
|
|
78
|
+
if (estimatedContextTokens > max) {
|
|
79
|
+
return { id, status: "fail", detail: `Context estimated at ${estimatedContextTokens} tokens exceeds budget ${max}.` };
|
|
80
|
+
}
|
|
81
|
+
return { id, status: "pass", detail: `Context within budget: ~${estimatedContextTokens}/${max} tokens.` };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function checkProofAvailable(cwd, signals) {
|
|
85
|
+
const id = "proof_available_or_next_action_clear";
|
|
86
|
+
const { proofAvailable, proofNextAction } = signals;
|
|
87
|
+
|
|
88
|
+
if (proofAvailable === true) {
|
|
89
|
+
return { id, status: "pass", detail: "Proof artifact available." };
|
|
90
|
+
}
|
|
91
|
+
if (proofNextAction) {
|
|
92
|
+
return { id, status: "warn", detail: `Proof not yet run. Next action: ${proofNextAction}` };
|
|
93
|
+
}
|
|
94
|
+
return { id, status: "warn", detail: "Proof not available and no clear next action. Run `avorelo proof` after task." };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function checkRequiredEvidencePreserved(cwd, signals) {
|
|
98
|
+
const id = "required_evidence_preserved";
|
|
99
|
+
const { evidenceLevel, tokenEvidencePath } = signals;
|
|
100
|
+
|
|
101
|
+
if (!evidenceLevel || evidenceLevel === "not_available") {
|
|
102
|
+
return { id, status: "warn", detail: "Token efficiency evidence not available. Run after task to generate." };
|
|
103
|
+
}
|
|
104
|
+
return { id, status: "pass", detail: `Token efficiency evidence available (level: ${evidenceLevel}).` };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function checkFallbackAvailable(cwd, signals) {
|
|
108
|
+
const id = "fallback_available";
|
|
109
|
+
const { contextPlanStrategy, repoMapStatus } = signals;
|
|
110
|
+
|
|
111
|
+
if (contextPlanStrategy && contextPlanStrategy.conservative) {
|
|
112
|
+
return { id, status: "pass", detail: "Conservative strategy provides implicit fallback." };
|
|
113
|
+
}
|
|
114
|
+
if (repoMapStatus && repoMapStatus !== "error") {
|
|
115
|
+
return { id, status: "pass", detail: "Repo map available as fallback for context selection." };
|
|
116
|
+
}
|
|
117
|
+
return { id, status: "warn", detail: "No explicit fallback strategy confirmed. Repo map or conservative strategy recommended." };
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function checkSecretsExcluded(cwd, signals) {
|
|
121
|
+
const id = "secrets_excluded";
|
|
122
|
+
const { repoMapSensitiveExcluded, contextPlanSecretsExcluded } = signals;
|
|
123
|
+
|
|
124
|
+
if (repoMapSensitiveExcluded === true || contextPlanSecretsExcluded === true) {
|
|
125
|
+
return { id, status: "pass", detail: "Sensitive/secret files excluded from context by repo map or plan." };
|
|
126
|
+
}
|
|
127
|
+
if (repoMapSensitiveExcluded === undefined && contextPlanSecretsExcluded === undefined) {
|
|
128
|
+
return { id, status: "warn", detail: "Cannot confirm secrets excluded. Ensure .env and sensitive files are not in context." };
|
|
129
|
+
}
|
|
130
|
+
return { id, status: "pass", detail: "Secrets exclusion confirmed." };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function checkDebugArtifactsAvailable(cwd, signals) {
|
|
134
|
+
const id = "debug_artifacts_available";
|
|
135
|
+
const toolOutputDir = path.join(cwd, ".claude/cco/orchestration/tool-output");
|
|
136
|
+
const hasToolArtifacts = fs.existsSync(toolOutputDir);
|
|
137
|
+
const supportBundlePath = path.join(cwd, ".claude/cco/support/latest-support-bundle.json");
|
|
138
|
+
const hasSupportBundle = fs.existsSync(supportBundlePath);
|
|
139
|
+
|
|
140
|
+
if (hasSupportBundle) {
|
|
141
|
+
return { id, status: "pass", detail: "Support bundle available for debug." };
|
|
142
|
+
}
|
|
143
|
+
if (hasToolArtifacts) {
|
|
144
|
+
return { id, status: "pass", detail: "Tool output artifacts available for debug." };
|
|
145
|
+
}
|
|
146
|
+
return { id, status: "warn", detail: "No debug artifacts found. Run `avorelo support-bundle` to generate." };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function checkSupportBundleNotLeaking(cwd, signals) {
|
|
150
|
+
const id = "support_bundle_not_leaking_raw";
|
|
151
|
+
const { supportBundleRedacted } = signals;
|
|
152
|
+
|
|
153
|
+
if (supportBundleRedacted === false) {
|
|
154
|
+
return { id, status: "fail", detail: "Support bundle is NOT marked redacted. Raw code/secrets may be exposed." };
|
|
155
|
+
}
|
|
156
|
+
// Check actual bundle file
|
|
157
|
+
try {
|
|
158
|
+
const bundlePath = path.join(cwd, ".claude/cco/support/latest-support-bundle.json");
|
|
159
|
+
if (fs.existsSync(bundlePath)) {
|
|
160
|
+
const bundle = JSON.parse(fs.readFileSync(bundlePath, "utf8"));
|
|
161
|
+
if (bundle.redacted === true) {
|
|
162
|
+
return { id, status: "pass", detail: "Support bundle is marked redacted." };
|
|
163
|
+
}
|
|
164
|
+
return { id, status: "warn", detail: "Support bundle exists but redacted flag not set." };
|
|
165
|
+
}
|
|
166
|
+
} catch {}
|
|
167
|
+
return { id, status: "pass", detail: "No support bundle generated yet; no leak possible." };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ── Gate runner ───────────────────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
function runTokenContextQualityGate(cwd, input = {}, options = {}) {
|
|
173
|
+
const {
|
|
174
|
+
contextPlanPath,
|
|
175
|
+
tokenEvidencePath,
|
|
176
|
+
toolOutputSandboxPath,
|
|
177
|
+
repoMapPath,
|
|
178
|
+
cacheReadinessPath,
|
|
179
|
+
} = input;
|
|
180
|
+
|
|
181
|
+
// Load signals from artifacts
|
|
182
|
+
const signals = {};
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const ep = path.join(cwd, tokenEvidencePath || ".claude/cco/orchestration/token-efficiency/latest-evidence.json");
|
|
186
|
+
if (fs.existsSync(ep)) {
|
|
187
|
+
const ev = JSON.parse(fs.readFileSync(ep, "utf8"));
|
|
188
|
+
signals.evidenceLevel = ev.evidenceLevel;
|
|
189
|
+
signals.estimatedContextTokens = ev.selectedContextTokens;
|
|
190
|
+
signals.tokenEvidencePath = tokenEvidencePath || ".claude/cco/orchestration/token-efficiency/latest-evidence.json";
|
|
191
|
+
}
|
|
192
|
+
} catch {}
|
|
193
|
+
|
|
194
|
+
try {
|
|
195
|
+
const cp = path.join(cwd, contextPlanPath || ".claude/cco/orchestration/context-acquisition/latest-plan.json");
|
|
196
|
+
if (fs.existsSync(cp)) {
|
|
197
|
+
const plan = JSON.parse(fs.readFileSync(cp, "utf8"));
|
|
198
|
+
signals.contextPlanStrategy = plan.strategy;
|
|
199
|
+
signals.contextBudget = plan.contextBudget;
|
|
200
|
+
if (plan.excluded) {
|
|
201
|
+
signals.contextPlanSecretsExcluded = plan.excluded.some((e) => e.reason && e.reason.includes("sensitive"));
|
|
202
|
+
}
|
|
203
|
+
if (!signals.estimatedContextTokens && plan.contextBudget) {
|
|
204
|
+
signals.estimatedContextTokens = plan.contextBudget.estimatedSelected;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
} catch {}
|
|
208
|
+
|
|
209
|
+
try {
|
|
210
|
+
const tp = path.join(cwd, toolOutputSandboxPath || ".claude/cco/orchestration/tool-output/latest-summary.json");
|
|
211
|
+
if (fs.existsSync(tp)) {
|
|
212
|
+
signals.toolOutputSandboxSummary = JSON.parse(fs.readFileSync(tp, "utf8"));
|
|
213
|
+
}
|
|
214
|
+
} catch {}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const rp = path.join(cwd, repoMapPath || ".claude/cco/orchestration/repo-map/latest-map.json");
|
|
218
|
+
if (fs.existsSync(rp)) {
|
|
219
|
+
const rm = JSON.parse(fs.readFileSync(rp, "utf8"));
|
|
220
|
+
signals.repoMapStatus = rm.status;
|
|
221
|
+
signals.repoMapSensitiveExcluded = rm.sensitiveExcluded > 0;
|
|
222
|
+
}
|
|
223
|
+
} catch {}
|
|
224
|
+
|
|
225
|
+
// Proof availability
|
|
226
|
+
try {
|
|
227
|
+
const proofPath = path.join(cwd, ".claude/cco/orchestration/safe-run/latest-run.json");
|
|
228
|
+
if (fs.existsSync(proofPath)) {
|
|
229
|
+
const run = JSON.parse(fs.readFileSync(proofPath, "utf8"));
|
|
230
|
+
signals.proofAvailable = run.decision === "prepared" || run.decision === "completed";
|
|
231
|
+
}
|
|
232
|
+
} catch {}
|
|
233
|
+
|
|
234
|
+
// Check support bundle redaction signal
|
|
235
|
+
try {
|
|
236
|
+
const bundlePath = path.join(cwd, ".claude/cco/support/latest-support-bundle.json");
|
|
237
|
+
if (fs.existsSync(bundlePath)) {
|
|
238
|
+
const bundle = JSON.parse(fs.readFileSync(bundlePath, "utf8"));
|
|
239
|
+
signals.supportBundleRedacted = bundle.redacted === true;
|
|
240
|
+
}
|
|
241
|
+
} catch {}
|
|
242
|
+
|
|
243
|
+
const checks = [
|
|
244
|
+
checkNoFullRepoDump(cwd, signals),
|
|
245
|
+
checkRawToolOutputNotInjected(cwd, signals),
|
|
246
|
+
checkContextBounded(cwd, signals),
|
|
247
|
+
checkProofAvailable(cwd, signals),
|
|
248
|
+
checkRequiredEvidencePreserved(cwd, signals),
|
|
249
|
+
checkFallbackAvailable(cwd, signals),
|
|
250
|
+
checkSecretsExcluded(cwd, signals),
|
|
251
|
+
checkDebugArtifactsAvailable(cwd, signals),
|
|
252
|
+
checkSupportBundleNotLeaking(cwd, signals),
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
const failed = checks.filter((c) => c.status === "fail");
|
|
256
|
+
const warned = checks.filter((c) => c.status === "warn");
|
|
257
|
+
|
|
258
|
+
let status, verdict;
|
|
259
|
+
if (failed.length > 0) {
|
|
260
|
+
status = "fail";
|
|
261
|
+
verdict = "failed";
|
|
262
|
+
} else if (warned.length > 3) {
|
|
263
|
+
status = "warn";
|
|
264
|
+
verdict = "weak";
|
|
265
|
+
} else if (warned.length > 0) {
|
|
266
|
+
status = "warn";
|
|
267
|
+
verdict = "acceptable";
|
|
268
|
+
} else {
|
|
269
|
+
status = "pass";
|
|
270
|
+
verdict = "strong";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const nextAction = failed.length > 0
|
|
274
|
+
? `Fix failures: ${failed.map((c) => c.id).join(", ")}`
|
|
275
|
+
: warned.length > 0
|
|
276
|
+
? `Review warnings: ${warned.map((c) => c.id).join(", ")}`
|
|
277
|
+
: "Token context quality gate passed. Proceed.";
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
contract: CONTRACT,
|
|
281
|
+
schemaVersion: SCHEMA_VERSION,
|
|
282
|
+
createdAt: nowIso(),
|
|
283
|
+
status,
|
|
284
|
+
verdict,
|
|
285
|
+
context: {
|
|
286
|
+
estimatedTokens: signals.estimatedContextTokens || null,
|
|
287
|
+
bounded: signals.contextBudget ? signals.contextBudget.withinBudget : null,
|
|
288
|
+
},
|
|
289
|
+
quality: {
|
|
290
|
+
checksPass: checks.filter((c) => c.status === "pass").length,
|
|
291
|
+
checksWarn: warned.length,
|
|
292
|
+
checksFail: failed.length,
|
|
293
|
+
},
|
|
294
|
+
latency: { evidenceLevel: "not_available" },
|
|
295
|
+
safety: {
|
|
296
|
+
secretsExcluded: signals.repoMapSensitiveExcluded || signals.contextPlanSecretsExcluded || null,
|
|
297
|
+
supportBundleRedacted: signals.supportBundleRedacted || null,
|
|
298
|
+
},
|
|
299
|
+
checks,
|
|
300
|
+
evidenceRefs: [
|
|
301
|
+
signals.tokenEvidencePath,
|
|
302
|
+
contextPlanPath || ".claude/cco/orchestration/context-acquisition/latest-plan.json",
|
|
303
|
+
].filter(Boolean),
|
|
304
|
+
nextAction,
|
|
305
|
+
redacted: true,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function buildContextQualityVerdict(cwd, signals = {}, options = {}) {
|
|
310
|
+
return runTokenContextQualityGate(cwd, signals, options);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function writeTokenContextQualityGate(cwd, gate) {
|
|
314
|
+
const dir = path.join(cwd, QUALITY_GATE_DIR_REL);
|
|
315
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
316
|
+
const abs = path.join(cwd, LATEST_GATE_REL);
|
|
317
|
+
fs.writeFileSync(abs, JSON.stringify(gate, null, 2), "utf8");
|
|
318
|
+
try {
|
|
319
|
+
appendProductLearningEvent(cwd, "token_context_quality_gate_run", {
|
|
320
|
+
status: gate.status,
|
|
321
|
+
verdict: gate.verdict,
|
|
322
|
+
checksPass: gate.quality.checksPass,
|
|
323
|
+
checksWarn: gate.quality.checksWarn,
|
|
324
|
+
checksFail: gate.quality.checksFail,
|
|
325
|
+
});
|
|
326
|
+
} catch {}
|
|
327
|
+
return abs;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function buildTokenContextQualitySurface(cwd, options = {}) {
|
|
331
|
+
const abs = path.join(cwd, LATEST_GATE_REL);
|
|
332
|
+
if (!fs.existsSync(abs)) {
|
|
333
|
+
return { status: "not_available", artifactPath: LATEST_GATE_REL };
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const gate = JSON.parse(fs.readFileSync(abs, "utf8"));
|
|
337
|
+
return {
|
|
338
|
+
status: gate.status,
|
|
339
|
+
verdict: gate.verdict,
|
|
340
|
+
checksPass: gate.quality.checksPass,
|
|
341
|
+
checksWarn: gate.quality.checksWarn,
|
|
342
|
+
checksFail: gate.quality.checksFail,
|
|
343
|
+
artifactPath: LATEST_GATE_REL,
|
|
344
|
+
};
|
|
345
|
+
} catch {
|
|
346
|
+
return { status: "error", artifactPath: LATEST_GATE_REL };
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function formatTokenContextQualityText(gate, options = {}) {
|
|
351
|
+
const lines = [`Token Context Quality Gate: ${gate.status} (${gate.verdict})`];
|
|
352
|
+
lines.push(` Checks: ${gate.quality.checksPass} pass, ${gate.quality.checksWarn} warn, ${gate.quality.checksFail} fail`);
|
|
353
|
+
for (const c of gate.checks) {
|
|
354
|
+
if (c.status !== "pass") lines.push(` [${c.status}] ${c.id}: ${c.detail}`);
|
|
355
|
+
}
|
|
356
|
+
lines.push(` Next: ${gate.nextAction}`);
|
|
357
|
+
return lines.join("\n");
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
module.exports = {
|
|
361
|
+
CONTRACT,
|
|
362
|
+
SCHEMA_VERSION,
|
|
363
|
+
QUALITY_GATE_DIR_REL,
|
|
364
|
+
LATEST_GATE_REL,
|
|
365
|
+
runTokenContextQualityGate,
|
|
366
|
+
buildContextQualityVerdict,
|
|
367
|
+
writeTokenContextQualityGate,
|
|
368
|
+
buildTokenContextQualitySurface,
|
|
369
|
+
formatTokenContextQualityText,
|
|
370
|
+
};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const crypto = require("crypto");
|
|
6
|
+
|
|
7
|
+
const { nowIso } = require("./fsx");
|
|
8
|
+
const { readOutcomeEvents, OUTCOME_EVENTS_REL } = require("./unified-events");
|
|
9
|
+
const { TOKEN_COST_ITEM_CONTRACT, TOKEN_COST_CAPTURE_JSONL_REL, TOKEN_COST_CAPTURE_RECEIPT_REL } = require("./prelaunch-evidence-store");
|
|
10
|
+
|
|
11
|
+
const CONTRACT = "avorelo.realUsageTokenCostCapture.v1";
|
|
12
|
+
const SCHEMA_VERSION = 1;
|
|
13
|
+
|
|
14
|
+
function sha256(value) {
|
|
15
|
+
return crypto.createHash("sha256").update(String(value || "")).digest("hex");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function sanitizeText(value, max = 64) {
|
|
19
|
+
if (value === null || value === undefined) return null;
|
|
20
|
+
const normalized = String(value).replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g, "").trim();
|
|
21
|
+
if (!normalized) return null;
|
|
22
|
+
return normalized.slice(0, max);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sanitizeNumber(value) {
|
|
26
|
+
if (value === null || value === undefined || value === "") return null;
|
|
27
|
+
const num = Number(value);
|
|
28
|
+
return Number.isFinite(num) ? num : null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function sanitizeMode(mode) {
|
|
32
|
+
const normalized = sanitizeText(mode, 24);
|
|
33
|
+
if (!normalized) return null;
|
|
34
|
+
return ["measured", "estimated", "heuristic", "missing"].includes(normalized) ? normalized : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function safeReadJson(absPath) {
|
|
38
|
+
try {
|
|
39
|
+
if (!fs.existsSync(absPath)) return null;
|
|
40
|
+
return JSON.parse(fs.readFileSync(absPath, "utf8").replace(/^\uFEFF/, ""));
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function buildCapturedRow(event) {
|
|
47
|
+
if (!event || typeof event !== "object") return null;
|
|
48
|
+
const attribution = event.attribution && typeof event.attribution === "object" ? event.attribution : {};
|
|
49
|
+
const metadata = event.metadata && typeof event.metadata === "object" ? event.metadata : {};
|
|
50
|
+
const totalTokens = sanitizeNumber(attribution.tokenUsage ?? metadata.totalTokens);
|
|
51
|
+
const measuredCostUsd = sanitizeNumber(attribution.cost ?? metadata.totalCost);
|
|
52
|
+
const provider = sanitizeText(attribution.provider ?? metadata.provider, 24) || "unknown";
|
|
53
|
+
const model = sanitizeText(attribution.model ?? metadata.model, 64) || "unknown";
|
|
54
|
+
const capturedAt = sanitizeText(event.timestamp, 40) || nowIso();
|
|
55
|
+
|
|
56
|
+
if (totalTokens === null && measuredCostUsd === null) return null;
|
|
57
|
+
|
|
58
|
+
const evidenceMode = measuredCostUsd !== null && totalTokens !== null
|
|
59
|
+
? "measured"
|
|
60
|
+
: sanitizeMode(metadata.evidenceMode) || "heuristic";
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
contract: TOKEN_COST_ITEM_CONTRACT,
|
|
64
|
+
schemaVersion: 1,
|
|
65
|
+
importedAt: nowIso(),
|
|
66
|
+
sourceType: "runtime_outcome_event",
|
|
67
|
+
capturedAt,
|
|
68
|
+
runIdHash: sha256(event.session_id || event.event_id || capturedAt),
|
|
69
|
+
tool: sanitizeText(event.tool, 48) || "unknown",
|
|
70
|
+
taskType: sanitizeText(event.category || event.event_name || event.action, 48) || "unknown",
|
|
71
|
+
provider,
|
|
72
|
+
modelFamily: model,
|
|
73
|
+
modelProvider: provider,
|
|
74
|
+
modelName: model,
|
|
75
|
+
inputTokens: null,
|
|
76
|
+
outputTokens: null,
|
|
77
|
+
promptTokens: null,
|
|
78
|
+
completionTokens: null,
|
|
79
|
+
totalTokens,
|
|
80
|
+
estimatedCostUsd: null,
|
|
81
|
+
measuredCostUsd,
|
|
82
|
+
costMicros: measuredCostUsd !== null ? Math.round(measuredCostUsd * 1000000) : null,
|
|
83
|
+
currency: measuredCostUsd !== null ? "USD" : null,
|
|
84
|
+
evidenceMode,
|
|
85
|
+
confidence: evidenceMode === "measured" ? "measured" : "heuristic",
|
|
86
|
+
notesCategory: "runtime_capture",
|
|
87
|
+
cacheHit: false,
|
|
88
|
+
estimatedOnly: false,
|
|
89
|
+
heuristic: evidenceMode !== "measured",
|
|
90
|
+
redacted: true,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function dedupeRows(rows) {
|
|
95
|
+
const map = new Map();
|
|
96
|
+
for (const row of rows) {
|
|
97
|
+
if (!row) continue;
|
|
98
|
+
const key = `${row.runIdHash || "unknown"}|${row.capturedAt || "unknown"}|${row.tool || "unknown"}|${row.totalTokens || 0}|${row.measuredCostUsd || 0}`;
|
|
99
|
+
map.set(key, row);
|
|
100
|
+
}
|
|
101
|
+
return Array.from(map.values());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function writeCaptureRows(cwd, rows) {
|
|
105
|
+
const abs = path.join(cwd, TOKEN_COST_CAPTURE_JSONL_REL);
|
|
106
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
107
|
+
const content = rows.length ? rows.map((row) => JSON.stringify(row)).join("\n") + "\n" : "";
|
|
108
|
+
fs.writeFileSync(abs, content, "utf8");
|
|
109
|
+
return abs;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function buildRealUsageTokenCostCapture(cwd) {
|
|
113
|
+
const events = readOutcomeEvents(cwd);
|
|
114
|
+
const rows = dedupeRows(events.map(buildCapturedRow).filter(Boolean));
|
|
115
|
+
const measuredRows = rows.filter((row) => row.evidenceMode === "measured" && typeof row.totalTokens === "number" && typeof row.measuredCostUsd === "number");
|
|
116
|
+
const heuristicRows = rows.filter((row) => row.evidenceMode === "heuristic");
|
|
117
|
+
|
|
118
|
+
let status = "warn";
|
|
119
|
+
if (measuredRows.length > 0) status = "pass";
|
|
120
|
+
else if (rows.length > 0) status = "warn";
|
|
121
|
+
else status = "info";
|
|
122
|
+
|
|
123
|
+
const missingEvidence = [];
|
|
124
|
+
const safeNextActions = [];
|
|
125
|
+
|
|
126
|
+
if (events.length === 0) {
|
|
127
|
+
missingEvidence.push("No local outcome events with token/cost attribution are available yet.");
|
|
128
|
+
safeNextActions.push("Complete a real local task that produces runtime attribution, then rerun: node bin/avorelo token-cost capture --json");
|
|
129
|
+
} else if (rows.length === 0) {
|
|
130
|
+
missingEvidence.push("Outcome events exist, but none include safe token/cost attribution fields.");
|
|
131
|
+
safeNextActions.push("Use a real local task path that records provider/model/token attribution, then rerun: node bin/avorelo token-cost capture --json");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (measuredRows.length === 0) {
|
|
135
|
+
missingEvidence.push("No measured token/cost samples are available from captured local usage.");
|
|
136
|
+
safeNextActions.push("Import redacted measured usage rows if local runtime cost is unavailable: node bin/avorelo token-cost import --file <redacted-json-or-jsonl> --json");
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (safeNextActions.length === 0) {
|
|
140
|
+
safeNextActions.push("Run: node bin/avorelo token-cost summary --json");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
contract: CONTRACT,
|
|
145
|
+
schemaVersion: SCHEMA_VERSION,
|
|
146
|
+
createdAt: nowIso(),
|
|
147
|
+
status,
|
|
148
|
+
evidenceAvailable: measuredRows.length > 0,
|
|
149
|
+
capturedRowsCount: rows.length,
|
|
150
|
+
realUsageSamplesCount: measuredRows.length,
|
|
151
|
+
heuristicSamplesCount: heuristicRows.length,
|
|
152
|
+
evidenceMode: measuredRows.length > 0 ? "measured" : rows.length > 0 ? "heuristic" : "missing",
|
|
153
|
+
confidence: measuredRows.length > 0 ? "measured" : rows.length > 0 ? "heuristic" : "missing",
|
|
154
|
+
source: {
|
|
155
|
+
outcomeEventsPath: OUTCOME_EVENTS_REL,
|
|
156
|
+
capturePath: TOKEN_COST_CAPTURE_JSONL_REL,
|
|
157
|
+
},
|
|
158
|
+
missingEvidence,
|
|
159
|
+
safeNextActions,
|
|
160
|
+
redacted: true,
|
|
161
|
+
noSavingsClaimUnlessMeasured: true,
|
|
162
|
+
rows,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function writeRealUsageTokenCostCapture(cwd, capture) {
|
|
167
|
+
const payload = capture || buildRealUsageTokenCostCapture(cwd);
|
|
168
|
+
writeCaptureRows(cwd, payload.rows || []);
|
|
169
|
+
const stored = { ...payload };
|
|
170
|
+
delete stored.rows;
|
|
171
|
+
const abs = path.join(cwd, TOKEN_COST_CAPTURE_RECEIPT_REL);
|
|
172
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
173
|
+
fs.writeFileSync(abs, JSON.stringify(stored, null, 2), "utf8");
|
|
174
|
+
return stored;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function readRealUsageTokenCostCapture(cwd) {
|
|
178
|
+
return safeReadJson(path.join(cwd, TOKEN_COST_CAPTURE_RECEIPT_REL));
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
CONTRACT,
|
|
183
|
+
SCHEMA_VERSION,
|
|
184
|
+
buildRealUsageTokenCostCapture,
|
|
185
|
+
writeRealUsageTokenCostCapture,
|
|
186
|
+
readRealUsageTokenCostCapture,
|
|
187
|
+
};
|