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.
Files changed (258) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +56 -0
  3. package/bin/avorelo +9 -0
  4. package/package.json +135 -0
  5. package/scripts/README.md +40 -0
  6. package/scripts/cco-dashboard.js +252 -0
  7. package/scripts/cco-status.js +430 -0
  8. package/scripts/lib/activation/account-state.js +37 -0
  9. package/scripts/lib/activation/activation-runner.js +546 -0
  10. package/scripts/lib/activation/activation-self-healing.js +480 -0
  11. package/scripts/lib/activation/activation-state.js +83 -0
  12. package/scripts/lib/activation/activation-summary.js +191 -0
  13. package/scripts/lib/activation/adapters/claude-code.js +77 -0
  14. package/scripts/lib/activation/adapters/codex-cli.js +52 -0
  15. package/scripts/lib/activation/adapters/cursor.js +37 -0
  16. package/scripts/lib/activation/adapters/github-agent.js +39 -0
  17. package/scripts/lib/activation/adapters/terminal.js +42 -0
  18. package/scripts/lib/activation/adapters/vscode.js +39 -0
  19. package/scripts/lib/activation/adapters/windsurf.js +37 -0
  20. package/scripts/lib/activation/ai-surface-detector.js +151 -0
  21. package/scripts/lib/activation/connect-account.js +145 -0
  22. package/scripts/lib/activation/detect-environment.js +75 -0
  23. package/scripts/lib/activation/detect-hosts.js +62 -0
  24. package/scripts/lib/activation/format-activation-output.js +109 -0
  25. package/scripts/lib/activation/next-action.js +43 -0
  26. package/scripts/lib/activation/repair-engine.js +219 -0
  27. package/scripts/lib/activation-distribution-readiness.js +507 -0
  28. package/scripts/lib/adapter-conformance.js +176 -0
  29. package/scripts/lib/adapter-readiness.js +417 -0
  30. package/scripts/lib/adapter-safety-boundaries.js +335 -0
  31. package/scripts/lib/adapter-technical-readiness-gate.js +205 -0
  32. package/scripts/lib/agent-access-governance.js +455 -0
  33. package/scripts/lib/agent-enforcement.js +765 -0
  34. package/scripts/lib/agent-policy-profile.js +210 -0
  35. package/scripts/lib/agent-security/action-evaluator.js +507 -0
  36. package/scripts/lib/agent-security/adapter-registry.js +98 -0
  37. package/scripts/lib/agent-security/auto-policy.js +139 -0
  38. package/scripts/lib/agent-security/bounded-scan.js +93 -0
  39. package/scripts/lib/agent-security/enforcement-adapter.js +174 -0
  40. package/scripts/lib/agent-security/enforcement-engine.js +1129 -0
  41. package/scripts/lib/agent-security/file-write-adapter.js +183 -0
  42. package/scripts/lib/agent-security/file-write-rules.js +178 -0
  43. package/scripts/lib/agent-security/index.js +3342 -0
  44. package/scripts/lib/agent-security/instruction-risk.js +181 -0
  45. package/scripts/lib/agent-security/mcp-action-adapter.js +185 -0
  46. package/scripts/lib/agent-security/mcp-action-rules.js +184 -0
  47. package/scripts/lib/agent-security/package-action-adapter.js +175 -0
  48. package/scripts/lib/agent-security/package-action-rules.js +233 -0
  49. package/scripts/lib/agent-security/performance.js +148 -0
  50. package/scripts/lib/agent-security/permission-minimizer.js +403 -0
  51. package/scripts/lib/agent-security/scan-cache.js +74 -0
  52. package/scripts/lib/agent-security/source-trust.js +146 -0
  53. package/scripts/lib/ai-install-prompt.js +288 -0
  54. package/scripts/lib/ai-workspace-hygiene.js +1499 -0
  55. package/scripts/lib/alpha-activation.js +520 -0
  56. package/scripts/lib/alpha-feedback.js +263 -0
  57. package/scripts/lib/alpha-readiness-gate.js +332 -0
  58. package/scripts/lib/anti-gaming.js +169 -0
  59. package/scripts/lib/artifact-health.js +431 -0
  60. package/scripts/lib/attribution.js +180 -0
  61. package/scripts/lib/audit.js +289 -0
  62. package/scripts/lib/avorelo-skill-registry.js +810 -0
  63. package/scripts/lib/batch-jobs.js +71 -0
  64. package/scripts/lib/brain-pack.js +578 -0
  65. package/scripts/lib/brand-boundary.js +424 -0
  66. package/scripts/lib/brand.js +74 -0
  67. package/scripts/lib/browser-capability.js +1048 -0
  68. package/scripts/lib/browser-proof-preflight.js +321 -0
  69. package/scripts/lib/cache-readiness.js +187 -0
  70. package/scripts/lib/canonical-reentry.js +162 -0
  71. package/scripts/lib/capability-packs.js +314 -0
  72. package/scripts/lib/capability-recommender.js +512 -0
  73. package/scripts/lib/capability-registry.js +1059 -0
  74. package/scripts/lib/carry-forward-surfacing.js +194 -0
  75. package/scripts/lib/ccusage-adapter.js +188 -0
  76. package/scripts/lib/company-loop.js +1149 -0
  77. package/scripts/lib/config.js +637 -0
  78. package/scripts/lib/context-acquisition-plan.js +287 -0
  79. package/scripts/lib/context-budget-guard.js +170 -0
  80. package/scripts/lib/context-budget-scanner.js +257 -0
  81. package/scripts/lib/context-optimizer.js +715 -0
  82. package/scripts/lib/context-reduction-plan.js +178 -0
  83. package/scripts/lib/context-safety.js +88 -0
  84. package/scripts/lib/context-savings-engine.js +158 -0
  85. package/scripts/lib/cost-evidence.js +254 -0
  86. package/scripts/lib/cross-host-install-plan.js +308 -0
  87. package/scripts/lib/cross-host-install-readiness.js +237 -0
  88. package/scripts/lib/cross-host-value-flow.js +268 -0
  89. package/scripts/lib/dashboard.js +900 -0
  90. package/scripts/lib/design-partner-feedback.js +346 -0
  91. package/scripts/lib/entitlements.js +100 -0
  92. package/scripts/lib/execution-packet.js +559 -0
  93. package/scripts/lib/experimentation-events.js +547 -0
  94. package/scripts/lib/external-capability-compliance.js +107 -0
  95. package/scripts/lib/external-user-simulation.js +166 -0
  96. package/scripts/lib/failure-recovery-readiness.js +81 -0
  97. package/scripts/lib/failure-recovery.js +419 -0
  98. package/scripts/lib/feedback-intelligence.js +537 -0
  99. package/scripts/lib/feedback-signals.js +205 -0
  100. package/scripts/lib/file-integrity.js +68 -0
  101. package/scripts/lib/fsx.js +127 -0
  102. package/scripts/lib/full-readiness-gate.js +451 -0
  103. package/scripts/lib/guidance-builder.js +174 -0
  104. package/scripts/lib/hook-apply.js +1019 -0
  105. package/scripts/lib/hook-baseline.js +310 -0
  106. package/scripts/lib/hook-config-preview.js +275 -0
  107. package/scripts/lib/hook-contracts.js +290 -0
  108. package/scripts/lib/hook-safety-boundary-readiness.js +80 -0
  109. package/scripts/lib/host-capability-matrix.js +351 -0
  110. package/scripts/lib/host-support-context.js +254 -0
  111. package/scripts/lib/http-hook-action.js +538 -0
  112. package/scripts/lib/install-ai-readiness.js +84 -0
  113. package/scripts/lib/install-intake-risk.js +1037 -0
  114. package/scripts/lib/install-journey-intelligence.js +329 -0
  115. package/scripts/lib/intervention-guidance.js +57 -0
  116. package/scripts/lib/known-limitations.js +115 -0
  117. package/scripts/lib/l8-path-truth.js +146 -0
  118. package/scripts/lib/launch-hardening-gate.js +436 -0
  119. package/scripts/lib/launch-readiness.js +628 -0
  120. package/scripts/lib/learning-memory.js +686 -0
  121. package/scripts/lib/lifecycle-hooks.js +802 -0
  122. package/scripts/lib/local-package-smoke.js +423 -0
  123. package/scripts/lib/local-pricing.js +299 -0
  124. package/scripts/lib/mcp-enforcement.js +311 -0
  125. package/scripts/lib/mcp-least-privilege-policy.js +303 -0
  126. package/scripts/lib/mcp-tool-inventory.js +388 -0
  127. package/scripts/lib/mcp-tool-risk.js +0 -0
  128. package/scripts/lib/memory.js +335 -0
  129. package/scripts/lib/metrics.js +699 -0
  130. package/scripts/lib/micro-proof.js +133 -0
  131. package/scripts/lib/next-run-context.js +436 -0
  132. package/scripts/lib/operating-value.js +1648 -0
  133. package/scripts/lib/optimization-v3.js +122 -0
  134. package/scripts/lib/orchestration/adapters/_shared.js +49 -0
  135. package/scripts/lib/orchestration/adapters/aider.js +18 -0
  136. package/scripts/lib/orchestration/adapters/claude-code.js +35 -0
  137. package/scripts/lib/orchestration/adapters/codex.js +35 -0
  138. package/scripts/lib/orchestration/adapters/gemini-cli.js +18 -0
  139. package/scripts/lib/orchestration/adapters/git.js +25 -0
  140. package/scripts/lib/orchestration/adapters/index.js +31 -0
  141. package/scripts/lib/orchestration/adapters/lm-studio.js +18 -0
  142. package/scripts/lib/orchestration/adapters/ollama.js +18 -0
  143. package/scripts/lib/orchestration/adapters/opencode.js +18 -0
  144. package/scripts/lib/orchestration/adapters/openrouter.js +18 -0
  145. package/scripts/lib/orchestration/adapters/test-runner.js +25 -0
  146. package/scripts/lib/orchestration/cli.js +438 -0
  147. package/scripts/lib/orchestration/execution-manager.js +279 -0
  148. package/scripts/lib/orchestration/handoff.js +314 -0
  149. package/scripts/lib/orchestration/index.js +456 -0
  150. package/scripts/lib/orchestration/inventory.js +47 -0
  151. package/scripts/lib/orchestration/model-discovery.js +498 -0
  152. package/scripts/lib/orchestration/model-profiler.js +170 -0
  153. package/scripts/lib/orchestration/model-profiles.js +252 -0
  154. package/scripts/lib/orchestration/model-refresh-policy.js +72 -0
  155. package/scripts/lib/orchestration/proof-writer.js +349 -0
  156. package/scripts/lib/orchestration/provider-discovery/aider.js +49 -0
  157. package/scripts/lib/orchestration/provider-discovery/claude-code.js +56 -0
  158. package/scripts/lib/orchestration/provider-discovery/codex.js +49 -0
  159. package/scripts/lib/orchestration/provider-discovery/common.js +186 -0
  160. package/scripts/lib/orchestration/provider-discovery/gemini.js +106 -0
  161. package/scripts/lib/orchestration/provider-discovery/lm-studio.js +118 -0
  162. package/scripts/lib/orchestration/provider-discovery/models-dev.js +12 -0
  163. package/scripts/lib/orchestration/provider-discovery/ollama.js +100 -0
  164. package/scripts/lib/orchestration/provider-discovery/opencode.js +47 -0
  165. package/scripts/lib/orchestration/provider-discovery/openrouter.js +44 -0
  166. package/scripts/lib/orchestration/risk-classifier.js +130 -0
  167. package/scripts/lib/orchestration/routing-policy.js +486 -0
  168. package/scripts/lib/orchestration/settings.js +112 -0
  169. package/scripts/lib/orchestration/state.js +165 -0
  170. package/scripts/lib/orchestration/verification-manager.js +138 -0
  171. package/scripts/lib/output-profiles.js +146 -0
  172. package/scripts/lib/package-content-audit.js +368 -0
  173. package/scripts/lib/package-runtime.js +278 -0
  174. package/scripts/lib/plan-surface.js +53 -0
  175. package/scripts/lib/plans.js +2318 -0
  176. package/scripts/lib/policy-provider.js +27 -0
  177. package/scripts/lib/prelaunch-activation-readiness.js +409 -0
  178. package/scripts/lib/prelaunch-evidence-store.js +816 -0
  179. package/scripts/lib/prelaunch-intelligence.js +869 -0
  180. package/scripts/lib/pricing-experiment.js +118 -0
  181. package/scripts/lib/pro-moment-events.js +77 -0
  182. package/scripts/lib/pro-moment-state.js +227 -0
  183. package/scripts/lib/pro-moments.js +1216 -0
  184. package/scripts/lib/product-learning-events.js +629 -0
  185. package/scripts/lib/project-profile.js +555 -0
  186. package/scripts/lib/prompt-compiler.js +280 -0
  187. package/scripts/lib/prompt-lint.js +32 -0
  188. package/scripts/lib/prompt-suggestions.js +52 -0
  189. package/scripts/lib/proof-canonical.js +398 -0
  190. package/scripts/lib/proof-drilldown.js +383 -0
  191. package/scripts/lib/proof-events.js +342 -0
  192. package/scripts/lib/proof-history.js +243 -0
  193. package/scripts/lib/proof-metrics.js +296 -0
  194. package/scripts/lib/proof-outcome-evidence.js +134 -0
  195. package/scripts/lib/proof-receipt.js +335 -0
  196. package/scripts/lib/proof-record.js +461 -0
  197. package/scripts/lib/public-activation-distribution-gate.js +258 -0
  198. package/scripts/lib/public-cli.js +3891 -0
  199. package/scripts/lib/public-distribution-truth.js +211 -0
  200. package/scripts/lib/public-install-claim-checker.js +294 -0
  201. package/scripts/lib/publish-provenance-readiness.js +283 -0
  202. package/scripts/lib/readiness-delta.js +218 -0
  203. package/scripts/lib/readiness-evidence-closure.js +196 -0
  204. package/scripts/lib/reentry-memory-capture.js +241 -0
  205. package/scripts/lib/reentry-memory-retrieval.js +302 -0
  206. package/scripts/lib/reentry-memory-status.js +146 -0
  207. package/scripts/lib/reentry-memory-store.js +178 -0
  208. package/scripts/lib/reentry-state.js +66 -0
  209. package/scripts/lib/release-candidate-bundle.js +166 -0
  210. package/scripts/lib/remediation.js +81 -0
  211. package/scripts/lib/repo-map.js +391 -0
  212. package/scripts/lib/run-improvements-lifecycle.js +330 -0
  213. package/scripts/lib/run-improvements.js +789 -0
  214. package/scripts/lib/runtime-decision-policy.js +387 -0
  215. package/scripts/lib/safe-path-engine.js +705 -0
  216. package/scripts/lib/safe-run-controller.js +887 -0
  217. package/scripts/lib/score.js +262 -0
  218. package/scripts/lib/seamless-enforcement.js +329 -0
  219. package/scripts/lib/seamless-outcome.js +689 -0
  220. package/scripts/lib/seamless-reality-gate.js +5043 -0
  221. package/scripts/lib/security-risk-classifier.js +511 -0
  222. package/scripts/lib/security-scan.js +384 -0
  223. package/scripts/lib/session-context-optimizer.js +1211 -0
  224. package/scripts/lib/session-timing.js +315 -0
  225. package/scripts/lib/skill-hygiene.js +805 -0
  226. package/scripts/lib/skill-packs.js +161 -0
  227. package/scripts/lib/skills-operating-layer.js +580 -0
  228. package/scripts/lib/smart-work-routing.js +768 -0
  229. package/scripts/lib/source-catalog.js +700 -0
  230. package/scripts/lib/status-value-summary.js +32 -0
  231. package/scripts/lib/support-bundle.js +578 -0
  232. package/scripts/lib/task-continuation.js +440 -0
  233. package/scripts/lib/test-helpers.js +15 -0
  234. package/scripts/lib/tier.js +38 -0
  235. package/scripts/lib/token-context-quality-gate.js +370 -0
  236. package/scripts/lib/token-cost-capture.js +187 -0
  237. package/scripts/lib/token-cost-intelligence.js +358 -0
  238. package/scripts/lib/token-efficiency-evidence.js +213 -0
  239. package/scripts/lib/token-evidence.js +699 -0
  240. package/scripts/lib/tokenish.js +17 -0
  241. package/scripts/lib/tool-output-sandbox.js +304 -0
  242. package/scripts/lib/trust-audit.js +136 -0
  243. package/scripts/lib/unified-events.js +396 -0
  244. package/scripts/lib/upgrade-interruption-recovery.js +407 -0
  245. package/scripts/lib/usage-ledger.js +201 -0
  246. package/scripts/lib/value-ledger.js +130 -0
  247. package/scripts/lib/value-proof-calibration.js +531 -0
  248. package/scripts/lib/visual-qa.js +231 -0
  249. package/scripts/lib/voice-alpha.js +29 -0
  250. package/scripts/lib/work-aware-orchestration.js +976 -0
  251. package/scripts/lib/work-control-receipts.js +577 -0
  252. package/scripts/lib/work-ledger.js +1123 -0
  253. package/scripts/lib/work-panel-preview.js +352 -0
  254. package/scripts/lib/workflow-discipline.js +280 -0
  255. package/scripts/lib/workflow-signals.js +419 -0
  256. package/scripts/lib/workspace-map.js +281 -0
  257. package/scripts/lib/workspace-registry.js +1367 -0
  258. package/scripts/lib/workspace-resolver.js +480 -0
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+
3
+ function summarizeLargeObject(obj) {
4
+ let s = "";
5
+ try {
6
+ s = JSON.stringify(obj);
7
+ } catch {
8
+ s = String(obj);
9
+ }
10
+ const bytes = Buffer.byteLength(s, "utf8");
11
+ if (bytes < 20000) return null;
12
+
13
+ const preview = s.slice(0, 2000) + "\n... (truncated)\n";
14
+ return { isLarge: true, bytes, preview };
15
+ }
16
+
17
+ module.exports = { summarizeLargeObject };
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+
3
+ // ── Tool Output Sandbox ───────────────────────────────────────────────────────
4
+ //
5
+ // Summarizes raw tool output before it enters context/proof/ledger.
6
+ // Raw output is stored as artifacts; compact summaries enter context.
7
+ //
8
+ // Contracts:
9
+ // avorelo.toolOutputSandbox.v1
10
+ // avorelo.toolOutputBudget.v1
11
+ //
12
+ // Non-goals: executing commands, replacing proof, hiding failures.
13
+ // Rules:
14
+ // - failure-first summarization
15
+ // - preserve failing test names + first relevant errors
16
+ // - strip ANSI codes
17
+ // - redact secrets/env-like values
18
+ // - max summary size enforced
19
+ // - debug references artifact path, not raw dump
20
+
21
+ const fs = require("node:fs");
22
+ const path = require("node:path");
23
+ const { nowIso } = require("./fsx");
24
+ const { appendProductLearningEvent } = require("./product-learning-events");
25
+
26
+ const CONTRACT = "avorelo.toolOutputSandbox.v1";
27
+ const BUDGET_CONTRACT = "avorelo.toolOutputBudget.v1";
28
+ const SCHEMA_VERSION = 1;
29
+
30
+ const TOOL_OUTPUT_DIR_REL = ".claude/cco/orchestration/tool-output";
31
+ const LATEST_SUMMARY_REL = `${TOOL_OUTPUT_DIR_REL}/latest-summary.json`;
32
+
33
+ const MAX_SUMMARY_CHARS = 4000;
34
+ const MAX_ARTIFACT_BYTES = 10 * 1024 * 1024; // 10 MB cap on artifact write
35
+ const MAX_LINES_CAPTURED = 50; // max lines in summary
36
+
37
+ // ── ANSI / secret stripping ───────────────────────────────────────────────────
38
+
39
+ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]/g;
40
+
41
+ function stripAnsi(text) {
42
+ return text.replace(ANSI_RE, "");
43
+ }
44
+
45
+ // Redact patterns: env vars, tokens, passwords, keys in output text
46
+ const SECRET_PATTERNS = [
47
+ /\b(password|passwd|secret|token|api[_-]?key|auth[_-]?key|private[_-]?key|access[_-]?token)\s*[:=]\s*\S+/gi,
48
+ /\b[A-Z0-9]{32,}\b/g, // long uppercase hex strings (tokens)
49
+ /ghp_[A-Za-z0-9]{36,}/g, // GitHub PATs
50
+ /sk-[A-Za-z0-9]{32,}/g, // OpenAI/Anthropic keys
51
+ /-----BEGIN [A-Z ]+KEY-----/g, // PEM blocks
52
+ ];
53
+
54
+ function redactSecrets(text) {
55
+ let out = text;
56
+ for (const re of SECRET_PATTERNS) {
57
+ out = out.replace(re, "[REDACTED]");
58
+ }
59
+ return out;
60
+ }
61
+
62
+ function sanitize(text) {
63
+ return redactSecrets(stripAnsi(String(text || "")));
64
+ }
65
+
66
+ // ── Output kind classification ────────────────────────────────────────────────
67
+
68
+ const SUPPORTED_KINDS = ["test", "build", "lint", "git_diff", "git_status", "rg_search", "log", "json", "generic"];
69
+
70
+ function classifyToolOutput(kind, output, options = {}) {
71
+ const k = kind && SUPPORTED_KINDS.includes(kind) ? kind : "generic";
72
+ const raw = sanitize(output);
73
+ const lineCount = raw.split("\n").length;
74
+ const charCount = raw.length;
75
+ return { kind: k, lineCount, charCount, oversized: charCount > MAX_SUMMARY_CHARS * 5 };
76
+ }
77
+
78
+ // ── Kind-specific summarizers ─────────────────────────────────────────────────
79
+
80
+ function summarizeTest(raw) {
81
+ const lines = raw.split("\n");
82
+ const failures = lines.filter((l) => /\b(FAIL|✗|×|not ok|Error:|AssertionError|▶ |failed)\b/i.test(l));
83
+ const summary = lines.filter((l) => /\b(passing|failing|pending|skipped|Tests:|Duration)\b/i.test(l));
84
+ const stackLines = [];
85
+ let inStack = false;
86
+ for (const l of lines) {
87
+ if (/^\s+at /.test(l)) { inStack = true; stackLines.push(l); }
88
+ else if (inStack && l.trim() === "") { inStack = false; }
89
+ }
90
+ const parts = [];
91
+ if (summary.length) parts.push(summary.slice(0, 3).join("\n"));
92
+ if (failures.length) parts.push("Failures:\n" + failures.slice(0, 10).join("\n"));
93
+ if (stackLines.length) parts.push("Stack (first 5):\n" + stackLines.slice(0, 5).join("\n"));
94
+ return parts.join("\n---\n") || lines.slice(0, 10).join("\n");
95
+ }
96
+
97
+ function summarizeBuild(raw) {
98
+ const lines = raw.split("\n");
99
+ const errors = lines.filter((l) => /\b(error|Error|ERROR|failed|FAILED)\b/.test(l));
100
+ const warnings = lines.filter((l) => /\b(warn|WARN|warning)\b/i.test(l));
101
+ const last = lines.filter((l) => l.trim()).slice(-5);
102
+ const parts = [];
103
+ if (errors.length) parts.push("Errors:\n" + errors.slice(0, 8).join("\n"));
104
+ else if (warnings.length) parts.push("Warnings:\n" + warnings.slice(0, 5).join("\n"));
105
+ if (last.length) parts.push("Last lines:\n" + last.join("\n"));
106
+ return parts.join("\n---\n") || lines.slice(0, 10).join("\n");
107
+ }
108
+
109
+ function summarizeLint(raw) {
110
+ const lines = raw.split("\n");
111
+ const errors = lines.filter((l) => /\berror\b/i.test(l));
112
+ const warnings = lines.filter((l) => /\bwarning\b/i.test(l));
113
+ const parts = [];
114
+ if (errors.length) parts.push(`${errors.length} error(s):\n` + errors.slice(0, 8).join("\n"));
115
+ if (warnings.length) parts.push(`${warnings.length} warning(s) (first 3):\n` + warnings.slice(0, 3).join("\n"));
116
+ return parts.join("\n---\n") || lines.slice(0, 10).join("\n");
117
+ }
118
+
119
+ function summarizeGitDiff(raw) {
120
+ const lines = raw.split("\n");
121
+ const fileLines = lines.filter((l) => /^diff --git|^\+\+\+|^---/.test(l));
122
+ const statLines = lines.filter((l) => /\|\s+\d+/.test(l));
123
+ const parts = [];
124
+ if (statLines.length) parts.push("Changed files:\n" + statLines.slice(0, 20).join("\n"));
125
+ else if (fileLines.length) parts.push("Files:\n" + fileLines.slice(0, 20).join("\n"));
126
+ parts.push(`Total lines: ${lines.length}`);
127
+ return parts.join("\n---\n");
128
+ }
129
+
130
+ function summarizeGitStatus(raw) {
131
+ const lines = raw.split("\n").filter((l) => l.trim());
132
+ return lines.slice(0, 30).join("\n");
133
+ }
134
+
135
+ function summarizeRgSearch(raw) {
136
+ const lines = raw.split("\n").filter((l) => l.trim());
137
+ const fileMatches = new Set(lines.map((l) => l.split(":")[0]).filter(Boolean));
138
+ return `${fileMatches.size} file(s) matched, ${lines.length} result(s)\n` + lines.slice(0, 20).join("\n");
139
+ }
140
+
141
+ function summarizeLog(raw) {
142
+ const lines = raw.split("\n");
143
+ const errors = lines.filter((l) => /\b(error|ERROR|FATAL|fatal|critical)\b/.test(l));
144
+ const warns = lines.filter((l) => /\b(warn|WARN|WARNING)\b/.test(l));
145
+ const parts = [];
146
+ if (errors.length) parts.push("Errors:\n" + errors.slice(0, 5).join("\n"));
147
+ if (warns.length) parts.push("Warnings:\n" + warns.slice(0, 5).join("\n"));
148
+ parts.push("Last lines:\n" + lines.filter((l) => l.trim()).slice(-5).join("\n"));
149
+ return parts.join("\n---\n");
150
+ }
151
+
152
+ function summarizeJson(raw) {
153
+ try {
154
+ const obj = JSON.parse(raw);
155
+ const keys = Object.keys(obj).slice(0, 10);
156
+ return `JSON object with keys: ${keys.join(", ")} (${raw.length} chars total)`;
157
+ } catch {
158
+ return `JSON-like output (${raw.length} chars, parse failed)`;
159
+ }
160
+ }
161
+
162
+ function summarizeGeneric(raw) {
163
+ const lines = raw.split("\n").filter((l) => l.trim());
164
+ return lines.slice(0, MAX_LINES_CAPTURED).join("\n");
165
+ }
166
+
167
+ function summarizeByKind(kind, raw) {
168
+ switch (kind) {
169
+ case "test": return summarizeTest(raw);
170
+ case "build": return summarizeBuild(raw);
171
+ case "lint": return summarizeLint(raw);
172
+ case "git_diff": return summarizeGitDiff(raw);
173
+ case "git_status": return summarizeGitStatus(raw);
174
+ case "rg_search": return summarizeRgSearch(raw);
175
+ case "log": return summarizeLog(raw);
176
+ case "json": return summarizeJson(raw);
177
+ default: return summarizeGeneric(raw);
178
+ }
179
+ }
180
+
181
+ // ── Main summarizer ───────────────────────────────────────────────────────────
182
+
183
+ function summarizeToolOutput(output, options = {}) {
184
+ const kind = options.kind || "generic";
185
+ const raw = sanitize(output);
186
+ const summary = summarizeByKind(kind, raw);
187
+ const truncated = summary.length > MAX_SUMMARY_CHARS ? summary.slice(0, MAX_SUMMARY_CHARS) + "\n[summary truncated]" : summary;
188
+ return {
189
+ kind,
190
+ summaryChars: truncated.length,
191
+ rawChars: raw.length,
192
+ summary: truncated,
193
+ oversized: raw.length > MAX_SUMMARY_CHARS * 5,
194
+ };
195
+ }
196
+
197
+ // ── Artifact write ────────────────────────────────────────────────────────────
198
+
199
+ function writeRawToolOutputArtifact(cwd, output, metadata = {}, options = {}) {
200
+ const dir = path.join(cwd, TOOL_OUTPUT_DIR_REL);
201
+ fs.mkdirSync(dir, { recursive: true });
202
+ const ts = Date.now();
203
+ const kind = metadata.kind || "generic";
204
+ const filename = `${ts}-${kind}.txt`;
205
+ const abs = path.join(dir, filename);
206
+ const raw = sanitize(output);
207
+ if (raw.length > MAX_ARTIFACT_BYTES) {
208
+ fs.writeFileSync(abs, raw.slice(0, MAX_ARTIFACT_BYTES) + "\n[artifact truncated]", "utf8");
209
+ } else {
210
+ fs.writeFileSync(abs, raw, "utf8");
211
+ }
212
+ try {
213
+ appendProductLearningEvent(cwd, "raw_tool_output_artifact_written", {
214
+ kind,
215
+ rawChars: raw.length,
216
+ artifactPath: path.relative(cwd, abs),
217
+ });
218
+ } catch {}
219
+ return path.relative(cwd, abs);
220
+ }
221
+
222
+ // ── Build sandbox result ──────────────────────────────────────────────────────
223
+
224
+ function buildToolOutputSandboxResult(cwd, input = {}, options = {}) {
225
+ const { output, kind, writeArtifact = true } = input;
226
+ const classification = classifyToolOutput(kind, output, options);
227
+ const summaryResult = summarizeToolOutput(output, { kind: classification.kind });
228
+
229
+ let artifactRef = null;
230
+ if (writeArtifact && classification.oversized) {
231
+ try {
232
+ artifactRef = writeRawToolOutputArtifact(cwd, output, { kind: classification.kind });
233
+ } catch {}
234
+ }
235
+
236
+ return {
237
+ contract: CONTRACT,
238
+ schemaVersion: SCHEMA_VERSION,
239
+ createdAt: nowIso(),
240
+ kind: classification.kind,
241
+ rawChars: classification.charCount,
242
+ summaryChars: summaryResult.summaryChars,
243
+ summary: summaryResult.summary,
244
+ oversized: classification.oversized,
245
+ artifactRef,
246
+ redacted: true,
247
+ };
248
+ }
249
+
250
+ function enforceToolOutputBudget(result, options = {}) {
251
+ const maxChars = options.maxChars || MAX_SUMMARY_CHARS;
252
+ if (result.summaryChars > maxChars) {
253
+ return {
254
+ ...result,
255
+ summary: result.summary.slice(0, maxChars) + "\n[budget enforced]",
256
+ summaryChars: maxChars,
257
+ budgetEnforced: true,
258
+ };
259
+ }
260
+ return { ...result, budgetEnforced: false };
261
+ }
262
+
263
+ function formatToolOutputSummary(result, options = {}) {
264
+ const lines = [`Tool Output [${result.kind}]`];
265
+ if (result.artifactRef) lines.push(` Raw artifact: ${result.artifactRef}`);
266
+ lines.push(` Raw: ${result.rawChars} chars → Summary: ${result.summaryChars} chars`);
267
+ lines.push(result.summary);
268
+ return lines.join("\n");
269
+ }
270
+
271
+ function writeSandboxSummary(cwd, result) {
272
+ const dir = path.join(cwd, TOOL_OUTPUT_DIR_REL);
273
+ fs.mkdirSync(dir, { recursive: true });
274
+ const abs = path.join(cwd, LATEST_SUMMARY_REL);
275
+ fs.writeFileSync(abs, JSON.stringify(result, null, 2), "utf8");
276
+ try {
277
+ appendProductLearningEvent(cwd, "tool_output_sandbox_summary_written", {
278
+ kind: result.kind,
279
+ rawChars: result.rawChars,
280
+ summaryChars: result.summaryChars,
281
+ oversized: result.oversized,
282
+ });
283
+ } catch {}
284
+ return abs;
285
+ }
286
+
287
+ module.exports = {
288
+ CONTRACT,
289
+ BUDGET_CONTRACT,
290
+ SCHEMA_VERSION,
291
+ TOOL_OUTPUT_DIR_REL,
292
+ LATEST_SUMMARY_REL,
293
+ SUPPORTED_KINDS,
294
+ sanitize,
295
+ stripAnsi,
296
+ redactSecrets,
297
+ classifyToolOutput,
298
+ summarizeToolOutput,
299
+ writeRawToolOutputArtifact,
300
+ buildToolOutputSandboxResult,
301
+ enforceToolOutputBudget,
302
+ formatToolOutputSummary,
303
+ writeSandboxSummary,
304
+ };
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const crypto = require("crypto");
6
+
7
+ function sha256(content) {
8
+ return crypto.createHash("sha256").update(content).digest("hex");
9
+ }
10
+
11
+ function hashFile(absPath) {
12
+ const data = fs.readFileSync(absPath);
13
+ return sha256(data);
14
+ }
15
+
16
+ function walkFiles(absDir, relRoot, out) {
17
+ if (!fs.existsSync(absDir)) return;
18
+ const entries = fs.readdirSync(absDir, { withFileTypes: true });
19
+ entries.forEach((e) => {
20
+ const abs = path.join(absDir, e.name);
21
+ const rel = path.posix.join(relRoot, e.name).replace(/\\/g, "/");
22
+ if (e.isDirectory()) {
23
+ walkFiles(abs, rel, out);
24
+ return;
25
+ }
26
+ if (!e.isFile()) return;
27
+ if (!/\.(md|json|js|ts)$/i.test(e.name)) return;
28
+ out.push({ rel, abs });
29
+ });
30
+ }
31
+
32
+ function collectTrustTargets(cwd) {
33
+ const out = [];
34
+ const roots = [
35
+ { rel: ".claude-plugin", abs: path.join(cwd, ".claude-plugin") },
36
+ { rel: "hooks", abs: path.join(cwd, "hooks") },
37
+ { rel: "skills", abs: path.join(cwd, "skills") },
38
+ { rel: "agents", abs: path.join(cwd, "agents") },
39
+ { rel: "scripts", abs: path.join(cwd, "scripts") },
40
+ ];
41
+
42
+ roots.forEach((root) => walkFiles(root.abs, root.rel, out));
43
+ return out;
44
+ }
45
+
46
+ function snapshotPath(cwd) {
47
+ return path.join(cwd, ".claude", "cco", "state", "trust-snapshot.json");
48
+ }
49
+
50
+ function loadSnapshot(cwd) {
51
+ try {
52
+ return JSON.parse(fs.readFileSync(snapshotPath(cwd), "utf8"));
53
+ } catch {
54
+ return { files: {}, capturedAt: null };
55
+ }
56
+ }
57
+
58
+ function saveSnapshot(cwd, filesMap) {
59
+ const p = snapshotPath(cwd);
60
+ fs.mkdirSync(path.dirname(p), { recursive: true });
61
+ fs.writeFileSync(
62
+ p,
63
+ JSON.stringify({ capturedAt: new Date().toISOString(), files: filesMap }, null, 2),
64
+ "utf8"
65
+ );
66
+ }
67
+
68
+ function pathBlockedByPolicy(rel, teamPolicy) {
69
+ const patterns = Array.isArray(teamPolicy?.blockedPatterns) ? teamPolicy.blockedPatterns : [];
70
+ return patterns.some((pat) => {
71
+ try {
72
+ return new RegExp(pat, "i").test(rel);
73
+ } catch {
74
+ return false;
75
+ }
76
+ });
77
+ }
78
+
79
+ function classifyChanges(currentFiles, previousSnapshot, teamPolicy) {
80
+ const prev = previousSnapshot?.files || {};
81
+ const changed = [];
82
+ const currentMap = {};
83
+
84
+ currentFiles.forEach((f) => {
85
+ const hash = hashFile(f.abs);
86
+ currentMap[f.rel] = hash;
87
+ const prevHash = prev[f.rel] || null;
88
+
89
+ if (!prevHash) {
90
+ changed.push({ path: f.rel, changeType: "new", trustLevel: pathBlockedByPolicy(f.rel, teamPolicy) ? "untrusted" : "unknown", hash });
91
+ return;
92
+ }
93
+
94
+ if (prevHash !== hash) {
95
+ changed.push({ path: f.rel, changeType: "changed", trustLevel: pathBlockedByPolicy(f.rel, teamPolicy) ? "untrusted" : "unknown", hash });
96
+ return;
97
+ }
98
+
99
+ changed.push({ path: f.rel, changeType: "unchanged", trustLevel: "trusted", hash });
100
+ });
101
+
102
+ Object.keys(prev).forEach((rel) => {
103
+ if (!currentMap[rel]) changed.push({ path: rel, changeType: "removed", trustLevel: "unknown", hash: null });
104
+ });
105
+
106
+ return { currentMap, changed };
107
+ }
108
+
109
+ function runTrustAudit({ cwd, teamPolicy, writeSnapshot = true }) {
110
+ const targets = collectTrustTargets(cwd);
111
+ const previous = loadSnapshot(cwd);
112
+ const { currentMap, changed } = classifyChanges(targets, previous, teamPolicy);
113
+
114
+ const summary = {
115
+ total: changed.length,
116
+ newOrChanged: changed.filter((x) => x.changeType === "new" || x.changeType === "changed").length,
117
+ unknown: changed.filter((x) => x.trustLevel === "unknown").length,
118
+ untrusted: changed.filter((x) => x.trustLevel === "untrusted").length,
119
+ trusted: changed.filter((x) => x.trustLevel === "trusted").length,
120
+ };
121
+
122
+ const audit = {
123
+ createdAt: new Date().toISOString(),
124
+ summary,
125
+ entries: changed,
126
+ unknownFiles: changed.filter((x) => x.trustLevel === "unknown").map((x) => x.path),
127
+ untrustedFiles: changed.filter((x) => x.trustLevel === "untrusted").map((x) => x.path),
128
+ };
129
+
130
+ if (writeSnapshot) saveSnapshot(cwd, currentMap);
131
+ return audit;
132
+ }
133
+
134
+ module.exports = {
135
+ runTrustAudit,
136
+ };