avorelo 0.3.8 → 0.4.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (394) hide show
  1. package/LICENSE +16 -23
  2. package/README.md +62 -72
  3. package/bin/avorelo +9 -0
  4. package/package.json +147 -139
  5. package/scripts/README.md +40 -0
  6. package/scripts/avorelo-session-collision-guard.js +540 -0
  7. package/scripts/avorelo-workstream-preflight.js +918 -0
  8. package/scripts/cco-dashboard.js +346 -0
  9. package/scripts/cco-posttooluse.js +504 -0
  10. package/scripts/cco-pretooluse.js +381 -0
  11. package/scripts/cco-status.js +540 -0
  12. package/scripts/cco-stop.js +77 -0
  13. package/scripts/cco-userprompt-submit.js +83 -0
  14. package/scripts/lib/activation/account-state.js +37 -0
  15. package/scripts/lib/activation/activate-command.js +114 -0
  16. package/scripts/lib/activation/activation-runner.js +607 -0
  17. package/scripts/lib/activation/activation-self-healing.js +481 -0
  18. package/scripts/lib/activation/activation-state.js +96 -0
  19. package/scripts/lib/activation/activation-summary.js +241 -0
  20. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  21. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  22. package/scripts/lib/activation/adapters/cursor.js +37 -0
  23. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  24. package/scripts/lib/activation/adapters/terminal.js +42 -0
  25. package/scripts/lib/activation/adapters/vscode.js +39 -0
  26. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  27. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  28. package/scripts/lib/activation/connect-account.js +145 -0
  29. package/scripts/lib/activation/detect-environment.js +75 -0
  30. package/scripts/lib/activation/detect-hosts.js +62 -0
  31. package/scripts/lib/activation/format-activation-output.js +109 -0
  32. package/scripts/lib/activation/next-action.js +43 -0
  33. package/scripts/lib/activation/repair-engine.js +219 -0
  34. package/scripts/lib/activation/run-entry.js +346 -0
  35. package/scripts/lib/activation-distribution-readiness.js +507 -0
  36. package/scripts/lib/adapter-conformance.js +176 -0
  37. package/scripts/lib/adapter-readiness.js +428 -0
  38. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  39. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  40. package/scripts/lib/agent-access-governance.js +455 -0
  41. package/scripts/lib/agent-activation-readiness.js +878 -0
  42. package/scripts/lib/agent-enforcement.js +765 -0
  43. package/scripts/lib/agent-evolution-loop.js +479 -0
  44. package/scripts/lib/agent-policy-profile.js +210 -0
  45. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  46. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  47. package/scripts/lib/agent-security/auto-policy.js +139 -0
  48. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  49. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  50. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  51. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  52. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  53. package/scripts/lib/agent-security/index.js +3342 -0
  54. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  55. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  56. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  57. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  58. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  59. package/scripts/lib/agent-security/performance.js +148 -0
  60. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  61. package/scripts/lib/agent-security/scan-cache.js +74 -0
  62. package/scripts/lib/agent-security/source-trust.js +146 -0
  63. package/scripts/lib/ai-efficiency-dogfood-pack.js +437 -0
  64. package/scripts/lib/ai-efficiency-eval.js +207 -0
  65. package/scripts/lib/ai-efficiency-guard-contract.js +209 -0
  66. package/scripts/lib/ai-efficiency-measured-value.js +106 -0
  67. package/scripts/lib/ai-install-prompt.js +293 -0
  68. package/scripts/lib/ai-task-inference.js +155 -0
  69. package/scripts/lib/ai-value-ledger.js +121 -0
  70. package/scripts/lib/ai-value-proof-table.js +151 -0
  71. package/scripts/lib/ai-work-efficiency-routing.js +623 -0
  72. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  73. package/scripts/lib/alpha-activation.js +520 -0
  74. package/scripts/lib/alpha-feedback.js +263 -0
  75. package/scripts/lib/alpha-readiness-gate.js +332 -0
  76. package/scripts/lib/anti-gaming.js +169 -0
  77. package/scripts/lib/artifact-health.js +431 -0
  78. package/scripts/lib/artifact-receipt-hygiene.js +619 -0
  79. package/scripts/lib/attribution.js +180 -0
  80. package/scripts/lib/audit.js +289 -0
  81. package/scripts/lib/avorelo-skill-registry.js +810 -0
  82. package/scripts/lib/batch-jobs.js +71 -0
  83. package/scripts/lib/brain-pack.js +578 -0
  84. package/scripts/lib/brand-boundary.js +424 -0
  85. package/scripts/lib/brand.js +74 -0
  86. package/scripts/lib/browser-capability.js +1048 -0
  87. package/scripts/lib/browser-proof-governed-workflow.js +1020 -0
  88. package/scripts/lib/browser-proof-preflight.js +321 -0
  89. package/scripts/lib/cache-readiness.js +296 -0
  90. package/scripts/lib/canonical-reentry.js +162 -0
  91. package/scripts/lib/capability-contract-validator.js +177 -0
  92. package/scripts/lib/capability-hygiene.js +504 -0
  93. package/scripts/lib/capability-intake-quarantine.js +789 -0
  94. package/scripts/lib/capability-kernel-registry.js +566 -0
  95. package/scripts/lib/capability-kernel.js +202 -0
  96. package/scripts/lib/capability-manifest.js +255 -0
  97. package/scripts/lib/capability-outcome-normalizer.js +104 -0
  98. package/scripts/lib/capability-packs.js +314 -0
  99. package/scripts/lib/capability-recommender.js +513 -0
  100. package/scripts/lib/capability-registry.js +1062 -0
  101. package/scripts/lib/carry-forward-surfacing.js +194 -0
  102. package/scripts/lib/ccusage-adapter.js +188 -0
  103. package/scripts/lib/claude-session-evidence/index.js +1027 -0
  104. package/scripts/lib/cli-output-sanitizer.js +118 -0
  105. package/scripts/lib/company-loop.js +1149 -0
  106. package/scripts/lib/config.js +641 -0
  107. package/scripts/lib/context-acquisition-plan.js +359 -0
  108. package/scripts/lib/context-authority-rules.js +138 -0
  109. package/scripts/lib/context-budget-guard.js +170 -0
  110. package/scripts/lib/context-budget-scanner.js +257 -0
  111. package/scripts/lib/context-budget.js +167 -0
  112. package/scripts/lib/context-claim-safety.js +116 -0
  113. package/scripts/lib/context-compiler.js +236 -0
  114. package/scripts/lib/context-optimizer.js +715 -0
  115. package/scripts/lib/context-packet.js +121 -0
  116. package/scripts/lib/context-receipt.js +57 -0
  117. package/scripts/lib/context-reduction-plan.js +179 -0
  118. package/scripts/lib/context-safety.js +88 -0
  119. package/scripts/lib/context-savings-engine.js +158 -0
  120. package/scripts/lib/context-source-registry.js +393 -0
  121. package/scripts/lib/context-task-profiles.js +147 -0
  122. package/scripts/lib/cost-abuse-guard.js +975 -0
  123. package/scripts/lib/cost-evidence.js +287 -0
  124. package/scripts/lib/cost-to-outcome-signals.js +639 -0
  125. package/scripts/lib/cross-host-install-plan.js +308 -0
  126. package/scripts/lib/cross-host-install-readiness.js +242 -0
  127. package/scripts/lib/cross-host-value-flow.js +268 -0
  128. package/scripts/lib/dashboard.js +1056 -0
  129. package/scripts/lib/data-permissions-guard.js +1187 -0
  130. package/scripts/lib/deployment-environment-guard.js +1236 -0
  131. package/scripts/lib/design-partner-feedback.js +346 -0
  132. package/scripts/lib/dogfood-evidence-freshness.js +567 -0
  133. package/scripts/lib/efficiency-receipt.js +354 -0
  134. package/scripts/lib/entitlements.js +100 -0
  135. package/scripts/lib/execution-packet.js +559 -0
  136. package/scripts/lib/execution-route-governor.js +179 -0
  137. package/scripts/lib/experimentation-events.js +547 -0
  138. package/scripts/lib/external-capability-compliance.js +107 -0
  139. package/scripts/lib/external-readiness/ai-visibility/ai-visibility-runner.js +322 -0
  140. package/scripts/lib/external-readiness/calibration/benchmark-site-catalog.js +121 -0
  141. package/scripts/lib/external-readiness/calibration/completion-gate.js +112 -0
  142. package/scripts/lib/external-readiness/calibration/real-site-benchmark-runner.js +284 -0
  143. package/scripts/lib/external-readiness/drift-detector.js +100 -0
  144. package/scripts/lib/external-readiness/endpoint-prober.js +332 -0
  145. package/scripts/lib/external-readiness/external-readiness-runner.js +112 -0
  146. package/scripts/lib/external-readiness/full-agent-readiness-scorer.js +220 -0
  147. package/scripts/lib/external-readiness/golden-path-runner.js +333 -0
  148. package/scripts/lib/external-readiness/inside-out-runner.js +118 -0
  149. package/scripts/lib/external-readiness/proof-receipt-writer.js +112 -0
  150. package/scripts/lib/external-readiness/safe-fix-executor.js +117 -0
  151. package/scripts/lib/external-readiness/skills/category-boundary-guard.js +30 -0
  152. package/scripts/lib/external-readiness/skills/pmm-positioning-guard.js +69 -0
  153. package/scripts/lib/external-readiness/skills/safe-fix-policy.js +35 -0
  154. package/scripts/lib/external-user-simulation.js +166 -0
  155. package/scripts/lib/failure-recovery-readiness.js +81 -0
  156. package/scripts/lib/failure-recovery.js +419 -0
  157. package/scripts/lib/feedback-intelligence.js +537 -0
  158. package/scripts/lib/feedback-signals.js +205 -0
  159. package/scripts/lib/file-integrity.js +68 -0
  160. package/scripts/lib/fsx.js +129 -0
  161. package/scripts/lib/full-readiness-gate.js +451 -0
  162. package/scripts/lib/governed-action-exposure-primitives.js +1514 -0
  163. package/scripts/lib/governed-exposure-planner.js +186 -0
  164. package/scripts/lib/governed-workspace-hygiene.js +946 -0
  165. package/scripts/lib/guard-suite-common.js +412 -0
  166. package/scripts/lib/guidance-builder.js +174 -0
  167. package/scripts/lib/hook-apply.js +1075 -0
  168. package/scripts/lib/hook-baseline.js +310 -0
  169. package/scripts/lib/hook-config-preview.js +275 -0
  170. package/scripts/lib/hook-contracts.js +290 -0
  171. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  172. package/scripts/lib/host-capability-matrix.js +351 -0
  173. package/scripts/lib/host-support-context.js +254 -0
  174. package/scripts/lib/http-hook-action.js +538 -0
  175. package/scripts/lib/install-ai-readiness.js +84 -0
  176. package/scripts/lib/install-intake-risk.js +1051 -0
  177. package/scripts/lib/install-journey-intelligence.js +329 -0
  178. package/scripts/lib/intervention-guidance.js +57 -0
  179. package/scripts/lib/known-limitations.js +115 -0
  180. package/scripts/lib/l8-path-truth.js +146 -0
  181. package/scripts/lib/launch-hardening-gate.js +436 -0
  182. package/scripts/lib/launch-readiness.js +635 -0
  183. package/scripts/lib/learning-experimentation-foundation.js +1484 -0
  184. package/scripts/lib/learning-memory.js +686 -0
  185. package/scripts/lib/lifecycle-hooks.js +982 -0
  186. package/scripts/lib/local-package-smoke.js +436 -0
  187. package/scripts/lib/local-pricing.js +299 -0
  188. package/scripts/lib/mcp-enforcement.js +311 -0
  189. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  190. package/scripts/lib/mcp-tool-inventory.js +388 -0
  191. package/scripts/lib/mcp-tool-risk.js +0 -0
  192. package/scripts/lib/memory.js +335 -0
  193. package/scripts/lib/metrics.js +699 -0
  194. package/scripts/lib/micro-proof.js +133 -0
  195. package/scripts/lib/model-work-class-routing.js +79 -0
  196. package/scripts/lib/next-run-context.js +436 -0
  197. package/scripts/lib/next-run-continuity.js +1207 -0
  198. package/scripts/lib/observability-recovery-guard.js +1030 -0
  199. package/scripts/lib/operating-value.js +1662 -0
  200. package/scripts/lib/optimization-v3.js +122 -0
  201. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  202. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  203. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  204. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  205. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  206. package/scripts/lib/orchestration/adapters/git.js +25 -0
  207. package/scripts/lib/orchestration/adapters/index.js +31 -0
  208. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  209. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  210. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  211. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  212. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  213. package/scripts/lib/orchestration/cli.js +438 -0
  214. package/scripts/lib/orchestration/execution-manager.js +279 -0
  215. package/scripts/lib/orchestration/handoff.js +314 -0
  216. package/scripts/lib/orchestration/index.js +456 -0
  217. package/scripts/lib/orchestration/inventory.js +47 -0
  218. package/scripts/lib/orchestration/model-discovery.js +498 -0
  219. package/scripts/lib/orchestration/model-profiler.js +170 -0
  220. package/scripts/lib/orchestration/model-profiles.js +252 -0
  221. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  222. package/scripts/lib/orchestration/proof-writer.js +349 -0
  223. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  224. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  225. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  226. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  227. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  228. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  229. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  230. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  231. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  232. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  233. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  234. package/scripts/lib/orchestration/routing-policy.js +486 -0
  235. package/scripts/lib/orchestration/settings.js +95 -0
  236. package/scripts/lib/orchestration/state.js +165 -0
  237. package/scripts/lib/orchestration/verification-manager.js +138 -0
  238. package/scripts/lib/outcome-evidence-stack.js +1083 -0
  239. package/scripts/lib/output-profiles.js +146 -0
  240. package/scripts/lib/package-content-audit.js +368 -0
  241. package/scripts/lib/package-runtime.js +278 -0
  242. package/scripts/lib/plan-surface.js +53 -0
  243. package/scripts/lib/plans.js +2327 -0
  244. package/scripts/lib/policy-provider.js +27 -0
  245. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  246. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  247. package/scripts/lib/prelaunch-intelligence.js +869 -0
  248. package/scripts/lib/pricing-experiment.js +118 -0
  249. package/scripts/lib/pro-moment-events.js +77 -0
  250. package/scripts/lib/pro-moment-state.js +227 -0
  251. package/scripts/lib/pro-moments.js +1216 -0
  252. package/scripts/lib/product-home-receipt.js +1790 -0
  253. package/scripts/lib/product-learning-events.js +696 -0
  254. package/scripts/lib/production-confidence-control-tower.js +1209 -0
  255. package/scripts/lib/production-confidence.js +1858 -0
  256. package/scripts/lib/project-profile.js +555 -0
  257. package/scripts/lib/prompt-compiler-plan.js +94 -0
  258. package/scripts/lib/prompt-compiler.js +280 -0
  259. package/scripts/lib/prompt-lint.js +32 -0
  260. package/scripts/lib/prompt-suggestions.js +52 -0
  261. package/scripts/lib/proof-canonical.js +398 -0
  262. package/scripts/lib/proof-drilldown.js +383 -0
  263. package/scripts/lib/proof-e2e/promise-completion.js +141 -0
  264. package/scripts/lib/proof-e2e/promise-dashboard-claims.js +135 -0
  265. package/scripts/lib/proof-e2e/promise-local-proof.js +156 -0
  266. package/scripts/lib/proof-e2e/promise-provider-neutral.js +136 -0
  267. package/scripts/lib/proof-e2e/promise-registry.js +219 -0
  268. package/scripts/lib/proof-e2e/promise-security-trust.js +141 -0
  269. package/scripts/lib/proof-e2e/promise-skills-risk.js +163 -0
  270. package/scripts/lib/proof-e2e/promise-token-savings.js +112 -0
  271. package/scripts/lib/proof-e2e/promise-work-management.js +126 -0
  272. package/scripts/lib/proof-events.js +342 -0
  273. package/scripts/lib/proof-history.js +243 -0
  274. package/scripts/lib/proof-metrics.js +296 -0
  275. package/scripts/lib/proof-outcome-evidence.js +134 -0
  276. package/scripts/lib/proof-receipt.js +335 -0
  277. package/scripts/lib/proof-record.js +461 -0
  278. package/scripts/lib/proof-savings-report.js +1549 -0
  279. package/scripts/lib/public-activation-distribution-gate.js +266 -0
  280. package/scripts/lib/public-cli.js +5631 -0
  281. package/scripts/lib/public-distribution-truth.js +211 -0
  282. package/scripts/lib/public-distribution.js +73 -0
  283. package/scripts/lib/public-install-claim-checker.js +300 -0
  284. package/scripts/lib/publish-provenance-readiness.js +287 -0
  285. package/scripts/lib/readiness-delta.js +218 -0
  286. package/scripts/lib/readiness-evidence-closure.js +196 -0
  287. package/scripts/lib/reentry-memory-capture.js +241 -0
  288. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  289. package/scripts/lib/reentry-memory-status.js +147 -0
  290. package/scripts/lib/reentry-memory-store.js +183 -0
  291. package/scripts/lib/reentry-state.js +66 -0
  292. package/scripts/lib/release-candidate-bundle.js +166 -0
  293. package/scripts/lib/remediation.js +81 -0
  294. package/scripts/lib/repo-map.js +391 -0
  295. package/scripts/lib/run-action-policy.js +104 -0
  296. package/scripts/lib/run-contract-receipt.js +48 -0
  297. package/scripts/lib/run-contract.js +288 -0
  298. package/scripts/lib/run-cost-to-verified-outcome.js +87 -0
  299. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  300. package/scripts/lib/run-improvements.js +789 -0
  301. package/scripts/lib/run-proof-contract.js +116 -0
  302. package/scripts/lib/run-risk-classifier.js +131 -0
  303. package/scripts/lib/run-task-classifier.js +216 -0
  304. package/scripts/lib/runtime-decision-policy.js +387 -0
  305. package/scripts/lib/safe-path-engine.js +705 -0
  306. package/scripts/lib/safe-repair.js +877 -0
  307. package/scripts/lib/safe-run-controller.js +887 -0
  308. package/scripts/lib/scope-repair-before-approval.js +865 -0
  309. package/scripts/lib/score.js +262 -0
  310. package/scripts/lib/seamless-enforcement.js +329 -0
  311. package/scripts/lib/seamless-outcome.js +689 -0
  312. package/scripts/lib/seamless-reality-gate.js +5053 -0
  313. package/scripts/lib/security-confidence/activation-safety.js +78 -0
  314. package/scripts/lib/security-confidence/adapters/adapter-contract.js +125 -0
  315. package/scripts/lib/security-confidence/adapters/mock-claude-bughunter-adapter.js +36 -0
  316. package/scripts/lib/security-confidence/adapters/mock-decepticon-adapter.js +34 -0
  317. package/scripts/lib/security-confidence/adapters/mock-mcp-scan-adapter.js +35 -0
  318. package/scripts/lib/security-confidence/adapters/mock-snyk-agent-scan-adapter.js +35 -0
  319. package/scripts/lib/security-confidence/adapters/mock-tank-adapter.js +34 -0
  320. package/scripts/lib/security-confidence/adapters/reference-registry.js +63 -0
  321. package/scripts/lib/security-confidence/auth-rbac-plan.js +39 -0
  322. package/scripts/lib/security-confidence/component-inventory.js +151 -0
  323. package/scripts/lib/security-confidence/constants.js +191 -0
  324. package/scripts/lib/security-confidence/dependency-script-risk.js +95 -0
  325. package/scripts/lib/security-confidence/evidence-hygiene.js +127 -0
  326. package/scripts/lib/security-confidence/feature-impact-classifier.js +80 -0
  327. package/scripts/lib/security-confidence/file-utils.js +161 -0
  328. package/scripts/lib/security-confidence/finding-triage.js +90 -0
  329. package/scripts/lib/security-confidence/findings.js +115 -0
  330. package/scripts/lib/security-confidence/index.js +29 -0
  331. package/scripts/lib/security-confidence/permission-budget.js +93 -0
  332. package/scripts/lib/security-confidence/redaction.js +82 -0
  333. package/scripts/lib/security-confidence/redteam-fixtures.js +53 -0
  334. package/scripts/lib/security-confidence/reference-adoption.js +229 -0
  335. package/scripts/lib/security-confidence/reference-taxonomy.js +155 -0
  336. package/scripts/lib/security-confidence/report-quality.js +99 -0
  337. package/scripts/lib/security-confidence/report-writer.js +79 -0
  338. package/scripts/lib/security-confidence/risk-classifier.js +187 -0
  339. package/scripts/lib/security-confidence/runner.js +179 -0
  340. package/scripts/lib/security-confidence/schema.js +188 -0
  341. package/scripts/lib/security-confidence/secret-safety.js +74 -0
  342. package/scripts/lib/security-confidence/security-capability-router.js +98 -0
  343. package/scripts/lib/security-confidence/web-security-smoke-plan.js +46 -0
  344. package/scripts/lib/security-risk-classifier.js +511 -0
  345. package/scripts/lib/security-scan.js +396 -0
  346. package/scripts/lib/session-context-optimizer.js +1211 -0
  347. package/scripts/lib/session-timing.js +322 -0
  348. package/scripts/lib/skill-hygiene.js +805 -0
  349. package/scripts/lib/skill-packs.js +161 -0
  350. package/scripts/lib/skills-operating-layer-v1.js +1023 -0
  351. package/scripts/lib/skills-operating-layer.js +629 -0
  352. package/scripts/lib/smallest-safe-plan.js +214 -0
  353. package/scripts/lib/smart-routing-plan.js +115 -0
  354. package/scripts/lib/smart-work-routing.js +768 -0
  355. package/scripts/lib/source-catalog.js +1022 -0
  356. package/scripts/lib/status-value-summary.js +32 -0
  357. package/scripts/lib/supply-chain-dependency-guard.js +1333 -0
  358. package/scripts/lib/supply-chain-guard.js +219 -0
  359. package/scripts/lib/support-bundle.js +561 -0
  360. package/scripts/lib/support-redaction.js +66 -0
  361. package/scripts/lib/task-continuation.js +440 -0
  362. package/scripts/lib/test-helpers.js +15 -0
  363. package/scripts/lib/tier.js +38 -0
  364. package/scripts/lib/token-context-quality-gate.js +370 -0
  365. package/scripts/lib/token-cost-capture.js +187 -0
  366. package/scripts/lib/token-cost-evidence.js +862 -0
  367. package/scripts/lib/token-cost-intelligence.js +358 -0
  368. package/scripts/lib/token-efficiency-evidence.js +213 -0
  369. package/scripts/lib/token-efficiency-map.js +110 -0
  370. package/scripts/lib/token-evidence.js +703 -0
  371. package/scripts/lib/tokenish.js +17 -0
  372. package/scripts/lib/tool-output-sandbox.js +559 -0
  373. package/scripts/lib/trust-audit.js +136 -0
  374. package/scripts/lib/unified-events.js +396 -0
  375. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  376. package/scripts/lib/usage-ledger.js +201 -0
  377. package/scripts/lib/value-ledger.js +130 -0
  378. package/scripts/lib/value-proof-calibration.js +531 -0
  379. package/scripts/lib/visual-qa.js +231 -0
  380. package/scripts/lib/voice-alpha.js +29 -0
  381. package/scripts/lib/webhook-idempotency-guard.js +990 -0
  382. package/scripts/lib/work-aware-orchestration.js +976 -0
  383. package/scripts/lib/work-control-receipts.js +577 -0
  384. package/scripts/lib/work-ledger.js +1123 -0
  385. package/scripts/lib/work-panel-preview.js +352 -0
  386. package/scripts/lib/workflow-discipline.js +280 -0
  387. package/scripts/lib/workflow-intelligence-radar.js +804 -0
  388. package/scripts/lib/workflow-signals.js +419 -0
  389. package/scripts/lib/workspace-map.js +281 -0
  390. package/scripts/lib/workspace-registry.js +1367 -0
  391. package/scripts/lib/workspace-resolver.js +542 -0
  392. package/scripts/wuz-doctor.js +315 -0
  393. package/bin/avorelo.mjs +0 -7
  394. package/dist/avorelo.mjs +0 -24678
@@ -0,0 +1,975 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const { nowIso, safeReadJson, safeWriteJson } = require("./fsx");
7
+
8
+ const CONTRACT = "avorelo.costAbuseGuard.v1";
9
+ const REPORT_DIR_REL = ".claude/cco/reports/cost-abuse-guard";
10
+ const REPORT_JSON_REL = `${REPORT_DIR_REL}/latest-report.json`;
11
+
12
+ const TEXT_FILE_RE = /\.(cjs|cts|env|ini|js|json|jsx|md|mjs|mts|prisma|sql|toml|ts|tsx|txt|yaml|yml)$/i;
13
+ const SKIP_DIRS = new Set([
14
+ ".claude",
15
+ ".git",
16
+ ".next",
17
+ ".nuxt",
18
+ ".turbo",
19
+ ".wasp",
20
+ "build",
21
+ "coverage",
22
+ "dist",
23
+ "node_modules",
24
+ ]);
25
+
26
+ // ---------------------------------------------------------------------------
27
+ // Detection patterns
28
+ // ---------------------------------------------------------------------------
29
+
30
+ const RETRY_RE = /\b(retry|retries|backoff|retryable|exponentialBackoff|retryWithBackoff|retryDelay|maxRetries|retryCount|retryInterval)\b/i;
31
+ const RETRY_BOUNDED_RE = /\b(maxRetries|max_retries|retryLimit|retry_limit|MAX_RETRIES|MAX_RETRY|retryCount\s*[<>=]|attempts\s*[<>=]|retries\s*[<>=])\b/i;
32
+ const POLLING_RE = /\b(setInterval|polling|poll|pollInterval|startPolling|pollUntil|longPoll)\b/i;
33
+ const POLLING_BOUNDED_RE = /\b(clearInterval|setTimeout|maxPolls|pollTimeout|stopPolling|MAX_POLLS|AbortController)\b/i;
34
+ const SCHEDULED_JOB_RE = /\b(cron|schedule|node-cron|node_cron|cronJob|scheduledTask|agenda|bull|bullmq|bree)\b/i;
35
+ const QUEUE_WORKER_RE = /\b(queue|worker|bull|bullmq|amqp|amqplib|rabbitmq|celery|bee-queue|pgboss|faktory)\b/i;
36
+ const BATCH_JOB_RE = /\b(batch|bulk|batchProcess|bulkInsert|bulkCreate|bulkWrite|batchSize|BATCH_SIZE)\b/i;
37
+ const BATCH_BOUNDED_RE = /\b(batchSize|batch_size|BATCH_SIZE|limit|chunkSize|chunk_size|pageSize|page_size)\b/i;
38
+ const RATE_LIMIT_RE = /\b(rateLimit|rate_limit|rateLimiter|throttle|express-rate-limit|@upstash\/ratelimit|bottleneck|p-throttle|limiter)\b/i;
39
+ const TIMEOUT_RE = /\b(timeout|setTimeout|AbortController|signal|requestTimeout|connectTimeout|idleTimeout|keepAliveTimeout)\b/i;
40
+ const CONCURRENCY_RE = /\b(concurrency|parallel|pool|pLimit|p-limit|Promise\.all|Promise\.allSettled|workerPool|threadPool|cluster\.fork)\b/i;
41
+ const CONCURRENCY_BOUNDED_RE = /\b(pLimit|p-limit|concurrency\s*[:=]\s*\d|maxConcurrency|MAX_CONCURRENCY|pool\s*[:=]\s*\d|poolSize|maxWorkers|max_workers)\b/i;
42
+ const PUBLIC_ENDPOINT_RE = /\b(app\.(get|post|put|patch|delete)\s*\(|router\.(get|post|put|patch|delete)\s*\(|export\s+(async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)\b|export\s+const\s+(GET|POST|PUT|PATCH|DELETE)\b)\b/;
43
+ const AUTH_MIDDLEWARE_RE = /\b(auth|authenticate|requireAuth|requireUser|verifyToken|getServerSession|clerkMiddleware|authMiddleware|isAuthenticated|passport\.authenticate|jwt\.verify|verifyAuth)\b/i;
44
+ const AI_TOOL_LOOP_RE = /\b(while|for)\b[\s\S]{0,200}\b(tool_call|toolCall|function_call|functionCall|tool_use|toolUse|runTools|executeTools|completions\.create|chat\.completions|anthropic\.messages|openai\.chat)\b/i;
45
+ const AI_MODEL_RE = /\b(openai|anthropic|completions\.create|chat\.completions|messages\.create|generateText|streamText|ai\.run|model\.generate|llm\.call)\b/i;
46
+ const EXPENSIVE_SCRIPT_RE = /\b(ffmpeg|sharp|imagemagick|puppeteer|playwright|chromium|headless|pdf|render|transcode|encode|compress|crypto\.pbkdf2|crypto\.scrypt|bcrypt\.hash)\b/i;
47
+ const PAYMENT_RE = /\b(stripe|payment|billing|charge|invoice|subscription|checkout|paymentIntent|createCharge|createPaymentIntent|createSubscription)\b/i;
48
+ const FREE_TRIAL_RE = /\b(trial|free.plan|freemium|free.tier|trialEnd|trialDays|isTrial|FREE_PLAN|FREE_TIER)\b/i;
49
+ const CACHE_RE = /\b(cache|redis|memoize|lru-cache|node-cache|ioredis|memcached|cacheControl|stale-while-revalidate|unstable_cache|revalidate)\b/i;
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Helpers
53
+ // ---------------------------------------------------------------------------
54
+
55
+ function unique(values) {
56
+ return [...new Set((values || []).filter(Boolean))];
57
+ }
58
+
59
+ function toPosix(value) {
60
+ return String(value || "").replace(/\\/g, "/");
61
+ }
62
+
63
+ function sanitizeText(value, max = 220) {
64
+ const text = String(value || "")
65
+ .replace(/\s+/g, " ")
66
+ .replace(/(?:sk-[A-Za-z0-9_-]{16,}|gh[ps]_[A-Za-z0-9]{12,}|npm_[A-Za-z0-9]{12,}|AKIA[0-9A-Z]{16})/g, "[redacted-token]")
67
+ .replace(/(bearer\s+)[A-Za-z0-9._-]{8,}/ig, "$1[redacted]")
68
+ .replace(/((?:api[_ -]?key|token|password|secret|authorization)\s*[:=]\s*)([^\s'"]+)/ig, "$1[redacted]")
69
+ .trim();
70
+ if (!text) return null;
71
+ return text.length > max ? `${text.slice(0, max - 3).trimEnd()}...` : text;
72
+ }
73
+
74
+ function sanitizeRef(ref) {
75
+ const text = String(ref || "").trim().replace(/\\/g, "/");
76
+ if (!text || text.startsWith("..")) return null;
77
+ return text;
78
+ }
79
+
80
+ function makeEvidenceRef(relPath, lineNumber, label) {
81
+ const base = sanitizeRef(relPath);
82
+ if (!base) return null;
83
+ const suffix = Number.isFinite(lineNumber) && lineNumber > 0 ? `:${lineNumber}` : "";
84
+ const note = label ? ` (${sanitizeText(label, 80)})` : "";
85
+ return `${base}${suffix}${note}`;
86
+ }
87
+
88
+ function severityRank(severity) {
89
+ if (severity === "block") return 4;
90
+ if (severity === "warn") return 3;
91
+ if (severity === "info") return 2;
92
+ return 1;
93
+ }
94
+
95
+ // ---------------------------------------------------------------------------
96
+ // Filesystem
97
+ // ---------------------------------------------------------------------------
98
+
99
+ function walkWorkspace(cwd) {
100
+ const files = [];
101
+
102
+ function visit(current, depth) {
103
+ if (depth > 8 || files.length >= 2200) return;
104
+ let entries = [];
105
+ try {
106
+ entries = fs.readdirSync(current, { withFileTypes: true });
107
+ } catch {
108
+ return;
109
+ }
110
+
111
+ for (const entry of entries) {
112
+ if (files.length >= 2200) return;
113
+ const absPath = path.join(current, entry.name);
114
+ const relPath = toPosix(path.relative(cwd, absPath));
115
+ if (entry.isDirectory()) {
116
+ if (SKIP_DIRS.has(entry.name)) continue;
117
+ visit(absPath, depth + 1);
118
+ continue;
119
+ }
120
+ if (!entry.isFile()) continue;
121
+ files.push({
122
+ absPath,
123
+ relPath,
124
+ text: undefined,
125
+ });
126
+ }
127
+ }
128
+
129
+ visit(cwd, 0);
130
+ return files;
131
+ }
132
+
133
+ function readText(file) {
134
+ if (file.text !== undefined) return file.text;
135
+ if (!TEXT_FILE_RE.test(file.relPath)) {
136
+ file.text = null;
137
+ return file.text;
138
+ }
139
+ try {
140
+ const stat = fs.statSync(file.absPath);
141
+ if (stat.size > 256 * 1024) {
142
+ file.text = null;
143
+ return file.text;
144
+ }
145
+ file.text = fs.readFileSync(file.absPath, "utf8");
146
+ return file.text;
147
+ } catch {
148
+ file.text = null;
149
+ return file.text;
150
+ }
151
+ }
152
+
153
+ function findLineNumber(text, index) {
154
+ return text.slice(0, index).split(/\r?\n/).length;
155
+ }
156
+
157
+ function collectPatternRefs(file, regex, options = {}) {
158
+ const text = readText(file);
159
+ if (!text) return [];
160
+ const limit = options.limit || 5;
161
+ const refs = [];
162
+ const flags = regex.flags.includes("g") ? regex.flags : `${regex.flags}g`;
163
+ const matcher = new RegExp(regex.source, flags);
164
+ let match;
165
+ while ((match = matcher.exec(text)) && refs.length < limit) {
166
+ const lineNumber = findLineNumber(text, match.index);
167
+ refs.push(makeEvidenceRef(file.relPath, lineNumber, options.label ? options.label(match) : null));
168
+ if (match.index === matcher.lastIndex) matcher.lastIndex += 1;
169
+ }
170
+ return refs.filter(Boolean);
171
+ }
172
+
173
+ // ---------------------------------------------------------------------------
174
+ // Report scaffold
175
+ // ---------------------------------------------------------------------------
176
+
177
+ function buildEmptyReport(startTime) {
178
+ return {
179
+ schemaVersion: CONTRACT,
180
+ generatedAt: nowIso(),
181
+ status: "no_data",
182
+ redacted: true,
183
+ summary: {
184
+ title: "Cost & Abuse Guard",
185
+ text: "No cost or abuse surface detected.",
186
+ findingCounts: { info: 0, warn: 0, block: 0 },
187
+ safeNextAction: "No cost or abuse surface was detected. Re-run after adding runtime, API, or scheduling code.",
188
+ },
189
+ surfaceEvidence: {
190
+ filesInspected: 0,
191
+ artifactsInspected: 0,
192
+ changedFilesConsidered: 0,
193
+ surfacesDetected: [],
194
+ limitsApplied: [],
195
+ },
196
+ findings: [],
197
+ productHome: {
198
+ cardSource: "costAbuseGuard",
199
+ status: "no_data",
200
+ found: [],
201
+ fixedOrPrepared: [],
202
+ savedOrProved: [],
203
+ forReview: [],
204
+ safeNextAction: "No cost or abuse surface was detected.",
205
+ },
206
+ valueProof: {
207
+ implemented: true,
208
+ dogfooded: false,
209
+ measured: false,
210
+ measurementType: "heuristic",
211
+ publicClaimAllowed: false,
212
+ evidenceRefs: [],
213
+ caveat: "Local static scan only. No billing or provider API calls.",
214
+ },
215
+ limits: {
216
+ localFirst: true,
217
+ deterministicFirst: true,
218
+ modelCalls: false,
219
+ networkCalls: false,
220
+ providerCalls: false,
221
+ rawLogDump: false,
222
+ rawPromptDump: false,
223
+ rawCodeDump: false,
224
+ secretDump: false,
225
+ autoFix: false,
226
+ },
227
+ overhead: {
228
+ durationMs: 0,
229
+ filesInspected: 0,
230
+ artifactsInspected: 0,
231
+ limitsApplied: [],
232
+ },
233
+ _startTime: startTime,
234
+ };
235
+ }
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // State
239
+ // ---------------------------------------------------------------------------
240
+
241
+ function createState() {
242
+ return {
243
+ seenFindingIds: new Set(),
244
+ surfacesDetected: new Set(),
245
+ limitsApplied: [],
246
+ findingCounter: 0,
247
+ };
248
+ }
249
+
250
+ // ---------------------------------------------------------------------------
251
+ // Finding helpers
252
+ // ---------------------------------------------------------------------------
253
+
254
+ function addFinding(report, state, fields) {
255
+ const evidenceRef = sanitizeRef(fields.evidenceRef);
256
+ const key = `${fields.reasonCode}:${evidenceRef || "none"}`;
257
+ if (state.seenFindingIds.has(key)) return;
258
+ state.seenFindingIds.add(key);
259
+
260
+ state.findingCounter += 1;
261
+ const id = `cost-abuse-${String(state.findingCounter).padStart(3, "0")}`;
262
+
263
+ const finding = {
264
+ id,
265
+ severity: fields.severity,
266
+ reasonCode: fields.reasonCode,
267
+ title: sanitizeText(fields.title, 120),
268
+ message: sanitizeText(fields.message || fields.title, 220),
269
+ evidenceRefs: [
270
+ evidenceRef ? { path: evidenceRef, kind: fields.kind || "pattern", selector: fields.selector || null, redacted: true } : null,
271
+ ].filter(Boolean),
272
+ safeNextAction: sanitizeText(fields.safeNextAction, 220) || "Review cost or abuse surfaces before trusting this path.",
273
+ };
274
+ report.findings.push(finding);
275
+ }
276
+
277
+ function addSurface(state, name) {
278
+ state.surfacesDetected.add(name);
279
+ }
280
+
281
+ // ---------------------------------------------------------------------------
282
+ // Detection: retry loops
283
+ // ---------------------------------------------------------------------------
284
+
285
+ function detectRetryLoops(report, state, files) {
286
+ for (const file of files) {
287
+ const text = readText(file);
288
+ if (!text) continue;
289
+ if (!RETRY_RE.test(text)) continue;
290
+
291
+ addSurface(state, "retry_loop");
292
+ const bounded = RETRY_BOUNDED_RE.test(text);
293
+ const refs = collectPatternRefs(file, RETRY_RE, { limit: 2 });
294
+ const ref = refs[0];
295
+
296
+ if (bounded) {
297
+ addFinding(report, state, {
298
+ severity: "info",
299
+ reasonCode: "COST_ABUSE_RETRY_BOUNDED",
300
+ title: "Retry with max cap detected",
301
+ message: "Retry loop appears bounded with a max-retries limit.",
302
+ evidenceRef: ref,
303
+ kind: "pattern",
304
+ safeNextAction: "Verify the retry cap is reasonable for production load.",
305
+ });
306
+ } else {
307
+ addFinding(report, state, {
308
+ severity: "warn",
309
+ reasonCode: "COST_ABUSE_RETRY_UNBOUNDED",
310
+ title: "Retry without max cap",
311
+ message: "Retry or backoff pattern found without a visible max-retries limit.",
312
+ evidenceRef: ref,
313
+ kind: "pattern",
314
+ safeNextAction: "Add a max-retries cap or circuit breaker before trusting this retry path.",
315
+ });
316
+ }
317
+ }
318
+ }
319
+
320
+ // ---------------------------------------------------------------------------
321
+ // Detection: polling loops
322
+ // ---------------------------------------------------------------------------
323
+
324
+ function detectPollingLoops(report, state, files) {
325
+ for (const file of files) {
326
+ const text = readText(file);
327
+ if (!text) continue;
328
+ if (!POLLING_RE.test(text)) continue;
329
+
330
+ addSurface(state, "polling_loop");
331
+ const bounded = POLLING_BOUNDED_RE.test(text);
332
+ const refs = collectPatternRefs(file, POLLING_RE, { limit: 2 });
333
+ const ref = refs[0];
334
+
335
+ if (bounded) {
336
+ addFinding(report, state, {
337
+ severity: "info",
338
+ reasonCode: "COST_ABUSE_POLLING_FOUND",
339
+ title: "Polling with timeout detected",
340
+ message: "Polling pattern found with visible timeout or cleanup.",
341
+ evidenceRef: ref,
342
+ kind: "pattern",
343
+ safeNextAction: "Verify the polling timeout is reasonable for production.",
344
+ });
345
+ } else {
346
+ addFinding(report, state, {
347
+ severity: "warn",
348
+ reasonCode: "COST_ABUSE_POLLING_UNBOUNDED",
349
+ title: "Polling without timeout",
350
+ message: "Polling or setInterval pattern found without a visible timeout or clearInterval.",
351
+ evidenceRef: ref,
352
+ kind: "pattern",
353
+ safeNextAction: "Add a timeout or max-polls limit before trusting this polling path.",
354
+ });
355
+ }
356
+ }
357
+ }
358
+
359
+ // ---------------------------------------------------------------------------
360
+ // Detection: scheduled jobs
361
+ // ---------------------------------------------------------------------------
362
+
363
+ function detectScheduledJobs(report, state, files) {
364
+ for (const file of files) {
365
+ const text = readText(file);
366
+ if (!text) continue;
367
+ if (!SCHEDULED_JOB_RE.test(text)) continue;
368
+
369
+ addSurface(state, "scheduled_job");
370
+ const refs = collectPatternRefs(file, SCHEDULED_JOB_RE, { limit: 2 });
371
+
372
+ addFinding(report, state, {
373
+ severity: "info",
374
+ reasonCode: "COST_ABUSE_SURFACE_FOUND",
375
+ title: "Scheduled job or cron pattern detected",
376
+ message: "Cron or scheduling pattern detected. Verify cost bounds.",
377
+ evidenceRef: refs[0],
378
+ kind: "pattern",
379
+ safeNextAction: "Verify that scheduled jobs have bounded execution time and cost limits.",
380
+ });
381
+ }
382
+ }
383
+
384
+ // ---------------------------------------------------------------------------
385
+ // Detection: queue workers
386
+ // ---------------------------------------------------------------------------
387
+
388
+ function detectQueueWorkers(report, state, files) {
389
+ for (const file of files) {
390
+ const text = readText(file);
391
+ if (!text) continue;
392
+ if (!QUEUE_WORKER_RE.test(text)) continue;
393
+
394
+ addSurface(state, "queue_worker");
395
+ const hasConcurrency = CONCURRENCY_BOUNDED_RE.test(text);
396
+ const refs = collectPatternRefs(file, QUEUE_WORKER_RE, { limit: 2 });
397
+
398
+ addFinding(report, state, {
399
+ severity: hasConcurrency ? "info" : "warn",
400
+ reasonCode: hasConcurrency ? "COST_ABUSE_QUEUE_WORKER_FOUND" : "COST_ABUSE_MISSING_CONCURRENCY_LIMIT",
401
+ title: hasConcurrency ? "Queue worker with concurrency limit" : "Queue worker without visible concurrency limit",
402
+ message: hasConcurrency
403
+ ? "Queue worker pattern detected with a visible concurrency limit."
404
+ : "Queue worker pattern detected without a visible concurrency or pool limit.",
405
+ evidenceRef: refs[0],
406
+ kind: "pattern",
407
+ safeNextAction: hasConcurrency
408
+ ? "Verify the concurrency limit is appropriate for production."
409
+ : "Add a concurrency or pool limit to the queue worker before trusting this path.",
410
+ });
411
+ }
412
+ }
413
+
414
+ // ---------------------------------------------------------------------------
415
+ // Detection: batch jobs
416
+ // ---------------------------------------------------------------------------
417
+
418
+ function detectBatchJobs(report, state, files) {
419
+ for (const file of files) {
420
+ const text = readText(file);
421
+ if (!text) continue;
422
+ if (!BATCH_JOB_RE.test(text)) continue;
423
+
424
+ addSurface(state, "batch_job");
425
+ const bounded = BATCH_BOUNDED_RE.test(text);
426
+ const refs = collectPatternRefs(file, BATCH_JOB_RE, { limit: 2 });
427
+
428
+ if (bounded) {
429
+ addFinding(report, state, {
430
+ severity: "info",
431
+ reasonCode: "COST_ABUSE_BATCH_JOB_FOUND",
432
+ title: "Batch job with size limit detected",
433
+ message: "Batch or bulk pattern detected with a visible size limit.",
434
+ evidenceRef: refs[0],
435
+ kind: "pattern",
436
+ safeNextAction: "Verify the batch size is reasonable for production load.",
437
+ });
438
+ } else {
439
+ addFinding(report, state, {
440
+ severity: "warn",
441
+ reasonCode: "COST_ABUSE_BATCH_UNBOUNDED",
442
+ title: "Batch job without visible size limit",
443
+ message: "Batch or bulk pattern detected without a visible batch size or limit.",
444
+ evidenceRef: refs[0],
445
+ kind: "pattern",
446
+ safeNextAction: "Add a batch size or limit before trusting this batch path.",
447
+ });
448
+ }
449
+ }
450
+ }
451
+
452
+ // ---------------------------------------------------------------------------
453
+ // Detection: rate limiting
454
+ // ---------------------------------------------------------------------------
455
+
456
+ function detectRateLimiting(report, state, files) {
457
+ let hasRateLimitLib = false;
458
+
459
+ for (const file of files) {
460
+ const text = readText(file);
461
+ if (!text) continue;
462
+ if (!RATE_LIMIT_RE.test(text)) continue;
463
+
464
+ addSurface(state, "rate_limit");
465
+ hasRateLimitLib = true;
466
+ const refs = collectPatternRefs(file, RATE_LIMIT_RE, { limit: 2 });
467
+
468
+ addFinding(report, state, {
469
+ severity: "info",
470
+ reasonCode: "COST_ABUSE_RATE_LIMIT_FOUND",
471
+ title: "Rate limiting evidence detected",
472
+ message: "Rate-limit or throttle pattern found.",
473
+ evidenceRef: refs[0],
474
+ kind: "pattern",
475
+ safeNextAction: "Verify rate limits are applied to all public and cost-sensitive endpoints.",
476
+ });
477
+ }
478
+
479
+ return hasRateLimitLib;
480
+ }
481
+
482
+ // ---------------------------------------------------------------------------
483
+ // Detection: timeouts
484
+ // ---------------------------------------------------------------------------
485
+
486
+ function detectTimeouts(report, state, files) {
487
+ for (const file of files) {
488
+ const text = readText(file);
489
+ if (!text) continue;
490
+ if (!TIMEOUT_RE.test(text)) continue;
491
+ // Skip if it's just a test file or config
492
+ if (/\.(test|spec|config)\./i.test(file.relPath)) continue;
493
+
494
+ addSurface(state, "timeout");
495
+ const refs = collectPatternRefs(file, /\b(AbortController|requestTimeout|connectTimeout|idleTimeout)\b/i, { limit: 2 });
496
+ if (refs.length > 0) {
497
+ addFinding(report, state, {
498
+ severity: "info",
499
+ reasonCode: "COST_ABUSE_TIMEOUT_FOUND",
500
+ title: "Timeout or abort evidence detected",
501
+ message: "Timeout or AbortController usage found.",
502
+ evidenceRef: refs[0],
503
+ kind: "pattern",
504
+ safeNextAction: "Verify timeouts cover all external and cost-sensitive calls.",
505
+ });
506
+ }
507
+ }
508
+ }
509
+
510
+ // ---------------------------------------------------------------------------
511
+ // Detection: concurrency
512
+ // ---------------------------------------------------------------------------
513
+
514
+ function detectConcurrency(report, state, files) {
515
+ for (const file of files) {
516
+ const text = readText(file);
517
+ if (!text) continue;
518
+ if (!CONCURRENCY_RE.test(text)) continue;
519
+
520
+ addSurface(state, "concurrency");
521
+ const bounded = CONCURRENCY_BOUNDED_RE.test(text);
522
+ const refs = collectPatternRefs(file, CONCURRENCY_RE, { limit: 2 });
523
+
524
+ if (bounded) {
525
+ addFinding(report, state, {
526
+ severity: "info",
527
+ reasonCode: "COST_ABUSE_CONCURRENCY_LIMIT_FOUND",
528
+ title: "Concurrency limit detected",
529
+ message: "Concurrency or parallel pattern with a visible limit.",
530
+ evidenceRef: refs[0],
531
+ kind: "pattern",
532
+ safeNextAction: "Verify the concurrency limit is appropriate for production.",
533
+ });
534
+ } else {
535
+ addFinding(report, state, {
536
+ severity: "warn",
537
+ reasonCode: "COST_ABUSE_MISSING_CONCURRENCY_LIMIT",
538
+ title: "Concurrency without visible limit",
539
+ message: "Parallel or pool pattern found without a visible concurrency cap.",
540
+ evidenceRef: refs[0],
541
+ kind: "pattern",
542
+ safeNextAction: "Add a concurrency limit before trusting this parallel execution path.",
543
+ });
544
+ }
545
+ }
546
+ }
547
+
548
+ // ---------------------------------------------------------------------------
549
+ // Detection: public endpoints without auth/rate-limit
550
+ // ---------------------------------------------------------------------------
551
+
552
+ function detectPublicEndpoints(report, state, files, hasRateLimitLib) {
553
+ for (const file of files) {
554
+ const text = readText(file);
555
+ if (!text) continue;
556
+ if (!PUBLIC_ENDPOINT_RE.test(text)) continue;
557
+
558
+ addSurface(state, "public_endpoint");
559
+ const hasAuth = AUTH_MIDDLEWARE_RE.test(text);
560
+ const hasRateLimit = RATE_LIMIT_RE.test(text);
561
+ const refs = collectPatternRefs(file, PUBLIC_ENDPOINT_RE, { limit: 2 });
562
+
563
+ if (!hasAuth && !hasRateLimit) {
564
+ addFinding(report, state, {
565
+ severity: "warn",
566
+ reasonCode: "COST_ABUSE_MISSING_RATE_LIMIT",
567
+ title: "Public endpoint without auth or rate limit",
568
+ message: "API endpoint found without visible auth middleware or rate limiting.",
569
+ evidenceRef: refs[0],
570
+ kind: "pattern",
571
+ safeNextAction: "Add rate limiting or auth middleware to public endpoints before production.",
572
+ });
573
+ } else {
574
+ addFinding(report, state, {
575
+ severity: "info",
576
+ reasonCode: "COST_ABUSE_PUBLIC_ENDPOINT_FOUND",
577
+ title: "Public endpoint with protection evidence",
578
+ message: `API endpoint found with ${hasAuth ? "auth" : ""}${hasAuth && hasRateLimit ? " and " : ""}${hasRateLimit ? "rate limit" : ""} evidence.`,
579
+ evidenceRef: refs[0],
580
+ kind: "pattern",
581
+ safeNextAction: "Verify all public endpoints have adequate rate limiting and auth.",
582
+ });
583
+ }
584
+ }
585
+ }
586
+
587
+ // ---------------------------------------------------------------------------
588
+ // Detection: AI tool loops
589
+ // ---------------------------------------------------------------------------
590
+
591
+ function detectAiToolLoops(report, state, files) {
592
+ for (const file of files) {
593
+ const text = readText(file);
594
+ if (!text) continue;
595
+ if (!AI_MODEL_RE.test(text)) continue;
596
+
597
+ const hasLoop = AI_TOOL_LOOP_RE.test(text);
598
+ if (hasLoop) {
599
+ addSurface(state, "ai_tool_loop");
600
+ const hasBound = /\b(maxIterations|max_iterations|MAX_ITERATIONS|maxSteps|max_steps|maxToolCalls|maxRounds|iteration\s*[<>=]|step\s*[<>=])\b/i.test(text);
601
+ const refs = collectPatternRefs(file, AI_TOOL_LOOP_RE, { limit: 2 });
602
+
603
+ if (hasBound) {
604
+ addFinding(report, state, {
605
+ severity: "warn",
606
+ reasonCode: "COST_ABUSE_AI_TOOL_LOOP_FOUND",
607
+ title: "AI tool loop with iteration bound",
608
+ message: "AI model call inside a loop with a visible iteration limit.",
609
+ evidenceRef: refs[0],
610
+ kind: "pattern",
611
+ safeNextAction: "Verify the AI loop iteration cap and cost budget are appropriate for production.",
612
+ });
613
+ } else {
614
+ addFinding(report, state, {
615
+ severity: "block",
616
+ reasonCode: "COST_ABUSE_AI_TOOL_LOOP_FOUND",
617
+ title: "AI tool loop without visible iteration bound",
618
+ message: "AI model call inside a loop without a visible max-iterations or max-steps limit. This is a cost runaway risk.",
619
+ evidenceRef: refs[0],
620
+ kind: "pattern",
621
+ safeNextAction: "Add a max-iterations or cost budget cap to the AI tool loop before production.",
622
+ });
623
+ }
624
+ }
625
+ }
626
+ }
627
+
628
+ // ---------------------------------------------------------------------------
629
+ // Detection: expensive scripts
630
+ // ---------------------------------------------------------------------------
631
+
632
+ function detectExpensiveScripts(report, state, files) {
633
+ for (const file of files) {
634
+ const text = readText(file);
635
+ if (!text) continue;
636
+ if (!EXPENSIVE_SCRIPT_RE.test(text)) continue;
637
+
638
+ addSurface(state, "expensive_script");
639
+ const hasTimeout = TIMEOUT_RE.test(text);
640
+ const refs = collectPatternRefs(file, EXPENSIVE_SCRIPT_RE, { limit: 2 });
641
+
642
+ addFinding(report, state, {
643
+ severity: hasTimeout ? "info" : "warn",
644
+ reasonCode: hasTimeout ? "COST_ABUSE_SURFACE_FOUND" : "COST_ABUSE_MISSING_TIMEOUT",
645
+ title: hasTimeout ? "Expensive compute with timeout" : "Expensive compute without visible timeout",
646
+ message: hasTimeout
647
+ ? "Heavy compute pattern detected with timeout evidence."
648
+ : "Heavy compute pattern (image processing, PDF, headless browser, etc.) without a visible timeout.",
649
+ evidenceRef: refs[0],
650
+ kind: "pattern",
651
+ safeNextAction: hasTimeout
652
+ ? "Verify the timeout is reasonable for production workloads."
653
+ : "Add a timeout or resource limit to expensive compute before production.",
654
+ });
655
+ }
656
+ }
657
+
658
+ // ---------------------------------------------------------------------------
659
+ // Detection: abuse surfaces (public endpoints without rate limit)
660
+ // ---------------------------------------------------------------------------
661
+
662
+ function detectAbuseSurfaces(report, state, files) {
663
+ // This is covered by detectPublicEndpoints above; we add a second pass
664
+ // specifically looking for API route files (Next.js / Express) that lack
665
+ // any rate-limit import in the same file OR a middleware-level rate limit.
666
+ for (const file of files) {
667
+ if (!/\/(app\/api\/|pages\/api\/|server\/routes\/)/.test(file.relPath)) continue;
668
+ const text = readText(file);
669
+ if (!text) continue;
670
+ if (RATE_LIMIT_RE.test(text)) continue;
671
+ if (!PUBLIC_ENDPOINT_RE.test(text) && !/\bexport\b/.test(text)) continue;
672
+
673
+ addSurface(state, "abuse_surface");
674
+ const refs = collectPatternRefs(file, /\bexport\s+(async\s+)?function\s+(GET|POST|PUT|PATCH|DELETE)\b/i, { limit: 1 });
675
+ if (refs.length === 0) continue;
676
+
677
+ addFinding(report, state, {
678
+ severity: "warn",
679
+ reasonCode: "COST_ABUSE_MISSING_RATE_LIMIT",
680
+ title: "API route without file-level rate limit",
681
+ message: "API route handler lacks a visible rate-limit import in the same file.",
682
+ evidenceRef: refs[0],
683
+ kind: "pattern",
684
+ safeNextAction: "Add rate limiting at the route or middleware level before production.",
685
+ });
686
+ }
687
+ }
688
+
689
+ // ---------------------------------------------------------------------------
690
+ // Detection: payment surfaces
691
+ // ---------------------------------------------------------------------------
692
+
693
+ function detectPaymentSurfaces(report, state, files) {
694
+ for (const file of files) {
695
+ const text = readText(file);
696
+ if (!text) continue;
697
+ if (!PAYMENT_RE.test(text)) continue;
698
+
699
+ addSurface(state, "payment_surface");
700
+ const refs = collectPatternRefs(file, PAYMENT_RE, { limit: 2 });
701
+
702
+ addFinding(report, state, {
703
+ severity: "warn",
704
+ reasonCode: "COST_ABUSE_PAYMENT_SURFACE_FOUND",
705
+ title: "Payment or billing surface detected",
706
+ message: "Stripe, payment, or billing pattern found. Review for idempotency and abuse protection.",
707
+ evidenceRef: refs[0],
708
+ kind: "pattern",
709
+ safeNextAction: "Verify payment endpoints have idempotency keys, rate limits, and webhook signature verification.",
710
+ });
711
+ }
712
+ }
713
+
714
+ // ---------------------------------------------------------------------------
715
+ // Detection: free trial surfaces
716
+ // ---------------------------------------------------------------------------
717
+
718
+ function detectFreeTrialSurfaces(report, state, files) {
719
+ for (const file of files) {
720
+ const text = readText(file);
721
+ if (!text) continue;
722
+ if (!FREE_TRIAL_RE.test(text)) continue;
723
+
724
+ addSurface(state, "free_trial_surface");
725
+ const refs = collectPatternRefs(file, FREE_TRIAL_RE, { limit: 2 });
726
+
727
+ addFinding(report, state, {
728
+ severity: "info",
729
+ reasonCode: "COST_ABUSE_FREE_TRIAL_SURFACE_FOUND",
730
+ title: "Free trial or freemium surface detected",
731
+ message: "Trial or free-plan pattern found. Review for abuse potential.",
732
+ evidenceRef: refs[0],
733
+ kind: "pattern",
734
+ safeNextAction: "Verify free-tier usage limits and trial abuse protections are in place.",
735
+ });
736
+ }
737
+ }
738
+
739
+ // ---------------------------------------------------------------------------
740
+ // Detection: cache surfaces
741
+ // ---------------------------------------------------------------------------
742
+
743
+ function detectCacheSurfaces(report, state, files) {
744
+ for (const file of files) {
745
+ const text = readText(file);
746
+ if (!text) continue;
747
+ if (!CACHE_RE.test(text)) continue;
748
+
749
+ addSurface(state, "cache_surface");
750
+ const refs = collectPatternRefs(file, CACHE_RE, { limit: 2 });
751
+
752
+ addFinding(report, state, {
753
+ severity: "info",
754
+ reasonCode: "COST_ABUSE_SURFACE_FOUND",
755
+ title: "Cache surface detected",
756
+ message: "Cache, Redis, or memoization pattern found.",
757
+ evidenceRef: refs[0],
758
+ kind: "pattern",
759
+ safeNextAction: "Verify cache TTLs and eviction are appropriate for cost control.",
760
+ });
761
+ }
762
+ }
763
+
764
+ // ---------------------------------------------------------------------------
765
+ // Finalization
766
+ // ---------------------------------------------------------------------------
767
+
768
+ function finalizeReport(report, state, startTime) {
769
+ // Remove internal field
770
+ delete report._startTime;
771
+
772
+ const findings = report.findings.sort((a, b) => severityRank(b.severity) - severityRank(a.severity));
773
+ report.findings = findings;
774
+
775
+ const infos = findings.filter((f) => f.severity === "info").length;
776
+ const warns = findings.filter((f) => f.severity === "warn").length;
777
+ const blocks = findings.filter((f) => f.severity === "block").length;
778
+
779
+ report.summary.findingCounts = { info: infos, warn: warns, block: blocks };
780
+
781
+ const surfacesDetected = [...state.surfacesDetected];
782
+ report.surfaceEvidence.surfacesDetected = surfacesDetected;
783
+
784
+ // Determine status
785
+ if (surfacesDetected.length === 0) {
786
+ report.status = "no_data";
787
+ report.summary.text = "No cost or abuse surface detected.";
788
+ report.summary.safeNextAction = "No cost or abuse surface was detected. Re-run after adding runtime, API, or scheduling code.";
789
+ } else if (blocks > 0) {
790
+ report.status = "blocked";
791
+ const firstBlock = findings.find((f) => f.severity === "block");
792
+ report.summary.text = `${surfacesDetected.length} surface(s) detected. ${blocks} blocker(s) found.`;
793
+ report.summary.safeNextAction = firstBlock?.safeNextAction || "Fix cost or abuse blockers before trusting this path.";
794
+ } else if (warns > 0) {
795
+ report.status = "warn";
796
+ const firstWarn = findings.find((f) => f.severity === "warn");
797
+ report.summary.text = `${surfacesDetected.length} surface(s) detected. ${warns} warning(s) need review.`;
798
+ report.summary.safeNextAction = firstWarn?.safeNextAction || "Add or verify bounds before trusting this path.";
799
+ } else {
800
+ report.status = "pass";
801
+ report.summary.text = `${surfacesDetected.length} surface(s) detected. Cost and abuse bounds appear adequate.`;
802
+ report.summary.safeNextAction = "Keep cost and abuse bounds current as the codebase changes.";
803
+ }
804
+
805
+ // Product home card
806
+ report.productHome.status = report.status;
807
+ report.productHome.found = surfacesDetected;
808
+ report.productHome.forReview = findings.filter((f) => f.severity === "warn" || f.severity === "block").map((f) => f.title).slice(0, 6);
809
+ report.productHome.savedOrProved = findings.filter((f) => f.severity === "info").map((f) => f.title).slice(0, 6);
810
+ report.productHome.safeNextAction = report.summary.safeNextAction;
811
+
812
+ // Overhead
813
+ const durationMs = Date.now() - startTime;
814
+ report.overhead.durationMs = durationMs;
815
+ report.overhead.filesInspected = report.surfaceEvidence.filesInspected;
816
+ report.overhead.artifactsInspected = report.surfaceEvidence.artifactsInspected;
817
+
818
+ return report;
819
+ }
820
+
821
+ // ---------------------------------------------------------------------------
822
+ // Build
823
+ // ---------------------------------------------------------------------------
824
+
825
+ function buildCostAbuseGuard(cwd = process.cwd(), options = {}) {
826
+ const startTime = Date.now();
827
+ const files = walkWorkspace(cwd);
828
+ const report = buildEmptyReport(startTime);
829
+ const state = createState();
830
+
831
+ report.surfaceEvidence.filesInspected = files.length;
832
+ report.surfaceEvidence.artifactsInspected = files.length;
833
+
834
+ // Run all detectors
835
+ detectRetryLoops(report, state, files);
836
+ detectPollingLoops(report, state, files);
837
+ detectScheduledJobs(report, state, files);
838
+ detectQueueWorkers(report, state, files);
839
+ detectBatchJobs(report, state, files);
840
+ const hasRateLimitLib = detectRateLimiting(report, state, files);
841
+ detectTimeouts(report, state, files);
842
+ detectConcurrency(report, state, files);
843
+ detectPublicEndpoints(report, state, files, hasRateLimitLib);
844
+ detectAiToolLoops(report, state, files);
845
+ detectExpensiveScripts(report, state, files);
846
+ detectAbuseSurfaces(report, state, files);
847
+ detectPaymentSurfaces(report, state, files);
848
+ detectFreeTrialSurfaces(report, state, files);
849
+ detectCacheSurfaces(report, state, files);
850
+
851
+ finalizeReport(report, state, startTime);
852
+
853
+ if (options.write !== false) {
854
+ safeWriteJson(cwd, REPORT_JSON_REL, report);
855
+ }
856
+ return report;
857
+ }
858
+
859
+ function generateCostAbuseGuard(cwd = process.cwd(), options = {}) {
860
+ const report = buildCostAbuseGuard(cwd, options);
861
+ return {
862
+ report,
863
+ reportPath: path.join(cwd, REPORT_JSON_REL),
864
+ };
865
+ }
866
+
867
+ function loadCostAbuseGuard(cwd = process.cwd()) {
868
+ return safeReadJson(cwd, REPORT_JSON_REL, null);
869
+ }
870
+
871
+ // ---------------------------------------------------------------------------
872
+ // Text formatting
873
+ // ---------------------------------------------------------------------------
874
+
875
+ function formatCostAbuseGuardText(report) {
876
+ if (!report || report.schemaVersion !== CONTRACT) {
877
+ return [
878
+ "Avorelo Cost & Abuse Guard",
879
+ "Status: NO DATA",
880
+ "No cost or abuse surface was detected yet.",
881
+ "Safe next action: Re-run after adding runtime, API, or scheduling code.",
882
+ ].join("\n");
883
+ }
884
+
885
+ const counts = report.summary?.findingCounts || { info: 0, warn: 0, block: 0 };
886
+ const surfaceCount = report.surfaceEvidence?.surfacesDetected?.length || 0;
887
+
888
+ const lines = [
889
+ "Avorelo Cost & Abuse Guard",
890
+ `Status: ${String(report.status || "no_data").replace(/_/g, " ").toUpperCase()} · redacted=yes`,
891
+ `Surfaces: ${surfaceCount} · info=${counts.info} · warn=${counts.warn} · block=${counts.block}`,
892
+ "Notes: local static scan only · no billing or provider calls",
893
+ ];
894
+
895
+ const findings = Array.isArray(report.findings) ? report.findings.slice(0, 8) : [];
896
+ if (findings.length > 0) {
897
+ lines.push("");
898
+ lines.push("Findings:");
899
+ for (const finding of findings) {
900
+ const ref = finding.evidenceRefs?.[0]?.path || "";
901
+ lines.push(`- [${finding.severity}] ${finding.title}${ref ? ` · ${ref}` : ""}`);
902
+ }
903
+ }
904
+
905
+ lines.push("");
906
+ lines.push(`Safe next action: ${report.summary?.safeNextAction || "Add or verify bounds before trusting this path."}`);
907
+ return lines.join("\n");
908
+ }
909
+
910
+ function formatCostAbuseGuardStatusLineFromReport(report) {
911
+ if (!report || report.schemaVersion !== CONTRACT) {
912
+ return "Cost-abuse guard: NOT RUN · run `avorelo cost-abuse-guard --text`";
913
+ }
914
+ const counts = report.summary?.findingCounts || { info: 0, warn: 0, block: 0 };
915
+ return `Cost-abuse guard: ${String(report.status || "no_data").replace(/_/g, " ").toUpperCase()}`
916
+ + ` · block=${counts.block}`
917
+ + ` · warn=${counts.warn}`
918
+ + ` · next: ${sanitizeText(report.summary?.safeNextAction, 140) || "Review cost and abuse surfaces."}`;
919
+ }
920
+
921
+ // ---------------------------------------------------------------------------
922
+ // Surface builder (for dashboard / status integration)
923
+ // ---------------------------------------------------------------------------
924
+
925
+ function buildCostAbuseGuardSurface(cwd = process.cwd(), options = {}) {
926
+ const report = loadCostAbuseGuard(cwd) || (options.buildIfMissing ? buildCostAbuseGuard(cwd, { write: false }) : null);
927
+ if (!report) {
928
+ return {
929
+ status: "not_run",
930
+ blockerCount: 0,
931
+ needsReviewCount: 0,
932
+ surfacesInspected: 0,
933
+ nextBestAction: "Run `avorelo cost-abuse-guard --text` before treating cost or abuse surfaces as safe.",
934
+ statusLine: "Cost-abuse guard: NOT RUN · run `avorelo cost-abuse-guard --text`",
935
+ showInStatus: true,
936
+ showInDashboard: true,
937
+ evidenceRef: null,
938
+ limitations: [
939
+ "Local static scan only. No billing or provider API calls.",
940
+ ],
941
+ };
942
+ }
943
+
944
+ const counts = report.summary?.findingCounts || { info: 0, warn: 0, block: 0 };
945
+ return {
946
+ status: report.status,
947
+ blockerCount: counts.block,
948
+ needsReviewCount: counts.warn,
949
+ surfacesInspected: report.surfaceEvidence?.surfacesDetected?.length || 0,
950
+ nextBestAction: report.summary?.safeNextAction,
951
+ statusLine: formatCostAbuseGuardStatusLineFromReport(report),
952
+ showInStatus: true,
953
+ showInDashboard: true,
954
+ evidenceRef: REPORT_JSON_REL,
955
+ limitations: [
956
+ "Local static scan only. No billing or provider API calls.",
957
+ "No model, network, or provider calls happen in this guard.",
958
+ ],
959
+ };
960
+ }
961
+
962
+ // ---------------------------------------------------------------------------
963
+ // Exports
964
+ // ---------------------------------------------------------------------------
965
+
966
+ module.exports = {
967
+ CONTRACT,
968
+ REPORT_JSON_REL,
969
+ buildCostAbuseGuard,
970
+ generateCostAbuseGuard,
971
+ loadCostAbuseGuard,
972
+ formatCostAbuseGuardText,
973
+ formatCostAbuseGuardStatusLineFromReport,
974
+ buildCostAbuseGuardSurface,
975
+ };