avorelo 0.3.4 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -55
- package/dist/avorelo.mjs +3297 -750
- package/package.json +8 -3
package/dist/avorelo.mjs
CHANGED
|
@@ -1702,8 +1702,8 @@ var init_generic = __esm({
|
|
|
1702
1702
|
const promptPath = join8(dir, ".avorelo", "prompt.md");
|
|
1703
1703
|
if (existsSync9(promptPath)) {
|
|
1704
1704
|
try {
|
|
1705
|
-
const { unlinkSync:
|
|
1706
|
-
|
|
1705
|
+
const { unlinkSync: unlinkSync6 } = __require("node:fs");
|
|
1706
|
+
unlinkSync6(promptPath);
|
|
1707
1707
|
return { removed: [promptPath], preserved: [] };
|
|
1708
1708
|
} catch {
|
|
1709
1709
|
return { removed: [], preserved: [promptPath] };
|
|
@@ -2562,6 +2562,7 @@ function buildCapabilityRouteDecision(input) {
|
|
|
2562
2562
|
const proposalHints = unique(input.proposalHints ?? []);
|
|
2563
2563
|
const allowedLegacyFeatures = input.allowedLegacyFeatures ?? [];
|
|
2564
2564
|
pushCapability(selected, expectedEvidence, reasonCodes, "context-check", "CONTEXT_CHECK_REUSED", ["context_check_result"]);
|
|
2565
|
+
pushCapability(selected, expectedEvidence, reasonCodes, "context-control", "CONTEXT_CONTROL_ACTIVE", ["context_control_summary"]);
|
|
2565
2566
|
pushCapability(selected, expectedEvidence, reasonCodes, "tool-governance", "TOOL_GOVERNANCE_REUSED", ["tool_routing_projection"]);
|
|
2566
2567
|
pushCapability(selected, expectedEvidence, reasonCodes, "receipt-trace", "KERNEL_RECEIPTS_REUSED", ["kernel_receipt_ref"]);
|
|
2567
2568
|
pushCapability(selected, expectedEvidence, reasonCodes, "model-routing", "MODEL_ROUTING_SUBORDINATE", ["model_routing_projection"]);
|
|
@@ -2752,7 +2753,8 @@ var init_work_controls = __esm({
|
|
|
2752
2753
|
"loop-control",
|
|
2753
2754
|
"drift-guard",
|
|
2754
2755
|
"company-loop",
|
|
2755
|
-
"founder-cockpit"
|
|
2756
|
+
"founder-cockpit",
|
|
2757
|
+
"context-control"
|
|
2756
2758
|
];
|
|
2757
2759
|
CAPABILITY_TO_LEGACY_FEATURE = {
|
|
2758
2760
|
"production-confidence": "visual-proof",
|
|
@@ -5510,6 +5512,15 @@ var init_plan_contract = __esm({
|
|
|
5510
5512
|
status: "implemented",
|
|
5511
5513
|
enforcementSurfaces: ["cli", "local-dashboard"],
|
|
5512
5514
|
privacyBoundary: "local_only"
|
|
5515
|
+
},
|
|
5516
|
+
{
|
|
5517
|
+
key: "artifact_guard_basic",
|
|
5518
|
+
pricingLabel: "Agent artifacts scanned for risks before AI touches the project",
|
|
5519
|
+
tier: "free",
|
|
5520
|
+
included: true,
|
|
5521
|
+
status: "implemented",
|
|
5522
|
+
enforcementSurfaces: ["cli"],
|
|
5523
|
+
privacyBoundary: "local_only"
|
|
5513
5524
|
}
|
|
5514
5525
|
];
|
|
5515
5526
|
PRO_CAPABILITIES = [
|
|
@@ -7325,8 +7336,8 @@ init_run();
|
|
|
7325
7336
|
init_work_contract();
|
|
7326
7337
|
init_receipts();
|
|
7327
7338
|
init_state_ledger();
|
|
7328
|
-
import { writeFileSync as
|
|
7329
|
-
import { join as
|
|
7339
|
+
import { writeFileSync as writeFileSync38, mkdirSync as mkdirSync40, existsSync as existsSync64, readFileSync as readFileSync50, appendFileSync as appendFileSync11, rmSync as rmSync3, unlinkSync as unlinkSync5 } from "node:fs";
|
|
7340
|
+
import { join as join66 } from "node:path";
|
|
7330
7341
|
|
|
7331
7342
|
// src/avorelo/capabilities/activation/index.ts
|
|
7332
7343
|
import { mkdtempSync, writeFileSync as writeFileSync11, mkdirSync as mkdirSync11, rmSync as rmSync2, existsSync as existsSync24 } from "node:fs";
|
|
@@ -8111,7 +8122,7 @@ function runFullActivation(targetDir) {
|
|
|
8111
8122
|
const dashboardPath = join27(targetDir, ".avorelo", "dashboard", "index.html");
|
|
8112
8123
|
const localDashboard = { available: existsSync30(dashboardPath), path: existsSync30(dashboardPath) ? dashboardPath : void 0 };
|
|
8113
8124
|
const blockers = repairsBlocked.length;
|
|
8114
|
-
const activationStatus = blockers > 0 ? "blocked" : "
|
|
8125
|
+
const activationStatus = blockers > 0 ? "blocked" : "active";
|
|
8115
8126
|
const existingState = readActivationState(targetDir);
|
|
8116
8127
|
const workspaceId = existingState?.workspaceId || makeWorkspaceId(targetDir);
|
|
8117
8128
|
const state = {
|
|
@@ -8836,7 +8847,7 @@ import { join as join40 } from "node:path";
|
|
|
8836
8847
|
|
|
8837
8848
|
// src/avorelo/capabilities/runtime-flow/index.ts
|
|
8838
8849
|
init_entitlement_resolver();
|
|
8839
|
-
import { createHash as
|
|
8850
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
8840
8851
|
import { mkdirSync as mkdirSync24, writeFileSync as writeFileSync23, appendFileSync as appendFileSync9, readFileSync as readFileSync23, existsSync as existsSync39 } from "node:fs";
|
|
8841
8852
|
import { join as join39 } from "node:path";
|
|
8842
8853
|
|
|
@@ -11464,137 +11475,1412 @@ function buildSummary(status, sourceCount, findingCount) {
|
|
|
11464
11475
|
return `Checked ${sourceCount} instruction source(s). ${findingCount} finding(s) detected.`;
|
|
11465
11476
|
}
|
|
11466
11477
|
|
|
11467
|
-
// src/avorelo/
|
|
11468
|
-
|
|
11469
|
-
|
|
11470
|
-
|
|
11478
|
+
// src/avorelo/kernel/context-control/classify.ts
|
|
11479
|
+
var SECRET_PATTERNS2 = [
|
|
11480
|
+
/(?:api[_-]?key|apikey)\s*[:=]\s*\S+/i,
|
|
11481
|
+
/(?:secret|token|password|passwd|pwd)\s*[:=]\s*\S+/i,
|
|
11482
|
+
/(?:aws|gcp|azure)[_-](?:access|secret|key)\s*[:=]/i,
|
|
11483
|
+
/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/,
|
|
11484
|
+
/(?:sk-|pk-|rk-)[a-zA-Z0-9]{20,}/,
|
|
11485
|
+
/ghp_[a-zA-Z0-9]{36}/,
|
|
11486
|
+
/glpat-[a-zA-Z0-9_-]{20,}/,
|
|
11487
|
+
/xox[bpsa]-[a-zA-Z0-9-]+/
|
|
11488
|
+
];
|
|
11489
|
+
var FORBIDDEN_PATTERNS = [
|
|
11490
|
+
/PRIVATE[_ ]KEY/,
|
|
11491
|
+
/\.env\b/,
|
|
11492
|
+
/credentials\.json/,
|
|
11493
|
+
/id_rsa/,
|
|
11494
|
+
/-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/
|
|
11495
|
+
];
|
|
11496
|
+
function classifyContentType(content, label) {
|
|
11497
|
+
const lower = label.toLowerCase();
|
|
11498
|
+
const trimmed = content.slice(0, 2e3);
|
|
11499
|
+
if (lower.includes("log") || lower.includes("output")) {
|
|
11500
|
+
if (/^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}/.test(trimmed)) return "log";
|
|
11501
|
+
if (/\b(?:ERROR|WARN|INFO|DEBUG|FATAL)\b/.test(trimmed)) return "log";
|
|
11502
|
+
if (/(?:exit code|exited with|status:)\s*\d/i.test(trimmed)) return "log";
|
|
11503
|
+
}
|
|
11504
|
+
if (lower.includes("test")) {
|
|
11505
|
+
if (/(?:PASS|FAIL|✓|✗|passed|failed)\s/i.test(trimmed)) return "test_output";
|
|
11506
|
+
if (/(?:tests?|specs?|suites?)\s+\d+/i.test(trimmed)) return "test_output";
|
|
11507
|
+
}
|
|
11508
|
+
if (lower.includes("diff") || lower.includes("patch")) return "diff";
|
|
11509
|
+
if (lower.includes("tree") || lower.includes("directory")) return "file_tree";
|
|
11510
|
+
if (lower.includes("conversation") || lower.includes("history") || lower.includes("session")) return "conversation_history";
|
|
11511
|
+
if (lower.includes("receipt") || lower.includes("proof") || lower.includes("evidence")) return "receipt_proof_evidence";
|
|
11512
|
+
if (lower.includes("doc") || lower.includes("readme") || lower.includes("markdown")) return "documentation";
|
|
11513
|
+
if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
|
|
11514
|
+
try {
|
|
11515
|
+
JSON.parse(content);
|
|
11516
|
+
return "json_tool_output";
|
|
11517
|
+
} catch {
|
|
11518
|
+
}
|
|
11519
|
+
}
|
|
11520
|
+
if (/^diff --git/.test(trimmed) || /^@@\s/.test(trimmed)) return "diff";
|
|
11521
|
+
if (/^[├└│─\s]+/.test(trimmed) || /^\s*[a-z_-]+\/$/m.test(trimmed)) return "file_tree";
|
|
11522
|
+
if (/\b(?:function|class|const|let|var|import|export|def |async )\b/.test(trimmed)) return "source_like";
|
|
11523
|
+
return "unknown";
|
|
11471
11524
|
}
|
|
11472
|
-
function
|
|
11473
|
-
|
|
11525
|
+
function classifySensitivity(content) {
|
|
11526
|
+
for (const pattern of FORBIDDEN_PATTERNS) {
|
|
11527
|
+
if (pattern.test(content)) return "forbidden";
|
|
11528
|
+
}
|
|
11529
|
+
for (const pattern of SECRET_PATTERNS2) {
|
|
11530
|
+
if (pattern.test(content)) return "secret_like";
|
|
11531
|
+
}
|
|
11532
|
+
if (/\b(?:internal|private|confidential)\b/i.test(content.slice(0, 500))) return "internal";
|
|
11533
|
+
return "public";
|
|
11534
|
+
}
|
|
11535
|
+
function classifyPersistence(sensitivity) {
|
|
11536
|
+
switch (sensitivity) {
|
|
11537
|
+
case "forbidden":
|
|
11538
|
+
return { safeForModel: false, safeForPersistence: false, safeForCloudSync: false, localOnly: true, forbidden: true };
|
|
11539
|
+
case "secret_like":
|
|
11540
|
+
return { safeForModel: false, safeForPersistence: false, safeForCloudSync: false, localOnly: true, forbidden: false };
|
|
11541
|
+
case "local_only":
|
|
11542
|
+
return { safeForModel: true, safeForPersistence: true, safeForCloudSync: false, localOnly: true, forbidden: false };
|
|
11543
|
+
case "internal":
|
|
11544
|
+
return { safeForModel: true, safeForPersistence: true, safeForCloudSync: false, localOnly: false, forbidden: false };
|
|
11545
|
+
case "public":
|
|
11546
|
+
return { safeForModel: true, safeForPersistence: true, safeForCloudSync: true, localOnly: false, forbidden: false };
|
|
11547
|
+
}
|
|
11548
|
+
}
|
|
11549
|
+
function classifyVolatility(contentType) {
|
|
11550
|
+
switch (contentType) {
|
|
11551
|
+
case "documentation":
|
|
11552
|
+
case "receipt_proof_evidence":
|
|
11553
|
+
return "stable";
|
|
11554
|
+
case "file_tree":
|
|
11555
|
+
case "source_like":
|
|
11556
|
+
return "semi_stable";
|
|
11557
|
+
default:
|
|
11558
|
+
return "volatile";
|
|
11559
|
+
}
|
|
11560
|
+
}
|
|
11561
|
+
function classifyEvidenceRequirement(contentType, sensitivity) {
|
|
11562
|
+
if (sensitivity === "forbidden") return "forbidden";
|
|
11563
|
+
if (contentType === "receipt_proof_evidence") return "proof_critical";
|
|
11564
|
+
if (contentType === "test_output" || contentType === "diff") return "proof_critical";
|
|
11565
|
+
if (contentType === "log") return "helpful";
|
|
11566
|
+
if (contentType === "file_tree") return "noise";
|
|
11567
|
+
return "helpful";
|
|
11568
|
+
}
|
|
11569
|
+
function classifyRoleRelevance(contentType) {
|
|
11570
|
+
switch (contentType) {
|
|
11571
|
+
case "log":
|
|
11572
|
+
case "test_output":
|
|
11573
|
+
return ["executor", "reviewer", "proof_adapter"];
|
|
11574
|
+
case "diff":
|
|
11575
|
+
return ["reviewer", "proof_adapter"];
|
|
11576
|
+
case "json_tool_output":
|
|
11577
|
+
return ["executor", "reviewer"];
|
|
11578
|
+
case "file_tree":
|
|
11579
|
+
return ["executor"];
|
|
11580
|
+
case "receipt_proof_evidence":
|
|
11581
|
+
return ["proof_adapter", "receipt", "status"];
|
|
11582
|
+
case "conversation_history":
|
|
11583
|
+
return ["executor"];
|
|
11584
|
+
case "documentation":
|
|
11585
|
+
return ["executor", "reviewer"];
|
|
11586
|
+
default:
|
|
11587
|
+
return ["executor"];
|
|
11588
|
+
}
|
|
11474
11589
|
}
|
|
11475
|
-
|
|
11476
|
-
|
|
11477
|
-
|
|
11478
|
-
|
|
11479
|
-
|
|
11480
|
-
|
|
11481
|
-
|
|
11482
|
-
|
|
11590
|
+
function estimateTokens(content) {
|
|
11591
|
+
return Math.ceil(content.length / 4);
|
|
11592
|
+
}
|
|
11593
|
+
function classifyItem(item2) {
|
|
11594
|
+
const contentType = classifyContentType(item2.content, item2.label);
|
|
11595
|
+
const sensitivity = classifySensitivity(item2.content);
|
|
11596
|
+
const persistence = classifyPersistence(sensitivity);
|
|
11597
|
+
const volatility = classifyVolatility(contentType);
|
|
11598
|
+
const evidenceRequirement = classifyEvidenceRequirement(contentType, sensitivity);
|
|
11599
|
+
const roleRelevance = classifyRoleRelevance(contentType);
|
|
11600
|
+
const tokens = estimateTokens(item2.content);
|
|
11601
|
+
return {
|
|
11602
|
+
contentType,
|
|
11603
|
+
sensitivity,
|
|
11604
|
+
persistence,
|
|
11605
|
+
roleRelevance,
|
|
11606
|
+
volatility,
|
|
11607
|
+
evidenceRequirement,
|
|
11608
|
+
estimatedTokens: tokens
|
|
11609
|
+
};
|
|
11610
|
+
}
|
|
11611
|
+
function containsSecrets(content) {
|
|
11612
|
+
return SECRET_PATTERNS2.some((p) => p.test(content));
|
|
11613
|
+
}
|
|
11614
|
+
function containsForbiddenContent(content) {
|
|
11615
|
+
return FORBIDDEN_PATTERNS.some((p) => p.test(content));
|
|
11616
|
+
}
|
|
11617
|
+
|
|
11618
|
+
// src/avorelo/kernel/context-control/evidence-store.ts
|
|
11619
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
11620
|
+
var evidenceCounter = 0;
|
|
11621
|
+
function createEvidenceRef(kind, sourceType, safeSummary, originalContent) {
|
|
11622
|
+
evidenceCounter++;
|
|
11623
|
+
const hash = createHash12("sha256").update(originalContent).digest("hex").slice(0, 16);
|
|
11624
|
+
return {
|
|
11625
|
+
id: `ctx-ev-${Date.now()}-${evidenceCounter}`,
|
|
11626
|
+
kind,
|
|
11627
|
+
sourceType,
|
|
11628
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11629
|
+
localOnly: true,
|
|
11630
|
+
safeSummary: safeSummary.slice(0, 200),
|
|
11631
|
+
retrievalPolicy: "available",
|
|
11632
|
+
redactionStatus: "clean",
|
|
11633
|
+
provenanceTags: [],
|
|
11634
|
+
contentHash: hash
|
|
11635
|
+
};
|
|
11636
|
+
}
|
|
11637
|
+
function createBlockedEvidenceRef(sourceType, reason) {
|
|
11638
|
+
evidenceCounter++;
|
|
11639
|
+
return {
|
|
11640
|
+
id: `ctx-ev-blocked-${Date.now()}-${evidenceCounter}`,
|
|
11641
|
+
kind: "BLOCKED",
|
|
11642
|
+
sourceType,
|
|
11643
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11644
|
+
localOnly: true,
|
|
11645
|
+
safeSummary: `Blocked: ${reason.slice(0, 150)}`,
|
|
11646
|
+
retrievalPolicy: "forbidden",
|
|
11647
|
+
redactionStatus: "forbidden",
|
|
11648
|
+
provenanceTags: [],
|
|
11649
|
+
contentHash: ""
|
|
11650
|
+
};
|
|
11651
|
+
}
|
|
11652
|
+
|
|
11653
|
+
// src/avorelo/kernel/context-control/reducers/logs.ts
|
|
11654
|
+
var ERROR_PATTERNS = [
|
|
11655
|
+
/\b(?:ERROR|FATAL|CRITICAL|PANIC)\b/i,
|
|
11656
|
+
/\bfailed\b/i,
|
|
11657
|
+
/\berror\b/i,
|
|
11658
|
+
/\bException\b/,
|
|
11659
|
+
/\bTraceback\b/,
|
|
11660
|
+
/exit code [1-9]/i,
|
|
11661
|
+
/exited with (?:code )?[1-9]/i,
|
|
11662
|
+
/\bsegfault\b/i,
|
|
11663
|
+
/\bOOM\b/,
|
|
11664
|
+
/\bkilled\b/i
|
|
11483
11665
|
];
|
|
11484
|
-
|
|
11666
|
+
var WARNING_PATTERNS = [
|
|
11667
|
+
/\bWARN(?:ING)?\b/i,
|
|
11668
|
+
/\bdeprecated\b/i,
|
|
11669
|
+
/\bskipped\b/i
|
|
11670
|
+
];
|
|
11671
|
+
var COMMAND_PATTERNS = [
|
|
11672
|
+
/^\$\s+.+/m,
|
|
11673
|
+
/^>\s+.+/m,
|
|
11674
|
+
/^Running\s+/m,
|
|
11675
|
+
/^Executing\s+/m
|
|
11676
|
+
];
|
|
11677
|
+
var SECRET_LINE_PATTERNS = [
|
|
11678
|
+
/(?:api[_-]?key|apikey|secret|token|password|passwd|pwd)\s*[:=]\s*\S+/i,
|
|
11679
|
+
/(?:sk-|pk-|rk-)[a-zA-Z0-9]{20,}/,
|
|
11680
|
+
/ghp_[a-zA-Z0-9]{36}/,
|
|
11681
|
+
/(?:aws|gcp|azure)[_-](?:access|secret|key)/i
|
|
11682
|
+
];
|
|
11683
|
+
function isImportantLine(line) {
|
|
11684
|
+
return ERROR_PATTERNS.some((p) => p.test(line)) || WARNING_PATTERNS.some((p) => p.test(line)) || COMMAND_PATTERNS.some((p) => p.test(line));
|
|
11685
|
+
}
|
|
11686
|
+
function containsSecret(line) {
|
|
11687
|
+
return SECRET_LINE_PATTERNS.some((p) => p.test(line));
|
|
11688
|
+
}
|
|
11689
|
+
function redactSecretLine(line) {
|
|
11690
|
+
return line.replace(
|
|
11691
|
+
/((?:api[_-]?key|apikey|secret|token|password|passwd|pwd)\s*[:=]\s*)\S+/gi,
|
|
11692
|
+
"$1[REDACTED]"
|
|
11693
|
+
).replace(
|
|
11694
|
+
/(?:sk-|pk-|rk-)[a-zA-Z0-9]{20,}/g,
|
|
11695
|
+
"[REDACTED_KEY]"
|
|
11696
|
+
).replace(
|
|
11697
|
+
/ghp_[a-zA-Z0-9]{36}/g,
|
|
11698
|
+
"[REDACTED_GITHUB_TOKEN]"
|
|
11699
|
+
);
|
|
11700
|
+
}
|
|
11701
|
+
function reduceLogs(content, label) {
|
|
11702
|
+
const tokensBefore = estimateTokens(content);
|
|
11703
|
+
const lines = content.split("\n");
|
|
11704
|
+
if (lines.length <= 50) {
|
|
11705
|
+
const cleaned = lines.map((l) => containsSecret(l) ? redactSecretLine(l) : l).join("\n");
|
|
11706
|
+
return {
|
|
11707
|
+
reduced: cleaned,
|
|
11708
|
+
estimatedTokensBefore: tokensBefore,
|
|
11709
|
+
estimatedTokensAfter: estimateTokens(cleaned),
|
|
11710
|
+
evidenceRef: createEvidenceRef("LOG_REDUCTION", "log", `Log: ${label}`, content),
|
|
11711
|
+
proofImpact: "PASS",
|
|
11712
|
+
reasonCodes: lines.some(containsSecret) ? ["SECRET_REDACTED_IN_LOG"] : ["SHORT_LOG_KEPT"],
|
|
11713
|
+
blockedContent: false
|
|
11714
|
+
};
|
|
11715
|
+
}
|
|
11716
|
+
const importantLines = [];
|
|
11717
|
+
const repeatedCounts = /* @__PURE__ */ new Map();
|
|
11718
|
+
let totalLines = lines.length;
|
|
11719
|
+
let errorCount = 0;
|
|
11720
|
+
let warnCount = 0;
|
|
11721
|
+
let secretsRedacted = 0;
|
|
11722
|
+
for (let i = 0; i < lines.length; i++) {
|
|
11723
|
+
const line = lines[i];
|
|
11724
|
+
const trimmed = line.trim();
|
|
11725
|
+
if (!trimmed) continue;
|
|
11726
|
+
if (containsSecret(line)) {
|
|
11727
|
+
secretsRedacted++;
|
|
11728
|
+
const redacted = redactSecretLine(line);
|
|
11729
|
+
if (isImportantLine(line)) {
|
|
11730
|
+
importantLines.push(redacted);
|
|
11731
|
+
}
|
|
11732
|
+
continue;
|
|
11733
|
+
}
|
|
11734
|
+
if (isImportantLine(line)) {
|
|
11735
|
+
if (ERROR_PATTERNS.some((p) => p.test(line))) errorCount++;
|
|
11736
|
+
if (WARNING_PATTERNS.some((p) => p.test(line))) warnCount++;
|
|
11737
|
+
importantLines.push(line);
|
|
11738
|
+
const contextStart = Math.max(0, i - 2);
|
|
11739
|
+
const contextEnd = Math.min(lines.length - 1, i + 2);
|
|
11740
|
+
for (let j = contextStart; j <= contextEnd; j++) {
|
|
11741
|
+
if (j !== i && lines[j].trim() && !isImportantLine(lines[j])) {
|
|
11742
|
+
if (!containsSecret(lines[j])) {
|
|
11743
|
+
importantLines.push(` ${lines[j]}`);
|
|
11744
|
+
}
|
|
11745
|
+
}
|
|
11746
|
+
}
|
|
11747
|
+
continue;
|
|
11748
|
+
}
|
|
11749
|
+
const normalized = trimmed.replace(/\d+/g, "N").replace(/0x[a-fA-F0-9]+/g, "0xN");
|
|
11750
|
+
repeatedCounts.set(normalized, (repeatedCounts.get(normalized) || 0) + 1);
|
|
11751
|
+
}
|
|
11752
|
+
const repeatedSummary = [];
|
|
11753
|
+
for (const [pattern, count] of repeatedCounts) {
|
|
11754
|
+
if (count >= 3) {
|
|
11755
|
+
repeatedSummary.push(` (${count}x) ${pattern.slice(0, 120)}`);
|
|
11756
|
+
}
|
|
11757
|
+
}
|
|
11758
|
+
const parts = [
|
|
11759
|
+
`--- Log Summary: ${label} ---`,
|
|
11760
|
+
`Total lines: ${totalLines}`,
|
|
11761
|
+
`Errors: ${errorCount} | Warnings: ${warnCount} | Secrets redacted: ${secretsRedacted}`,
|
|
11762
|
+
"",
|
|
11763
|
+
"--- Important Lines ---",
|
|
11764
|
+
...importantLines.slice(0, 100)
|
|
11765
|
+
];
|
|
11766
|
+
if (repeatedSummary.length > 0) {
|
|
11767
|
+
parts.push("", "--- Repeated Patterns ---", ...repeatedSummary.slice(0, 20));
|
|
11768
|
+
}
|
|
11769
|
+
const reduced = parts.join("\n");
|
|
11770
|
+
const tokensAfter = estimateTokens(reduced);
|
|
11771
|
+
const reasonCodes = ["LARGE_LOG_REDUCED"];
|
|
11772
|
+
if (secretsRedacted > 0) reasonCodes.push("SECRET_REDACTED_IN_LOG");
|
|
11773
|
+
if (errorCount > 0) reasonCodes.push("ERRORS_PRESERVED");
|
|
11774
|
+
if (repeatedSummary.length > 0) reasonCodes.push("REPEATED_LINES_SUMMARIZED");
|
|
11775
|
+
const proofImpact = errorCount > 0 && importantLines.length > 0 ? "PASS" : "DEGRADED";
|
|
11485
11776
|
return {
|
|
11486
|
-
|
|
11487
|
-
|
|
11488
|
-
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
verifierPlan: extraVerifierPlan,
|
|
11777
|
+
reduced,
|
|
11778
|
+
estimatedTokensBefore: tokensBefore,
|
|
11779
|
+
estimatedTokensAfter: tokensAfter,
|
|
11780
|
+
evidenceRef: createEvidenceRef("LOG_REDUCTION", "log", `Log reduced: ${label}`, content),
|
|
11781
|
+
proofImpact,
|
|
11492
11782
|
reasonCodes,
|
|
11493
|
-
|
|
11494
|
-
modelMayAssist: false,
|
|
11495
|
-
modelMayDecide: false,
|
|
11496
|
-
scannerMayDecide: false,
|
|
11497
|
-
finalDecisionOwner: "kernel/stop-continue-gate",
|
|
11498
|
-
containsRawPrompt: false,
|
|
11499
|
-
containsRawSource: false,
|
|
11500
|
-
containsRawSecret: false
|
|
11783
|
+
blockedContent: false
|
|
11501
11784
|
};
|
|
11502
11785
|
}
|
|
11503
|
-
|
|
11504
|
-
|
|
11786
|
+
|
|
11787
|
+
// src/avorelo/kernel/context-control/reducers/json.ts
|
|
11788
|
+
var MAX_ARRAY_ITEMS = 3;
|
|
11789
|
+
var MAX_STRING_LENGTH2 = 200;
|
|
11790
|
+
var MAX_DEPTH = 6;
|
|
11791
|
+
function truncateValue(value, depth) {
|
|
11792
|
+
if (depth > MAX_DEPTH) return "[... nested beyond depth limit]";
|
|
11793
|
+
if (value === null || value === void 0) return value;
|
|
11794
|
+
if (typeof value === "boolean" || typeof value === "number") return value;
|
|
11795
|
+
if (typeof value === "string") {
|
|
11796
|
+
if (value.length > MAX_STRING_LENGTH2) {
|
|
11797
|
+
return value.slice(0, MAX_STRING_LENGTH2) + `... (${value.length} chars)`;
|
|
11798
|
+
}
|
|
11799
|
+
return value;
|
|
11800
|
+
}
|
|
11801
|
+
if (Array.isArray(value)) {
|
|
11802
|
+
if (value.length === 0) return [];
|
|
11803
|
+
const truncated = value.slice(0, MAX_ARRAY_ITEMS).map((v) => truncateValue(v, depth + 1));
|
|
11804
|
+
if (value.length > MAX_ARRAY_ITEMS) {
|
|
11805
|
+
truncated.push(`... (${value.length - MAX_ARRAY_ITEMS} more items, ${value.length} total)`);
|
|
11806
|
+
}
|
|
11807
|
+
return truncated;
|
|
11808
|
+
}
|
|
11809
|
+
if (typeof value === "object") {
|
|
11810
|
+
const obj = value;
|
|
11811
|
+
const keys = Object.keys(obj);
|
|
11812
|
+
const result3 = {};
|
|
11813
|
+
const priorityKeys = ["error", "message", "status", "code", "name", "type", "id", "path"];
|
|
11814
|
+
const sortedKeys = [
|
|
11815
|
+
...priorityKeys.filter((k) => keys.includes(k)),
|
|
11816
|
+
...keys.filter((k) => !priorityKeys.includes(k))
|
|
11817
|
+
];
|
|
11818
|
+
for (const key of sortedKeys) {
|
|
11819
|
+
result3[key] = truncateValue(obj[key], depth + 1);
|
|
11820
|
+
}
|
|
11821
|
+
return result3;
|
|
11822
|
+
}
|
|
11823
|
+
return String(value);
|
|
11505
11824
|
}
|
|
11506
|
-
function
|
|
11825
|
+
function reduceJson(content, label) {
|
|
11826
|
+
const tokensBefore = estimateTokens(content);
|
|
11827
|
+
let parsed;
|
|
11507
11828
|
try {
|
|
11508
|
-
|
|
11829
|
+
parsed = JSON.parse(content);
|
|
11509
11830
|
} catch {
|
|
11510
|
-
|
|
11831
|
+
const fallback = content.slice(0, 2e3) + (content.length > 2e3 ? `
|
|
11832
|
+
... (${content.length} chars total, malformed JSON)` : "");
|
|
11833
|
+
return {
|
|
11834
|
+
reduced: fallback,
|
|
11835
|
+
estimatedTokensBefore: tokensBefore,
|
|
11836
|
+
estimatedTokensAfter: estimateTokens(fallback),
|
|
11837
|
+
evidenceRef: createEvidenceRef("JSON_REDUCTION", "json_tool_output", `JSON fallback: ${label}`, content),
|
|
11838
|
+
proofImpact: "DEGRADED",
|
|
11839
|
+
reasonCodes: ["MALFORMED_JSON_TRUNCATED"],
|
|
11840
|
+
blockedContent: false
|
|
11841
|
+
};
|
|
11842
|
+
}
|
|
11843
|
+
const truncated = truncateValue(parsed, 0);
|
|
11844
|
+
const reduced = JSON.stringify(truncated, null, 2);
|
|
11845
|
+
const tokensAfter = estimateTokens(reduced);
|
|
11846
|
+
const reasonCodes = ["JSON_STRUCTURE_PRESERVED"];
|
|
11847
|
+
if (tokensAfter < tokensBefore * 0.8) {
|
|
11848
|
+
reasonCodes.push("JSON_ARRAYS_TRUNCATED");
|
|
11511
11849
|
}
|
|
11850
|
+
return {
|
|
11851
|
+
reduced,
|
|
11852
|
+
estimatedTokensBefore: tokensBefore,
|
|
11853
|
+
estimatedTokensAfter: tokensAfter,
|
|
11854
|
+
evidenceRef: createEvidenceRef("JSON_REDUCTION", "json_tool_output", `JSON reduced: ${label}`, content),
|
|
11855
|
+
proofImpact: "PASS",
|
|
11856
|
+
reasonCodes,
|
|
11857
|
+
blockedContent: false
|
|
11858
|
+
};
|
|
11512
11859
|
}
|
|
11513
|
-
|
|
11514
|
-
|
|
11515
|
-
|
|
11516
|
-
|
|
11860
|
+
|
|
11861
|
+
// src/avorelo/kernel/context-control/reducers/file-tree.ts
|
|
11862
|
+
var GENERATED_DIRS = /* @__PURE__ */ new Set([
|
|
11863
|
+
"node_modules",
|
|
11864
|
+
"dist",
|
|
11865
|
+
"build",
|
|
11866
|
+
"out",
|
|
11867
|
+
".next",
|
|
11868
|
+
".nuxt",
|
|
11869
|
+
".output",
|
|
11870
|
+
"coverage",
|
|
11871
|
+
".nyc_output",
|
|
11872
|
+
"__pycache__",
|
|
11873
|
+
".pytest_cache",
|
|
11874
|
+
".git",
|
|
11875
|
+
".svn",
|
|
11876
|
+
".hg",
|
|
11877
|
+
"vendor",
|
|
11878
|
+
"bower_components",
|
|
11879
|
+
".cache",
|
|
11880
|
+
".parcel-cache",
|
|
11881
|
+
".turbo",
|
|
11882
|
+
"target",
|
|
11883
|
+
"bin",
|
|
11884
|
+
"obj",
|
|
11885
|
+
".terraform",
|
|
11886
|
+
".serverless"
|
|
11887
|
+
]);
|
|
11888
|
+
var PRIORITY_PATTERNS = [
|
|
11889
|
+
/^src\//,
|
|
11890
|
+
/^lib\//,
|
|
11891
|
+
/^app\//,
|
|
11892
|
+
/^pages\//,
|
|
11893
|
+
/^components\//,
|
|
11894
|
+
/^tests?\//,
|
|
11895
|
+
/^__tests__\//,
|
|
11896
|
+
/^spec\//,
|
|
11897
|
+
/^scripts?\//,
|
|
11898
|
+
/^config/,
|
|
11899
|
+
/^\.(?:env|eslint|prettier|babel|jest|vitest)/,
|
|
11900
|
+
/(?:package|tsconfig|webpack|vite|rollup)\.(?:json|config|js|ts|mjs|cjs)$/,
|
|
11901
|
+
/(?:README|CHANGELOG|LICENSE|CLAUDE|AGENTS|CONTRIBUTING)\b/i,
|
|
11902
|
+
/^docs?\//
|
|
11903
|
+
];
|
|
11904
|
+
function isPriority(path) {
|
|
11905
|
+
return PRIORITY_PATTERNS.some((p) => p.test(path));
|
|
11906
|
+
}
|
|
11907
|
+
function isGeneratedDir(segment) {
|
|
11908
|
+
return GENERATED_DIRS.has(segment.toLowerCase());
|
|
11909
|
+
}
|
|
11910
|
+
function reduceFileTree(content, label) {
|
|
11911
|
+
const tokensBefore = estimateTokens(content);
|
|
11912
|
+
const lines = content.split("\n").filter((l) => l.trim());
|
|
11913
|
+
if (lines.length <= 100) {
|
|
11914
|
+
return {
|
|
11915
|
+
reduced: content,
|
|
11916
|
+
estimatedTokensBefore: tokensBefore,
|
|
11917
|
+
estimatedTokensAfter: tokensBefore,
|
|
11918
|
+
evidenceRef: null,
|
|
11919
|
+
proofImpact: "PASS",
|
|
11920
|
+
reasonCodes: ["SMALL_TREE_KEPT"],
|
|
11921
|
+
blockedContent: false
|
|
11922
|
+
};
|
|
11517
11923
|
}
|
|
11518
|
-
|
|
11924
|
+
const priorityPaths = [];
|
|
11925
|
+
const generatedDirs = /* @__PURE__ */ new Map();
|
|
11926
|
+
const otherPaths = [];
|
|
11927
|
+
for (const line of lines) {
|
|
11928
|
+
const cleaned = line.replace(/^[├└│─\s│]+/, "").trim();
|
|
11929
|
+
if (!cleaned) continue;
|
|
11930
|
+
const firstSegment = cleaned.split("/")[0];
|
|
11931
|
+
if (isGeneratedDir(firstSegment)) {
|
|
11932
|
+
generatedDirs.set(firstSegment, (generatedDirs.get(firstSegment) || 0) + 1);
|
|
11933
|
+
continue;
|
|
11934
|
+
}
|
|
11935
|
+
if (isPriority(cleaned)) {
|
|
11936
|
+
priorityPaths.push(cleaned);
|
|
11937
|
+
} else {
|
|
11938
|
+
otherPaths.push(cleaned);
|
|
11939
|
+
}
|
|
11940
|
+
}
|
|
11941
|
+
const parts = [
|
|
11942
|
+
`--- File Tree Summary: ${label} ---`,
|
|
11943
|
+
`Total entries: ${lines.length}`,
|
|
11944
|
+
"",
|
|
11945
|
+
"--- Source & Config (priority) ---",
|
|
11946
|
+
...priorityPaths.slice(0, 80)
|
|
11947
|
+
];
|
|
11948
|
+
if (otherPaths.length > 0) {
|
|
11949
|
+
parts.push("", `--- Other (${otherPaths.length} entries) ---`);
|
|
11950
|
+
parts.push(...otherPaths.slice(0, 30));
|
|
11951
|
+
if (otherPaths.length > 30) {
|
|
11952
|
+
parts.push(` ... (${otherPaths.length - 30} more)`);
|
|
11953
|
+
}
|
|
11954
|
+
}
|
|
11955
|
+
if (generatedDirs.size > 0) {
|
|
11956
|
+
parts.push("", "--- Generated/Vendor (summarized) ---");
|
|
11957
|
+
for (const [dir, count] of generatedDirs) {
|
|
11958
|
+
parts.push(` ${dir}/ (${count} entries)`);
|
|
11959
|
+
}
|
|
11960
|
+
}
|
|
11961
|
+
const reduced = parts.join("\n");
|
|
11962
|
+
return {
|
|
11963
|
+
reduced,
|
|
11964
|
+
estimatedTokensBefore: tokensBefore,
|
|
11965
|
+
estimatedTokensAfter: estimateTokens(reduced),
|
|
11966
|
+
evidenceRef: createEvidenceRef("FILE_TREE_REDUCTION", "file_tree", `Tree reduced: ${label}`, content),
|
|
11967
|
+
proofImpact: "PASS",
|
|
11968
|
+
reasonCodes: ["FILE_TREE_SUMMARIZED", "GENERATED_DIRS_COLLAPSED"],
|
|
11969
|
+
blockedContent: false
|
|
11970
|
+
};
|
|
11519
11971
|
}
|
|
11520
|
-
|
|
11521
|
-
|
|
11972
|
+
|
|
11973
|
+
// src/avorelo/kernel/context-control/reducers/test-output.ts
|
|
11974
|
+
var FAIL_PATTERNS = [
|
|
11975
|
+
/\b(?:FAIL|FAILED|FAILURE)\b/i,
|
|
11976
|
+
/✗|✘|×|❌/,
|
|
11977
|
+
/\bnot ok\b/i,
|
|
11978
|
+
/AssertionError/i,
|
|
11979
|
+
/\bExpected\b.*\bReceived\b/i,
|
|
11980
|
+
/\bexpected\b.*\bto (?:equal|be|match|include|have)\b/i,
|
|
11981
|
+
/Error:\s/,
|
|
11982
|
+
/\bat\s+\S+\s+\(/
|
|
11983
|
+
];
|
|
11984
|
+
var PASS_PATTERNS = [
|
|
11985
|
+
/\b(?:PASS|PASSED|OK)\b/i,
|
|
11986
|
+
/✓|✔|✅/,
|
|
11987
|
+
/\bok\b/i
|
|
11988
|
+
];
|
|
11989
|
+
var SUMMARY_PATTERNS = [
|
|
11990
|
+
/Tests?:\s*\d+/i,
|
|
11991
|
+
/Suites?:\s*\d+/i,
|
|
11992
|
+
/\d+\s+(?:passing|failing|pending|skipped)/i,
|
|
11993
|
+
/Test Files\s/i,
|
|
11994
|
+
/Duration[:\s]/i,
|
|
11995
|
+
/Time:\s/i,
|
|
11996
|
+
/Ran all test suites/i
|
|
11997
|
+
];
|
|
11998
|
+
var COMMAND_PATTERNS2 = [
|
|
11999
|
+
/^\$\s+/,
|
|
12000
|
+
/^>\s+/,
|
|
12001
|
+
/^Running\s+/i,
|
|
12002
|
+
/^Executing\s+/i,
|
|
12003
|
+
/^npm\s+(?:run|test)/i,
|
|
12004
|
+
/^node\s+/,
|
|
12005
|
+
/^npx\s+/
|
|
12006
|
+
];
|
|
12007
|
+
function reduceTestOutput(content, label) {
|
|
12008
|
+
const tokensBefore = estimateTokens(content);
|
|
12009
|
+
const lines = content.split("\n");
|
|
12010
|
+
if (lines.length <= 80) {
|
|
12011
|
+
return {
|
|
12012
|
+
reduced: content,
|
|
12013
|
+
estimatedTokensBefore: tokensBefore,
|
|
12014
|
+
estimatedTokensAfter: tokensBefore,
|
|
12015
|
+
evidenceRef: createEvidenceRef("TEST_OUTPUT_REDUCTION", "test_output", `Test output: ${label}`, content),
|
|
12016
|
+
proofImpact: "PASS",
|
|
12017
|
+
reasonCodes: ["SHORT_TEST_OUTPUT_KEPT"],
|
|
12018
|
+
blockedContent: false
|
|
12019
|
+
};
|
|
12020
|
+
}
|
|
12021
|
+
const failingLines = [];
|
|
12022
|
+
const summaryLines = [];
|
|
12023
|
+
const commandLines = [];
|
|
12024
|
+
let passingCount = 0;
|
|
12025
|
+
let failingCount = 0;
|
|
12026
|
+
let inFailBlock = false;
|
|
12027
|
+
let failBlockLines = 0;
|
|
12028
|
+
for (let i = 0; i < lines.length; i++) {
|
|
12029
|
+
const line = lines[i];
|
|
12030
|
+
if (COMMAND_PATTERNS2.some((p) => p.test(line))) {
|
|
12031
|
+
commandLines.push(line);
|
|
12032
|
+
continue;
|
|
12033
|
+
}
|
|
12034
|
+
if (SUMMARY_PATTERNS.some((p) => p.test(line))) {
|
|
12035
|
+
summaryLines.push(line);
|
|
12036
|
+
continue;
|
|
12037
|
+
}
|
|
12038
|
+
if (FAIL_PATTERNS.some((p) => p.test(line))) {
|
|
12039
|
+
inFailBlock = true;
|
|
12040
|
+
failBlockLines = 0;
|
|
12041
|
+
failingCount++;
|
|
12042
|
+
failingLines.push(line);
|
|
12043
|
+
continue;
|
|
12044
|
+
}
|
|
12045
|
+
if (inFailBlock && failBlockLines < 15) {
|
|
12046
|
+
failBlockLines++;
|
|
12047
|
+
failingLines.push(line);
|
|
12048
|
+
if (line.trim() === "" || failBlockLines >= 15) {
|
|
12049
|
+
inFailBlock = false;
|
|
12050
|
+
}
|
|
12051
|
+
continue;
|
|
12052
|
+
}
|
|
12053
|
+
if (PASS_PATTERNS.some((p) => p.test(line))) {
|
|
12054
|
+
passingCount++;
|
|
12055
|
+
inFailBlock = false;
|
|
12056
|
+
continue;
|
|
12057
|
+
}
|
|
12058
|
+
inFailBlock = false;
|
|
12059
|
+
}
|
|
12060
|
+
const parts = [
|
|
12061
|
+
`--- Test Output Summary: ${label} ---`
|
|
12062
|
+
];
|
|
12063
|
+
if (commandLines.length > 0) {
|
|
12064
|
+
parts.push("Command: " + commandLines[0]);
|
|
12065
|
+
}
|
|
12066
|
+
parts.push(`Passing: ${passingCount} | Failing: ${failingCount}`);
|
|
12067
|
+
if (failingLines.length > 0) {
|
|
12068
|
+
parts.push("", "--- Failing Tests ---");
|
|
12069
|
+
parts.push(...failingLines.slice(0, 200));
|
|
12070
|
+
}
|
|
12071
|
+
if (summaryLines.length > 0) {
|
|
12072
|
+
parts.push("", "--- Summary ---");
|
|
12073
|
+
parts.push(...summaryLines);
|
|
12074
|
+
}
|
|
12075
|
+
if (failingCount === 0 && passingCount > 0) {
|
|
12076
|
+
parts.push("", "All tests passing.");
|
|
12077
|
+
}
|
|
12078
|
+
const reduced = parts.join("\n");
|
|
12079
|
+
const proofImpact = failingCount > 0 && failingLines.length > 0 ? "PASS" : failingCount > 0 ? "DEGRADED" : "PASS";
|
|
12080
|
+
return {
|
|
12081
|
+
reduced,
|
|
12082
|
+
estimatedTokensBefore: tokensBefore,
|
|
12083
|
+
estimatedTokensAfter: estimateTokens(reduced),
|
|
12084
|
+
evidenceRef: createEvidenceRef("TEST_OUTPUT_REDUCTION", "test_output", `Tests reduced: ${label}`, content),
|
|
12085
|
+
proofImpact,
|
|
12086
|
+
reasonCodes: [
|
|
12087
|
+
"TEST_OUTPUT_REDUCED",
|
|
12088
|
+
...failingCount > 0 ? ["FAILING_TESTS_PRESERVED"] : ["ALL_TESTS_PASSING"],
|
|
12089
|
+
...passingCount > 10 ? ["PASSING_TESTS_SUMMARIZED"] : []
|
|
12090
|
+
],
|
|
12091
|
+
blockedContent: false
|
|
12092
|
+
};
|
|
11522
12093
|
}
|
|
11523
|
-
|
|
11524
|
-
|
|
11525
|
-
|
|
11526
|
-
|
|
11527
|
-
|
|
11528
|
-
|
|
11529
|
-
|
|
11530
|
-
|
|
11531
|
-
|
|
11532
|
-
|
|
11533
|
-
|
|
11534
|
-
|
|
11535
|
-
|
|
11536
|
-
|
|
11537
|
-
|
|
11538
|
-
|
|
11539
|
-
|
|
11540
|
-
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
12094
|
+
|
|
12095
|
+
// src/avorelo/kernel/context-control/reducers/diff-summary.ts
|
|
12096
|
+
var GENERATED_FILE_PATTERNS = [
|
|
12097
|
+
/package-lock\.json$/,
|
|
12098
|
+
/yarn\.lock$/,
|
|
12099
|
+
/pnpm-lock\.yaml$/,
|
|
12100
|
+
/\.min\.\w+$/,
|
|
12101
|
+
/\.map$/,
|
|
12102
|
+
/dist\//,
|
|
12103
|
+
/build\//,
|
|
12104
|
+
/\.generated\./,
|
|
12105
|
+
/node_modules\//
|
|
12106
|
+
];
|
|
12107
|
+
function isGeneratedFile(path) {
|
|
12108
|
+
return GENERATED_FILE_PATTERNS.some((p) => p.test(path));
|
|
12109
|
+
}
|
|
12110
|
+
function reduceDiffSummary(content, label) {
|
|
12111
|
+
const tokensBefore = estimateTokens(content);
|
|
12112
|
+
const lines = content.split("\n");
|
|
12113
|
+
const fileChanges = [];
|
|
12114
|
+
let totalInsertions = 0;
|
|
12115
|
+
let totalDeletions = 0;
|
|
12116
|
+
let currentFile = null;
|
|
12117
|
+
let currentInsertions = 0;
|
|
12118
|
+
let currentDeletions = 0;
|
|
12119
|
+
for (const line of lines) {
|
|
12120
|
+
const diffGit = line.match(/^diff --git a\/(.+?) b\//);
|
|
12121
|
+
if (diffGit) {
|
|
12122
|
+
if (currentFile) {
|
|
12123
|
+
fileChanges.push({
|
|
12124
|
+
path: currentFile,
|
|
12125
|
+
insertions: currentInsertions,
|
|
12126
|
+
deletions: currentDeletions,
|
|
12127
|
+
isGenerated: isGeneratedFile(currentFile)
|
|
12128
|
+
});
|
|
12129
|
+
totalInsertions += currentInsertions;
|
|
12130
|
+
totalDeletions += currentDeletions;
|
|
12131
|
+
}
|
|
12132
|
+
currentFile = diffGit[1];
|
|
12133
|
+
currentInsertions = 0;
|
|
12134
|
+
currentDeletions = 0;
|
|
12135
|
+
continue;
|
|
12136
|
+
}
|
|
12137
|
+
if (line.startsWith("+") && !line.startsWith("+++")) currentInsertions++;
|
|
12138
|
+
if (line.startsWith("-") && !line.startsWith("---")) currentDeletions++;
|
|
11551
12139
|
}
|
|
11552
|
-
if (
|
|
11553
|
-
|
|
11554
|
-
|
|
11555
|
-
|
|
11556
|
-
|
|
11557
|
-
|
|
12140
|
+
if (currentFile) {
|
|
12141
|
+
fileChanges.push({
|
|
12142
|
+
path: currentFile,
|
|
12143
|
+
insertions: currentInsertions,
|
|
12144
|
+
deletions: currentDeletions,
|
|
12145
|
+
isGenerated: isGeneratedFile(currentFile)
|
|
11558
12146
|
});
|
|
12147
|
+
totalInsertions += currentInsertions;
|
|
12148
|
+
totalDeletions += currentDeletions;
|
|
11559
12149
|
}
|
|
11560
|
-
|
|
12150
|
+
if (fileChanges.length === 0) {
|
|
12151
|
+
const statLine = lines.find((l) => /\d+ files? changed/.test(l));
|
|
12152
|
+
if (statLine) {
|
|
12153
|
+
return {
|
|
12154
|
+
reduced: `--- Diff Summary: ${label} ---
|
|
12155
|
+
${statLine}`,
|
|
12156
|
+
estimatedTokensBefore: tokensBefore,
|
|
12157
|
+
estimatedTokensAfter: estimateTokens(statLine),
|
|
12158
|
+
evidenceRef: createEvidenceRef("DIFF_SUMMARY", "diff", `Diff stats: ${label}`, content),
|
|
12159
|
+
proofImpact: "PASS",
|
|
12160
|
+
reasonCodes: ["DIFF_STAT_ONLY"],
|
|
12161
|
+
blockedContent: false
|
|
12162
|
+
};
|
|
12163
|
+
}
|
|
12164
|
+
return {
|
|
12165
|
+
reduced: `--- Diff Summary: ${label} ---
|
|
12166
|
+
No parseable diff content.`,
|
|
12167
|
+
estimatedTokensBefore: tokensBefore,
|
|
12168
|
+
estimatedTokensAfter: 10,
|
|
12169
|
+
evidenceRef: createEvidenceRef("DIFF_SUMMARY", "diff", `Diff empty: ${label}`, content),
|
|
12170
|
+
proofImpact: "UNKNOWN",
|
|
12171
|
+
reasonCodes: ["DIFF_NOT_PARSEABLE"],
|
|
12172
|
+
blockedContent: false
|
|
12173
|
+
};
|
|
12174
|
+
}
|
|
12175
|
+
const sourceFiles = fileChanges.filter((f) => !f.isGenerated);
|
|
12176
|
+
const generatedFiles = fileChanges.filter((f) => f.isGenerated);
|
|
12177
|
+
const riskMarkers = [];
|
|
12178
|
+
for (const f of sourceFiles) {
|
|
12179
|
+
if (/(?:auth|security|secret|credential|password|token)/i.test(f.path)) riskMarkers.push(`SECURITY_RELATED: ${f.path}`);
|
|
12180
|
+
if (/(?:migration|schema|database)/i.test(f.path)) riskMarkers.push(`MIGRATION: ${f.path}`);
|
|
12181
|
+
if (/(?:deploy|prod|release|ci|cd)/i.test(f.path)) riskMarkers.push(`DEPLOYMENT: ${f.path}`);
|
|
12182
|
+
}
|
|
12183
|
+
const parts = [
|
|
12184
|
+
`--- Diff Summary: ${label} ---`,
|
|
12185
|
+
`Files changed: ${fileChanges.length} (${sourceFiles.length} source, ${generatedFiles.length} generated)`,
|
|
12186
|
+
`Insertions: +${totalInsertions} | Deletions: -${totalDeletions}`
|
|
12187
|
+
];
|
|
12188
|
+
if (riskMarkers.length > 0) {
|
|
12189
|
+
parts.push("", "Risk markers:", ...riskMarkers.map((r2) => ` \u26A0 ${r2}`));
|
|
12190
|
+
}
|
|
12191
|
+
parts.push("", "Source files:");
|
|
12192
|
+
for (const f of sourceFiles) {
|
|
12193
|
+
parts.push(` ${f.path} (+${f.insertions}/-${f.deletions})`);
|
|
12194
|
+
}
|
|
12195
|
+
if (generatedFiles.length > 0) {
|
|
12196
|
+
parts.push("", `Generated files: ${generatedFiles.length} (${generatedFiles.map((f) => f.path).join(", ")})`);
|
|
12197
|
+
}
|
|
12198
|
+
const reduced = parts.join("\n");
|
|
12199
|
+
return {
|
|
12200
|
+
reduced,
|
|
12201
|
+
estimatedTokensBefore: tokensBefore,
|
|
12202
|
+
estimatedTokensAfter: estimateTokens(reduced),
|
|
12203
|
+
evidenceRef: createEvidenceRef("DIFF_SUMMARY", "diff", `Diff summarized: ${label}`, content),
|
|
12204
|
+
proofImpact: "PASS",
|
|
12205
|
+
reasonCodes: ["DIFF_STATS_PRESERVED", "RAW_DIFF_NOT_PERSISTED", ...riskMarkers.length > 0 ? ["RISK_MARKERS_DETECTED"] : []],
|
|
12206
|
+
blockedContent: false
|
|
12207
|
+
};
|
|
11561
12208
|
}
|
|
11562
|
-
|
|
11563
|
-
|
|
11564
|
-
|
|
11565
|
-
|
|
11566
|
-
|
|
11567
|
-
|
|
11568
|
-
|
|
11569
|
-
|
|
11570
|
-
|
|
11571
|
-
|
|
11572
|
-
|
|
11573
|
-
|
|
11574
|
-
|
|
11575
|
-
|
|
11576
|
-
|
|
11577
|
-
|
|
11578
|
-
|
|
11579
|
-
|
|
11580
|
-
|
|
11581
|
-
|
|
11582
|
-
|
|
11583
|
-
|
|
11584
|
-
|
|
11585
|
-
|
|
11586
|
-
|
|
11587
|
-
|
|
11588
|
-
|
|
11589
|
-
|
|
11590
|
-
|
|
11591
|
-
const
|
|
11592
|
-
|
|
11593
|
-
|
|
11594
|
-
|
|
11595
|
-
|
|
11596
|
-
|
|
11597
|
-
|
|
12209
|
+
|
|
12210
|
+
// src/avorelo/kernel/context-control/reducers/conversation.ts
|
|
12211
|
+
var OBJECTIVE_PATTERNS = [
|
|
12212
|
+
/\b(?:objective|goal|task|mission|purpose)\s*[:]/i,
|
|
12213
|
+
/\b(?:we need to|I need to|please|implement|fix|add|create|build|update|change|remove|refactor)\b/i
|
|
12214
|
+
];
|
|
12215
|
+
var CONSTRAINT_PATTERNS = [
|
|
12216
|
+
/\b(?:must not|do not|don't|never|avoid|forbidden|required|constraint|boundary|limit)\b/i,
|
|
12217
|
+
/\b(?:before|after|unless|only if|except)\b/i
|
|
12218
|
+
];
|
|
12219
|
+
var DECISION_PATTERNS = [
|
|
12220
|
+
/\b(?:decided|decision|chose|chosen|agreed|confirmed|approved|rejected|accepted)\b/i,
|
|
12221
|
+
/\b(?:we'll|we will|let's|approach|strategy|plan is)\b/i
|
|
12222
|
+
];
|
|
12223
|
+
var UNRESOLVED_PATTERNS = [
|
|
12224
|
+
/\b(?:TODO|FIXME|HACK|QUESTION|TBD|unclear|not sure|need to figure|open question)\b/i,
|
|
12225
|
+
/\?\s*$/
|
|
12226
|
+
];
|
|
12227
|
+
var STATUS_PATTERNS = [
|
|
12228
|
+
/\b(?:verified|tested|confirmed|working|broken|failing|passed|deployed|merged)\b/i,
|
|
12229
|
+
/\b(?:status|result|outcome|state)\s*[:]/i
|
|
12230
|
+
];
|
|
12231
|
+
var NOISE_PATTERNS = [
|
|
12232
|
+
/^(?:okay|ok|sure|yes|no|thanks|thank you|got it|understood|right|exactly|perfect)[\.\!\s]*$/i,
|
|
12233
|
+
/^(?:let me|I'll|I will)\s+(?:check|look|see|read|examine)/i,
|
|
12234
|
+
/^(?:here's|here is)\s+(?:the|what)/i,
|
|
12235
|
+
/^(?:great|good|nice|excellent|awesome)/i
|
|
12236
|
+
];
|
|
12237
|
+
function isNoise(line) {
|
|
12238
|
+
const trimmed = line.trim();
|
|
12239
|
+
if (!trimmed) return true;
|
|
12240
|
+
return NOISE_PATTERNS.some((p) => p.test(trimmed));
|
|
12241
|
+
}
|
|
12242
|
+
function matchesAny(line, patterns) {
|
|
12243
|
+
return patterns.some((p) => p.test(line));
|
|
12244
|
+
}
|
|
12245
|
+
function reduceConversation(content, label) {
|
|
12246
|
+
const tokensBefore = estimateTokens(content);
|
|
12247
|
+
const lines = content.split("\n");
|
|
12248
|
+
if (lines.length <= 60) {
|
|
12249
|
+
return {
|
|
12250
|
+
reduced: content,
|
|
12251
|
+
estimatedTokensBefore: tokensBefore,
|
|
12252
|
+
estimatedTokensAfter: tokensBefore,
|
|
12253
|
+
evidenceRef: createEvidenceRef("CONVERSATION_STATE_SUMMARY", "conversation_history", `Conversation: ${label}`, content),
|
|
12254
|
+
proofImpact: "PASS",
|
|
12255
|
+
reasonCodes: ["SHORT_CONVERSATION_KEPT"],
|
|
12256
|
+
blockedContent: false
|
|
12257
|
+
};
|
|
12258
|
+
}
|
|
12259
|
+
const objectives = [];
|
|
12260
|
+
const constraints = [];
|
|
12261
|
+
const decisions = [];
|
|
12262
|
+
const unresolved = [];
|
|
12263
|
+
const statusUpdates = [];
|
|
12264
|
+
let noiseRemoved = 0;
|
|
12265
|
+
for (const line of lines) {
|
|
12266
|
+
const trimmed = line.trim();
|
|
12267
|
+
if (!trimmed) continue;
|
|
12268
|
+
if (isNoise(trimmed)) {
|
|
12269
|
+
noiseRemoved++;
|
|
12270
|
+
continue;
|
|
12271
|
+
}
|
|
12272
|
+
if (matchesAny(trimmed, OBJECTIVE_PATTERNS)) objectives.push(trimmed);
|
|
12273
|
+
else if (matchesAny(trimmed, CONSTRAINT_PATTERNS)) constraints.push(trimmed);
|
|
12274
|
+
else if (matchesAny(trimmed, DECISION_PATTERNS)) decisions.push(trimmed);
|
|
12275
|
+
else if (matchesAny(trimmed, UNRESOLVED_PATTERNS)) unresolved.push(trimmed);
|
|
12276
|
+
else if (matchesAny(trimmed, STATUS_PATTERNS)) statusUpdates.push(trimmed);
|
|
12277
|
+
}
|
|
12278
|
+
const parts = [`--- Conversation Summary: ${label} ---`];
|
|
12279
|
+
if (objectives.length > 0) {
|
|
12280
|
+
parts.push("", "Objectives:", ...objectives.slice(0, 10).map((o) => ` - ${o.slice(0, 200)}`));
|
|
12281
|
+
}
|
|
12282
|
+
if (constraints.length > 0) {
|
|
12283
|
+
parts.push("", "Constraints:", ...constraints.slice(0, 10).map((c) => ` - ${c.slice(0, 200)}`));
|
|
12284
|
+
}
|
|
12285
|
+
if (decisions.length > 0) {
|
|
12286
|
+
parts.push("", "Decisions:", ...decisions.slice(0, 10).map((d) => ` - ${d.slice(0, 200)}`));
|
|
12287
|
+
}
|
|
12288
|
+
if (unresolved.length > 0) {
|
|
12289
|
+
parts.push("", "Unresolved:", ...unresolved.slice(0, 10).map((u) => ` - ${u.slice(0, 200)}`));
|
|
12290
|
+
}
|
|
12291
|
+
if (statusUpdates.length > 0) {
|
|
12292
|
+
parts.push("", "Latest status:", ...statusUpdates.slice(-5).map((s) => ` - ${s.slice(0, 200)}`));
|
|
12293
|
+
}
|
|
12294
|
+
parts.push("", `Noise removed: ${noiseRemoved} lines`);
|
|
12295
|
+
const reduced = parts.join("\n");
|
|
12296
|
+
return {
|
|
12297
|
+
reduced,
|
|
12298
|
+
estimatedTokensBefore: tokensBefore,
|
|
12299
|
+
estimatedTokensAfter: estimateTokens(reduced),
|
|
12300
|
+
evidenceRef: createEvidenceRef("CONVERSATION_STATE_SUMMARY", "conversation_history", `Conversation reduced: ${label}`, content),
|
|
12301
|
+
proofImpact: objectives.length > 0 || decisions.length > 0 ? "PASS" : "DEGRADED",
|
|
12302
|
+
reasonCodes: [
|
|
12303
|
+
"CONVERSATION_SUMMARIZED",
|
|
12304
|
+
...noiseRemoved > 0 ? ["NOISE_REMOVED"] : [],
|
|
12305
|
+
...objectives.length > 0 ? ["OBJECTIVES_PRESERVED"] : [],
|
|
12306
|
+
...decisions.length > 0 ? ["DECISIONS_PRESERVED"] : [],
|
|
12307
|
+
...unresolved.length > 0 ? ["UNRESOLVED_FLAGGED"] : []
|
|
12308
|
+
],
|
|
12309
|
+
blockedContent: false
|
|
12310
|
+
};
|
|
12311
|
+
}
|
|
12312
|
+
|
|
12313
|
+
// src/avorelo/kernel/context-control/cache-align.ts
|
|
12314
|
+
function analyzeCacheAlignment(items) {
|
|
12315
|
+
let stableCount = 0;
|
|
12316
|
+
let semiStableCount = 0;
|
|
12317
|
+
let volatileCount = 0;
|
|
12318
|
+
const invalidationReasons = [];
|
|
12319
|
+
for (const item2 of items) {
|
|
12320
|
+
const contentType = classifyContentType(item2.content, item2.label);
|
|
12321
|
+
const volatility = classifyVolatility(contentType);
|
|
12322
|
+
switch (volatility) {
|
|
12323
|
+
case "stable":
|
|
12324
|
+
stableCount++;
|
|
12325
|
+
break;
|
|
12326
|
+
case "semi_stable":
|
|
12327
|
+
semiStableCount++;
|
|
12328
|
+
break;
|
|
12329
|
+
case "volatile":
|
|
12330
|
+
volatileCount++;
|
|
12331
|
+
break;
|
|
12332
|
+
}
|
|
12333
|
+
}
|
|
12334
|
+
const totalItems = items.length;
|
|
12335
|
+
const cacheBreakRisk = totalItems > 0 && volatileCount > stableCount;
|
|
12336
|
+
if (stableCount === 0 && totalItems > 0) {
|
|
12337
|
+
invalidationReasons.push("NO_STABLE_CONTEXT_BLOCKS");
|
|
12338
|
+
}
|
|
12339
|
+
if (volatileCount > stableCount * 2) {
|
|
12340
|
+
invalidationReasons.push("VOLATILE_DOMINATES_STABLE");
|
|
12341
|
+
}
|
|
12342
|
+
if (totalItems > 20) {
|
|
12343
|
+
invalidationReasons.push("HIGH_ITEM_COUNT_MAY_FRAGMENT_CACHE");
|
|
12344
|
+
}
|
|
12345
|
+
const layout = [];
|
|
12346
|
+
if (stableCount > 0) layout.push("1. System/product contract (stable)");
|
|
12347
|
+
if (stableCount > 0) layout.push("2. Project facts & conventions (stable)");
|
|
12348
|
+
if (semiStableCount > 0) layout.push("3. Repo map & dependencies (semi-stable)");
|
|
12349
|
+
if (volatileCount > 0) layout.push("4. Current task & tool output (volatile)");
|
|
12350
|
+
return {
|
|
12351
|
+
stableBlockCount: stableCount,
|
|
12352
|
+
semiStableBlockCount: semiStableCount,
|
|
12353
|
+
volatileBlockCount: volatileCount,
|
|
12354
|
+
cacheStablePrefixAvailable: stableCount > 0,
|
|
12355
|
+
cacheBreakRisk,
|
|
12356
|
+
cacheInvalidationReasons: invalidationReasons,
|
|
12357
|
+
suggestedPromptLayout: layout
|
|
12358
|
+
};
|
|
12359
|
+
}
|
|
12360
|
+
|
|
12361
|
+
// src/avorelo/kernel/context-control/diagnostics.ts
|
|
12362
|
+
function generateDiagnostics(result3) {
|
|
12363
|
+
const diagnostics = [];
|
|
12364
|
+
if (result3.blockedItemsCount > 0) {
|
|
12365
|
+
diagnostics.push({
|
|
12366
|
+
severity: "warning",
|
|
12367
|
+
code: "CONTEXT_ITEMS_BLOCKED",
|
|
12368
|
+
message: `${result3.blockedItemsCount} context item(s) were blocked due to forbidden or secret-like content.`,
|
|
12369
|
+
remediation: "Review blocked items and redact sensitive content before including in context.",
|
|
12370
|
+
safeForReceipt: true
|
|
12371
|
+
});
|
|
12372
|
+
}
|
|
12373
|
+
if (result3.proofImpact === "DEGRADED") {
|
|
12374
|
+
diagnostics.push({
|
|
12375
|
+
severity: "warning",
|
|
12376
|
+
code: "PROOF_IMPACT_DEGRADED",
|
|
12377
|
+
message: "Context compression may have removed proof-critical evidence.",
|
|
12378
|
+
remediation: "Rerun with evidence retrieval or use manual review to verify proof chain.",
|
|
12379
|
+
safeForReceipt: true
|
|
12380
|
+
});
|
|
12381
|
+
}
|
|
12382
|
+
if (result3.proofImpact === "UNKNOWN") {
|
|
12383
|
+
diagnostics.push({
|
|
12384
|
+
severity: "blocker",
|
|
12385
|
+
code: "PROOF_IMPACT_UNKNOWN",
|
|
12386
|
+
message: "Unable to determine proof impact of context compression.",
|
|
12387
|
+
remediation: "Review original context to determine if proof-critical evidence was affected.",
|
|
12388
|
+
safeForReceipt: true
|
|
12389
|
+
});
|
|
12390
|
+
}
|
|
12391
|
+
if (result3.compressionRatio > 0.95) {
|
|
12392
|
+
diagnostics.push({
|
|
12393
|
+
severity: "info",
|
|
12394
|
+
code: "HIGH_COMPRESSION_RATIO",
|
|
12395
|
+
message: `Context was compressed by ${Math.round(result3.compressionRatio * 100)}%. Verify that important context was preserved.`,
|
|
12396
|
+
remediation: "Check evidence refs to confirm key information is retrievable.",
|
|
12397
|
+
safeForReceipt: true
|
|
12398
|
+
});
|
|
12399
|
+
}
|
|
12400
|
+
if (result3.cacheAlignment.cacheBreakRisk) {
|
|
12401
|
+
diagnostics.push({
|
|
12402
|
+
severity: "info",
|
|
12403
|
+
code: "CACHE_BREAK_RISK",
|
|
12404
|
+
message: "Volatile context blocks outnumber stable blocks, reducing cache effectiveness.",
|
|
12405
|
+
remediation: "Move stable project context (system prompt, conventions) to the front of the prompt.",
|
|
12406
|
+
safeForReceipt: true
|
|
12407
|
+
});
|
|
12408
|
+
}
|
|
12409
|
+
if (!result3.cacheAlignment.cacheStablePrefixAvailable) {
|
|
12410
|
+
diagnostics.push({
|
|
12411
|
+
severity: "info",
|
|
12412
|
+
code: "NO_STABLE_PREFIX",
|
|
12413
|
+
message: "No stable context prefix detected. Provider prompt caching will be less effective.",
|
|
12414
|
+
remediation: "Add stable system/project context blocks at the beginning of the prompt.",
|
|
12415
|
+
safeForReceipt: true
|
|
12416
|
+
});
|
|
12417
|
+
}
|
|
12418
|
+
if (result3.estimatedInputTokensBefore > 1e5) {
|
|
12419
|
+
diagnostics.push({
|
|
12420
|
+
severity: "warning",
|
|
12421
|
+
code: "VERY_LARGE_CONTEXT",
|
|
12422
|
+
message: `Input context estimated at ${result3.estimatedInputTokensBefore.toLocaleString()} tokens before reduction.`,
|
|
12423
|
+
remediation: "Consider narrowing task scope or excluding unnecessary context sources.",
|
|
12424
|
+
safeForReceipt: true
|
|
12425
|
+
});
|
|
12426
|
+
}
|
|
12427
|
+
if (result3.evidenceRefs.length === 0 && result3.estimatedInputTokensBefore > result3.estimatedInputTokensAfter) {
|
|
12428
|
+
diagnostics.push({
|
|
12429
|
+
severity: "warning",
|
|
12430
|
+
code: "COMPRESSION_WITHOUT_EVIDENCE",
|
|
12431
|
+
message: "Context was compressed but no evidence refs were created for recovery.",
|
|
12432
|
+
remediation: "Ensure reducers create evidence refs for recoverable content.",
|
|
12433
|
+
safeForReceipt: true
|
|
12434
|
+
});
|
|
12435
|
+
}
|
|
12436
|
+
return diagnostics;
|
|
12437
|
+
}
|
|
12438
|
+
function buildReceiptSummary(result3) {
|
|
12439
|
+
const reductionPercent = result3.estimatedInputTokensBefore > 0 ? Math.round((result3.estimatedInputTokensBefore - result3.estimatedInputTokensAfter) / result3.estimatedInputTokensBefore * 100) : 0;
|
|
12440
|
+
let status;
|
|
12441
|
+
if (result3.decision === "BLOCK_FORBIDDEN_CONTEXT") {
|
|
12442
|
+
status = "BLOCKED";
|
|
12443
|
+
} else if (result3.proofImpact === "DEGRADED" || result3.proofImpact === "UNKNOWN") {
|
|
12444
|
+
status = "DEGRADED";
|
|
12445
|
+
} else if (result3.decision === "ALLOW_AS_IS" && result3.estimatedInputTokensBefore === result3.estimatedInputTokensAfter) {
|
|
12446
|
+
status = "INACTIVE";
|
|
12447
|
+
} else {
|
|
12448
|
+
status = "ACTIVE";
|
|
12449
|
+
}
|
|
12450
|
+
const summaryParts = [];
|
|
12451
|
+
if (status === "ACTIVE") {
|
|
12452
|
+
summaryParts.push(`Context reduced from ~${result3.estimatedInputTokensBefore.toLocaleString()} to ~${result3.estimatedInputTokensAfter.toLocaleString()} tokens (${reductionPercent}% reduction).`);
|
|
12453
|
+
} else if (status === "BLOCKED") {
|
|
12454
|
+
summaryParts.push("Context blocked due to forbidden content.");
|
|
12455
|
+
} else if (status === "DEGRADED") {
|
|
12456
|
+
summaryParts.push("Context compressed but proof chain may be weakened.");
|
|
12457
|
+
} else {
|
|
12458
|
+
summaryParts.push("Context passed through without reduction.");
|
|
12459
|
+
}
|
|
12460
|
+
if (result3.evidenceRefs.length > 0) {
|
|
12461
|
+
summaryParts.push(`${result3.evidenceRefs.length} evidence ref(s) available for retrieval.`);
|
|
12462
|
+
}
|
|
12463
|
+
return {
|
|
12464
|
+
contextControlStatus: status,
|
|
12465
|
+
estimatedContextBefore: result3.estimatedInputTokensBefore,
|
|
12466
|
+
estimatedContextSent: result3.estimatedInputTokensAfter,
|
|
12467
|
+
reductionPercent,
|
|
12468
|
+
evidenceRefCount: result3.evidenceRefs.length,
|
|
12469
|
+
blockedItemsCount: result3.blockedItemsCount,
|
|
12470
|
+
retrievalAvailable: result3.evidenceRefs.some((r2) => r2.retrievalPolicy === "available"),
|
|
12471
|
+
proofImpact: result3.proofImpact,
|
|
12472
|
+
topReasonCodes: result3.reasonCodes.slice(0, 5),
|
|
12473
|
+
summary: summaryParts.join(" ")
|
|
12474
|
+
};
|
|
12475
|
+
}
|
|
12476
|
+
function renderContextStats(summary) {
|
|
12477
|
+
const lines = [
|
|
12478
|
+
`Context Control: ${summary.contextControlStatus}`,
|
|
12479
|
+
`Estimated context before: ${summary.estimatedContextBefore.toLocaleString()} tokens`,
|
|
12480
|
+
`Estimated context sent: ${summary.estimatedContextSent.toLocaleString()} tokens`,
|
|
12481
|
+
`Reduction: ${summary.reductionPercent}%`,
|
|
12482
|
+
`Evidence refs: ${summary.evidenceRefCount}`,
|
|
12483
|
+
`Blocked items: ${summary.blockedItemsCount}`,
|
|
12484
|
+
`Retrieval available: ${summary.retrievalAvailable ? "yes" : "no"}`,
|
|
12485
|
+
`Proof impact: ${summary.proofImpact}`
|
|
12486
|
+
];
|
|
12487
|
+
if (summary.topReasonCodes.length > 0) {
|
|
12488
|
+
lines.push(`Top reasons: ${summary.topReasonCodes.join(", ")}`);
|
|
12489
|
+
}
|
|
12490
|
+
return lines.join("\n");
|
|
12491
|
+
}
|
|
12492
|
+
function renderContextExplain(summary, diagnostics) {
|
|
12493
|
+
const parts = [
|
|
12494
|
+
renderContextStats(summary),
|
|
12495
|
+
"",
|
|
12496
|
+
"--- Explanation ---",
|
|
12497
|
+
summary.summary
|
|
12498
|
+
];
|
|
12499
|
+
if (diagnostics.length > 0) {
|
|
12500
|
+
parts.push("", "--- Diagnostics ---");
|
|
12501
|
+
for (const d of diagnostics) {
|
|
12502
|
+
parts.push(`[${d.severity.toUpperCase()}] ${d.code}: ${d.message}`);
|
|
12503
|
+
parts.push(` Action: ${d.remediation}`);
|
|
12504
|
+
}
|
|
12505
|
+
}
|
|
12506
|
+
return parts.join("\n");
|
|
12507
|
+
}
|
|
12508
|
+
|
|
12509
|
+
// src/avorelo/kernel/context-control/learn.ts
|
|
12510
|
+
var KNOWN_FAILURE_MAPPINGS = [
|
|
12511
|
+
{
|
|
12512
|
+
pattern: /npm publish.*(?:OTP|auth|ENEEDAUTH)/i,
|
|
12513
|
+
candidateType: "WORKFLOW_CORRECTION",
|
|
12514
|
+
template: () => "When npm publish or dist-tag promotion requires auth/OTP, generate a local PowerShell script for the owner to run. Do not ask for OTP in chat.",
|
|
12515
|
+
target: "CLAUDE.md"
|
|
12516
|
+
},
|
|
12517
|
+
{
|
|
12518
|
+
pattern: /(?:EACCES|permission denied|EPERM)/i,
|
|
12519
|
+
candidateType: "TOOL_LIMITATION",
|
|
12520
|
+
template: (match) => `Permission error encountered: ${match.slice(0, 100)}. Consider running with elevated permissions or checking file ownership.`,
|
|
12521
|
+
target: "AGENTS.md"
|
|
12522
|
+
},
|
|
12523
|
+
{
|
|
12524
|
+
pattern: /(?:ENOSPC|disk full|no space)/i,
|
|
12525
|
+
candidateType: "TOOL_LIMITATION",
|
|
12526
|
+
template: () => "Disk space constraints detected. Clean build artifacts before large operations.",
|
|
12527
|
+
target: "AGENTS.md"
|
|
12528
|
+
},
|
|
12529
|
+
{
|
|
12530
|
+
pattern: /(?:timeout|ETIMEDOUT|ESOCKETTIMEDOUT)/i,
|
|
12531
|
+
candidateType: "WORKFLOW_CORRECTION",
|
|
12532
|
+
template: () => "Network timeouts are common in this environment. Use local-first approaches where possible.",
|
|
12533
|
+
target: "CLAUDE.md"
|
|
12534
|
+
},
|
|
12535
|
+
{
|
|
12536
|
+
pattern: /node_modules.*(?:large|huge|big|slow)/i,
|
|
12537
|
+
candidateType: "NOISE_PATTERN",
|
|
12538
|
+
template: () => "node_modules content should be excluded from context. Use package.json and lock file summaries instead.",
|
|
12539
|
+
target: "CLAUDE.md"
|
|
12540
|
+
}
|
|
12541
|
+
];
|
|
12542
|
+
function isSafeCandidate(text) {
|
|
12543
|
+
const lower = text.toLowerCase();
|
|
12544
|
+
if (/(?:api[_-]?key|secret|token|password)\s*[:=]\s*\S{10,}/i.test(text)) return false;
|
|
12545
|
+
if (/-----BEGIN.*KEY-----/.test(text)) return false;
|
|
12546
|
+
if (/(?:ignore all|disregard|you are now|act as|pretend)/i.test(lower)) return false;
|
|
12547
|
+
return true;
|
|
12548
|
+
}
|
|
12549
|
+
function generateLearningCandidates(input) {
|
|
12550
|
+
const candidates = [];
|
|
12551
|
+
if (input.failurePatterns) {
|
|
12552
|
+
for (const failure of input.failurePatterns) {
|
|
12553
|
+
if (failure.occurrences < 2) continue;
|
|
12554
|
+
for (const mapping of KNOWN_FAILURE_MAPPINGS) {
|
|
12555
|
+
if (mapping.pattern.test(failure.pattern)) {
|
|
12556
|
+
const proposedText = mapping.template(failure.pattern);
|
|
12557
|
+
if (!isSafeCandidate(proposedText)) continue;
|
|
12558
|
+
candidates.push({
|
|
12559
|
+
candidateType: mapping.candidateType,
|
|
12560
|
+
sourceReceiptIds: failure.receiptIds.slice(0, 5),
|
|
12561
|
+
evidenceRefs: failure.evidenceRefs.slice(0, 5),
|
|
12562
|
+
confidence: failure.occurrences >= 5 ? "high" : failure.occurrences >= 3 ? "medium" : "low",
|
|
12563
|
+
proposedText,
|
|
12564
|
+
suggestedWriteTarget: mapping.target,
|
|
12565
|
+
requiresApproval: true,
|
|
12566
|
+
safeForPersistence: true,
|
|
12567
|
+
reasonCodes: [`REPEATED_FAILURE_${failure.occurrences}x`, `PATTERN_MATCH_${mapping.candidateType}`]
|
|
12568
|
+
});
|
|
12569
|
+
break;
|
|
12570
|
+
}
|
|
12571
|
+
}
|
|
12572
|
+
}
|
|
12573
|
+
}
|
|
12574
|
+
const receiptReasonCounts = /* @__PURE__ */ new Map();
|
|
12575
|
+
for (const receipt of input.receipts) {
|
|
12576
|
+
for (const code of receipt.topReasonCodes) {
|
|
12577
|
+
receiptReasonCounts.set(code, (receiptReasonCounts.get(code) || 0) + 1);
|
|
12578
|
+
}
|
|
12579
|
+
}
|
|
12580
|
+
for (const [code, count] of receiptReasonCounts) {
|
|
12581
|
+
if (count >= 3 && code.includes("BLOCKED")) {
|
|
12582
|
+
candidates.push({
|
|
12583
|
+
candidateType: "SAFETY_RULE",
|
|
12584
|
+
sourceReceiptIds: [],
|
|
12585
|
+
evidenceRefs: [],
|
|
12586
|
+
confidence: "medium",
|
|
12587
|
+
proposedText: `Frequently blocked context pattern: ${code}. Review whether this pattern should be allowlisted or permanently blocked.`,
|
|
12588
|
+
suggestedWriteTarget: "CLAUDE.md",
|
|
12589
|
+
requiresApproval: true,
|
|
12590
|
+
safeForPersistence: true,
|
|
12591
|
+
reasonCodes: [`FREQUENT_BLOCK_${count}x`, code]
|
|
12592
|
+
});
|
|
12593
|
+
}
|
|
12594
|
+
}
|
|
12595
|
+
return {
|
|
12596
|
+
candidates,
|
|
12597
|
+
dryRun: true,
|
|
12598
|
+
filesWritten: 0,
|
|
12599
|
+
summary: candidates.length > 0 ? `Generated ${candidates.length} learning candidate(s). All require explicit approval before writing.` : "No learning candidates generated from current evidence."
|
|
12600
|
+
};
|
|
12601
|
+
}
|
|
12602
|
+
|
|
12603
|
+
// src/avorelo/kernel/context-control/index.ts
|
|
12604
|
+
function selectReducer(item2) {
|
|
12605
|
+
const classification = item2.classification ?? classifyItem(item2);
|
|
12606
|
+
switch (classification.contentType) {
|
|
12607
|
+
case "log":
|
|
12608
|
+
return reduceLogs;
|
|
12609
|
+
case "json_tool_output":
|
|
12610
|
+
return reduceJson;
|
|
12611
|
+
case "file_tree":
|
|
12612
|
+
return reduceFileTree;
|
|
12613
|
+
case "test_output":
|
|
12614
|
+
return reduceTestOutput;
|
|
12615
|
+
case "diff":
|
|
12616
|
+
return reduceDiffSummary;
|
|
12617
|
+
case "conversation_history":
|
|
12618
|
+
return reduceConversation;
|
|
12619
|
+
default:
|
|
12620
|
+
return null;
|
|
12621
|
+
}
|
|
12622
|
+
}
|
|
12623
|
+
function isForbidden(item2, input) {
|
|
12624
|
+
if (containsForbiddenContent(item2.content)) return true;
|
|
12625
|
+
if (input.forbiddenContext) {
|
|
12626
|
+
for (const pattern of input.forbiddenContext) {
|
|
12627
|
+
if (item2.label.includes(pattern) || item2.content.includes(pattern)) return true;
|
|
12628
|
+
}
|
|
12629
|
+
}
|
|
12630
|
+
return false;
|
|
12631
|
+
}
|
|
12632
|
+
function isAllowed(item2, input) {
|
|
12633
|
+
if (!input.allowedContext || input.allowedContext.length === 0) return true;
|
|
12634
|
+
return input.allowedContext.some((a) => item2.label.includes(a) || item2.content.includes(a));
|
|
12635
|
+
}
|
|
12636
|
+
function needsManualApproval(item2, input) {
|
|
12637
|
+
if (input.riskLevel === "critical") return true;
|
|
12638
|
+
const classification = item2.classification ?? classifyItem(item2);
|
|
12639
|
+
if (classification.sensitivity === "secret_like" && input.redactionPolicy === "strict") return true;
|
|
12640
|
+
return false;
|
|
12641
|
+
}
|
|
12642
|
+
function processContextControl(input) {
|
|
12643
|
+
const classifiedItems = input.items.map((item2) => ({
|
|
12644
|
+
...item2,
|
|
12645
|
+
classification: item2.classification ?? classifyItem(item2)
|
|
12646
|
+
}));
|
|
12647
|
+
const totalTokensBefore = classifiedItems.reduce((sum, item2) => sum + item2.classification.estimatedTokens, 0);
|
|
12648
|
+
const reducedParts = [];
|
|
12649
|
+
const evidenceRefs = [];
|
|
12650
|
+
const reasonCodes = [];
|
|
12651
|
+
const learningSignals = [];
|
|
12652
|
+
let blockedCount = 0;
|
|
12653
|
+
let overallProofImpact = "PASS";
|
|
12654
|
+
let overallDecision = "ALLOW_AS_IS";
|
|
12655
|
+
let overallReductionKind = "NONE";
|
|
12656
|
+
let anyReduced = false;
|
|
12657
|
+
let anyBlocked = false;
|
|
12658
|
+
for (const item2 of classifiedItems) {
|
|
12659
|
+
if (isForbidden(item2, input)) {
|
|
12660
|
+
blockedCount++;
|
|
12661
|
+
anyBlocked = true;
|
|
12662
|
+
evidenceRefs.push(createBlockedEvidenceRef(item2.classification.contentType, `Forbidden: ${item2.label}`));
|
|
12663
|
+
reasonCodes.push("FORBIDDEN_CONTEXT_BLOCKED");
|
|
12664
|
+
continue;
|
|
12665
|
+
}
|
|
12666
|
+
if (!isAllowed(item2, input)) {
|
|
12667
|
+
blockedCount++;
|
|
12668
|
+
evidenceRefs.push(createBlockedEvidenceRef(item2.classification.contentType, `Not in allowlist: ${item2.label}`));
|
|
12669
|
+
reasonCodes.push("CONTEXT_NOT_IN_ALLOWLIST");
|
|
12670
|
+
continue;
|
|
12671
|
+
}
|
|
12672
|
+
if (needsManualApproval(item2, input)) {
|
|
12673
|
+
blockedCount++;
|
|
12674
|
+
evidenceRefs.push(createBlockedEvidenceRef(item2.classification.contentType, `Requires approval: ${item2.label}`));
|
|
12675
|
+
reasonCodes.push("MANUAL_APPROVAL_REQUIRED");
|
|
12676
|
+
continue;
|
|
12677
|
+
}
|
|
12678
|
+
if (containsSecrets(item2.content)) {
|
|
12679
|
+
blockedCount++;
|
|
12680
|
+
anyBlocked = true;
|
|
12681
|
+
evidenceRefs.push(createBlockedEvidenceRef(item2.classification.contentType, `Secret-like content: ${item2.label}`));
|
|
12682
|
+
reasonCodes.push("SECRET_LIKE_CONTEXT_BLOCKED");
|
|
12683
|
+
continue;
|
|
12684
|
+
}
|
|
12685
|
+
const reducer = selectReducer(item2);
|
|
12686
|
+
if (reducer && item2.classification.estimatedTokens > 200) {
|
|
12687
|
+
const result3 = reducer(item2.content, item2.label);
|
|
12688
|
+
reducedParts.push(result3.reduced);
|
|
12689
|
+
if (result3.evidenceRef) evidenceRefs.push(result3.evidenceRef);
|
|
12690
|
+
reasonCodes.push(...result3.reasonCodes);
|
|
12691
|
+
anyReduced = true;
|
|
12692
|
+
if (result3.proofImpact === "DEGRADED") overallProofImpact = "DEGRADED";
|
|
12693
|
+
if (result3.proofImpact === "UNKNOWN" && overallProofImpact !== "DEGRADED") overallProofImpact = "UNKNOWN";
|
|
12694
|
+
} else {
|
|
12695
|
+
reducedParts.push(item2.content);
|
|
12696
|
+
}
|
|
12697
|
+
}
|
|
12698
|
+
const reducedContent = reducedParts.join("\n\n");
|
|
12699
|
+
const totalTokensAfter = estimateTokens(reducedContent);
|
|
12700
|
+
if (anyBlocked && blockedCount === classifiedItems.length) {
|
|
12701
|
+
overallDecision = "BLOCK_FORBIDDEN_CONTEXT";
|
|
12702
|
+
overallReductionKind = "BLOCKED";
|
|
12703
|
+
} else if (anyBlocked) {
|
|
12704
|
+
overallDecision = "COMPRESS_WITH_EVIDENCE";
|
|
12705
|
+
overallReductionKind = "BLOCKED";
|
|
12706
|
+
} else if (anyReduced) {
|
|
12707
|
+
overallDecision = "COMPRESS_WITH_EVIDENCE";
|
|
12708
|
+
const dominantType = classifiedItems.filter((i) => !isForbidden(i, input)).sort((a, b) => b.classification.estimatedTokens - a.classification.estimatedTokens)[0];
|
|
12709
|
+
if (dominantType) {
|
|
12710
|
+
const typeToKind = {
|
|
12711
|
+
log: "LOG_REDUCTION",
|
|
12712
|
+
json_tool_output: "JSON_REDUCTION",
|
|
12713
|
+
file_tree: "FILE_TREE_REDUCTION",
|
|
12714
|
+
test_output: "TEST_OUTPUT_REDUCTION",
|
|
12715
|
+
diff: "DIFF_SUMMARY",
|
|
12716
|
+
conversation_history: "CONVERSATION_STATE_SUMMARY"
|
|
12717
|
+
};
|
|
12718
|
+
overallReductionKind = typeToKind[dominantType.classification.contentType] ?? "NONE";
|
|
12719
|
+
}
|
|
12720
|
+
}
|
|
12721
|
+
if (totalTokensAfter > input.contextBudget) {
|
|
12722
|
+
reasonCodes.push("CONTEXT_EXCEEDS_BUDGET");
|
|
12723
|
+
if (overallDecision === "ALLOW_AS_IS") overallDecision = "ASK_FOR_NARROWER_SCOPE";
|
|
12724
|
+
}
|
|
12725
|
+
const compressionRatio = totalTokensBefore > 0 ? (totalTokensBefore - totalTokensAfter) / totalTokensBefore : 0;
|
|
12726
|
+
const deduplicatedReasonCodes = [...new Set(reasonCodes)];
|
|
12727
|
+
const cacheAlignment = analyzeCacheAlignment(classifiedItems);
|
|
12728
|
+
const partialResult = {
|
|
12729
|
+
decision: overallDecision,
|
|
12730
|
+
reductionKind: overallReductionKind,
|
|
12731
|
+
reasonCodes: deduplicatedReasonCodes,
|
|
12732
|
+
riskLevel: input.riskLevel ?? "low",
|
|
12733
|
+
safeForModel: !anyBlocked || blockedCount < classifiedItems.length,
|
|
12734
|
+
safeForPersistence: !deduplicatedReasonCodes.includes("SECRET_LIKE_CONTEXT_BLOCKED"),
|
|
12735
|
+
estimatedInputTokensBefore: totalTokensBefore,
|
|
12736
|
+
estimatedInputTokensAfter: totalTokensAfter,
|
|
12737
|
+
compressionRatio,
|
|
12738
|
+
evidenceRefs,
|
|
12739
|
+
blockedItemsCount: blockedCount,
|
|
12740
|
+
retrievalAvailable: evidenceRefs.some((r2) => r2.retrievalPolicy === "available"),
|
|
12741
|
+
proofImpact: overallProofImpact,
|
|
12742
|
+
receiptSafeSummary: null,
|
|
12743
|
+
diagnostics: [],
|
|
12744
|
+
cacheAlignment,
|
|
12745
|
+
learningSignals,
|
|
12746
|
+
reducedContent
|
|
12747
|
+
};
|
|
12748
|
+
partialResult.diagnostics = generateDiagnostics(partialResult);
|
|
12749
|
+
partialResult.receiptSafeSummary = buildReceiptSummary(partialResult);
|
|
12750
|
+
return partialResult;
|
|
12751
|
+
}
|
|
12752
|
+
|
|
12753
|
+
// src/avorelo/capabilities/runtime-flow/index.ts
|
|
12754
|
+
init_redaction();
|
|
12755
|
+
function runtimeDir(dir) {
|
|
12756
|
+
return join39(dir, ".avorelo", "runtime");
|
|
12757
|
+
}
|
|
12758
|
+
function contextDir(dir) {
|
|
12759
|
+
return join39(dir, ".avorelo", "context");
|
|
12760
|
+
}
|
|
12761
|
+
var SAFE_FALLBACK_FORBIDDEN_ACTIONS = [
|
|
12762
|
+
"persist_raw_prompt",
|
|
12763
|
+
"persist_raw_source",
|
|
12764
|
+
"persist_raw_secret",
|
|
12765
|
+
"model_owns_READY",
|
|
12766
|
+
"model_owns_entitlement",
|
|
12767
|
+
"model_owns_production_readiness",
|
|
12768
|
+
"claim_savings_without_evidence"
|
|
12769
|
+
];
|
|
12770
|
+
function safeFallbackProjection(reasonCodes, extraVerifierPlan = []) {
|
|
12771
|
+
return {
|
|
12772
|
+
selectedPrimitive: "deterministic_local_read",
|
|
12773
|
+
selectedModelProfile: "none",
|
|
12774
|
+
resolverStatus: reasonCodes.includes("MODEL_ROUTING_VERIFIER_REJECTED") ? "verifier_rejected" : "routing_unavailable",
|
|
12775
|
+
providerClass: "none",
|
|
12776
|
+
fallbackPlan: [],
|
|
12777
|
+
verifierPlan: extraVerifierPlan,
|
|
12778
|
+
reasonCodes,
|
|
12779
|
+
forbiddenActions: [...SAFE_FALLBACK_FORBIDDEN_ACTIONS],
|
|
12780
|
+
modelMayAssist: false,
|
|
12781
|
+
modelMayDecide: false,
|
|
12782
|
+
scannerMayDecide: false,
|
|
12783
|
+
finalDecisionOwner: "kernel/stop-continue-gate",
|
|
12784
|
+
containsRawPrompt: false,
|
|
12785
|
+
containsRawSource: false,
|
|
12786
|
+
containsRawSecret: false
|
|
12787
|
+
};
|
|
12788
|
+
}
|
|
12789
|
+
function freshRuntimeId(seed) {
|
|
12790
|
+
return "rts_" + createHash13("sha256").update(seed).digest("hex").slice(0, 12);
|
|
12791
|
+
}
|
|
12792
|
+
function coded(text) {
|
|
12793
|
+
try {
|
|
12794
|
+
return String(redact(text).value).slice(0, 160);
|
|
12795
|
+
} catch {
|
|
12796
|
+
return "redacted";
|
|
12797
|
+
}
|
|
12798
|
+
}
|
|
12799
|
+
function mergeRuntimeGate(baseGate, actionVerdict) {
|
|
12800
|
+
if (baseGate === "blocked" || actionVerdict === "block") return "blocked";
|
|
12801
|
+
if (baseGate === "require_approval" || actionVerdict === "require_approval" || actionVerdict === "suggest_safer_action") {
|
|
12802
|
+
return "require_approval";
|
|
12803
|
+
}
|
|
12804
|
+
return "allow";
|
|
12805
|
+
}
|
|
12806
|
+
function isProofAdapter(adapterId) {
|
|
12807
|
+
return adapterId === "semgrep" || adapterId === "playwright-proof" || adapterId === "github-actions";
|
|
12808
|
+
}
|
|
12809
|
+
function buildProofAdapterReportItems(toolExecution) {
|
|
12810
|
+
const found = [];
|
|
12811
|
+
const verified = [];
|
|
12812
|
+
const needsAttention = [];
|
|
12813
|
+
const metadata = toolExecution.proofMetadata;
|
|
12814
|
+
if (!metadata || !isProofAdapter(toolExecution.selectedAdapter)) return { found, verified, needsAttention };
|
|
12815
|
+
const confidence = metadata.fake ? "inferred" : "measured";
|
|
12816
|
+
if (toolExecution.executionStatus === "executed") {
|
|
12817
|
+
verified.push({
|
|
12818
|
+
code: "PROOF_ADAPTER_EXECUTED",
|
|
12819
|
+
title: "Proof adapter executed",
|
|
12820
|
+
summary: `${toolExecution.selectedAdapter}: ${metadata.summary}`,
|
|
12821
|
+
confidence
|
|
12822
|
+
});
|
|
12823
|
+
} else if (toolExecution.executionStatus === "skipped") {
|
|
12824
|
+
needsAttention.push({
|
|
12825
|
+
code: "PROOF_ADAPTER_SKIPPED",
|
|
12826
|
+
title: "Proof adapter skipped",
|
|
12827
|
+
summary: `${toolExecution.selectedAdapter}: ${toolExecution.reasonCodes.join(",")}`,
|
|
12828
|
+
confidence: "unavailable"
|
|
12829
|
+
});
|
|
12830
|
+
} else if (toolExecution.executionStatus === "failed" || toolExecution.executionStatus === "blocked") {
|
|
12831
|
+
needsAttention.push({
|
|
12832
|
+
code: "PROOF_ADAPTER_NOT_READY",
|
|
12833
|
+
title: "Proof adapter needs attention",
|
|
12834
|
+
summary: `${toolExecution.selectedAdapter}: ${toolExecution.reasonCodes.join(",")}`,
|
|
12835
|
+
confidence
|
|
12836
|
+
});
|
|
12837
|
+
}
|
|
12838
|
+
if (metadata.findingCount > 0) {
|
|
12839
|
+
found.push({
|
|
12840
|
+
code: metadata.adapterClass === "ci_readonly" ? "CI_FINDINGS_SUMMARIZED" : "PROOF_FINDINGS_SUMMARIZED",
|
|
12841
|
+
title: "Summarized proof findings",
|
|
12842
|
+
summary: `${toolExecution.selectedAdapter}: findings=${metadata.findingCount} artifacts=${metadata.artifactCount}`,
|
|
12843
|
+
confidence
|
|
12844
|
+
});
|
|
12845
|
+
}
|
|
12846
|
+
return { found, verified, needsAttention };
|
|
12847
|
+
}
|
|
12848
|
+
function runRuntimeSession(input) {
|
|
12849
|
+
const { task, dir } = input;
|
|
12850
|
+
const planTier = input.planTier ?? "Free";
|
|
12851
|
+
const now = input.now ?? Date.now();
|
|
12852
|
+
const createdAt = input.createdAt ?? new Date(now).toISOString();
|
|
12853
|
+
const warnings = [];
|
|
12854
|
+
let compiledContextPacket = null;
|
|
12855
|
+
let executorContextPack = null;
|
|
12856
|
+
let plannedToolExecution = null;
|
|
12857
|
+
const routing = decideRouting({ task, dir, planTier });
|
|
12858
|
+
const c = routing.contract;
|
|
12859
|
+
const displayTask = routing.displayTask;
|
|
12860
|
+
const proposalHints = detectProposalHints(displayTask);
|
|
12861
|
+
const entitlementState = resolveSubscriptionEntitlements({ source: "runtime_flow" });
|
|
12862
|
+
const capabilityRoute = buildCapabilityRouteDecision({
|
|
12863
|
+
taskType: c.route === "deterministic_only" ? "docs" : c.route === "blocked" ? "deploy" : "code_generation",
|
|
12864
|
+
riskClass: c.riskClass,
|
|
12865
|
+
proofTier: c.proofTier,
|
|
12866
|
+
approvalPolicy: c.approvalPolicy,
|
|
12867
|
+
proposalHints,
|
|
12868
|
+
paymentTouched: c.safetyBoundary.secretRiskCodes.includes("payment") || /payment|billing|invoice/i.test(displayTask),
|
|
12869
|
+
authTouched: c.safetyBoundary.secretRiskCodes.includes("auth") || /auth|login|session/i.test(displayTask),
|
|
12870
|
+
dashboardTouched: /dashboard|cockpit/i.test(displayTask),
|
|
12871
|
+
publicCopyTouched: /public|pricing|landing/i.test(displayTask),
|
|
12872
|
+
mcpTouched: /mcp|connector|tool config/i.test(displayTask),
|
|
12873
|
+
deepMode: /\bdeep|loop|retry until|autonomous\b/i.test(displayTask),
|
|
12874
|
+
browserAvailable: false,
|
|
12875
|
+
allowedLegacyFeatures: entitlementState.legacyFeatures
|
|
12876
|
+
});
|
|
12877
|
+
const actionWorthiness = evaluateActionWorthiness({
|
|
12878
|
+
objective: displayTask,
|
|
12879
|
+
riskClass: c.riskClass,
|
|
12880
|
+
approvalPolicy: c.approvalPolicy,
|
|
12881
|
+
proposalHints
|
|
12882
|
+
});
|
|
12883
|
+
const workControlSummary = buildWorkControlReceiptSummary(capabilityRoute, actionWorthiness);
|
|
11598
12884
|
const effectiveGate = mergeRuntimeGate(routing.gate, actionWorthiness.verdict);
|
|
11599
12885
|
const layers = [{
|
|
11600
12886
|
order: 1,
|
|
@@ -11851,6 +13137,54 @@ function runRuntimeSession(input) {
|
|
|
11851
13137
|
} catch {
|
|
11852
13138
|
base.contextPack = void 0;
|
|
11853
13139
|
}
|
|
13140
|
+
try {
|
|
13141
|
+
if (compiledContextPacket) {
|
|
13142
|
+
const ccItems = compiledContextPacket.selectedRefs.map((ref2, i) => ({
|
|
13143
|
+
id: `cc_${i}`,
|
|
13144
|
+
label: ref2.label,
|
|
13145
|
+
content: `[${ref2.kind}] ${ref2.label} (${ref2.includeMode}/${ref2.safety})`
|
|
13146
|
+
}));
|
|
13147
|
+
const forbiddenLabels = compiledContextPacket.excludedRefs.map((r2) => r2.label);
|
|
13148
|
+
const budgetMap = { tiny: 4e3, small: 16e3, medium: 5e4, deep: 12e4 };
|
|
13149
|
+
const ccResult = processContextControl({
|
|
13150
|
+
consumerRole: "executor",
|
|
13151
|
+
items: ccItems,
|
|
13152
|
+
contextBudget: budgetMap[compiledContextPacket.contextBudget.targetSize] ?? 5e4,
|
|
13153
|
+
forbiddenContext: forbiddenLabels,
|
|
13154
|
+
redactionPolicy: "standard",
|
|
13155
|
+
riskLevel: c.riskClass === "critical" ? "critical" : c.riskClass
|
|
13156
|
+
});
|
|
13157
|
+
const summary = ccResult.receiptSafeSummary;
|
|
13158
|
+
const status = summary.controlStatus;
|
|
13159
|
+
const ref = (() => {
|
|
13160
|
+
try {
|
|
13161
|
+
const ccDir = join39(dir, ".avorelo", "context-control");
|
|
13162
|
+
mkdirSync24(ccDir, { recursive: true });
|
|
13163
|
+
const p = join39(ccDir, "latest.json");
|
|
13164
|
+
writeFileSync23(p, JSON.stringify(summary, null, 2));
|
|
13165
|
+
return p;
|
|
13166
|
+
} catch {
|
|
13167
|
+
return null;
|
|
13168
|
+
}
|
|
13169
|
+
})();
|
|
13170
|
+
const detail = `status=${status} context=${summary.estimatedContextBefore}\u2192${summary.estimatedContextSent} reduction=${summary.reductionPercent}%`;
|
|
13171
|
+
base.contextControl = {
|
|
13172
|
+
status,
|
|
13173
|
+
estimatedContextBefore: summary.estimatedContextBefore,
|
|
13174
|
+
estimatedContextAfter: summary.estimatedContextSent,
|
|
13175
|
+
reductionPercent: summary.reductionPercent,
|
|
13176
|
+
evidenceRefCount: summary.evidenceRefCount,
|
|
13177
|
+
blockedItemsCount: summary.blockedItemsCount,
|
|
13178
|
+
retrievalAvailable: summary.retrievalAvailable,
|
|
13179
|
+
proofImpact: summary.proofImpact,
|
|
13180
|
+
reasonCodes: summary.reasonCodes,
|
|
13181
|
+
ref
|
|
13182
|
+
};
|
|
13183
|
+
layers.push({ order: 3.5, layer: "context_control", capability: "context-control", status: "completed", ref, detail: coded(detail) });
|
|
13184
|
+
}
|
|
13185
|
+
} catch (e) {
|
|
13186
|
+
layers.push({ order: 3.5, layer: "context_control", capability: "context-control", status: "unavailable", ref: null, detail: coded(errCode(e)) });
|
|
13187
|
+
}
|
|
11854
13188
|
if (plannedToolExecution) {
|
|
11855
13189
|
const execCtx = {
|
|
11856
13190
|
dir,
|
|
@@ -12393,6 +13727,18 @@ function buildControlCenter(dir, opts) {
|
|
|
12393
13727
|
}
|
|
12394
13728
|
} catch {
|
|
12395
13729
|
}
|
|
13730
|
+
const contextControlSection = runtime?.contextControl ? {
|
|
13731
|
+
status: "available",
|
|
13732
|
+
controlStatus: runtime.contextControl.status,
|
|
13733
|
+
estimatedContextBefore: runtime.contextControl.estimatedContextBefore,
|
|
13734
|
+
estimatedContextAfter: runtime.contextControl.estimatedContextAfter,
|
|
13735
|
+
reductionPercent: runtime.contextControl.reductionPercent,
|
|
13736
|
+
evidenceRefCount: runtime.contextControl.evidenceRefCount,
|
|
13737
|
+
blockedItemsCount: runtime.contextControl.blockedItemsCount,
|
|
13738
|
+
retrievalAvailable: runtime.contextControl.retrievalAvailable,
|
|
13739
|
+
proofImpact: runtime.contextControl.proofImpact,
|
|
13740
|
+
reasonCodes: runtime.contextControl.reasonCodes
|
|
13741
|
+
} : { status: "unavailable" };
|
|
12396
13742
|
const dash = buildLocalDashboard(dir, { now: opts.now, staleWindowMs: opts.staleWindowMs });
|
|
12397
13743
|
const receiptsSection = {
|
|
12398
13744
|
total: dash.totals.total,
|
|
@@ -12495,6 +13841,7 @@ function buildControlCenter(dir, opts) {
|
|
|
12495
13841
|
continuity: continuitySection,
|
|
12496
13842
|
efficiencySync: efficiencySection,
|
|
12497
13843
|
contextCheck: contextCheckSection,
|
|
13844
|
+
contextControl: contextControlSection,
|
|
12498
13845
|
receipts: receiptsSection,
|
|
12499
13846
|
entitlementGate: entitlementGateSection,
|
|
12500
13847
|
modelRouting: modelRoutingSection,
|
|
@@ -12534,6 +13881,7 @@ function renderText2(m) {
|
|
|
12534
13881
|
lines.push(` Continuity:${s.continuity.status === "available" ? ` ${s.continuity.packetStatus} \xB7 ${s.continuity.safeNextActionCount} next action(s) \xB7 ${s.continuity.proofMissingCount} proof gap(s)` : " none"}`);
|
|
12535
13882
|
lines.push(` Sync: ${s.efficiencySync.status === "available" ? `${s.efficiencySync.mode} \xB7 ${s.efficiencySync.eligibleCount} eligible / ${s.efficiencySync.blockedCount} blocked` : "none"}`);
|
|
12536
13883
|
lines.push(` Context: ${s.contextCheck.status === "available" ? `${s.contextCheck.checkStatus} \xB7 risk=${s.contextCheck.riskLevel} \xB7 ${s.contextCheck.inputsChecked} source(s) \xB7 ${s.contextCheck.findingCount} finding(s) \xB7 policy=${s.contextCheck.policyPresent}` : "none"}`);
|
|
13884
|
+
lines.push(` Ctx ctrl: ${s.contextControl.status === "available" ? `${s.contextControl.controlStatus} \xB7 context=${s.contextControl.estimatedContextBefore}\u2192${s.contextControl.estimatedContextAfter} \xB7 reduction=${s.contextControl.reductionPercent}% \xB7 evidence=${s.contextControl.evidenceRefCount} \xB7 blocked=${s.contextControl.blockedItemsCount} \xB7 proof=${s.contextControl.proofImpact}` : "none"}`);
|
|
12537
13885
|
lines.push(` Receipts: ${s.receipts.total} \xB7 done ${s.receipts.done} \xB7 blocked ${s.receipts.blocked} \xB7 needs-attention ${s.receipts.needsAttention} \xB7 stale ${s.receipts.stale}`);
|
|
12538
13886
|
if (s.modelRouting.status === "available") {
|
|
12539
13887
|
lines.push(` Routing: primitive=${s.modelRouting.selectedPrimitive} profile=${s.modelRouting.selectedModelProfile} resolver=${s.modelRouting.resolverStatus} provider=${s.modelRouting.providerClass}`);
|
|
@@ -12613,7 +13961,7 @@ function openControlCenter(dir, opts) {
|
|
|
12613
13961
|
}
|
|
12614
13962
|
|
|
12615
13963
|
// src/avorelo/capabilities/activation/init.ts
|
|
12616
|
-
import { createHash as
|
|
13964
|
+
import { createHash as createHash14, randomUUID as randomUUID4 } from "node:crypto";
|
|
12617
13965
|
import { existsSync as existsSync41, mkdirSync as mkdirSync26, writeFileSync as writeFileSync25, readFileSync as readFileSync25, statSync as statSync4, accessSync, constants } from "node:fs";
|
|
12618
13966
|
import { join as join41 } from "node:path";
|
|
12619
13967
|
var ACTIVATION_V1_CONTRACT = "avorelo.activation.v1";
|
|
@@ -12628,7 +13976,7 @@ function activationPath(dir) {
|
|
|
12628
13976
|
return join41(avoreloDir(dir), "activation.json");
|
|
12629
13977
|
}
|
|
12630
13978
|
function freshActivationId(seed) {
|
|
12631
|
-
return "act_" +
|
|
13979
|
+
return "act_" + createHash14("sha256").update(seed).digest("hex").slice(0, 12);
|
|
12632
13980
|
}
|
|
12633
13981
|
function loadWorkspace(dir) {
|
|
12634
13982
|
const p = workspacePath(dir);
|
|
@@ -12833,14 +14181,14 @@ function renderDogfoodCheck(r2) {
|
|
|
12833
14181
|
}
|
|
12834
14182
|
|
|
12835
14183
|
// src/avorelo/capabilities/core-readiness/index.ts
|
|
12836
|
-
import { createHash as
|
|
14184
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
12837
14185
|
import { existsSync as existsSync43, readFileSync as readFileSync27 } from "node:fs";
|
|
12838
14186
|
import { join as join43 } from "node:path";
|
|
12839
14187
|
|
|
12840
14188
|
// src/avorelo/capabilities/canonical-readiness/index.ts
|
|
12841
14189
|
import { existsSync as existsSync42, readFileSync as readFileSync26, readdirSync as readdirSync11, statSync as statSync5 } from "node:fs";
|
|
12842
14190
|
import { join as join42 } from "node:path";
|
|
12843
|
-
import { createHash as
|
|
14191
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
12844
14192
|
var FORBIDDEN_CLAIM_PATTERNS = [
|
|
12845
14193
|
{ code: "guaranteed_savings", re: /guaranteed savings|guarantee[sd]? .{0,20}savings/ },
|
|
12846
14194
|
{ code: "zero_leak_guarantee", re: /zero[- ]leak guarantee|guarantee[sd]? .{0,20}no leak|no secret will ever leak/ },
|
|
@@ -13046,7 +14394,7 @@ function buildCanonicalReadinessReport(target, opts = {}) {
|
|
|
13046
14394
|
contract: "avorelo.canonicalReadiness.v1",
|
|
13047
14395
|
schemaVersion: 1,
|
|
13048
14396
|
createdAt,
|
|
13049
|
-
readinessId: "rdy_" +
|
|
14397
|
+
readinessId: "rdy_" + createHash15("sha256").update(`${createdAt}:${result3}:${blockers.length}`).digest("hex").slice(0, 12),
|
|
13050
14398
|
result: result3,
|
|
13051
14399
|
phaseCoverage,
|
|
13052
14400
|
oldRepoCapabilityCoverage,
|
|
@@ -13239,7 +14587,7 @@ function buildCoreReadiness(opts) {
|
|
|
13239
14587
|
contract: CORE_READINESS_CONTRACT,
|
|
13240
14588
|
schemaVersion: 1,
|
|
13241
14589
|
createdAt,
|
|
13242
|
-
reportId: "core_" +
|
|
14590
|
+
reportId: "core_" + createHash16("sha256").update(`${createdAt}:${result3}`).digest("hex").slice(0, 12),
|
|
13243
14591
|
result: result3,
|
|
13244
14592
|
checks,
|
|
13245
14593
|
safetyInvariants,
|
|
@@ -16076,7 +17424,7 @@ var FORBIDDEN_KEYS2 = /* @__PURE__ */ new Set([
|
|
|
16076
17424
|
"rawPath",
|
|
16077
17425
|
"settings"
|
|
16078
17426
|
]);
|
|
16079
|
-
var
|
|
17427
|
+
var SECRET_PATTERNS3 = [
|
|
16080
17428
|
/ghp_[A-Za-z0-9]{36}/,
|
|
16081
17429
|
/gho_[A-Za-z0-9]{36}/,
|
|
16082
17430
|
/github_pat_[A-Za-z0-9_]{22,}/,
|
|
@@ -16122,7 +17470,7 @@ function validateDogfoodLearningPayload(payload) {
|
|
|
16122
17470
|
function inspectStringValue(val, key) {
|
|
16123
17471
|
if (val.length > 256) return { valid: false, code: "value_too_long", detail: `${key}: ${val.length} chars` };
|
|
16124
17472
|
if (val.includes("\n") && val.split("\n").length > 2) return { valid: false, code: "multiline_value", detail: key };
|
|
16125
|
-
for (const pat of
|
|
17473
|
+
for (const pat of SECRET_PATTERNS3) {
|
|
16126
17474
|
if (pat.test(val)) return { valid: false, code: "secret_pattern", detail: key };
|
|
16127
17475
|
}
|
|
16128
17476
|
if (PATH_PATTERN.test(val) && key !== "createdAt") return { valid: false, code: "path_value", detail: key };
|
|
@@ -16416,9 +17764,9 @@ function renderPayloadPreview(p) {
|
|
|
16416
17764
|
}
|
|
16417
17765
|
|
|
16418
17766
|
// src/avorelo/capabilities/cloud-sync/claim-token.ts
|
|
16419
|
-
import { randomBytes, createHash as
|
|
17767
|
+
import { randomBytes, createHash as createHash17 } from "node:crypto";
|
|
16420
17768
|
function generateLocalFingerprint(target) {
|
|
16421
|
-
return
|
|
17769
|
+
return createHash17("sha256").update(target).digest("hex").slice(0, 16);
|
|
16422
17770
|
}
|
|
16423
17771
|
var CLAIM_EXPIRY_MS = 24 * 60 * 60 * 1e3;
|
|
16424
17772
|
|
|
@@ -16724,7 +18072,7 @@ function getTelemetryConfig(dir, state) {
|
|
|
16724
18072
|
}
|
|
16725
18073
|
|
|
16726
18074
|
// src/avorelo/telemetry/privacy.ts
|
|
16727
|
-
import { createHmac as createHmac2, createHash as
|
|
18075
|
+
import { createHmac as createHmac2, createHash as createHash18, randomUUID as randomUUID9 } from "node:crypto";
|
|
16728
18076
|
import { basename as basename4, resolve as resolve4 } from "node:path";
|
|
16729
18077
|
var FORBIDDEN_FIELD_PATTERNS = [
|
|
16730
18078
|
/rawprompt/i,
|
|
@@ -16883,7 +18231,7 @@ function inferAgentType(commandName) {
|
|
|
16883
18231
|
function sanitizeTelemetryEvent(rawEvent, ctx) {
|
|
16884
18232
|
const unsafeFields = detectUnsafeTelemetryFields(rawEvent);
|
|
16885
18233
|
const excludedFields = new Set(unsafeFields);
|
|
16886
|
-
const eventId = typeof rawEvent.eventId === "string" && rawEvent.eventId ? rawEvent.eventId : `evt_${
|
|
18234
|
+
const eventId = typeof rawEvent.eventId === "string" && rawEvent.eventId ? rawEvent.eventId : `evt_${createHash18("sha256").update(randomUUID9()).digest("hex").slice(0, 16)}`;
|
|
16887
18235
|
const occurredAt = typeof rawEvent.occurredAt === "string" && rawEvent.occurredAt ? rawEvent.occurredAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
16888
18236
|
const repoRoot2 = typeof rawEvent.repoRoot === "string" ? rawEvent.repoRoot : void 0;
|
|
16889
18237
|
const remoteUrl = typeof rawEvent.remoteUrl === "string" ? rawEvent.remoteUrl : void 0;
|
|
@@ -16949,662 +18297,1726 @@ function readJsonl(path) {
|
|
|
16949
18297
|
const parsed = safeParseJson(trimmed);
|
|
16950
18298
|
if (parsed) values.push(parsed);
|
|
16951
18299
|
}
|
|
16952
|
-
return values;
|
|
16953
|
-
}
|
|
16954
|
-
function writeJson(path, value) {
|
|
16955
|
-
ensureParent(path);
|
|
16956
|
-
writeFileSync36(path, JSON.stringify(value, null, 2));
|
|
16957
|
-
}
|
|
16958
|
-
function writeJsonl(path, values) {
|
|
16959
|
-
ensureParent(path);
|
|
16960
|
-
const body = values.map((value) => JSON.stringify(value)).join("\n");
|
|
16961
|
-
writeFileSync36(path, body.length > 0 ? `${body}
|
|
16962
|
-
` : "");
|
|
18300
|
+
return values;
|
|
18301
|
+
}
|
|
18302
|
+
function writeJson(path, value) {
|
|
18303
|
+
ensureParent(path);
|
|
18304
|
+
writeFileSync36(path, JSON.stringify(value, null, 2));
|
|
18305
|
+
}
|
|
18306
|
+
function writeJsonl(path, values) {
|
|
18307
|
+
ensureParent(path);
|
|
18308
|
+
const body = values.map((value) => JSON.stringify(value)).join("\n");
|
|
18309
|
+
writeFileSync36(path, body.length > 0 ? `${body}
|
|
18310
|
+
` : "");
|
|
18311
|
+
}
|
|
18312
|
+
function ensureTelemetryState(dir, now = Date.now()) {
|
|
18313
|
+
const paths = getTelemetryConfig(dir, buildTelemetryState(now));
|
|
18314
|
+
if (existsSync60(paths.statePath)) {
|
|
18315
|
+
const parsed = safeParseJson(readFileSync44(paths.statePath, "utf8"));
|
|
18316
|
+
if (parsed?.contract === "avorelo.telemetry.state.v1") return parsed;
|
|
18317
|
+
}
|
|
18318
|
+
const created = buildTelemetryState(now);
|
|
18319
|
+
writeTelemetryState(dir, created);
|
|
18320
|
+
return created;
|
|
18321
|
+
}
|
|
18322
|
+
function readTelemetryState(dir) {
|
|
18323
|
+
return ensureTelemetryState(dir);
|
|
18324
|
+
}
|
|
18325
|
+
function writeTelemetryState(dir, state) {
|
|
18326
|
+
const paths = getTelemetryConfig(dir, state);
|
|
18327
|
+
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18328
|
+
writeJson(paths.statePath, state);
|
|
18329
|
+
}
|
|
18330
|
+
function appendTelemetryEvent(dir, event) {
|
|
18331
|
+
const state = ensureTelemetryState(dir);
|
|
18332
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18333
|
+
ensureParent(config2.eventsPath);
|
|
18334
|
+
appendFileSync10(config2.eventsPath, `${JSON.stringify(event)}
|
|
18335
|
+
`);
|
|
18336
|
+
}
|
|
18337
|
+
function readTelemetryEvents(dir) {
|
|
18338
|
+
const state = ensureTelemetryState(dir);
|
|
18339
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18340
|
+
return readJsonl(config2.eventsPath);
|
|
18341
|
+
}
|
|
18342
|
+
function readTelemetryQueue(dir) {
|
|
18343
|
+
const state = ensureTelemetryState(dir);
|
|
18344
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18345
|
+
return readJsonl(config2.queuePath);
|
|
18346
|
+
}
|
|
18347
|
+
function writeTelemetryQueue(dir, records) {
|
|
18348
|
+
const state = ensureTelemetryState(dir);
|
|
18349
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18350
|
+
writeJsonl(config2.queuePath, records);
|
|
18351
|
+
}
|
|
18352
|
+
function writeStoredTelemetryRollups(dir, rollups) {
|
|
18353
|
+
const state = ensureTelemetryState(dir);
|
|
18354
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18355
|
+
writeJson(config2.rollupsPath, rollups);
|
|
18356
|
+
}
|
|
18357
|
+
|
|
18358
|
+
// src/avorelo/telemetry/queue.ts
|
|
18359
|
+
import { randomUUID as randomUUID10 } from "node:crypto";
|
|
18360
|
+
var BACKOFF_MS = [
|
|
18361
|
+
15 * 60 * 1e3,
|
|
18362
|
+
60 * 60 * 1e3,
|
|
18363
|
+
6 * 60 * 60 * 1e3,
|
|
18364
|
+
24 * 60 * 60 * 1e3
|
|
18365
|
+
];
|
|
18366
|
+
function nextRetryDelayMs(attempts) {
|
|
18367
|
+
return BACKOFF_MS[Math.min(Math.max(attempts - 1, 0), BACKOFF_MS.length - 1)];
|
|
18368
|
+
}
|
|
18369
|
+
function enqueueTelemetryEvent(dir, event, now = Date.now()) {
|
|
18370
|
+
const queue = readTelemetryQueue(dir);
|
|
18371
|
+
const record = {
|
|
18372
|
+
schemaVersion: "avorelo.telemetry.queue.v1",
|
|
18373
|
+
queueId: `queue_${randomUUID10().replace(/-/g, "").slice(0, 16)}`,
|
|
18374
|
+
event,
|
|
18375
|
+
enqueuedAt: new Date(now).toISOString(),
|
|
18376
|
+
nextAttemptAt: new Date(now).toISOString(),
|
|
18377
|
+
attempts: 0,
|
|
18378
|
+
sentAt: null,
|
|
18379
|
+
quarantinedAt: null,
|
|
18380
|
+
lastErrorCode: null
|
|
18381
|
+
};
|
|
18382
|
+
queue.push(record);
|
|
18383
|
+
writeTelemetryQueue(dir, queue);
|
|
18384
|
+
return record;
|
|
18385
|
+
}
|
|
18386
|
+
function getQueuedTelemetryEvents(dir, now = Date.now()) {
|
|
18387
|
+
return readTelemetryQueue(dir).filter((record) => !record.sentAt && !record.quarantinedAt && Date.parse(record.nextAttemptAt) <= now);
|
|
18388
|
+
}
|
|
18389
|
+
function countQueuedTelemetryEvents(dir) {
|
|
18390
|
+
return readTelemetryQueue(dir).filter((record) => !record.sentAt && !record.quarantinedAt).length;
|
|
18391
|
+
}
|
|
18392
|
+
function markTelemetryBatchSent(dir, queueIds, sentAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
18393
|
+
const queue = readTelemetryQueue(dir).map((record) => queueIds.includes(record.queueId) ? { ...record, sentAt, lastErrorCode: null } : record);
|
|
18394
|
+
writeTelemetryQueue(dir, queue);
|
|
18395
|
+
}
|
|
18396
|
+
function markTelemetryBatchRetry(dir, queueIds, errorCode, now = Date.now()) {
|
|
18397
|
+
const queue = readTelemetryQueue(dir).map((record) => {
|
|
18398
|
+
if (!queueIds.includes(record.queueId)) return record;
|
|
18399
|
+
const attempts = record.attempts + 1;
|
|
18400
|
+
return {
|
|
18401
|
+
...record,
|
|
18402
|
+
attempts,
|
|
18403
|
+
lastErrorCode: errorCode,
|
|
18404
|
+
nextAttemptAt: new Date(now + nextRetryDelayMs(attempts)).toISOString()
|
|
18405
|
+
};
|
|
18406
|
+
});
|
|
18407
|
+
writeTelemetryQueue(dir, queue);
|
|
18408
|
+
}
|
|
18409
|
+
function quarantineTelemetryBatch(dir, queueIds, errorCode, now = Date.now()) {
|
|
18410
|
+
const quarantinedAt = new Date(now).toISOString();
|
|
18411
|
+
const queue = readTelemetryQueue(dir).map((record) => queueIds.includes(record.queueId) ? { ...record, lastErrorCode: errorCode, quarantinedAt } : record);
|
|
18412
|
+
writeTelemetryQueue(dir, queue);
|
|
18413
|
+
}
|
|
18414
|
+
|
|
18415
|
+
// src/avorelo/telemetry/rollups.ts
|
|
18416
|
+
function nowIso2() {
|
|
18417
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
18418
|
+
}
|
|
18419
|
+
function periodStart(period, now = Date.now()) {
|
|
18420
|
+
switch (period) {
|
|
18421
|
+
case "24h":
|
|
18422
|
+
return now - 24 * 60 * 60 * 1e3;
|
|
18423
|
+
case "7d":
|
|
18424
|
+
return now - 7 * 24 * 60 * 60 * 1e3;
|
|
18425
|
+
case "30d":
|
|
18426
|
+
return now - 30 * 24 * 60 * 60 * 1e3;
|
|
18427
|
+
case "90d":
|
|
18428
|
+
return now - 90 * 24 * 60 * 60 * 1e3;
|
|
18429
|
+
}
|
|
18430
|
+
}
|
|
18431
|
+
function filterByPeriod(events, period, now = Date.now()) {
|
|
18432
|
+
const start = periodStart(period, now);
|
|
18433
|
+
return events.filter((event) => {
|
|
18434
|
+
const ts = Date.parse(event.occurredAt);
|
|
18435
|
+
return Number.isFinite(ts) && ts >= start && ts <= now;
|
|
18436
|
+
});
|
|
18437
|
+
}
|
|
18438
|
+
function countEvents(events, name) {
|
|
18439
|
+
return events.filter((event) => event.eventName === name).length;
|
|
18440
|
+
}
|
|
18441
|
+
function uniqueCount(events, selector) {
|
|
18442
|
+
const values = /* @__PURE__ */ new Set();
|
|
18443
|
+
for (const event of events) {
|
|
18444
|
+
const value = selector(event);
|
|
18445
|
+
if (value) values.add(value);
|
|
18446
|
+
}
|
|
18447
|
+
return values.size;
|
|
18448
|
+
}
|
|
18449
|
+
function rate(numerator, denominator) {
|
|
18450
|
+
if (denominator <= 0) return 0;
|
|
18451
|
+
return Number((numerator / denominator).toFixed(4));
|
|
18452
|
+
}
|
|
18453
|
+
function weekStart(iso) {
|
|
18454
|
+
const date = new Date(iso);
|
|
18455
|
+
const day = (date.getUTCDay() + 6) % 7;
|
|
18456
|
+
date.setUTCDate(date.getUTCDate() - day);
|
|
18457
|
+
date.setUTCHours(0, 0, 0, 0);
|
|
18458
|
+
return date.toISOString();
|
|
18459
|
+
}
|
|
18460
|
+
function buildWeeklyTrend(events) {
|
|
18461
|
+
const weekly = /* @__PURE__ */ new Map();
|
|
18462
|
+
for (const event of events) {
|
|
18463
|
+
if (event.eventName !== "controlled_session_completed" && event.eventName !== "controlled_session_blocked") continue;
|
|
18464
|
+
const key = weekStart(event.occurredAt);
|
|
18465
|
+
const bucket = weekly.get(key) ?? { controlledSessions: 0, activeRepos: /* @__PURE__ */ new Set() };
|
|
18466
|
+
bucket.controlledSessions += 1;
|
|
18467
|
+
if (event.repoFingerprint) bucket.activeRepos.add(event.repoFingerprint);
|
|
18468
|
+
weekly.set(key, bucket);
|
|
18469
|
+
}
|
|
18470
|
+
return Array.from(weekly.entries()).sort(([a], [b]) => a.localeCompare(b)).slice(-12).map(([start, value]) => ({
|
|
18471
|
+
weekStart: start,
|
|
18472
|
+
controlledSessions: value.controlledSessions,
|
|
18473
|
+
activeRepos: value.activeRepos.size
|
|
18474
|
+
}));
|
|
16963
18475
|
}
|
|
16964
|
-
function
|
|
16965
|
-
const
|
|
16966
|
-
|
|
16967
|
-
|
|
16968
|
-
|
|
18476
|
+
function returningAfterDays(events, days) {
|
|
18477
|
+
const byInstallation = /* @__PURE__ */ new Map();
|
|
18478
|
+
for (const event of events) {
|
|
18479
|
+
if (!event.anonymousInstallationId) continue;
|
|
18480
|
+
const list = byInstallation.get(event.anonymousInstallationId) ?? [];
|
|
18481
|
+
list.push(event.occurredAt);
|
|
18482
|
+
byInstallation.set(event.anonymousInstallationId, list);
|
|
16969
18483
|
}
|
|
16970
|
-
|
|
16971
|
-
|
|
16972
|
-
|
|
18484
|
+
let count = 0;
|
|
18485
|
+
const thresholdMs = days * 24 * 60 * 60 * 1e3;
|
|
18486
|
+
for (const timestamps of byInstallation.values()) {
|
|
18487
|
+
const sorted = timestamps.map((ts) => Date.parse(ts)).filter(Number.isFinite).sort((a, b) => a - b);
|
|
18488
|
+
if (sorted.length < 2) continue;
|
|
18489
|
+
if (sorted[sorted.length - 1] - sorted[0] >= thresholdMs) count++;
|
|
18490
|
+
}
|
|
18491
|
+
return count;
|
|
16973
18492
|
}
|
|
16974
|
-
function
|
|
16975
|
-
|
|
18493
|
+
function retainedWeeks(events, weeks, kind) {
|
|
18494
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
18495
|
+
for (const event of events) {
|
|
18496
|
+
const key = kind === "repo" ? event.repoFingerprint : event.anonymousInstallationId;
|
|
18497
|
+
if (!key) continue;
|
|
18498
|
+
const weeksSeen = byKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
18499
|
+
weeksSeen.add(weekStart(event.occurredAt));
|
|
18500
|
+
byKey.set(key, weeksSeen);
|
|
18501
|
+
}
|
|
18502
|
+
let count = 0;
|
|
18503
|
+
for (const weeksSeen of byKey.values()) {
|
|
18504
|
+
if (weeksSeen.size >= weeks) count++;
|
|
18505
|
+
}
|
|
18506
|
+
return count;
|
|
16976
18507
|
}
|
|
16977
|
-
function
|
|
16978
|
-
const
|
|
16979
|
-
|
|
16980
|
-
|
|
18508
|
+
function providerCounts(events, selector) {
|
|
18509
|
+
const counts = {};
|
|
18510
|
+
for (const event of events) {
|
|
18511
|
+
const value = selector(event);
|
|
18512
|
+
if (!value) continue;
|
|
18513
|
+
counts[value] = (counts[value] ?? 0) + 1;
|
|
18514
|
+
}
|
|
18515
|
+
return counts;
|
|
16981
18516
|
}
|
|
16982
|
-
function
|
|
16983
|
-
const
|
|
16984
|
-
const
|
|
16985
|
-
|
|
16986
|
-
|
|
16987
|
-
|
|
18517
|
+
function computeTelemetrySummary(events, state, period, now = Date.now()) {
|
|
18518
|
+
const scoped = filterByPeriod(events, period, now);
|
|
18519
|
+
const weeklyTrend = buildWeeklyTrend(events);
|
|
18520
|
+
const latestWeek = weeklyTrend[weeklyTrend.length - 1];
|
|
18521
|
+
const previousWeek = weeklyTrend[weeklyTrend.length - 2];
|
|
18522
|
+
const completedSessions = countEvents(scoped, "controlled_session_completed");
|
|
18523
|
+
const failedSessions = countEvents(scoped, "controlled_session_failed");
|
|
18524
|
+
const blockedSessions = countEvents(scoped, "controlled_session_blocked");
|
|
18525
|
+
const startedSessions = countEvents(scoped, "controlled_session_started");
|
|
18526
|
+
const controlledSessions = Math.max(startedSessions, completedSessions + failedSessions + blockedSessions);
|
|
18527
|
+
const receiptsGenerated = countEvents(scoped, "receipt_generated") + countEvents(scoped, "first_receipt_created");
|
|
18528
|
+
const receiptsOpened = countEvents(scoped, "receipt_opened") + countEvents(scoped, "first_open_used");
|
|
18529
|
+
const activeRepos = uniqueCount(scoped, (event) => event.repoFingerprint);
|
|
18530
|
+
const claimStarted = countEvents(scoped, "claim_started");
|
|
18531
|
+
const claimCompleted = countEvents(scoped, "claim_completed");
|
|
18532
|
+
const prLinkedReceipts = countEvents(scoped, "receipt_linked_to_pr");
|
|
18533
|
+
return {
|
|
18534
|
+
period,
|
|
18535
|
+
generatedAt: nowIso2(),
|
|
18536
|
+
telemetryMode: state.modeOverride ?? "default_cloud",
|
|
18537
|
+
lastUploadAt: state.lastUploadAt,
|
|
18538
|
+
privacy: {
|
|
18539
|
+
rawCodeCollected: 0,
|
|
18540
|
+
rawPromptsCollected: 0,
|
|
18541
|
+
rawDiffsCollected: 0,
|
|
18542
|
+
secretsCollected: 0,
|
|
18543
|
+
envVarsCollected: 0,
|
|
18544
|
+
terminalOutputCollected: 0,
|
|
18545
|
+
unsafePayloadsRejected: state.unsafePayloadsRejected,
|
|
18546
|
+
telemetryBatchesAccepted: state.acceptedBatches,
|
|
18547
|
+
telemetryBatchesRejected: state.rejectedBatches
|
|
18548
|
+
},
|
|
18549
|
+
topCards: {
|
|
18550
|
+
controlledSessions,
|
|
18551
|
+
completedSessions,
|
|
18552
|
+
failedSessions,
|
|
18553
|
+
blockedSessions,
|
|
18554
|
+
activeRepos,
|
|
18555
|
+
receiptsGenerated,
|
|
18556
|
+
receiptsOpened,
|
|
18557
|
+
returningRepos4w: retainedWeeks(events, 4, "repo"),
|
|
18558
|
+
prLinkedReceipts,
|
|
18559
|
+
gatesTriggered: countEvents(scoped, "approval_gate_required") + countEvents(scoped, "unsafe_action_blocked"),
|
|
18560
|
+
claimConversion: claimStarted > 0 ? Number((claimCompleted / claimStarted).toFixed(4)) : 0
|
|
18561
|
+
},
|
|
18562
|
+
northStar: {
|
|
18563
|
+
weeklyControlledAgentWorkSessionsInActiveRepos: latestWeek?.controlledSessions ?? 0,
|
|
18564
|
+
sessionsPerActiveRepo: activeRepos > 0 ? Number((completedSessions + blockedSessions) / activeRepos) : 0,
|
|
18565
|
+
weekOverWeekGrowth: latestWeek && previousWeek && previousWeek.controlledSessions > 0 ? Number(((latestWeek.controlledSessions - previousWeek.controlledSessions) / previousWeek.controlledSessions).toFixed(4)) : null,
|
|
18566
|
+
twelveWeekTrend: weeklyTrend
|
|
18567
|
+
},
|
|
18568
|
+
activation: {
|
|
18569
|
+
firstCommand: countEvents(scoped, "first_command_run"),
|
|
18570
|
+
firstReceipt: countEvents(scoped, "first_receipt_created"),
|
|
18571
|
+
firstOpen: countEvents(scoped, "first_open_used"),
|
|
18572
|
+
secondSession: countEvents(scoped, "controlled_session_completed") + countEvents(scoped, "controlled_session_failed") + countEvents(scoped, "controlled_session_blocked"),
|
|
18573
|
+
resumeFromReceipt: countEvents(scoped, "receipt_used_for_resume")
|
|
18574
|
+
},
|
|
18575
|
+
retention: {
|
|
18576
|
+
d1ReturningInstallations: returningAfterDays(events, 1),
|
|
18577
|
+
d7ReturningInstallations: returningAfterDays(events, 7),
|
|
18578
|
+
d28ReturningInstallations: returningAfterDays(events, 28),
|
|
18579
|
+
w1RetainedRepos: retainedWeeks(events, 1, "repo"),
|
|
18580
|
+
w2RetainedRepos: retainedWeeks(events, 2, "repo"),
|
|
18581
|
+
w4RetainedRepos: retainedWeeks(events, 4, "repo"),
|
|
18582
|
+
w1RetainedInstallations: retainedWeeks(events, 1, "installation"),
|
|
18583
|
+
w2RetainedInstallations: retainedWeeks(events, 2, "installation"),
|
|
18584
|
+
w4RetainedInstallations: retainedWeeks(events, 4, "installation"),
|
|
18585
|
+
fourWeekRetainedRepos: retainedWeeks(events, 4, "repo"),
|
|
18586
|
+
fourWeekRetainedInstallations: retainedWeeks(events, 4, "installation")
|
|
18587
|
+
},
|
|
18588
|
+
receipts: {
|
|
18589
|
+
generated: receiptsGenerated,
|
|
18590
|
+
opened: receiptsOpened,
|
|
18591
|
+
exported: countEvents(scoped, "receipt_exported"),
|
|
18592
|
+
resumeUsed: countEvents(scoped, "receipt_used_for_resume"),
|
|
18593
|
+
reviewUsed: countEvents(scoped, "receipt_used_for_review"),
|
|
18594
|
+
prLinked: prLinkedReceipts,
|
|
18595
|
+
openRate: rate(receiptsOpened, receiptsGenerated),
|
|
18596
|
+
resumeRate: rate(countEvents(scoped, "receipt_used_for_resume"), receiptsGenerated),
|
|
18597
|
+
reviewRate: rate(countEvents(scoped, "receipt_used_for_review"), receiptsGenerated),
|
|
18598
|
+
exportRate: rate(countEvents(scoped, "receipt_exported"), receiptsGenerated),
|
|
18599
|
+
prLinkRate: rate(prLinkedReceipts, receiptsGenerated)
|
|
18600
|
+
},
|
|
18601
|
+
workControl: {
|
|
18602
|
+
approvalGates: countEvents(scoped, "approval_gate_required") + countEvents(scoped, "approval_gate_passed") + countEvents(scoped, "approval_gate_rejected"),
|
|
18603
|
+
unsafeActionsBlocked: countEvents(scoped, "unsafe_action_blocked"),
|
|
18604
|
+
secretRisksDetected: countEvents(scoped, "secret_risk_detected"),
|
|
18605
|
+
policyEvents: countEvents(scoped, "policy_config_created") + countEvents(scoped, "policy_config_updated"),
|
|
18606
|
+
blockedSessions
|
|
18607
|
+
},
|
|
18608
|
+
integrations: {
|
|
18609
|
+
gitProviders: providerCounts(scoped, (event) => event.gitProvider),
|
|
18610
|
+
ciProviders: providerCounts(scoped, (event) => event.ciProvider),
|
|
18611
|
+
prContextDetected: countEvents(scoped, "pr_context_detected"),
|
|
18612
|
+
prReceiptAttached: countEvents(scoped, "pr_receipt_attached"),
|
|
18613
|
+
ciChecksCreated: countEvents(scoped, "ci_check_created"),
|
|
18614
|
+
ciChecksPassed: countEvents(scoped, "ci_check_passed"),
|
|
18615
|
+
ciChecksFailed: countEvents(scoped, "ci_check_failed"),
|
|
18616
|
+
jiraIssuesLinked: countEvents(scoped, "jira_issue_linked")
|
|
18617
|
+
},
|
|
18618
|
+
commercialIntent: {
|
|
18619
|
+
pricingViews: countEvents(scoped, "pricing_viewed"),
|
|
18620
|
+
proLimitReached: countEvents(scoped, "pro_limit_reached"),
|
|
18621
|
+
teamLimitReached: countEvents(scoped, "team_limit_reached"),
|
|
18622
|
+
claimStarted,
|
|
18623
|
+
claimCompleted,
|
|
18624
|
+
teamInviteStarted: countEvents(scoped, "team_invite_started"),
|
|
18625
|
+
ssoInterest: countEvents(scoped, "sso_interest"),
|
|
18626
|
+
enterpriseWaitlistJoined: countEvents(scoped, "enterprise_waitlist_joined"),
|
|
18627
|
+
teamAdminInterest: countEvents(scoped, "team_admin_interest")
|
|
18628
|
+
}
|
|
18629
|
+
};
|
|
16988
18630
|
}
|
|
16989
|
-
function
|
|
16990
|
-
|
|
16991
|
-
|
|
16992
|
-
|
|
18631
|
+
function buildStoredTelemetryRollups(events, state, now = Date.now()) {
|
|
18632
|
+
return {
|
|
18633
|
+
schemaVersion: "avorelo.telemetry.rollups.v1",
|
|
18634
|
+
generatedAt: new Date(now).toISOString(),
|
|
18635
|
+
summaries: {
|
|
18636
|
+
"24h": computeTelemetrySummary(events, state, "24h", now),
|
|
18637
|
+
"7d": computeTelemetrySummary(events, state, "7d", now),
|
|
18638
|
+
"30d": computeTelemetrySummary(events, state, "30d", now),
|
|
18639
|
+
"90d": computeTelemetrySummary(events, state, "90d", now)
|
|
18640
|
+
}
|
|
18641
|
+
};
|
|
16993
18642
|
}
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
18643
|
+
|
|
18644
|
+
// src/avorelo/telemetry/events.ts
|
|
18645
|
+
function refreshRollups(dir) {
|
|
18646
|
+
const state = readTelemetryState(dir);
|
|
18647
|
+
const events = readTelemetryEvents(dir);
|
|
18648
|
+
writeStoredTelemetryRollups(dir, buildStoredTelemetryRollups(events, state));
|
|
16998
18649
|
}
|
|
16999
|
-
function
|
|
17000
|
-
|
|
17001
|
-
const config2 = getTelemetryConfig(dir, state);
|
|
17002
|
-
writeJsonl(config2.queuePath, records);
|
|
18650
|
+
function shouldQueue(event) {
|
|
18651
|
+
return event.eventName !== "telemetry_batch_sent" && event.eventName !== "telemetry_batch_failed";
|
|
17003
18652
|
}
|
|
17004
|
-
function
|
|
17005
|
-
const state =
|
|
18653
|
+
function recordTelemetryEvent(dir, rawEvent) {
|
|
18654
|
+
const state = readTelemetryState(dir);
|
|
17006
18655
|
const config2 = getTelemetryConfig(dir, state);
|
|
17007
|
-
|
|
17008
|
-
}
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17014
|
-
|
|
17015
|
-
|
|
17016
|
-
|
|
17017
|
-
|
|
17018
|
-
|
|
17019
|
-
|
|
17020
|
-
|
|
17021
|
-
|
|
17022
|
-
|
|
17023
|
-
|
|
17024
|
-
|
|
17025
|
-
|
|
17026
|
-
|
|
17027
|
-
|
|
17028
|
-
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
18656
|
+
if (!config2.enabled || config2.mode === "off") {
|
|
18657
|
+
return { ok: false, queued: false, excludedFields: [], unsafeFields: [], reason: "telemetry_disabled" };
|
|
18658
|
+
}
|
|
18659
|
+
const mode = config2.mode === "claimed_team" ? "claimed_team" : config2.mode === "local_only" ? "local_only" : "default_cloud";
|
|
18660
|
+
const sanitized = sanitizeTelemetryEvent(rawEvent, {
|
|
18661
|
+
appVersion: config2.appVersion,
|
|
18662
|
+
mode,
|
|
18663
|
+
anonymousInstallationId: config2.anonymousInstallationId,
|
|
18664
|
+
anonymousWorkspaceId: config2.anonymousWorkspaceId,
|
|
18665
|
+
anonymousTeamId: config2.anonymousTeamId,
|
|
18666
|
+
osFamily: config2.osFamily,
|
|
18667
|
+
salt: state.installationSalt
|
|
18668
|
+
});
|
|
18669
|
+
appendTelemetryEvent(dir, sanitized.event);
|
|
18670
|
+
let queued = false;
|
|
18671
|
+
if (shouldQueue(sanitized.event) && config2.mode !== "local_only" && config2.endpointUrl) {
|
|
18672
|
+
enqueueTelemetryEvent(dir, sanitized.event);
|
|
18673
|
+
state.lastQueuedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18674
|
+
writeTelemetryState(dir, state);
|
|
18675
|
+
queued = true;
|
|
18676
|
+
}
|
|
18677
|
+
refreshRollups(dir);
|
|
18678
|
+
return {
|
|
18679
|
+
ok: true,
|
|
18680
|
+
queued,
|
|
18681
|
+
event: sanitized.event,
|
|
18682
|
+
excludedFields: sanitized.excludedFields,
|
|
18683
|
+
unsafeFields: sanitized.unsafeFields
|
|
17033
18684
|
};
|
|
17034
|
-
queue.push(record);
|
|
17035
|
-
writeTelemetryQueue(dir, queue);
|
|
17036
|
-
return record;
|
|
17037
18685
|
}
|
|
17038
|
-
function
|
|
17039
|
-
|
|
18686
|
+
function disableTelemetry(dir, mode = "off") {
|
|
18687
|
+
const state = readTelemetryState(dir);
|
|
18688
|
+
state.modeOverride = mode;
|
|
18689
|
+
writeTelemetryState(dir, state);
|
|
17040
18690
|
}
|
|
17041
|
-
function
|
|
17042
|
-
|
|
18691
|
+
function setTelemetryMode(dir, mode) {
|
|
18692
|
+
const state = readTelemetryState(dir);
|
|
18693
|
+
state.modeOverride = mode;
|
|
18694
|
+
writeTelemetryState(dir, state);
|
|
17043
18695
|
}
|
|
17044
|
-
|
|
17045
|
-
|
|
17046
|
-
|
|
18696
|
+
|
|
18697
|
+
// src/avorelo/telemetry/telemetry-status.ts
|
|
18698
|
+
function buildTelemetryStatus(dir) {
|
|
18699
|
+
const state = readTelemetryState(dir);
|
|
18700
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18701
|
+
return {
|
|
18702
|
+
defaultOn: true,
|
|
18703
|
+
enabled: config2.enabled,
|
|
18704
|
+
mode: config2.mode,
|
|
18705
|
+
lastUpload: state.lastUploadAt,
|
|
18706
|
+
queuedEventCount: countQueuedTelemetryEvents(dir),
|
|
18707
|
+
collected: [
|
|
18708
|
+
"Anonymized installation/workspace/team identifiers",
|
|
18709
|
+
"Repo fingerprints",
|
|
18710
|
+
"Command names and safe status buckets",
|
|
18711
|
+
"Receipt, session, and integration metadata",
|
|
18712
|
+
"Work-control and commercial intent event counts"
|
|
18713
|
+
],
|
|
18714
|
+
neverCollected: [
|
|
18715
|
+
"Source code",
|
|
18716
|
+
"Raw prompts",
|
|
18717
|
+
"Raw diffs or patches",
|
|
18718
|
+
"Secrets or tokens",
|
|
18719
|
+
"Env var values",
|
|
18720
|
+
"Terminal output",
|
|
18721
|
+
"Repo names",
|
|
18722
|
+
"Org names",
|
|
18723
|
+
"Remote URLs",
|
|
18724
|
+
"Absolute file paths",
|
|
18725
|
+
"Emails and usernames"
|
|
18726
|
+
],
|
|
18727
|
+
disableInstructions: [
|
|
18728
|
+
"AVORELO_TELEMETRY=off",
|
|
18729
|
+
"avorelo telemetry disable",
|
|
18730
|
+
"avorelo config set telemetry.mode local-only"
|
|
18731
|
+
]
|
|
18732
|
+
};
|
|
17047
18733
|
}
|
|
17048
|
-
function
|
|
17049
|
-
|
|
17050
|
-
|
|
17051
|
-
|
|
17052
|
-
|
|
17053
|
-
|
|
17054
|
-
|
|
17055
|
-
|
|
17056
|
-
|
|
17057
|
-
|
|
18734
|
+
function renderTelemetryStatus(status) {
|
|
18735
|
+
return [
|
|
18736
|
+
"Avorelo Telemetry Status",
|
|
18737
|
+
" Telemetry is on by default: true",
|
|
18738
|
+
` Enabled: ${status.enabled}`,
|
|
18739
|
+
` Mode: ${status.mode}`,
|
|
18740
|
+
` Last upload: ${status.lastUpload ?? "never"}`,
|
|
18741
|
+
` Queued events: ${status.queuedEventCount}`,
|
|
18742
|
+
"",
|
|
18743
|
+
" Collected:",
|
|
18744
|
+
...status.collected.map((line) => ` - ${line}`),
|
|
18745
|
+
"",
|
|
18746
|
+
" Never collected:",
|
|
18747
|
+
...status.neverCollected.map((line) => ` - ${line}`),
|
|
18748
|
+
"",
|
|
18749
|
+
" Disable/local-only:",
|
|
18750
|
+
...status.disableInstructions.map((line) => ` ${line}`),
|
|
18751
|
+
""
|
|
18752
|
+
].join("\n");
|
|
18753
|
+
}
|
|
18754
|
+
|
|
18755
|
+
// src/avorelo/telemetry/telemetry-preview.ts
|
|
18756
|
+
function buildTelemetryPreview(dir) {
|
|
18757
|
+
const state = readTelemetryState(dir);
|
|
18758
|
+
state.lastPreviewAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
18759
|
+
writeTelemetryState(dir, state);
|
|
18760
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18761
|
+
const samplePayload = sanitizeTelemetryEvent({
|
|
18762
|
+
eventName: "controlled_session_completed",
|
|
18763
|
+
commandName: "run",
|
|
18764
|
+
durationMs: 42e3,
|
|
18765
|
+
repoRoot: dir,
|
|
18766
|
+
repoName: "demo-private-repo",
|
|
18767
|
+
remoteUrl: "https://github.com/acme/private-demo",
|
|
18768
|
+
sessionId: "session-local-123",
|
|
18769
|
+
receiptId: "receipt-local-456",
|
|
18770
|
+
rawPrompt: "write code",
|
|
18771
|
+
sourceCode: "const secret = process.env.API_KEY;",
|
|
18772
|
+
errorMessage: "network timeout while syncing"
|
|
18773
|
+
}, {
|
|
18774
|
+
appVersion: config2.appVersion,
|
|
18775
|
+
mode: config2.mode === "claimed_team" ? "claimed_team" : config2.mode === "local_only" ? "local_only" : "default_cloud",
|
|
18776
|
+
anonymousInstallationId: config2.anonymousInstallationId,
|
|
18777
|
+
anonymousWorkspaceId: config2.anonymousWorkspaceId,
|
|
18778
|
+
anonymousTeamId: config2.anonymousTeamId,
|
|
18779
|
+
osFamily: config2.osFamily,
|
|
18780
|
+
salt: state.installationSalt
|
|
17058
18781
|
});
|
|
17059
|
-
|
|
18782
|
+
return {
|
|
18783
|
+
samplePayload,
|
|
18784
|
+
excludedSensitiveFields: samplePayload.excludedFields
|
|
18785
|
+
};
|
|
17060
18786
|
}
|
|
17061
|
-
function
|
|
17062
|
-
|
|
17063
|
-
|
|
17064
|
-
|
|
18787
|
+
function renderTelemetryPreview(preview) {
|
|
18788
|
+
return [
|
|
18789
|
+
"Avorelo Telemetry Preview",
|
|
18790
|
+
" Sample safe payload:",
|
|
18791
|
+
JSON.stringify(preview.samplePayload.event, null, 2),
|
|
18792
|
+
"",
|
|
18793
|
+
" Excluded sensitive fields:",
|
|
18794
|
+
...preview.excludedSensitiveFields.map((field) => ` - ${field}`),
|
|
18795
|
+
"",
|
|
18796
|
+
" No source, prompts, diffs, secrets, env values, terminal output, repo names, or org names are included.",
|
|
18797
|
+
""
|
|
18798
|
+
].join("\n");
|
|
17065
18799
|
}
|
|
17066
18800
|
|
|
17067
|
-
// src/avorelo/telemetry/
|
|
17068
|
-
|
|
17069
|
-
|
|
17070
|
-
|
|
17071
|
-
|
|
17072
|
-
|
|
17073
|
-
|
|
17074
|
-
|
|
17075
|
-
|
|
17076
|
-
|
|
17077
|
-
|
|
17078
|
-
|
|
17079
|
-
|
|
17080
|
-
|
|
18801
|
+
// src/avorelo/telemetry/integration-detection.ts
|
|
18802
|
+
import { existsSync as existsSync61, readFileSync as readFileSync45 } from "node:fs";
|
|
18803
|
+
import { join as join60 } from "node:path";
|
|
18804
|
+
function detectGitProviderFromConfig(dir) {
|
|
18805
|
+
const configPath = join60(dir, ".git", "config");
|
|
18806
|
+
if (!existsSync61(configPath)) return "unknown";
|
|
18807
|
+
try {
|
|
18808
|
+
const content = readFileSync45(configPath, "utf8").toLowerCase();
|
|
18809
|
+
if (content.includes("github.com")) return "github";
|
|
18810
|
+
if (content.includes("gitlab.com")) return "gitlab";
|
|
18811
|
+
if (content.includes("bitbucket.org")) return "bitbucket";
|
|
18812
|
+
return "local";
|
|
18813
|
+
} catch {
|
|
18814
|
+
return "unknown";
|
|
17081
18815
|
}
|
|
17082
18816
|
}
|
|
17083
|
-
function
|
|
17084
|
-
|
|
17085
|
-
|
|
17086
|
-
|
|
17087
|
-
|
|
17088
|
-
|
|
17089
|
-
}
|
|
17090
|
-
function countEvents(events, name) {
|
|
17091
|
-
return events.filter((event) => event.eventName === name).length;
|
|
18817
|
+
function detectCiProvider(dir) {
|
|
18818
|
+
if (existsSync61(join60(dir, ".github", "workflows"))) return "github_actions";
|
|
18819
|
+
if (existsSync61(join60(dir, ".gitlab-ci.yml"))) return "gitlab_ci";
|
|
18820
|
+
if (existsSync61(join60(dir, ".circleci", "config.yml"))) return "circle";
|
|
18821
|
+
if (existsSync61(join60(dir, "Jenkinsfile"))) return "jenkins";
|
|
18822
|
+
return "unknown";
|
|
17092
18823
|
}
|
|
17093
|
-
function
|
|
17094
|
-
const
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
|
|
18824
|
+
function detectRepoVisibility(dir) {
|
|
18825
|
+
const configPath = join60(dir, ".git", "config");
|
|
18826
|
+
if (!existsSync61(configPath)) return "unknown";
|
|
18827
|
+
try {
|
|
18828
|
+
const content = readFileSync45(configPath, "utf8").toLowerCase();
|
|
18829
|
+
if (content.includes("github.com")) {
|
|
18830
|
+
return content.includes("/public") ? "public" : "private";
|
|
18831
|
+
}
|
|
18832
|
+
return "unknown";
|
|
18833
|
+
} catch {
|
|
18834
|
+
return "unknown";
|
|
17098
18835
|
}
|
|
17099
|
-
return values.size;
|
|
17100
18836
|
}
|
|
17101
|
-
function
|
|
17102
|
-
|
|
17103
|
-
|
|
18837
|
+
function detectIntegrations(dir) {
|
|
18838
|
+
return {
|
|
18839
|
+
gitProvider: detectGitProviderFromConfig(dir),
|
|
18840
|
+
ciProvider: detectCiProvider(dir),
|
|
18841
|
+
repoVisibility: detectRepoVisibility(dir)
|
|
18842
|
+
};
|
|
17104
18843
|
}
|
|
17105
|
-
|
|
17106
|
-
|
|
17107
|
-
|
|
17108
|
-
|
|
17109
|
-
|
|
17110
|
-
|
|
18844
|
+
|
|
18845
|
+
// src/avorelo/telemetry/sender.ts
|
|
18846
|
+
import { randomUUID as randomUUID11 } from "node:crypto";
|
|
18847
|
+
function buildBatch(dir, limit) {
|
|
18848
|
+
const state = readTelemetryState(dir);
|
|
18849
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18850
|
+
const records = getQueuedTelemetryEvents(dir).slice(0, limit);
|
|
18851
|
+
if (records.length === 0) return { batch: null, queueIds: [] };
|
|
18852
|
+
return {
|
|
18853
|
+
batch: {
|
|
18854
|
+
schemaVersion: "avorelo.telemetry.batch.v1",
|
|
18855
|
+
batchId: `batch_${randomUUID11().replace(/-/g, "").slice(0, 16)}`,
|
|
18856
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18857
|
+
appVersion: config2.appVersion,
|
|
18858
|
+
anonymousInstallationId: config2.anonymousInstallationId,
|
|
18859
|
+
events: records.map((record) => record.event),
|
|
18860
|
+
privacy: {
|
|
18861
|
+
rawCodeIncluded: false,
|
|
18862
|
+
rawPromptsIncluded: false,
|
|
18863
|
+
rawDiffsIncluded: false,
|
|
18864
|
+
secretsIncluded: false,
|
|
18865
|
+
envIncluded: false,
|
|
18866
|
+
terminalOutputIncluded: false
|
|
18867
|
+
}
|
|
18868
|
+
},
|
|
18869
|
+
queueIds: records.map((record) => record.queueId)
|
|
18870
|
+
};
|
|
18871
|
+
}
|
|
18872
|
+
function appendTransportEvent(dir, eventName, errorClass) {
|
|
18873
|
+
const state = readTelemetryState(dir);
|
|
18874
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18875
|
+
appendTelemetryEvent(dir, {
|
|
18876
|
+
schemaVersion: "avorelo.telemetry.v1",
|
|
18877
|
+
eventId: `evt_${randomUUID11().replace(/-/g, "").slice(0, 16)}`,
|
|
18878
|
+
occurredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
18879
|
+
eventName,
|
|
18880
|
+
mode: config2.mode === "claimed_team" ? "claimed_team" : config2.mode === "local_only" ? "local_only" : "default_cloud",
|
|
18881
|
+
anonymousInstallationId: config2.anonymousInstallationId,
|
|
18882
|
+
anonymousWorkspaceId: config2.anonymousWorkspaceId ?? void 0,
|
|
18883
|
+
anonymousTeamId: config2.anonymousTeamId ?? void 0,
|
|
18884
|
+
appVersion: config2.appVersion,
|
|
18885
|
+
osFamily: config2.osFamily,
|
|
18886
|
+
errorClass
|
|
18887
|
+
});
|
|
17111
18888
|
}
|
|
17112
|
-
function
|
|
17113
|
-
const
|
|
17114
|
-
|
|
17115
|
-
|
|
17116
|
-
|
|
17117
|
-
|
|
17118
|
-
bucket.controlledSessions += 1;
|
|
17119
|
-
if (event.repoFingerprint) bucket.activeRepos.add(event.repoFingerprint);
|
|
17120
|
-
weekly.set(key, bucket);
|
|
18889
|
+
async function sendDueTelemetry(dir, opts) {
|
|
18890
|
+
const state = readTelemetryState(dir);
|
|
18891
|
+
const config2 = getTelemetryConfig(dir, state);
|
|
18892
|
+
const queuedCount = countQueuedTelemetryEvents(dir);
|
|
18893
|
+
if (!config2.enabled || config2.mode === "off") {
|
|
18894
|
+
return { attempted: false, sent: false, reason: "telemetry_disabled", queuedCount };
|
|
17121
18895
|
}
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
controlledSessions: value.controlledSessions,
|
|
17125
|
-
activeRepos: value.activeRepos.size
|
|
17126
|
-
}));
|
|
17127
|
-
}
|
|
17128
|
-
function returningAfterDays(events, days) {
|
|
17129
|
-
const byInstallation = /* @__PURE__ */ new Map();
|
|
17130
|
-
for (const event of events) {
|
|
17131
|
-
if (!event.anonymousInstallationId) continue;
|
|
17132
|
-
const list = byInstallation.get(event.anonymousInstallationId) ?? [];
|
|
17133
|
-
list.push(event.occurredAt);
|
|
17134
|
-
byInstallation.set(event.anonymousInstallationId, list);
|
|
18896
|
+
if (config2.mode === "local_only" || !config2.endpointUrl) {
|
|
18897
|
+
return { attempted: false, sent: false, reason: "local_only_or_no_endpoint", queuedCount };
|
|
17135
18898
|
}
|
|
17136
|
-
|
|
17137
|
-
const
|
|
17138
|
-
|
|
17139
|
-
|
|
17140
|
-
|
|
17141
|
-
|
|
18899
|
+
const now = opts?.now ?? Date.now();
|
|
18900
|
+
const lastUploadMs = state.lastUploadAt ? Date.parse(state.lastUploadAt) : 0;
|
|
18901
|
+
const dueByInterval = lastUploadMs === 0 || now - lastUploadMs >= config2.uploadIntervalMs;
|
|
18902
|
+
const dueByQueueSize = queuedCount >= config2.batchSize;
|
|
18903
|
+
const dueByOpportunistic = lastUploadMs === 0 || now - lastUploadMs >= config2.opportunisticIntervalMs;
|
|
18904
|
+
if (!opts?.force && !dueByInterval && !dueByQueueSize && !dueByOpportunistic) {
|
|
18905
|
+
return { attempted: false, sent: false, reason: "not_due", queuedCount };
|
|
17142
18906
|
}
|
|
17143
|
-
|
|
17144
|
-
|
|
17145
|
-
|
|
17146
|
-
const byKey = /* @__PURE__ */ new Map();
|
|
17147
|
-
for (const event of events) {
|
|
17148
|
-
const key = kind === "repo" ? event.repoFingerprint : event.anonymousInstallationId;
|
|
17149
|
-
if (!key) continue;
|
|
17150
|
-
const weeksSeen = byKey.get(key) ?? /* @__PURE__ */ new Set();
|
|
17151
|
-
weeksSeen.add(weekStart(event.occurredAt));
|
|
17152
|
-
byKey.set(key, weeksSeen);
|
|
18907
|
+
const { batch, queueIds } = buildBatch(dir, config2.batchSize);
|
|
18908
|
+
if (!batch || queueIds.length === 0) {
|
|
18909
|
+
return { attempted: false, sent: false, reason: "queue_empty", queuedCount };
|
|
17153
18910
|
}
|
|
17154
|
-
|
|
17155
|
-
|
|
17156
|
-
|
|
18911
|
+
const controller = new AbortController();
|
|
18912
|
+
const timeout = setTimeout(() => controller.abort(), TELEMETRY_TIMEOUT_MS);
|
|
18913
|
+
try {
|
|
18914
|
+
const response = await fetch(config2.endpointUrl, {
|
|
18915
|
+
method: "POST",
|
|
18916
|
+
headers: { "Content-Type": "application/json" },
|
|
18917
|
+
body: JSON.stringify(batch),
|
|
18918
|
+
signal: controller.signal
|
|
18919
|
+
});
|
|
18920
|
+
clearTimeout(timeout);
|
|
18921
|
+
if (response.ok) {
|
|
18922
|
+
markTelemetryBatchSent(dir, queueIds, new Date(now).toISOString());
|
|
18923
|
+
state.lastUploadAt = new Date(now).toISOString();
|
|
18924
|
+
state.lastBatchId = batch.batchId;
|
|
18925
|
+
state.lastBatchFailureAt = null;
|
|
18926
|
+
state.lastBatchFailureCode = null;
|
|
18927
|
+
state.acceptedBatches += 1;
|
|
18928
|
+
writeTelemetryState(dir, state);
|
|
18929
|
+
appendTransportEvent(dir, "telemetry_batch_sent");
|
|
18930
|
+
return { attempted: true, sent: true, reason: "accepted", queuedCount };
|
|
18931
|
+
}
|
|
18932
|
+
if (response.status >= 400 && response.status < 500) {
|
|
18933
|
+
quarantineTelemetryBatch(dir, queueIds, `http_${response.status}`, now);
|
|
18934
|
+
state.rejectedBatches += 1;
|
|
18935
|
+
state.lastBatchFailureAt = new Date(now).toISOString();
|
|
18936
|
+
state.lastBatchFailureCode = `http_${response.status}`;
|
|
18937
|
+
writeTelemetryState(dir, state);
|
|
18938
|
+
appendTransportEvent(dir, "telemetry_batch_failed", `http_${response.status}`);
|
|
18939
|
+
return { attempted: true, sent: false, reason: `client_error_${response.status}`, queuedCount };
|
|
18940
|
+
}
|
|
18941
|
+
markTelemetryBatchRetry(dir, queueIds, `http_${response.status}`, now);
|
|
18942
|
+
state.lastBatchFailureAt = new Date(now).toISOString();
|
|
18943
|
+
state.lastBatchFailureCode = `http_${response.status}`;
|
|
18944
|
+
writeTelemetryState(dir, state);
|
|
18945
|
+
appendTransportEvent(dir, "telemetry_batch_failed", `http_${response.status}`);
|
|
18946
|
+
return { attempted: true, sent: false, reason: `server_error_${response.status}`, queuedCount };
|
|
18947
|
+
} catch (error) {
|
|
18948
|
+
clearTimeout(timeout);
|
|
18949
|
+
const code = error instanceof Error && error.name === "AbortError" ? "timeout" : "network_error";
|
|
18950
|
+
markTelemetryBatchRetry(dir, queueIds, code, now);
|
|
18951
|
+
state.lastBatchFailureAt = new Date(now).toISOString();
|
|
18952
|
+
state.lastBatchFailureCode = code;
|
|
18953
|
+
writeTelemetryState(dir, state);
|
|
18954
|
+
appendTransportEvent(dir, "telemetry_batch_failed", code);
|
|
18955
|
+
return { attempted: true, sent: false, reason: code, queuedCount };
|
|
17157
18956
|
}
|
|
17158
|
-
return count;
|
|
17159
18957
|
}
|
|
17160
|
-
|
|
17161
|
-
|
|
17162
|
-
|
|
17163
|
-
|
|
17164
|
-
|
|
17165
|
-
|
|
18958
|
+
|
|
18959
|
+
// src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
|
|
18960
|
+
import { existsSync as existsSync63, readFileSync as readFileSync49 } from "node:fs";
|
|
18961
|
+
import { join as join65 } from "node:path";
|
|
18962
|
+
|
|
18963
|
+
// src/avorelo/kernel/agent-artifact-guard/index.ts
|
|
18964
|
+
import { join as join64 } from "node:path";
|
|
18965
|
+
import { readFileSync as readFileSync48 } from "node:fs";
|
|
18966
|
+
|
|
18967
|
+
// src/avorelo/kernel/agent-artifact-guard/artifact-discovery.ts
|
|
18968
|
+
import { readdirSync as readdirSync18, statSync as statSync13, existsSync as existsSync62 } from "node:fs";
|
|
18969
|
+
import { join as join61, relative as relative3, extname as extname2 } from "node:path";
|
|
18970
|
+
var SCANNED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
18971
|
+
".md",
|
|
18972
|
+
".js",
|
|
18973
|
+
".ts",
|
|
18974
|
+
".json",
|
|
18975
|
+
".yml",
|
|
18976
|
+
".yaml",
|
|
18977
|
+
".sh",
|
|
18978
|
+
".py",
|
|
18979
|
+
".txt",
|
|
18980
|
+
".toml"
|
|
18981
|
+
]);
|
|
18982
|
+
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
18983
|
+
"node_modules",
|
|
18984
|
+
"dist",
|
|
18985
|
+
".git",
|
|
18986
|
+
"build",
|
|
18987
|
+
"out",
|
|
18988
|
+
".next",
|
|
18989
|
+
"__pycache__"
|
|
18990
|
+
]);
|
|
18991
|
+
var ARTIFACT_PATTERNS = [
|
|
18992
|
+
{ kind: "claude-skill", paths: [".claude/skills"], recursive: true },
|
|
18993
|
+
{ kind: "claude-instructions", paths: ["CLAUDE.md", ".claude/CLAUDE.md"] },
|
|
18994
|
+
{ kind: "codex-instructions", paths: ["AGENTS.md", "CODEX.md", ".codex/CODEX.md"] },
|
|
18995
|
+
{ kind: "cursor-rules", paths: [".cursor/rules", ".cursorrules"], recursive: true },
|
|
18996
|
+
{ kind: "mcp-config", paths: [".claude/mcp.json", ".cursor/mcp.json", "mcp.json", ".mcp.json"] },
|
|
18997
|
+
{ kind: "github-actions", paths: [".github/workflows"], recursive: true },
|
|
18998
|
+
{ kind: "package-scripts", paths: ["package.json"] },
|
|
18999
|
+
{ kind: "hooks", paths: [".husky", ".git/hooks", ".claude/hooks"], recursive: true }
|
|
19000
|
+
];
|
|
19001
|
+
function walkDir2(dir, results) {
|
|
19002
|
+
let entries;
|
|
19003
|
+
try {
|
|
19004
|
+
entries = readdirSync18(dir);
|
|
19005
|
+
} catch {
|
|
19006
|
+
return;
|
|
19007
|
+
}
|
|
19008
|
+
for (const entry of entries) {
|
|
19009
|
+
if (IGNORED_DIRS.has(entry)) continue;
|
|
19010
|
+
const full = join61(dir, entry);
|
|
19011
|
+
let st;
|
|
19012
|
+
try {
|
|
19013
|
+
st = statSync13(full);
|
|
19014
|
+
} catch {
|
|
19015
|
+
continue;
|
|
19016
|
+
}
|
|
19017
|
+
if (st.isDirectory()) {
|
|
19018
|
+
walkDir2(full, results);
|
|
19019
|
+
} else if (st.isFile() && SCANNED_EXTENSIONS.has(extname2(full).toLowerCase())) {
|
|
19020
|
+
results.push(full);
|
|
19021
|
+
}
|
|
17166
19022
|
}
|
|
17167
|
-
return counts;
|
|
17168
19023
|
}
|
|
17169
|
-
function
|
|
17170
|
-
const
|
|
17171
|
-
const
|
|
17172
|
-
|
|
17173
|
-
|
|
17174
|
-
|
|
17175
|
-
|
|
17176
|
-
|
|
17177
|
-
|
|
17178
|
-
|
|
17179
|
-
|
|
17180
|
-
|
|
17181
|
-
|
|
17182
|
-
|
|
17183
|
-
|
|
17184
|
-
|
|
17185
|
-
|
|
17186
|
-
|
|
17187
|
-
|
|
17188
|
-
|
|
17189
|
-
|
|
17190
|
-
|
|
17191
|
-
|
|
17192
|
-
|
|
17193
|
-
|
|
17194
|
-
|
|
17195
|
-
|
|
17196
|
-
|
|
17197
|
-
|
|
17198
|
-
telemetryBatchesAccepted: state.acceptedBatches,
|
|
17199
|
-
telemetryBatchesRejected: state.rejectedBatches
|
|
17200
|
-
},
|
|
17201
|
-
topCards: {
|
|
17202
|
-
controlledSessions,
|
|
17203
|
-
completedSessions,
|
|
17204
|
-
failedSessions,
|
|
17205
|
-
blockedSessions,
|
|
17206
|
-
activeRepos,
|
|
17207
|
-
receiptsGenerated,
|
|
17208
|
-
receiptsOpened,
|
|
17209
|
-
returningRepos4w: retainedWeeks(events, 4, "repo"),
|
|
17210
|
-
prLinkedReceipts,
|
|
17211
|
-
gatesTriggered: countEvents(scoped, "approval_gate_required") + countEvents(scoped, "unsafe_action_blocked"),
|
|
17212
|
-
claimConversion: claimStarted > 0 ? Number((claimCompleted / claimStarted).toFixed(4)) : 0
|
|
17213
|
-
},
|
|
17214
|
-
northStar: {
|
|
17215
|
-
weeklyControlledAgentWorkSessionsInActiveRepos: latestWeek?.controlledSessions ?? 0,
|
|
17216
|
-
sessionsPerActiveRepo: activeRepos > 0 ? Number((completedSessions + blockedSessions) / activeRepos) : 0,
|
|
17217
|
-
weekOverWeekGrowth: latestWeek && previousWeek && previousWeek.controlledSessions > 0 ? Number(((latestWeek.controlledSessions - previousWeek.controlledSessions) / previousWeek.controlledSessions).toFixed(4)) : null,
|
|
17218
|
-
twelveWeekTrend: weeklyTrend
|
|
17219
|
-
},
|
|
17220
|
-
activation: {
|
|
17221
|
-
firstCommand: countEvents(scoped, "first_command_run"),
|
|
17222
|
-
firstReceipt: countEvents(scoped, "first_receipt_created"),
|
|
17223
|
-
firstOpen: countEvents(scoped, "first_open_used"),
|
|
17224
|
-
secondSession: countEvents(scoped, "controlled_session_completed") + countEvents(scoped, "controlled_session_failed") + countEvents(scoped, "controlled_session_blocked"),
|
|
17225
|
-
resumeFromReceipt: countEvents(scoped, "receipt_used_for_resume")
|
|
17226
|
-
},
|
|
17227
|
-
retention: {
|
|
17228
|
-
d1ReturningInstallations: returningAfterDays(events, 1),
|
|
17229
|
-
d7ReturningInstallations: returningAfterDays(events, 7),
|
|
17230
|
-
d28ReturningInstallations: returningAfterDays(events, 28),
|
|
17231
|
-
w1RetainedRepos: retainedWeeks(events, 1, "repo"),
|
|
17232
|
-
w2RetainedRepos: retainedWeeks(events, 2, "repo"),
|
|
17233
|
-
w4RetainedRepos: retainedWeeks(events, 4, "repo"),
|
|
17234
|
-
w1RetainedInstallations: retainedWeeks(events, 1, "installation"),
|
|
17235
|
-
w2RetainedInstallations: retainedWeeks(events, 2, "installation"),
|
|
17236
|
-
w4RetainedInstallations: retainedWeeks(events, 4, "installation"),
|
|
17237
|
-
fourWeekRetainedRepos: retainedWeeks(events, 4, "repo"),
|
|
17238
|
-
fourWeekRetainedInstallations: retainedWeeks(events, 4, "installation")
|
|
17239
|
-
},
|
|
17240
|
-
receipts: {
|
|
17241
|
-
generated: receiptsGenerated,
|
|
17242
|
-
opened: receiptsOpened,
|
|
17243
|
-
exported: countEvents(scoped, "receipt_exported"),
|
|
17244
|
-
resumeUsed: countEvents(scoped, "receipt_used_for_resume"),
|
|
17245
|
-
reviewUsed: countEvents(scoped, "receipt_used_for_review"),
|
|
17246
|
-
prLinked: prLinkedReceipts,
|
|
17247
|
-
openRate: rate(receiptsOpened, receiptsGenerated),
|
|
17248
|
-
resumeRate: rate(countEvents(scoped, "receipt_used_for_resume"), receiptsGenerated),
|
|
17249
|
-
reviewRate: rate(countEvents(scoped, "receipt_used_for_review"), receiptsGenerated),
|
|
17250
|
-
exportRate: rate(countEvents(scoped, "receipt_exported"), receiptsGenerated),
|
|
17251
|
-
prLinkRate: rate(prLinkedReceipts, receiptsGenerated)
|
|
17252
|
-
},
|
|
17253
|
-
workControl: {
|
|
17254
|
-
approvalGates: countEvents(scoped, "approval_gate_required") + countEvents(scoped, "approval_gate_passed") + countEvents(scoped, "approval_gate_rejected"),
|
|
17255
|
-
unsafeActionsBlocked: countEvents(scoped, "unsafe_action_blocked"),
|
|
17256
|
-
secretRisksDetected: countEvents(scoped, "secret_risk_detected"),
|
|
17257
|
-
policyEvents: countEvents(scoped, "policy_config_created") + countEvents(scoped, "policy_config_updated"),
|
|
17258
|
-
blockedSessions
|
|
17259
|
-
},
|
|
17260
|
-
integrations: {
|
|
17261
|
-
gitProviders: providerCounts(scoped, (event) => event.gitProvider),
|
|
17262
|
-
ciProviders: providerCounts(scoped, (event) => event.ciProvider),
|
|
17263
|
-
prContextDetected: countEvents(scoped, "pr_context_detected"),
|
|
17264
|
-
prReceiptAttached: countEvents(scoped, "pr_receipt_attached"),
|
|
17265
|
-
ciChecksCreated: countEvents(scoped, "ci_check_created"),
|
|
17266
|
-
ciChecksPassed: countEvents(scoped, "ci_check_passed"),
|
|
17267
|
-
ciChecksFailed: countEvents(scoped, "ci_check_failed"),
|
|
17268
|
-
jiraIssuesLinked: countEvents(scoped, "jira_issue_linked")
|
|
17269
|
-
},
|
|
17270
|
-
commercialIntent: {
|
|
17271
|
-
pricingViews: countEvents(scoped, "pricing_viewed"),
|
|
17272
|
-
proLimitReached: countEvents(scoped, "pro_limit_reached"),
|
|
17273
|
-
teamLimitReached: countEvents(scoped, "team_limit_reached"),
|
|
17274
|
-
claimStarted,
|
|
17275
|
-
claimCompleted,
|
|
17276
|
-
teamInviteStarted: countEvents(scoped, "team_invite_started"),
|
|
17277
|
-
ssoInterest: countEvents(scoped, "sso_interest"),
|
|
17278
|
-
enterpriseWaitlistJoined: countEvents(scoped, "enterprise_waitlist_joined"),
|
|
17279
|
-
teamAdminInterest: countEvents(scoped, "team_admin_interest")
|
|
19024
|
+
function discoverArtifacts(projectRoot) {
|
|
19025
|
+
const artifacts = [];
|
|
19026
|
+
for (const pattern of ARTIFACT_PATTERNS) {
|
|
19027
|
+
for (const p of pattern.paths) {
|
|
19028
|
+
const fullPath = join61(projectRoot, p);
|
|
19029
|
+
if (!existsSync62(fullPath)) continue;
|
|
19030
|
+
let st;
|
|
19031
|
+
try {
|
|
19032
|
+
st = statSync13(fullPath);
|
|
19033
|
+
} catch {
|
|
19034
|
+
continue;
|
|
19035
|
+
}
|
|
19036
|
+
if (st.isFile()) {
|
|
19037
|
+
if (SCANNED_EXTENSIONS.has(extname2(fullPath).toLowerCase()) || fullPath.endsWith(".json")) {
|
|
19038
|
+
artifacts.push({
|
|
19039
|
+
kind: pattern.kind,
|
|
19040
|
+
path: relative3(projectRoot, fullPath)
|
|
19041
|
+
});
|
|
19042
|
+
}
|
|
19043
|
+
} else if (st.isDirectory() && pattern.recursive) {
|
|
19044
|
+
const files = [];
|
|
19045
|
+
walkDir2(fullPath, files);
|
|
19046
|
+
for (const f of files) {
|
|
19047
|
+
artifacts.push({
|
|
19048
|
+
kind: pattern.kind,
|
|
19049
|
+
path: relative3(projectRoot, f)
|
|
19050
|
+
});
|
|
19051
|
+
}
|
|
19052
|
+
}
|
|
17280
19053
|
}
|
|
17281
|
-
}
|
|
19054
|
+
}
|
|
19055
|
+
return artifacts;
|
|
17282
19056
|
}
|
|
17283
|
-
function
|
|
17284
|
-
|
|
17285
|
-
|
|
17286
|
-
|
|
17287
|
-
|
|
17288
|
-
|
|
17289
|
-
|
|
17290
|
-
|
|
17291
|
-
|
|
17292
|
-
|
|
17293
|
-
|
|
19057
|
+
function classifyArtifact(filePath) {
|
|
19058
|
+
const lower = filePath.toLowerCase().replace(/\\/g, "/");
|
|
19059
|
+
if (lower.includes(".claude/skills/")) return "claude-skill";
|
|
19060
|
+
if (lower.includes("claude.md")) return "claude-instructions";
|
|
19061
|
+
if (lower.includes("agents.md") || lower.includes("codex.md")) return "codex-instructions";
|
|
19062
|
+
if (lower.includes(".cursor/rules") || lower.includes(".cursorrules")) return "cursor-rules";
|
|
19063
|
+
if (lower.includes("mcp.json") || lower.includes("mcp.yaml")) return "mcp-config";
|
|
19064
|
+
if (lower.includes(".github/workflows/")) return "github-actions";
|
|
19065
|
+
if (lower === "package.json" || lower.endsWith("/package.json")) return "package-scripts";
|
|
19066
|
+
if (lower.includes(".husky/") || lower.includes("/hooks/")) return "hooks";
|
|
19067
|
+
if (lower.endsWith(".sh")) return "shell-script";
|
|
19068
|
+
return "unknown";
|
|
17294
19069
|
}
|
|
17295
19070
|
|
|
17296
|
-
// src/avorelo/
|
|
17297
|
-
|
|
17298
|
-
|
|
17299
|
-
|
|
17300
|
-
|
|
19071
|
+
// src/avorelo/kernel/agent-artifact-guard/rules.ts
|
|
19072
|
+
var RULES2 = [
|
|
19073
|
+
// --- Destructive commands ---
|
|
19074
|
+
{
|
|
19075
|
+
id: "destructive-rm-rf",
|
|
19076
|
+
title: "Recursive force delete",
|
|
19077
|
+
severity: "critical",
|
|
19078
|
+
category: "destructive-command",
|
|
19079
|
+
pattern: /rm\s+-(r|f|rf|fr)\s/,
|
|
19080
|
+
artifactKinds: "all",
|
|
19081
|
+
description: "Recursively and forcibly deletes files or directories. Can cause irreversible data loss.",
|
|
19082
|
+
recommendedAction: "Remove or scope the delete command. Use safe alternatives with explicit paths."
|
|
19083
|
+
},
|
|
19084
|
+
{
|
|
19085
|
+
id: "destructive-drop-table",
|
|
19086
|
+
title: "SQL DROP TABLE",
|
|
19087
|
+
severity: "critical",
|
|
19088
|
+
category: "destructive-command",
|
|
19089
|
+
pattern: /DROP\s+TABLE/i,
|
|
19090
|
+
artifactKinds: "all",
|
|
19091
|
+
description: "Drops a database table, causing irreversible data loss.",
|
|
19092
|
+
recommendedAction: "Remove DROP TABLE or add explicit safety guards."
|
|
19093
|
+
},
|
|
19094
|
+
{
|
|
19095
|
+
id: "destructive-format-disk",
|
|
19096
|
+
title: "Disk format command",
|
|
19097
|
+
severity: "critical",
|
|
19098
|
+
category: "destructive-command",
|
|
19099
|
+
pattern: /(?:mkfs|format\s+[A-Z]:)/i,
|
|
19100
|
+
artifactKinds: "all",
|
|
19101
|
+
description: "Formats a disk or partition, destroying all data.",
|
|
19102
|
+
recommendedAction: "Remove disk formatting commands from agent artifacts."
|
|
19103
|
+
},
|
|
19104
|
+
// --- Remote code execution ---
|
|
19105
|
+
{
|
|
19106
|
+
id: "rce-curl-pipe-bash",
|
|
19107
|
+
title: "Pipe curl to shell",
|
|
19108
|
+
severity: "critical",
|
|
19109
|
+
category: "remote-code-execution",
|
|
19110
|
+
pattern: /curl\s[^|]*\|\s*(?:bash|sh|zsh)/,
|
|
19111
|
+
artifactKinds: "all",
|
|
19112
|
+
description: "Downloads and immediately executes a remote script. Classic remote code execution vector.",
|
|
19113
|
+
recommendedAction: "Download the script first, review it, then execute separately."
|
|
19114
|
+
},
|
|
19115
|
+
{
|
|
19116
|
+
id: "rce-wget-pipe-sh",
|
|
19117
|
+
title: "Pipe wget to shell",
|
|
19118
|
+
severity: "critical",
|
|
19119
|
+
category: "remote-code-execution",
|
|
19120
|
+
pattern: /wget\s[^|]*\|\s*(?:bash|sh|zsh)/,
|
|
19121
|
+
artifactKinds: "all",
|
|
19122
|
+
description: "Downloads and immediately executes a remote script via wget.",
|
|
19123
|
+
recommendedAction: "Download the script first, review it, then execute separately."
|
|
19124
|
+
},
|
|
19125
|
+
{
|
|
19126
|
+
id: "rce-base64-decode-exec",
|
|
19127
|
+
title: "Base64 decode and execute",
|
|
19128
|
+
severity: "critical",
|
|
19129
|
+
category: "remote-code-execution",
|
|
19130
|
+
pattern: /base64\s+-d\s*\|\s*(?:bash|sh)/,
|
|
19131
|
+
artifactKinds: "all",
|
|
19132
|
+
description: "Decodes an obfuscated payload and pipes it to a shell.",
|
|
19133
|
+
recommendedAction: "Remove obfuscated execution. Use plain, reviewable scripts."
|
|
19134
|
+
},
|
|
19135
|
+
{
|
|
19136
|
+
id: "rce-reverse-shell",
|
|
19137
|
+
title: "Reverse or bind shell",
|
|
19138
|
+
severity: "critical",
|
|
19139
|
+
category: "remote-code-execution",
|
|
19140
|
+
pattern: /(?:nc\s+-e|\/dev\/tcp\/|ncat\s.*-e|socat\s.*exec)/,
|
|
19141
|
+
artifactKinds: "all",
|
|
19142
|
+
description: "Opens an interactive shell to a remote host. Post-exploitation technique.",
|
|
19143
|
+
recommendedAction: "Remove reverse/bind shell commands."
|
|
19144
|
+
},
|
|
19145
|
+
// --- Secret exposure ---
|
|
19146
|
+
{
|
|
19147
|
+
id: "secret-private-key",
|
|
19148
|
+
title: "Private key material",
|
|
19149
|
+
severity: "critical",
|
|
19150
|
+
category: "secret-exposure",
|
|
19151
|
+
pattern: /-----BEGIN (?:RSA |EC |DSA |OPENSSH )?PRIVATE KEY-----/,
|
|
19152
|
+
artifactKinds: "all",
|
|
19153
|
+
description: "Contains or references private key material.",
|
|
19154
|
+
recommendedAction: "Remove private key content. Use secret management instead."
|
|
19155
|
+
},
|
|
19156
|
+
{
|
|
19157
|
+
id: "secret-id-rsa",
|
|
19158
|
+
title: "SSH private key file reference",
|
|
19159
|
+
severity: "critical",
|
|
19160
|
+
category: "secret-exposure",
|
|
19161
|
+
pattern: /(?:id_rsa|id_ed25519|id_ecdsa)(?:\s|$|")/,
|
|
19162
|
+
artifactKinds: "all",
|
|
19163
|
+
description: "References an SSH private key file directly.",
|
|
19164
|
+
recommendedAction: "Use SSH agent or key references instead of direct file paths."
|
|
19165
|
+
},
|
|
19166
|
+
{
|
|
19167
|
+
id: "secret-ssh-dir",
|
|
19168
|
+
title: "SSH directory access",
|
|
19169
|
+
severity: "high",
|
|
19170
|
+
category: "secret-exposure",
|
|
19171
|
+
pattern: /~\/\.ssh\//,
|
|
19172
|
+
artifactKinds: "all",
|
|
19173
|
+
description: "Accesses the SSH directory containing private keys and host configurations.",
|
|
19174
|
+
recommendedAction: "Avoid direct SSH directory access in agent artifacts."
|
|
19175
|
+
},
|
|
19176
|
+
{
|
|
19177
|
+
id: "secret-env-file",
|
|
19178
|
+
title: ".env file reference",
|
|
19179
|
+
severity: "medium",
|
|
19180
|
+
category: "secret-exposure",
|
|
19181
|
+
pattern: /(?:\.env(?:\.\w+)?)\b/,
|
|
19182
|
+
artifactKinds: "all",
|
|
19183
|
+
description: "References a .env file which typically contains secrets and API keys.",
|
|
19184
|
+
recommendedAction: "Review whether the .env reference exposes sensitive values."
|
|
19185
|
+
},
|
|
19186
|
+
{
|
|
19187
|
+
id: "secret-process-env",
|
|
19188
|
+
title: "Environment variable access",
|
|
19189
|
+
severity: "low",
|
|
19190
|
+
category: "secret-exposure",
|
|
19191
|
+
pattern: /process\.env\b/,
|
|
19192
|
+
artifactKinds: "all",
|
|
19193
|
+
description: "Accesses environment variables which may contain secrets.",
|
|
19194
|
+
recommendedAction: "Verify that accessed environment variables do not contain sensitive values."
|
|
19195
|
+
},
|
|
19196
|
+
{
|
|
19197
|
+
id: "secret-api-key-pattern",
|
|
19198
|
+
title: "API key or token pattern",
|
|
19199
|
+
severity: "high",
|
|
19200
|
+
category: "secret-exposure",
|
|
19201
|
+
pattern: /(?:api[_-]?key|api[_-]?secret|auth[_-]?token)\s*[:=]/i,
|
|
19202
|
+
artifactKinds: "all",
|
|
19203
|
+
description: "Contains an API key or authentication token assignment.",
|
|
19204
|
+
recommendedAction: "Move credentials to environment variables or a secret manager."
|
|
19205
|
+
},
|
|
19206
|
+
// --- Privilege escalation ---
|
|
19207
|
+
{
|
|
19208
|
+
id: "priv-sudo",
|
|
19209
|
+
title: "Privilege escalation via sudo",
|
|
19210
|
+
severity: "high",
|
|
19211
|
+
category: "privilege-escalation",
|
|
19212
|
+
pattern: /\bsudo\b/,
|
|
19213
|
+
artifactKinds: "all",
|
|
19214
|
+
description: "Runs commands with elevated privileges.",
|
|
19215
|
+
recommendedAction: "Review whether elevated privileges are necessary. Scope to specific commands."
|
|
19216
|
+
},
|
|
19217
|
+
{
|
|
19218
|
+
id: "priv-chmod-exec",
|
|
19219
|
+
title: "Make file executable",
|
|
19220
|
+
severity: "medium",
|
|
19221
|
+
category: "privilege-escalation",
|
|
19222
|
+
pattern: /chmod\s+\+x/,
|
|
19223
|
+
artifactKinds: "all",
|
|
19224
|
+
description: "Marks a file as executable, often preceding execution of a downloaded binary.",
|
|
19225
|
+
recommendedAction: "Verify the file being made executable is trusted."
|
|
19226
|
+
},
|
|
19227
|
+
// --- Prompt injection / hidden instructions ---
|
|
19228
|
+
{
|
|
19229
|
+
id: "injection-prompt-override",
|
|
19230
|
+
title: "Prompt injection phrasing",
|
|
19231
|
+
severity: "high",
|
|
19232
|
+
category: "prompt-injection",
|
|
19233
|
+
pattern: /(?:ignore\s+(?:all\s+)?(?:previous|prior|above)\s+instructions|you\s+are\s+now\s+a|forget\s+(?:all\s+)?(?:your|previous)\s+instructions|disregard\s+(?:all\s+)?(?:previous|prior)\s+instructions|override\s+(?:all\s+)?(?:safety|security)\s+(?:rules|policies|instructions))/i,
|
|
19234
|
+
artifactKinds: "all",
|
|
19235
|
+
description: "Contains phrasing commonly used in prompt injection attacks to override agent instructions.",
|
|
19236
|
+
recommendedAction: "Remove instruction-override phrasing. Rewrite as direct task instructions."
|
|
19237
|
+
},
|
|
19238
|
+
{
|
|
19239
|
+
id: "injection-do-not-tell",
|
|
19240
|
+
title: "Concealment instruction",
|
|
19241
|
+
severity: "high",
|
|
19242
|
+
category: "hidden-instruction",
|
|
19243
|
+
pattern: /(?:do\s+not\s+tell\s+(?:the\s+)?user|hide\s+(?:this|these)\s+(?:changes?|from)|don'?t\s+(?:mention|reveal|show|disclose)\s+(?:this|these))/i,
|
|
19244
|
+
artifactKinds: "all",
|
|
19245
|
+
description: "Instructs the agent to conceal actions or information from the user.",
|
|
19246
|
+
recommendedAction: "Remove concealment instructions. Agent actions should be transparent."
|
|
19247
|
+
},
|
|
19248
|
+
{
|
|
19249
|
+
id: "injection-skip-tests",
|
|
19250
|
+
title: "Test bypass instruction",
|
|
19251
|
+
severity: "high",
|
|
19252
|
+
category: "hidden-instruction",
|
|
19253
|
+
pattern: /(?:skip\s+(?:all\s+)?tests?|don'?t\s+run\s+tests?|ignore\s+(?:test|lint|check)\s+(?:failures?|errors?|results?)|bypass\s+(?:ci|checks?|validation))/i,
|
|
19254
|
+
artifactKinds: "all",
|
|
19255
|
+
description: "Instructs the agent to skip testing, linting, or validation checks.",
|
|
19256
|
+
recommendedAction: "Remove test-bypass instructions. All changes should be validated."
|
|
19257
|
+
},
|
|
19258
|
+
{
|
|
19259
|
+
id: "injection-ignore-policy",
|
|
19260
|
+
title: "Policy bypass instruction",
|
|
19261
|
+
severity: "high",
|
|
19262
|
+
category: "hidden-instruction",
|
|
19263
|
+
pattern: /(?:ignore\s+(?:all\s+)?polic(?:y|ies)|always\s+approve|auto[_-]?approve\s+(?:all|everything)|skip\s+(?:review|approval))/i,
|
|
19264
|
+
artifactKinds: "all",
|
|
19265
|
+
description: "Instructs the agent to bypass approval policies or auto-approve actions.",
|
|
19266
|
+
recommendedAction: "Remove policy-bypass instructions. Approval workflows should be respected."
|
|
19267
|
+
},
|
|
19268
|
+
{
|
|
19269
|
+
id: "injection-push-to-main",
|
|
19270
|
+
title: "Direct push to main branch",
|
|
19271
|
+
severity: "high",
|
|
19272
|
+
category: "hidden-instruction",
|
|
19273
|
+
pattern: /(?:push\s+(?:directly\s+)?to\s+(?:main|master|production)|force[_-]?push|--force\s+(?:origin\s+)?(?:main|master))/i,
|
|
19274
|
+
artifactKinds: "all",
|
|
19275
|
+
description: "Instructs the agent to push directly to a protected branch or force-push.",
|
|
19276
|
+
recommendedAction: "Use pull requests and standard review workflows instead of direct pushes."
|
|
19277
|
+
},
|
|
19278
|
+
// --- Self-modifying ---
|
|
19279
|
+
{
|
|
19280
|
+
id: "selfmod-modify-own-skill",
|
|
19281
|
+
title: "Self-modifying skill instruction",
|
|
19282
|
+
severity: "high",
|
|
19283
|
+
category: "self-modifying",
|
|
19284
|
+
pattern: /(?:modify\s+(?:this|your\s+own)\s+(?:skill|instructions?|rules?|config)|write\s+(?:to|back\s+to)\s+(?:SKILL|CLAUDE|AGENTS)\.md|update\s+(?:your\s+own|this)\s+(?:skill|config)\s+file)/i,
|
|
19285
|
+
artifactKinds: "all",
|
|
19286
|
+
description: "Instructs the agent to modify its own skill, instructions, or configuration files.",
|
|
19287
|
+
recommendedAction: "Remove self-modification instructions. Agent artifacts should be immutable during execution."
|
|
19288
|
+
},
|
|
19289
|
+
// --- MCP tool poisoning ---
|
|
19290
|
+
{
|
|
19291
|
+
id: "mcp-hidden-instruction",
|
|
19292
|
+
title: "Hidden instruction in MCP tool description",
|
|
19293
|
+
severity: "high",
|
|
19294
|
+
category: "mcp-tool-poisoning",
|
|
19295
|
+
pattern: /(?:when\s+(?:the\s+)?user\s+(?:asks?|requests?)|secretly|silently\s+(?:send|post|upload|forward))/i,
|
|
19296
|
+
artifactKinds: ["mcp-config"],
|
|
19297
|
+
description: "MCP tool description contains instruction-like content that could manipulate agent behavior.",
|
|
19298
|
+
recommendedAction: "Review MCP tool descriptions for hidden instructions. Descriptions should only describe functionality."
|
|
19299
|
+
},
|
|
19300
|
+
// --- Package lifecycle risk ---
|
|
19301
|
+
{
|
|
19302
|
+
id: "pkg-lifecycle-exec",
|
|
19303
|
+
title: "Process execution in package lifecycle script",
|
|
19304
|
+
severity: "high",
|
|
19305
|
+
category: "package-lifecycle-risk",
|
|
19306
|
+
pattern: /(?:child_process|exec|spawn|execSync|spawnSync)\s*\(/,
|
|
19307
|
+
artifactKinds: ["package-scripts", "shell-script"],
|
|
19308
|
+
description: "Package lifecycle script or hook executes arbitrary processes.",
|
|
19309
|
+
recommendedAction: "Review lifecycle scripts for unexpected process execution."
|
|
19310
|
+
},
|
|
19311
|
+
{
|
|
19312
|
+
id: "pkg-lifecycle-network",
|
|
19313
|
+
title: "Network access in package lifecycle script",
|
|
19314
|
+
severity: "high",
|
|
19315
|
+
category: "package-lifecycle-risk",
|
|
19316
|
+
pattern: /(?:fetch|axios|https?\.(?:get|post|request)|XMLHttpRequest|node-fetch)\s*\(/,
|
|
19317
|
+
artifactKinds: ["package-scripts", "shell-script"],
|
|
19318
|
+
description: "Package lifecycle script makes network requests, potentially exfiltrating data.",
|
|
19319
|
+
recommendedAction: "Review lifecycle scripts for unexpected network access."
|
|
19320
|
+
},
|
|
19321
|
+
// --- Network exfiltration ---
|
|
19322
|
+
{
|
|
19323
|
+
id: "network-external-call",
|
|
19324
|
+
title: "External network request",
|
|
19325
|
+
severity: "medium",
|
|
19326
|
+
category: "network-exfiltration",
|
|
19327
|
+
pattern: /\b(?:curl|wget|fetch|axios|requests\.(?:get|post))\b/,
|
|
19328
|
+
artifactKinds: ["claude-skill", "claude-instructions", "cursor-rules", "codex-instructions", "hooks"],
|
|
19329
|
+
description: "Makes an external network request which could exfiltrate data or retrieve malicious payloads.",
|
|
19330
|
+
recommendedAction: "Verify the network call is necessary and targets a trusted endpoint."
|
|
19331
|
+
},
|
|
19332
|
+
// --- Eval / dynamic execution ---
|
|
19333
|
+
{
|
|
19334
|
+
id: "exec-eval",
|
|
19335
|
+
title: "Dynamic code execution",
|
|
19336
|
+
severity: "high",
|
|
19337
|
+
category: "remote-code-execution",
|
|
19338
|
+
pattern: /\b(?:eval|Invoke-Expression|Function\s*\()\s*\(/,
|
|
19339
|
+
artifactKinds: "all",
|
|
19340
|
+
description: "Executes a string as code, enabling injection and obfuscation attacks.",
|
|
19341
|
+
recommendedAction: "Remove eval/dynamic execution. Use explicit, reviewable code paths."
|
|
19342
|
+
},
|
|
19343
|
+
{
|
|
19344
|
+
id: "exec-child-process",
|
|
19345
|
+
title: "Node child_process import",
|
|
19346
|
+
severity: "high",
|
|
19347
|
+
category: "remote-code-execution",
|
|
19348
|
+
pattern: /require\s*\(\s*['"]child_process['"]\s*\)|from\s+['"]child_process['"]/,
|
|
19349
|
+
artifactKinds: "all",
|
|
19350
|
+
description: "Imports Node.js child_process module for arbitrary command execution.",
|
|
19351
|
+
recommendedAction: "Review whether child_process usage is necessary and scoped."
|
|
19352
|
+
},
|
|
19353
|
+
// --- Filesystem access ---
|
|
19354
|
+
{
|
|
19355
|
+
id: "fs-broad-access",
|
|
19356
|
+
title: "Broad filesystem access",
|
|
19357
|
+
severity: "medium",
|
|
19358
|
+
category: "filesystem-access",
|
|
19359
|
+
pattern: /(?:fs\.(?:readdir|rmdir|unlink)Sync|glob\s*\(\s*['"]\/|readdir\s*\(\s*['"]\/)/,
|
|
19360
|
+
artifactKinds: "all",
|
|
19361
|
+
description: "Performs broad filesystem operations that could access or delete files outside the project.",
|
|
19362
|
+
recommendedAction: "Scope filesystem operations to the project directory."
|
|
19363
|
+
},
|
|
19364
|
+
// --- Deploy / publish ---
|
|
19365
|
+
{
|
|
19366
|
+
id: "deploy-npm-publish",
|
|
19367
|
+
title: "npm publish command",
|
|
19368
|
+
severity: "high",
|
|
19369
|
+
category: "deploy-publish",
|
|
19370
|
+
pattern: /npm\s+publish/,
|
|
19371
|
+
artifactKinds: "all",
|
|
19372
|
+
description: "Publishes a package to npm, which is an irreversible public action.",
|
|
19373
|
+
recommendedAction: "Remove automatic publish commands. Publishing should be a deliberate human action."
|
|
19374
|
+
},
|
|
19375
|
+
{
|
|
19376
|
+
id: "deploy-dangerous-action",
|
|
19377
|
+
title: "Dangerous deploy/publish in CI",
|
|
19378
|
+
severity: "high",
|
|
19379
|
+
category: "deploy-publish",
|
|
19380
|
+
pattern: /(?:deploy\s+(?:--)?(?:prod|production)|push\s+(?:to\s+)?(?:prod|production|live))/i,
|
|
19381
|
+
artifactKinds: ["github-actions", "shell-script", "hooks"],
|
|
19382
|
+
description: "Deploys directly to production without explicit approval gates.",
|
|
19383
|
+
recommendedAction: "Add approval gates before production deployments."
|
|
19384
|
+
}
|
|
19385
|
+
];
|
|
19386
|
+
function getRule(id) {
|
|
19387
|
+
return RULES2.find((r2) => r2.id === id);
|
|
17301
19388
|
}
|
|
17302
|
-
|
|
17303
|
-
|
|
19389
|
+
|
|
19390
|
+
// src/avorelo/kernel/agent-artifact-guard/scanner.ts
|
|
19391
|
+
var MAX_PREVIEW_LENGTH = 80;
|
|
19392
|
+
function safeMatchPreview(match) {
|
|
19393
|
+
let preview = match.trim();
|
|
19394
|
+
if (preview.length > MAX_PREVIEW_LENGTH) {
|
|
19395
|
+
preview = preview.slice(0, MAX_PREVIEW_LENGTH) + "...";
|
|
19396
|
+
}
|
|
19397
|
+
preview = preview.replace(
|
|
19398
|
+
/(?:key|secret|token|password|passwd|pwd)\s*[:=]\s*['"]?[a-zA-Z0-9_\-./+=]{8,}/gi,
|
|
19399
|
+
(m) => {
|
|
19400
|
+
const eqIdx = m.search(/[:=]/);
|
|
19401
|
+
if (eqIdx >= 0) return m.slice(0, eqIdx + 1) + " [REDACTED]";
|
|
19402
|
+
return "[REDACTED]";
|
|
19403
|
+
}
|
|
19404
|
+
);
|
|
19405
|
+
return preview;
|
|
17304
19406
|
}
|
|
17305
|
-
function
|
|
17306
|
-
|
|
17307
|
-
|
|
17308
|
-
|
|
17309
|
-
|
|
19407
|
+
function rulesForArtifact(kind) {
|
|
19408
|
+
return RULES2.filter(
|
|
19409
|
+
(r2) => r2.artifactKinds === "all" || r2.artifactKinds.includes(kind)
|
|
19410
|
+
);
|
|
19411
|
+
}
|
|
19412
|
+
function scanContent2(content, filePath, artifactKind) {
|
|
19413
|
+
const kind = artifactKind ?? classifyArtifact(filePath);
|
|
19414
|
+
const applicable = rulesForArtifact(kind);
|
|
19415
|
+
const findings = [];
|
|
19416
|
+
const lines = content.split("\n");
|
|
19417
|
+
for (const rule of applicable) {
|
|
19418
|
+
if (rule.multiline) continue;
|
|
19419
|
+
for (let i = 0; i < lines.length; i++) {
|
|
19420
|
+
const match = rule.pattern.exec(lines[i]);
|
|
19421
|
+
if (match) {
|
|
19422
|
+
findings.push({
|
|
19423
|
+
ruleId: rule.id,
|
|
19424
|
+
title: rule.title,
|
|
19425
|
+
severity: rule.severity,
|
|
19426
|
+
category: rule.category,
|
|
19427
|
+
file: filePath,
|
|
19428
|
+
line: i + 1,
|
|
19429
|
+
matchPreview: safeMatchPreview(match[0]),
|
|
19430
|
+
description: rule.description,
|
|
19431
|
+
recommendedAction: rule.recommendedAction,
|
|
19432
|
+
artifactKind: kind
|
|
19433
|
+
});
|
|
19434
|
+
}
|
|
19435
|
+
}
|
|
17310
19436
|
}
|
|
17311
|
-
const
|
|
17312
|
-
|
|
17313
|
-
|
|
17314
|
-
|
|
17315
|
-
|
|
17316
|
-
|
|
17317
|
-
|
|
17318
|
-
|
|
17319
|
-
|
|
17320
|
-
|
|
17321
|
-
|
|
17322
|
-
|
|
17323
|
-
|
|
17324
|
-
|
|
17325
|
-
|
|
17326
|
-
|
|
17327
|
-
|
|
19437
|
+
for (const rule of applicable) {
|
|
19438
|
+
if (!rule.multiline) continue;
|
|
19439
|
+
const match = rule.pattern.exec(content);
|
|
19440
|
+
if (match) {
|
|
19441
|
+
const lineNumber = content.slice(0, match.index).split("\n").length;
|
|
19442
|
+
findings.push({
|
|
19443
|
+
ruleId: rule.id,
|
|
19444
|
+
title: rule.title,
|
|
19445
|
+
severity: rule.severity,
|
|
19446
|
+
category: rule.category,
|
|
19447
|
+
file: filePath,
|
|
19448
|
+
line: lineNumber,
|
|
19449
|
+
matchPreview: safeMatchPreview(match[0]),
|
|
19450
|
+
description: rule.description,
|
|
19451
|
+
recommendedAction: rule.recommendedAction,
|
|
19452
|
+
artifactKind: kind
|
|
19453
|
+
});
|
|
19454
|
+
}
|
|
17328
19455
|
}
|
|
17329
|
-
|
|
17330
|
-
return
|
|
17331
|
-
ok: true,
|
|
17332
|
-
queued,
|
|
17333
|
-
event: sanitized.event,
|
|
17334
|
-
excludedFields: sanitized.excludedFields,
|
|
17335
|
-
unsafeFields: sanitized.unsafeFields
|
|
17336
|
-
};
|
|
19456
|
+
findings.sort((a, b) => a.line - b.line);
|
|
19457
|
+
return findings;
|
|
17337
19458
|
}
|
|
17338
|
-
|
|
17339
|
-
|
|
17340
|
-
|
|
17341
|
-
|
|
19459
|
+
|
|
19460
|
+
// src/avorelo/kernel/agent-artifact-guard/scorer.ts
|
|
19461
|
+
var SEVERITY_WEIGHTS = {
|
|
19462
|
+
low: 1,
|
|
19463
|
+
medium: 3,
|
|
19464
|
+
high: 6,
|
|
19465
|
+
critical: 10
|
|
19466
|
+
};
|
|
19467
|
+
function countBySeverity(findings) {
|
|
19468
|
+
const counts = { critical: 0, high: 0, medium: 0, low: 0 };
|
|
19469
|
+
for (const f of findings) {
|
|
19470
|
+
counts[f.severity]++;
|
|
19471
|
+
}
|
|
19472
|
+
return counts;
|
|
17342
19473
|
}
|
|
17343
|
-
function
|
|
17344
|
-
|
|
17345
|
-
|
|
17346
|
-
|
|
19474
|
+
function computeRiskScore(counts) {
|
|
19475
|
+
if (counts.critical > 0) return 10;
|
|
19476
|
+
const weighted = counts.high * SEVERITY_WEIGHTS.high + counts.medium * SEVERITY_WEIGHTS.medium + counts.low * SEVERITY_WEIGHTS.low;
|
|
19477
|
+
if (weighted === 0) return 0;
|
|
19478
|
+
const score = Math.min(9.9, Math.log2(weighted + 1) * 1.5);
|
|
19479
|
+
return Math.round(score * 10) / 10;
|
|
17347
19480
|
}
|
|
17348
19481
|
|
|
17349
|
-
// src/avorelo/
|
|
17350
|
-
function
|
|
17351
|
-
|
|
17352
|
-
|
|
17353
|
-
|
|
17354
|
-
|
|
17355
|
-
|
|
17356
|
-
|
|
17357
|
-
|
|
17358
|
-
|
|
17359
|
-
|
|
17360
|
-
|
|
17361
|
-
|
|
17362
|
-
|
|
17363
|
-
|
|
17364
|
-
|
|
17365
|
-
|
|
17366
|
-
|
|
17367
|
-
|
|
17368
|
-
|
|
17369
|
-
|
|
17370
|
-
|
|
17371
|
-
|
|
17372
|
-
|
|
17373
|
-
"
|
|
17374
|
-
|
|
17375
|
-
"Remote URLs",
|
|
17376
|
-
"Absolute file paths",
|
|
17377
|
-
"Emails and usernames"
|
|
17378
|
-
],
|
|
17379
|
-
disableInstructions: [
|
|
17380
|
-
"AVORELO_TELEMETRY=off",
|
|
17381
|
-
"avorelo telemetry disable",
|
|
17382
|
-
"avorelo config set telemetry.mode local-only"
|
|
17383
|
-
]
|
|
17384
|
-
};
|
|
17385
|
-
}
|
|
17386
|
-
function renderTelemetryStatus(status) {
|
|
17387
|
-
return [
|
|
17388
|
-
"Avorelo Telemetry Status",
|
|
17389
|
-
" Telemetry is on by default: true",
|
|
17390
|
-
` Enabled: ${status.enabled}`,
|
|
17391
|
-
` Mode: ${status.mode}`,
|
|
17392
|
-
` Last upload: ${status.lastUpload ?? "never"}`,
|
|
17393
|
-
` Queued events: ${status.queuedEventCount}`,
|
|
17394
|
-
"",
|
|
17395
|
-
" Collected:",
|
|
17396
|
-
...status.collected.map((line) => ` - ${line}`),
|
|
17397
|
-
"",
|
|
17398
|
-
" Never collected:",
|
|
17399
|
-
...status.neverCollected.map((line) => ` - ${line}`),
|
|
17400
|
-
"",
|
|
17401
|
-
" Disable/local-only:",
|
|
17402
|
-
...status.disableInstructions.map((line) => ` ${line}`),
|
|
17403
|
-
""
|
|
17404
|
-
].join("\n");
|
|
19482
|
+
// src/avorelo/kernel/agent-artifact-guard/policy.ts
|
|
19483
|
+
function evaluatePolicy2(counts, riskScore) {
|
|
19484
|
+
let findingOutcome = "pass";
|
|
19485
|
+
if (counts.critical > 0) findingOutcome = "blocked";
|
|
19486
|
+
else if (counts.high > 0) findingOutcome = "requires_approval";
|
|
19487
|
+
else if (counts.medium > 0) findingOutcome = "warn";
|
|
19488
|
+
let scoreOutcome = "pass";
|
|
19489
|
+
if (riskScore >= 9) scoreOutcome = "blocked";
|
|
19490
|
+
else if (riskScore >= 6) scoreOutcome = "requires_approval";
|
|
19491
|
+
else if (riskScore >= 3) scoreOutcome = "warn";
|
|
19492
|
+
const ORDER = ["pass", "warn", "requires_approval", "blocked"];
|
|
19493
|
+
const findingRank = ORDER.indexOf(findingOutcome);
|
|
19494
|
+
const scoreRank = ORDER.indexOf(scoreOutcome);
|
|
19495
|
+
return ORDER[Math.max(findingRank, scoreRank)];
|
|
19496
|
+
}
|
|
19497
|
+
function computeNextBestAction(outcome, counts) {
|
|
19498
|
+
switch (outcome) {
|
|
19499
|
+
case "blocked":
|
|
19500
|
+
return `${counts.critical} critical finding(s) must be resolved before proceeding.`;
|
|
19501
|
+
case "requires_approval":
|
|
19502
|
+
return `${counts.high} high-severity finding(s) require review and approval before proceeding.`;
|
|
19503
|
+
case "warn":
|
|
19504
|
+
return `${counts.medium} medium-severity finding(s) detected. Review recommended.`;
|
|
19505
|
+
case "pass":
|
|
19506
|
+
return "No blocking findings. Artifacts are ready for use.";
|
|
19507
|
+
}
|
|
17405
19508
|
}
|
|
17406
19509
|
|
|
17407
|
-
// src/avorelo/
|
|
17408
|
-
|
|
17409
|
-
|
|
17410
|
-
state.lastPreviewAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
17411
|
-
writeTelemetryState(dir, state);
|
|
17412
|
-
const config2 = getTelemetryConfig(dir, state);
|
|
17413
|
-
const samplePayload = sanitizeTelemetryEvent({
|
|
17414
|
-
eventName: "controlled_session_completed",
|
|
17415
|
-
commandName: "run",
|
|
17416
|
-
durationMs: 42e3,
|
|
17417
|
-
repoRoot: dir,
|
|
17418
|
-
repoName: "demo-private-repo",
|
|
17419
|
-
remoteUrl: "https://github.com/acme/private-demo",
|
|
17420
|
-
sessionId: "session-local-123",
|
|
17421
|
-
receiptId: "receipt-local-456",
|
|
17422
|
-
rawPrompt: "write code",
|
|
17423
|
-
sourceCode: "const secret = process.env.API_KEY;",
|
|
17424
|
-
errorMessage: "network timeout while syncing"
|
|
17425
|
-
}, {
|
|
17426
|
-
appVersion: config2.appVersion,
|
|
17427
|
-
mode: config2.mode === "claimed_team" ? "claimed_team" : config2.mode === "local_only" ? "local_only" : "default_cloud",
|
|
17428
|
-
anonymousInstallationId: config2.anonymousInstallationId,
|
|
17429
|
-
anonymousWorkspaceId: config2.anonymousWorkspaceId,
|
|
17430
|
-
anonymousTeamId: config2.anonymousTeamId,
|
|
17431
|
-
osFamily: config2.osFamily,
|
|
17432
|
-
salt: state.installationSalt
|
|
17433
|
-
});
|
|
19510
|
+
// src/avorelo/kernel/agent-artifact-guard/receipt.ts
|
|
19511
|
+
import { createHash as createHash19 } from "node:crypto";
|
|
19512
|
+
function createReceipt(result3) {
|
|
17434
19513
|
return {
|
|
17435
|
-
|
|
17436
|
-
|
|
19514
|
+
receiptType: "agent-artifact-guard-v1",
|
|
19515
|
+
scannedAt: result3.scannedAt,
|
|
19516
|
+
projectRootHash: createHash19("sha256").update(result3.projectRoot).digest("hex"),
|
|
19517
|
+
filesScanned: result3.filesScanned,
|
|
19518
|
+
artifacts: result3.artifacts.map((a) => ({ kind: a.kind, path: a.path })),
|
|
19519
|
+
findingSummary: {
|
|
19520
|
+
total: result3.findings.length,
|
|
19521
|
+
...result3.counts
|
|
19522
|
+
},
|
|
19523
|
+
findings: result3.findings.map((f) => ({
|
|
19524
|
+
ruleId: f.ruleId,
|
|
19525
|
+
severity: f.severity,
|
|
19526
|
+
file: f.file,
|
|
19527
|
+
line: f.line,
|
|
19528
|
+
title: f.title,
|
|
19529
|
+
matchPreview: f.matchPreview,
|
|
19530
|
+
recommendedAction: f.recommendedAction
|
|
19531
|
+
})),
|
|
19532
|
+
riskScore: result3.riskScore,
|
|
19533
|
+
policyOutcome: result3.policyOutcome,
|
|
19534
|
+
nextBestAction: result3.nextBestAction,
|
|
19535
|
+
containsRawPrompt: false,
|
|
19536
|
+
containsRawSource: false,
|
|
19537
|
+
containsRawSecret: false,
|
|
19538
|
+
contentStored: false
|
|
17437
19539
|
};
|
|
17438
19540
|
}
|
|
17439
|
-
|
|
17440
|
-
|
|
17441
|
-
|
|
17442
|
-
|
|
17443
|
-
|
|
17444
|
-
|
|
17445
|
-
|
|
17446
|
-
|
|
17447
|
-
|
|
17448
|
-
|
|
17449
|
-
|
|
17450
|
-
|
|
19541
|
+
|
|
19542
|
+
// src/avorelo/kernel/agent-artifact-guard/reporter.ts
|
|
19543
|
+
var SEVERITY_BADGE = {
|
|
19544
|
+
critical: "[CRITICAL]",
|
|
19545
|
+
high: "[HIGH]",
|
|
19546
|
+
medium: "[MEDIUM]",
|
|
19547
|
+
low: "[LOW]"
|
|
19548
|
+
};
|
|
19549
|
+
var OUTCOME_LABEL = {
|
|
19550
|
+
pass: "PASS",
|
|
19551
|
+
warn: "WARN",
|
|
19552
|
+
requires_approval: "REQUIRES APPROVAL",
|
|
19553
|
+
blocked: "BLOCKED"
|
|
19554
|
+
};
|
|
19555
|
+
function riskBar(score) {
|
|
19556
|
+
const filled = Math.round(score / 10 * 20);
|
|
19557
|
+
return "\u2588".repeat(filled) + "\u2591".repeat(20 - filled);
|
|
19558
|
+
}
|
|
19559
|
+
function groupByFile(findings) {
|
|
19560
|
+
const map = /* @__PURE__ */ new Map();
|
|
19561
|
+
for (const f of findings) {
|
|
19562
|
+
const group = map.get(f.file) ?? [];
|
|
19563
|
+
group.push(f);
|
|
19564
|
+
map.set(f.file, group);
|
|
19565
|
+
}
|
|
19566
|
+
return map;
|
|
19567
|
+
}
|
|
19568
|
+
function renderReport(result3) {
|
|
19569
|
+
const lines = [];
|
|
19570
|
+
lines.push(`Artifact Guard Scan`);
|
|
19571
|
+
lines.push(`Path: ${result3.projectRoot}`);
|
|
19572
|
+
lines.push(`Files scanned: ${result3.filesScanned}`);
|
|
19573
|
+
lines.push(`Artifacts found: ${result3.artifacts.length}`);
|
|
19574
|
+
lines.push(`Findings: ${result3.findings.length}`);
|
|
19575
|
+
lines.push("");
|
|
19576
|
+
if (result3.findings.length > 0) {
|
|
19577
|
+
const grouped = groupByFile(result3.findings);
|
|
19578
|
+
for (const [file, findings] of grouped) {
|
|
19579
|
+
lines.push(` ${file}`);
|
|
19580
|
+
for (const f of findings) {
|
|
19581
|
+
lines.push(` L${f.line} ${SEVERITY_BADGE[f.severity]} ${f.title} (${f.ruleId})`);
|
|
19582
|
+
lines.push(` ${f.matchPreview}`);
|
|
19583
|
+
lines.push(` ${f.description}`);
|
|
19584
|
+
}
|
|
19585
|
+
lines.push("");
|
|
19586
|
+
}
|
|
19587
|
+
}
|
|
19588
|
+
lines.push(`Risk: ${riskBar(result3.riskScore)} ${result3.riskScore}/10`);
|
|
19589
|
+
lines.push(`Outcome: ${OUTCOME_LABEL[result3.policyOutcome]}`);
|
|
19590
|
+
lines.push("");
|
|
19591
|
+
lines.push(`Next: ${result3.nextBestAction}`);
|
|
19592
|
+
return lines.join("\n");
|
|
19593
|
+
}
|
|
19594
|
+
function renderSummary(result3) {
|
|
19595
|
+
const parts = [];
|
|
19596
|
+
parts.push(`Artifact Guard: ${OUTCOME_LABEL[result3.policyOutcome]}`);
|
|
19597
|
+
if (result3.findings.length > 0) {
|
|
19598
|
+
const counts = [];
|
|
19599
|
+
if (result3.counts.critical > 0) counts.push(`${result3.counts.critical} critical`);
|
|
19600
|
+
if (result3.counts.high > 0) counts.push(`${result3.counts.high} high`);
|
|
19601
|
+
if (result3.counts.medium > 0) counts.push(`${result3.counts.medium} medium`);
|
|
19602
|
+
if (result3.counts.low > 0) counts.push(`${result3.counts.low} low`);
|
|
19603
|
+
parts.push(`(${counts.join(", ")})`);
|
|
19604
|
+
} else {
|
|
19605
|
+
parts.push("(clean)");
|
|
19606
|
+
}
|
|
19607
|
+
return parts.join(" ");
|
|
19608
|
+
}
|
|
19609
|
+
function toJson(result3) {
|
|
19610
|
+
return JSON.stringify(result3, null, 2);
|
|
17451
19611
|
}
|
|
17452
19612
|
|
|
17453
|
-
// src/avorelo/
|
|
17454
|
-
import {
|
|
17455
|
-
import { join as
|
|
17456
|
-
function
|
|
17457
|
-
const
|
|
17458
|
-
if (!existsSync61(configPath)) return "unknown";
|
|
19613
|
+
// src/avorelo/kernel/agent-artifact-guard/allowlist.ts
|
|
19614
|
+
import { readFileSync as readFileSync46 } from "node:fs";
|
|
19615
|
+
import { join as join62 } from "node:path";
|
|
19616
|
+
function loadAllowlist(projectRoot) {
|
|
19617
|
+
const filePath = join62(projectRoot, ".avorelo", "artifact-guard", "allowlist.json");
|
|
17459
19618
|
try {
|
|
17460
|
-
const
|
|
17461
|
-
|
|
17462
|
-
if (
|
|
17463
|
-
|
|
17464
|
-
return "local";
|
|
19619
|
+
const raw = readFileSync46(filePath, "utf-8");
|
|
19620
|
+
const parsed = JSON.parse(raw);
|
|
19621
|
+
if (!Array.isArray(parsed.entries)) return [];
|
|
19622
|
+
return parsed.entries.filter((e) => typeof e.reason === "string" && e.reason.length > 0);
|
|
17465
19623
|
} catch {
|
|
17466
|
-
return
|
|
19624
|
+
return [];
|
|
17467
19625
|
}
|
|
17468
19626
|
}
|
|
17469
|
-
function
|
|
17470
|
-
if (
|
|
17471
|
-
|
|
17472
|
-
|
|
17473
|
-
|
|
17474
|
-
|
|
19627
|
+
function isAllowlisted(finding, allowlist) {
|
|
19628
|
+
if (finding.severity === "critical") return false;
|
|
19629
|
+
for (const entry of allowlist) {
|
|
19630
|
+
if (!entry.reason) continue;
|
|
19631
|
+
const ruleMatch = !entry.ruleId || entry.ruleId === finding.ruleId;
|
|
19632
|
+
const fileMatch = !entry.file || finding.file === entry.file || finding.file.startsWith(entry.file);
|
|
19633
|
+
if (ruleMatch && fileMatch) return true;
|
|
19634
|
+
}
|
|
19635
|
+
return false;
|
|
17475
19636
|
}
|
|
17476
|
-
|
|
17477
|
-
|
|
17478
|
-
|
|
19637
|
+
|
|
19638
|
+
// src/avorelo/kernel/agent-artifact-guard/receipt-store.ts
|
|
19639
|
+
import { mkdirSync as mkdirSync39, writeFileSync as writeFileSync37, readdirSync as readdirSync19, readFileSync as readFileSync47, unlinkSync as unlinkSync4 } from "node:fs";
|
|
19640
|
+
import { join as join63 } from "node:path";
|
|
19641
|
+
var RECEIPT_DIR = "artifact-guard";
|
|
19642
|
+
var MAX_STORED_RECEIPTS = 50;
|
|
19643
|
+
function receiptDir(projectRoot) {
|
|
19644
|
+
return join63(projectRoot, ".avorelo", RECEIPT_DIR);
|
|
19645
|
+
}
|
|
19646
|
+
function receiptFilename(receipt) {
|
|
19647
|
+
const ts = receipt.scannedAt.replace(/[:.]/g, "-");
|
|
19648
|
+
return `receipt-${ts}.json`;
|
|
19649
|
+
}
|
|
19650
|
+
function storeReceipt(projectRoot, receipt) {
|
|
19651
|
+
const dir = receiptDir(projectRoot);
|
|
19652
|
+
mkdirSync39(dir, { recursive: true });
|
|
19653
|
+
const filename = receiptFilename(receipt);
|
|
19654
|
+
const filePath = join63(dir, filename);
|
|
19655
|
+
writeFileSync37(filePath, JSON.stringify(receipt, null, 2), "utf-8");
|
|
19656
|
+
pruneOldReceipts(dir);
|
|
19657
|
+
return filePath;
|
|
19658
|
+
}
|
|
19659
|
+
function loadLatestReceipt(projectRoot) {
|
|
19660
|
+
const dir = receiptDir(projectRoot);
|
|
19661
|
+
let files;
|
|
17479
19662
|
try {
|
|
17480
|
-
|
|
17481
|
-
|
|
17482
|
-
|
|
19663
|
+
files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json"));
|
|
19664
|
+
} catch {
|
|
19665
|
+
return null;
|
|
19666
|
+
}
|
|
19667
|
+
if (files.length === 0) return null;
|
|
19668
|
+
files.sort();
|
|
19669
|
+
const latest = files[files.length - 1];
|
|
19670
|
+
try {
|
|
19671
|
+
const raw = readFileSync47(join63(dir, latest), "utf-8");
|
|
19672
|
+
return JSON.parse(raw);
|
|
19673
|
+
} catch {
|
|
19674
|
+
return null;
|
|
19675
|
+
}
|
|
19676
|
+
}
|
|
19677
|
+
function pruneOldReceipts(dir) {
|
|
19678
|
+
try {
|
|
19679
|
+
const files = readdirSync19(dir).filter((f) => f.startsWith("receipt-") && f.endsWith(".json")).sort();
|
|
19680
|
+
while (files.length > MAX_STORED_RECEIPTS) {
|
|
19681
|
+
const oldest = files.shift();
|
|
19682
|
+
try {
|
|
19683
|
+
unlinkSync4(join63(dir, oldest));
|
|
19684
|
+
} catch {
|
|
19685
|
+
break;
|
|
19686
|
+
}
|
|
17483
19687
|
}
|
|
17484
|
-
return "unknown";
|
|
17485
19688
|
} catch {
|
|
17486
|
-
return "unknown";
|
|
17487
19689
|
}
|
|
17488
19690
|
}
|
|
17489
|
-
|
|
19691
|
+
|
|
19692
|
+
// src/avorelo/kernel/agent-artifact-guard/index.ts
|
|
19693
|
+
function scan(projectRoot, options) {
|
|
19694
|
+
const artifacts = discoverArtifacts(projectRoot);
|
|
19695
|
+
const allFindings = [];
|
|
19696
|
+
for (const artifact of artifacts) {
|
|
19697
|
+
const fullPath = join64(projectRoot, artifact.path);
|
|
19698
|
+
let content;
|
|
19699
|
+
try {
|
|
19700
|
+
content = readFileSync48(fullPath, "utf-8");
|
|
19701
|
+
} catch {
|
|
19702
|
+
continue;
|
|
19703
|
+
}
|
|
19704
|
+
const findings = scanContent2(content, artifact.path, artifact.kind);
|
|
19705
|
+
allFindings.push(...findings);
|
|
19706
|
+
}
|
|
19707
|
+
let effectiveFindings = allFindings;
|
|
19708
|
+
let allowlistedCount = 0;
|
|
19709
|
+
if (options?.applyAllowlist !== false) {
|
|
19710
|
+
const allowlist = loadAllowlist(projectRoot);
|
|
19711
|
+
if (allowlist.length > 0) {
|
|
19712
|
+
effectiveFindings = [];
|
|
19713
|
+
for (const f of allFindings) {
|
|
19714
|
+
if (isAllowlisted(f, allowlist)) {
|
|
19715
|
+
allowlistedCount++;
|
|
19716
|
+
} else {
|
|
19717
|
+
effectiveFindings.push(f);
|
|
19718
|
+
}
|
|
19719
|
+
}
|
|
19720
|
+
}
|
|
19721
|
+
}
|
|
19722
|
+
const counts = countBySeverity(effectiveFindings);
|
|
19723
|
+
const riskScore = computeRiskScore(counts);
|
|
19724
|
+
const policyOutcome = evaluatePolicy2(counts, riskScore);
|
|
19725
|
+
let nextBestAction = computeNextBestAction(policyOutcome, counts);
|
|
19726
|
+
if (allowlistedCount > 0) {
|
|
19727
|
+
nextBestAction += ` (${allowlistedCount} finding(s) allowlisted)`;
|
|
19728
|
+
}
|
|
17490
19729
|
return {
|
|
17491
|
-
|
|
17492
|
-
|
|
17493
|
-
|
|
19730
|
+
scannedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
19731
|
+
projectRoot,
|
|
19732
|
+
filesScanned: artifacts.length,
|
|
19733
|
+
artifacts,
|
|
19734
|
+
findings: effectiveFindings,
|
|
19735
|
+
counts,
|
|
19736
|
+
riskScore,
|
|
19737
|
+
policyOutcome,
|
|
19738
|
+
nextBestAction
|
|
17494
19739
|
};
|
|
17495
19740
|
}
|
|
17496
19741
|
|
|
17497
|
-
// src/avorelo/
|
|
17498
|
-
|
|
17499
|
-
|
|
17500
|
-
|
|
17501
|
-
|
|
17502
|
-
|
|
17503
|
-
|
|
19742
|
+
// src/avorelo/kernel/agent-artifact-guard/guard-handler.ts
|
|
19743
|
+
function checkActivation(dir) {
|
|
19744
|
+
const configPath = join65(dir, ".avorelo", "model-routing", "config.json");
|
|
19745
|
+
try {
|
|
19746
|
+
if (existsSync63(configPath)) {
|
|
19747
|
+
const raw = JSON.parse(readFileSync49(configPath, "utf-8"));
|
|
19748
|
+
return {
|
|
19749
|
+
activated: true,
|
|
19750
|
+
routingMode: raw.mode ?? "seamless",
|
|
19751
|
+
localFirst: raw.localFirst ?? true,
|
|
19752
|
+
registryLoaded: raw.registryLoaded ?? false,
|
|
19753
|
+
cloudRoutingEnabled: raw.cloudRoutingEnabled ?? false,
|
|
19754
|
+
defaultPolicy: raw.defaultPolicy ?? "safety_proof_privacy_before_cost"
|
|
19755
|
+
};
|
|
19756
|
+
}
|
|
19757
|
+
} catch {
|
|
19758
|
+
}
|
|
17504
19759
|
return {
|
|
17505
|
-
|
|
17506
|
-
|
|
17507
|
-
|
|
17508
|
-
|
|
17509
|
-
|
|
17510
|
-
|
|
17511
|
-
|
|
17512
|
-
|
|
17513
|
-
|
|
17514
|
-
|
|
17515
|
-
|
|
17516
|
-
|
|
17517
|
-
|
|
17518
|
-
|
|
19760
|
+
activated: false,
|
|
19761
|
+
routingMode: "seamless",
|
|
19762
|
+
localFirst: true,
|
|
19763
|
+
registryLoaded: false,
|
|
19764
|
+
cloudRoutingEnabled: false,
|
|
19765
|
+
defaultPolicy: "safety_proof_privacy_before_cost"
|
|
19766
|
+
};
|
|
19767
|
+
}
|
|
19768
|
+
function getRoutingStatus(state) {
|
|
19769
|
+
return `Routing: ${state.activated ? "active" : "inactive"} (${state.routingMode})`;
|
|
19770
|
+
}
|
|
19771
|
+
function activationWarning(outcome, nextAction) {
|
|
19772
|
+
switch (outcome) {
|
|
19773
|
+
case "blocked":
|
|
19774
|
+
return `Activation warning: ${nextAction} Activation cannot claim safe readiness.`;
|
|
19775
|
+
case "requires_approval":
|
|
19776
|
+
return `Review needed: ${nextAction}`;
|
|
19777
|
+
case "warn":
|
|
19778
|
+
return `Note: ${nextAction}`;
|
|
19779
|
+
case "pass":
|
|
19780
|
+
return null;
|
|
19781
|
+
}
|
|
19782
|
+
}
|
|
19783
|
+
function handleActivate(dir, options) {
|
|
19784
|
+
const state = checkActivation(dir);
|
|
19785
|
+
const guardResult = scan(dir);
|
|
19786
|
+
const receipt = createReceipt(guardResult);
|
|
19787
|
+
const receiptPath = storeReceipt(dir, receipt);
|
|
19788
|
+
const guardLine = renderSummary(guardResult);
|
|
19789
|
+
const warning = activationWarning(guardResult.policyOutcome, guardResult.nextBestAction);
|
|
19790
|
+
const lines = [getRoutingStatus(state), guardLine];
|
|
19791
|
+
if (warning) lines.push(warning);
|
|
19792
|
+
lines.push(`Receipt: ${receiptPath}`);
|
|
19793
|
+
if (options.json) {
|
|
19794
|
+
return {
|
|
19795
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
19796
|
+
stdout: lines.join("\n"),
|
|
19797
|
+
json: {
|
|
19798
|
+
activated: state.activated,
|
|
19799
|
+
artifactGuard: {
|
|
19800
|
+
policyOutcome: guardResult.policyOutcome,
|
|
19801
|
+
riskScore: guardResult.riskScore,
|
|
19802
|
+
findingsCount: guardResult.findings.length,
|
|
19803
|
+
counts: guardResult.counts,
|
|
19804
|
+
nextBestAction: guardResult.nextBestAction
|
|
19805
|
+
},
|
|
19806
|
+
receiptPath
|
|
17519
19807
|
}
|
|
17520
|
-
}
|
|
17521
|
-
|
|
19808
|
+
};
|
|
19809
|
+
}
|
|
19810
|
+
return {
|
|
19811
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
19812
|
+
stdout: lines.join("\n")
|
|
17522
19813
|
};
|
|
17523
19814
|
}
|
|
17524
|
-
function
|
|
17525
|
-
const state =
|
|
17526
|
-
const
|
|
17527
|
-
|
|
17528
|
-
|
|
17529
|
-
|
|
17530
|
-
|
|
17531
|
-
|
|
17532
|
-
|
|
17533
|
-
|
|
17534
|
-
|
|
17535
|
-
|
|
17536
|
-
|
|
17537
|
-
|
|
17538
|
-
|
|
17539
|
-
|
|
19815
|
+
function handleStatus(dir, options) {
|
|
19816
|
+
const state = checkActivation(dir);
|
|
19817
|
+
const status = getRoutingStatus(state);
|
|
19818
|
+
const guardResult = scan(dir);
|
|
19819
|
+
if (options.json) {
|
|
19820
|
+
return {
|
|
19821
|
+
exitCode: 0,
|
|
19822
|
+
stdout: `${status}
|
|
19823
|
+
${renderSummary(guardResult)}`,
|
|
19824
|
+
json: {
|
|
19825
|
+
routing: {
|
|
19826
|
+
active: state.activated,
|
|
19827
|
+
mode: state.routingMode,
|
|
19828
|
+
localFirst: state.localFirst
|
|
19829
|
+
},
|
|
19830
|
+
artifactGuard: {
|
|
19831
|
+
policyOutcome: guardResult.policyOutcome,
|
|
19832
|
+
riskScore: guardResult.riskScore,
|
|
19833
|
+
findingsCount: guardResult.findings.length
|
|
19834
|
+
}
|
|
19835
|
+
}
|
|
19836
|
+
};
|
|
19837
|
+
}
|
|
19838
|
+
return { exitCode: 0, stdout: `${status}
|
|
19839
|
+
${renderSummary(guardResult)}` };
|
|
17540
19840
|
}
|
|
17541
|
-
|
|
17542
|
-
const state =
|
|
17543
|
-
const
|
|
17544
|
-
const
|
|
17545
|
-
|
|
17546
|
-
|
|
19841
|
+
function handleDoctor(dir, options) {
|
|
19842
|
+
const state = checkActivation(dir);
|
|
19843
|
+
const guardResult = scan(dir);
|
|
19844
|
+
const lines = [
|
|
19845
|
+
getRoutingStatus(state),
|
|
19846
|
+
`Registry: ${state.registryLoaded ? "loaded" : "not loaded"}`,
|
|
19847
|
+
`Cloud: ${state.cloudRoutingEnabled ? "enabled" : "disabled"}`,
|
|
19848
|
+
`Policy: ${state.defaultPolicy}`,
|
|
19849
|
+
renderSummary(guardResult)
|
|
19850
|
+
];
|
|
19851
|
+
if (options.json) {
|
|
19852
|
+
return {
|
|
19853
|
+
exitCode: 0,
|
|
19854
|
+
stdout: lines.join("\n"),
|
|
19855
|
+
json: {
|
|
19856
|
+
...state,
|
|
19857
|
+
artifactGuard: {
|
|
19858
|
+
policyOutcome: guardResult.policyOutcome,
|
|
19859
|
+
riskScore: guardResult.riskScore,
|
|
19860
|
+
findingsCount: guardResult.findings.length,
|
|
19861
|
+
counts: guardResult.counts
|
|
19862
|
+
}
|
|
19863
|
+
}
|
|
19864
|
+
};
|
|
17547
19865
|
}
|
|
17548
|
-
|
|
17549
|
-
|
|
19866
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
19867
|
+
}
|
|
19868
|
+
function handleReadiness(dir, options) {
|
|
19869
|
+
const state = checkActivation(dir);
|
|
19870
|
+
const guardResult = scan(dir);
|
|
19871
|
+
const receipt = createReceipt(guardResult);
|
|
19872
|
+
storeReceipt(dir, receipt);
|
|
19873
|
+
const latestReceipt = loadLatestReceipt(dir);
|
|
19874
|
+
const artifactKinds = [...new Set(guardResult.artifacts.map((a) => a.kind))];
|
|
19875
|
+
const highestSeverity = guardResult.counts.critical > 0 ? "critical" : guardResult.counts.high > 0 ? "high" : guardResult.counts.medium > 0 ? "medium" : guardResult.counts.low > 0 ? "low" : "none";
|
|
19876
|
+
const blockingCount = guardResult.counts.critical + guardResult.counts.high;
|
|
19877
|
+
const lines = [
|
|
19878
|
+
`Readiness Check`,
|
|
19879
|
+
``,
|
|
19880
|
+
`Routing: ${state.activated ? "active" : "inactive"}`,
|
|
19881
|
+
`Artifact Guard: available`,
|
|
19882
|
+
` Files scanned: ${guardResult.filesScanned}`,
|
|
19883
|
+
` Artifact kinds: ${artifactKinds.length > 0 ? artifactKinds.join(", ") : "none detected"}`,
|
|
19884
|
+
` Highest severity: ${highestSeverity}`,
|
|
19885
|
+
` Policy outcome: ${guardResult.policyOutcome.toUpperCase()}`,
|
|
19886
|
+
` Blocking findings: ${blockingCount}`,
|
|
19887
|
+
` Latest receipt: ${latestReceipt ? "available" : "none"}`,
|
|
19888
|
+
` Next: ${guardResult.nextBestAction}`
|
|
19889
|
+
];
|
|
19890
|
+
if (guardResult.policyOutcome === "blocked") {
|
|
19891
|
+
lines.push(``);
|
|
19892
|
+
lines.push(`Readiness: NOT READY \u2014 critical findings must be resolved`);
|
|
19893
|
+
} else if (guardResult.policyOutcome === "requires_approval") {
|
|
19894
|
+
lines.push(``);
|
|
19895
|
+
lines.push(`Readiness: REVIEW NEEDED \u2014 high-severity findings require approval`);
|
|
19896
|
+
} else if (guardResult.policyOutcome === "warn") {
|
|
19897
|
+
lines.push(``);
|
|
19898
|
+
lines.push(`Readiness: READY (with warnings)`);
|
|
19899
|
+
} else {
|
|
19900
|
+
lines.push(``);
|
|
19901
|
+
lines.push(`Readiness: READY`);
|
|
17550
19902
|
}
|
|
17551
|
-
|
|
17552
|
-
|
|
17553
|
-
|
|
17554
|
-
|
|
17555
|
-
|
|
17556
|
-
|
|
17557
|
-
|
|
19903
|
+
if (options.json) {
|
|
19904
|
+
return {
|
|
19905
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
19906
|
+
stdout: lines.join("\n"),
|
|
19907
|
+
json: {
|
|
19908
|
+
routing: { active: state.activated },
|
|
19909
|
+
artifactGuard: {
|
|
19910
|
+
available: true,
|
|
19911
|
+
filesScanned: guardResult.filesScanned,
|
|
19912
|
+
artifactKinds,
|
|
19913
|
+
highestSeverity,
|
|
19914
|
+
policyOutcome: guardResult.policyOutcome,
|
|
19915
|
+
riskScore: guardResult.riskScore,
|
|
19916
|
+
blockingFindings: blockingCount,
|
|
19917
|
+
counts: guardResult.counts,
|
|
19918
|
+
latestReceipt: latestReceipt ? true : false,
|
|
19919
|
+
nextBestAction: guardResult.nextBestAction
|
|
19920
|
+
},
|
|
19921
|
+
readiness: guardResult.policyOutcome === "blocked" ? "not_ready" : guardResult.policyOutcome === "requires_approval" ? "review_needed" : "ready"
|
|
19922
|
+
}
|
|
19923
|
+
};
|
|
17558
19924
|
}
|
|
17559
|
-
|
|
17560
|
-
|
|
17561
|
-
|
|
19925
|
+
return {
|
|
19926
|
+
exitCode: guardResult.policyOutcome === "blocked" ? 1 : 0,
|
|
19927
|
+
stdout: lines.join("\n")
|
|
19928
|
+
};
|
|
19929
|
+
}
|
|
19930
|
+
function handleReceipt(dir, options) {
|
|
19931
|
+
const sub = options.receiptSubcommand ?? "latest";
|
|
19932
|
+
if (sub === "latest") {
|
|
19933
|
+
const receipt = loadLatestReceipt(dir);
|
|
19934
|
+
if (!receipt) {
|
|
19935
|
+
return { exitCode: 0, stdout: "No artifact guard receipt found. Run `avorelo guard scan` first." };
|
|
19936
|
+
}
|
|
19937
|
+
if (options.json) {
|
|
19938
|
+
return {
|
|
19939
|
+
exitCode: 0,
|
|
19940
|
+
stdout: JSON.stringify(receipt, null, 2),
|
|
19941
|
+
json: receipt
|
|
19942
|
+
};
|
|
19943
|
+
}
|
|
19944
|
+
const lines = [
|
|
19945
|
+
`Latest Artifact Guard Receipt`,
|
|
19946
|
+
` Type: ${receipt.receiptType}`,
|
|
19947
|
+
` Scanned: ${receipt.scannedAt}`,
|
|
19948
|
+
` Files: ${receipt.filesScanned}`,
|
|
19949
|
+
` Findings: ${receipt.findingSummary.total}`,
|
|
19950
|
+
` Risk score: ${receipt.riskScore}/10`,
|
|
19951
|
+
` Outcome: ${receipt.policyOutcome.toUpperCase()}`,
|
|
19952
|
+
` Next: ${receipt.nextBestAction}`,
|
|
19953
|
+
` Raw content stored: ${receipt.contentStored}`
|
|
19954
|
+
];
|
|
19955
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
17562
19956
|
}
|
|
17563
|
-
|
|
17564
|
-
|
|
17565
|
-
|
|
17566
|
-
|
|
17567
|
-
|
|
17568
|
-
|
|
17569
|
-
|
|
17570
|
-
|
|
17571
|
-
});
|
|
17572
|
-
clearTimeout(timeout);
|
|
17573
|
-
if (response.ok) {
|
|
17574
|
-
markTelemetryBatchSent(dir, queueIds, new Date(now).toISOString());
|
|
17575
|
-
state.lastUploadAt = new Date(now).toISOString();
|
|
17576
|
-
state.lastBatchId = batch.batchId;
|
|
17577
|
-
state.lastBatchFailureAt = null;
|
|
17578
|
-
state.lastBatchFailureCode = null;
|
|
17579
|
-
state.acceptedBatches += 1;
|
|
17580
|
-
writeTelemetryState(dir, state);
|
|
17581
|
-
appendTransportEvent(dir, "telemetry_batch_sent");
|
|
17582
|
-
return { attempted: true, sent: true, reason: "accepted", queuedCount };
|
|
19957
|
+
return { exitCode: 2, stdout: "Usage: avorelo receipt latest" };
|
|
19958
|
+
}
|
|
19959
|
+
function handleGuard(dir, options) {
|
|
19960
|
+
const sub = options.guardSubcommand ?? "scan";
|
|
19961
|
+
if (sub === "explain") {
|
|
19962
|
+
const ruleId = options.guardArgs?.[0];
|
|
19963
|
+
if (!ruleId) {
|
|
19964
|
+
return { exitCode: 2, stdout: "Usage: avorelo guard explain <ruleId>" };
|
|
17583
19965
|
}
|
|
17584
|
-
|
|
17585
|
-
|
|
17586
|
-
|
|
17587
|
-
state.lastBatchFailureAt = new Date(now).toISOString();
|
|
17588
|
-
state.lastBatchFailureCode = `http_${response.status}`;
|
|
17589
|
-
writeTelemetryState(dir, state);
|
|
17590
|
-
appendTransportEvent(dir, "telemetry_batch_failed", `http_${response.status}`);
|
|
17591
|
-
return { attempted: true, sent: false, reason: `client_error_${response.status}`, queuedCount };
|
|
19966
|
+
const rule = getRule(ruleId);
|
|
19967
|
+
if (!rule) {
|
|
19968
|
+
return { exitCode: 2, stdout: `Unknown rule: ${ruleId}` };
|
|
17592
19969
|
}
|
|
17593
|
-
|
|
17594
|
-
|
|
17595
|
-
|
|
17596
|
-
|
|
17597
|
-
|
|
17598
|
-
|
|
17599
|
-
|
|
17600
|
-
|
|
17601
|
-
|
|
17602
|
-
|
|
17603
|
-
|
|
17604
|
-
|
|
17605
|
-
|
|
17606
|
-
|
|
17607
|
-
|
|
19970
|
+
const lines = [
|
|
19971
|
+
`Rule: ${rule.id}`,
|
|
19972
|
+
`Title: ${rule.title}`,
|
|
19973
|
+
`Severity: ${rule.severity}`,
|
|
19974
|
+
`Category: ${rule.category}`,
|
|
19975
|
+
`Description: ${rule.description}`,
|
|
19976
|
+
`Recommended action: ${rule.recommendedAction}`,
|
|
19977
|
+
`Applies to: ${rule.artifactKinds === "all" ? "all artifact kinds" : rule.artifactKinds.join(", ")}`
|
|
19978
|
+
];
|
|
19979
|
+
return { exitCode: 0, stdout: lines.join("\n") };
|
|
19980
|
+
}
|
|
19981
|
+
const result3 = scan(dir);
|
|
19982
|
+
const receipt = createReceipt(result3);
|
|
19983
|
+
const receiptPath = storeReceipt(dir, receipt);
|
|
19984
|
+
if (options.json) {
|
|
19985
|
+
return {
|
|
19986
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
19987
|
+
stdout: toJson(result3),
|
|
19988
|
+
json: result3
|
|
19989
|
+
};
|
|
19990
|
+
}
|
|
19991
|
+
if (options.ci) {
|
|
19992
|
+
return {
|
|
19993
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
19994
|
+
stdout: toJson(result3)
|
|
19995
|
+
};
|
|
19996
|
+
}
|
|
19997
|
+
const report = renderReport(result3);
|
|
19998
|
+
return {
|
|
19999
|
+
exitCode: result3.policyOutcome === "blocked" ? 1 : 0,
|
|
20000
|
+
stdout: `${report}
|
|
20001
|
+
Receipt: ${receiptPath}`
|
|
20002
|
+
};
|
|
20003
|
+
}
|
|
20004
|
+
function handleCli(options, dir) {
|
|
20005
|
+
switch (options.command) {
|
|
20006
|
+
case "activate":
|
|
20007
|
+
return handleActivate(dir, options);
|
|
20008
|
+
case "status":
|
|
20009
|
+
return handleStatus(dir, options);
|
|
20010
|
+
case "doctor":
|
|
20011
|
+
return handleDoctor(dir, options);
|
|
20012
|
+
case "guard":
|
|
20013
|
+
return handleGuard(dir, options);
|
|
20014
|
+
case "readiness":
|
|
20015
|
+
return handleReadiness(dir, options);
|
|
20016
|
+
case "receipt":
|
|
20017
|
+
return handleReceipt(dir, options);
|
|
20018
|
+
case "run":
|
|
20019
|
+
return { exitCode: 0, stdout: "run not implemented in guard handler" };
|
|
17608
20020
|
}
|
|
17609
20021
|
}
|
|
17610
20022
|
|
|
@@ -17695,6 +20107,10 @@ function help() {
|
|
|
17695
20107
|
"",
|
|
17696
20108
|
" context check [--target <dir>] [--json] [--strict] [--ci] Agent context integrity check",
|
|
17697
20109
|
"",
|
|
20110
|
+
" guard scan [--target <dir>] [--json] [--ci] Scan agent artifacts for risks",
|
|
20111
|
+
" guard explain <rule-id> Explain a detection rule",
|
|
20112
|
+
" receipt latest [--target <dir>] [--json] Show latest artifact guard receipt",
|
|
20113
|
+
"",
|
|
17698
20114
|
" dogfood-learning status [--target <dir>] [--json] Show learning status",
|
|
17699
20115
|
" dogfood-learning preview [--target <dir>] [--json] Preview sanitized learning payload",
|
|
17700
20116
|
" dogfood-learning send [--target <dir>] [--dry-run] [--json] Send or queue learning signal",
|
|
@@ -17738,8 +20154,8 @@ async function cmdActivate(args) {
|
|
|
17738
20154
|
const r2 = activate(target, { approve: true });
|
|
17739
20155
|
persistReceipt(target, r2.receipt);
|
|
17740
20156
|
const state2 = runFullActivation(target);
|
|
17741
|
-
state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath:
|
|
17742
|
-
if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path:
|
|
20157
|
+
state2.setupSteps.push({ id: "hooks_installed", label: "Claude Code hooks installed", status: r2.ok ? "passed" : "blocked", evidencePath: join66(target, ".claude", "settings.json") });
|
|
20158
|
+
if (r2.ok) state2.receipts.push({ id: r2.receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${r2.receipt.receiptId}.json`), type: "activation_with_hooks" });
|
|
17743
20159
|
persistActivationV2(target, state2);
|
|
17744
20160
|
process.stdout.write(JSON.stringify({ ok: r2.ok, mode: "activate-with-hooks", target, installed: r2.validate.wellFormed, receipt: r2.receipt.receiptId }, null, 2) + "\n");
|
|
17745
20161
|
return r2.ok ? 0 : 1;
|
|
@@ -17762,7 +20178,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17762
20178
|
process.stderr.write("Continuing with activation despite warnings...\n\n");
|
|
17763
20179
|
}
|
|
17764
20180
|
const state = runFullActivation(target);
|
|
17765
|
-
const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [
|
|
20181
|
+
const contract = createWorkContract({ contractId: "canonical-activate", objective: "full local-first activation", allowedPaths: [join66(target, ".avorelo")], planTier: "Free" });
|
|
17766
20182
|
const ledger = new StateLedger();
|
|
17767
20183
|
const receipt = writeReceipt(ledger, {
|
|
17768
20184
|
contractId: contract.contractId,
|
|
@@ -17777,7 +20193,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17777
20193
|
redactionClasses: [],
|
|
17778
20194
|
receiptId: "rcpt_canonical_activation"
|
|
17779
20195
|
});
|
|
17780
|
-
state.receipts.push({ id: receipt.receiptId, path:
|
|
20196
|
+
state.receipts.push({ id: receipt.receiptId, path: join66(target, ".avorelo", "receipts", `${receipt.receiptId}.json`), type: "canonical_activation" });
|
|
17781
20197
|
persistActivationV2(target, state);
|
|
17782
20198
|
persistReceipt(target, receipt);
|
|
17783
20199
|
const detection = runFullDetection(target);
|
|
@@ -17790,7 +20206,7 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17790
20206
|
` Mode: ${state.activationMode}`,
|
|
17791
20207
|
` Scope: ${scope}`,
|
|
17792
20208
|
` Status: ${state.activationStatus}`,
|
|
17793
|
-
` State: ${
|
|
20209
|
+
` State: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`
|
|
17794
20210
|
];
|
|
17795
20211
|
if (fv.found.length > 0) {
|
|
17796
20212
|
lines.push("", " Found:");
|
|
@@ -17805,17 +20221,22 @@ Activation taxonomy: ${preflight.taxonomy}
|
|
|
17805
20221
|
lines.push("", " Fixed:");
|
|
17806
20222
|
for (const f of fv.fixed) lines.push(` ${f}`);
|
|
17807
20223
|
}
|
|
17808
|
-
|
|
20224
|
+
const blockedItems = fv.needsAttention.filter((n) => n.startsWith("Blocked:"));
|
|
20225
|
+
if (blockedItems.length > 0) {
|
|
17809
20226
|
lines.push("", " Needs attention:");
|
|
17810
|
-
for (const n of
|
|
20227
|
+
for (const n of blockedItems) lines.push(` ${n}`);
|
|
17811
20228
|
}
|
|
17812
20229
|
lines.push(
|
|
17813
20230
|
"",
|
|
17814
20231
|
` Run entry: ${state.runEntry.installed ? "installed" : "not installed"}`,
|
|
17815
|
-
`
|
|
17816
|
-
`
|
|
17817
|
-
` Production: not ready`
|
|
20232
|
+
` Agent Guard: available`,
|
|
20233
|
+
` Receipts: working`
|
|
17818
20234
|
);
|
|
20235
|
+
const optionalItems = fv.needsAttention.filter((n) => !n.startsWith("Blocked:"));
|
|
20236
|
+
if (optionalItems.length > 0) {
|
|
20237
|
+
lines.push("", " Optional (not required for local activation):");
|
|
20238
|
+
for (const n of optionalItems) lines.push(` ${n}`);
|
|
20239
|
+
}
|
|
17819
20240
|
let linkResult = "no claim provided";
|
|
17820
20241
|
if (claimToken) {
|
|
17821
20242
|
const baseUrl = process.env.APP_BASE_URL ?? "https://app.avorelo.com";
|
|
@@ -17906,6 +20327,14 @@ async function cmdDoctor(args) {
|
|
|
17906
20327
|
if (ctxResult.findings.length > 0) {
|
|
17907
20328
|
for (const f of ctxResult.findings.slice(0, 3)) lines.push(` ${f.severity}: ${f.message}`);
|
|
17908
20329
|
}
|
|
20330
|
+
const ccLatest = loadLatestRuntimeSession(target);
|
|
20331
|
+
if (ccLatest?.contextControl) {
|
|
20332
|
+
const cc = ccLatest.contextControl;
|
|
20333
|
+
lines.push("");
|
|
20334
|
+
lines.push(` Context Control: ${cc.status} (reduction=${cc.reductionPercent}%, evidence=${cc.evidenceRefCount}, blocked=${cc.blockedItemsCount})`);
|
|
20335
|
+
if (cc.blockedItemsCount > 0) lines.push(` WARNING: ${cc.blockedItemsCount} context item(s) blocked by policy`);
|
|
20336
|
+
if (cc.proofImpact === "degraded") lines.push(` WARNING: proof impact is degraded \u2014 evidence refs may be incomplete`);
|
|
20337
|
+
}
|
|
17909
20338
|
try {
|
|
17910
20339
|
const { getModelRegistry: getModelRegistry2, getLocalModels: getLocalModels2, getAllProviders: getAllProviders2, isProviderAvailable: isProviderAvailable2 } = (init_model_routing2(), __toCommonJS(model_routing_exports));
|
|
17911
20340
|
const models = getModelRegistry2();
|
|
@@ -18043,6 +20472,7 @@ function cmdStatus(args) {
|
|
|
18043
20472
|
` detected: ${[contract.gitDetected ? "git" : "no-git", contract.packageDetected ? "package" : "no-package"].join(", ")}`,
|
|
18044
20473
|
` workspace: local-only \xB7 cloud not claimed`,
|
|
18045
20474
|
` last run: ${latest ? `${latest.status} (${latest.runtimeSessionId})` : "none yet"}`,
|
|
20475
|
+
` ctx control: ${latest?.contextControl ? `${latest.contextControl.status} \xB7 reduction=${latest.contextControl.reductionPercent}%` : "not yet run"}`,
|
|
18046
20476
|
` routing: seamless model routing active (local-first, no credentials required)`,
|
|
18047
20477
|
` control ctr: available \u2014 avorelo control-center --target .`,
|
|
18048
20478
|
` next: ${contract.firstRunRecommended.command} \u2014 ${contract.firstRunRecommended.reason}`,
|
|
@@ -18071,7 +20501,7 @@ function cmdStatus(args) {
|
|
|
18071
20501
|
` activation: ${state.activationStatus}`,
|
|
18072
20502
|
` mode: ${state.activationMode}`,
|
|
18073
20503
|
` contract: ${state.contract}`,
|
|
18074
|
-
` state: ${
|
|
20504
|
+
` state: ${join66(target, ACTIVATION_STATE_DIR, ACTIVATION_STATE_FILE)}`,
|
|
18075
20505
|
` hooks: ${v.installed ? v.wellFormed ? "installed (6/6)" : `partial` : "not installed"}`
|
|
18076
20506
|
];
|
|
18077
20507
|
if (isV2) {
|
|
@@ -18144,7 +20574,7 @@ function cmdVerify(args) {
|
|
|
18144
20574
|
}
|
|
18145
20575
|
const input = loadProofInput(target);
|
|
18146
20576
|
if (input) {
|
|
18147
|
-
const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [
|
|
20577
|
+
const contract = createWorkContract({ contractId: "verify", objective: input.objective ?? "verify real-workflow proof", allowedPaths: [join66(target, "src")], planTier: "Free" });
|
|
18148
20578
|
const r2 = evaluateProof({ contract, artifacts: input.artifacts, readbacks: input.readbacks, dir: target, sampleSize: input.sampleSize, receiptId: "rcpt_verify" });
|
|
18149
20579
|
process.stdout.write(JSON.stringify({ scope: "full", activationState: { valid: true }, proof: { decision: r2.decision, confidence: r2.confidence } }, null, 2) + "\n");
|
|
18150
20580
|
return r2.decision === "STOP_DONE" ? 0 : 1;
|
|
@@ -18153,13 +20583,13 @@ function cmdVerify(args) {
|
|
|
18153
20583
|
return 0;
|
|
18154
20584
|
}
|
|
18155
20585
|
function cmdSite(args) {
|
|
18156
|
-
const outDir = arg(args, "--out",
|
|
20586
|
+
const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
|
|
18157
20587
|
const r2 = buildSite(outDir);
|
|
18158
20588
|
process.stdout.write(JSON.stringify({ ok: r2.ok, outDir: r2.outDir, indexPath: r2.indexPath, pages: r2.pages }, null, 2) + "\n");
|
|
18159
20589
|
return r2.ok ? 0 : 1;
|
|
18160
20590
|
}
|
|
18161
20591
|
function cmdServe(args) {
|
|
18162
|
-
const outDir = arg(args, "--out",
|
|
20592
|
+
const outDir = arg(args, "--out", join66(process.cwd(), ".avorelo", "site"));
|
|
18163
20593
|
const port = Number(arg(args, "--port", "0"));
|
|
18164
20594
|
buildSite(outDir);
|
|
18165
20595
|
serve(outDir, { port: Number.isFinite(port) ? port : 0 }).then((h) => {
|
|
@@ -18191,7 +20621,7 @@ function cmdServe(args) {
|
|
|
18191
20621
|
}
|
|
18192
20622
|
function readStdinJson() {
|
|
18193
20623
|
try {
|
|
18194
|
-
const raw =
|
|
20624
|
+
const raw = readFileSync50(0, "utf8");
|
|
18195
20625
|
return raw.trim() ? JSON.parse(raw) : {};
|
|
18196
20626
|
} catch {
|
|
18197
20627
|
return {};
|
|
@@ -18215,9 +20645,9 @@ function cmdLifecycleHook(args) {
|
|
|
18215
20645
|
const sourceLabel = String(ccEvent.tool_name ?? "").toLowerCase().includes("mcp") ? "tool_output" : "tool_output";
|
|
18216
20646
|
const r3 = scanContent({ content: toolOutput, sourceKind: sourceLabel });
|
|
18217
20647
|
try {
|
|
18218
|
-
const dir =
|
|
18219
|
-
|
|
18220
|
-
appendFileSync11(
|
|
20648
|
+
const dir = join66(cwd, ".avorelo", "secret-boundary");
|
|
20649
|
+
mkdirSync40(dir, { recursive: true });
|
|
20650
|
+
appendFileSync11(join66(dir, "receipts.jsonl"), JSON.stringify(redact(r3.receipt).value) + "\n");
|
|
18221
20651
|
} catch {
|
|
18222
20652
|
}
|
|
18223
20653
|
process.stdout.write(JSON.stringify({
|
|
@@ -18234,13 +20664,13 @@ function cmdLifecycleHook(args) {
|
|
|
18234
20664
|
}
|
|
18235
20665
|
const looksLikeToolRequest = payloadRaw && ccEvent.tool !== void 0 && ccEvent.workingDir !== void 0;
|
|
18236
20666
|
const req = looksLikeToolRequest ? ccEvent : mapClaudeCodeEvent(ccEvent, cwd);
|
|
18237
|
-
const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [
|
|
20667
|
+
const contract = createWorkContract({ contractId: "hook", objective: "lifecycle hook", allowedPaths: [join66(cwd, "src")], planTier: "Free" });
|
|
18238
20668
|
const r2 = handleLifecycleHook(event, req, { contract });
|
|
18239
20669
|
try {
|
|
18240
|
-
const dir =
|
|
18241
|
-
|
|
20670
|
+
const dir = join66(cwd, ".avorelo", "events");
|
|
20671
|
+
mkdirSync40(dir, { recursive: true });
|
|
18242
20672
|
const entry = redact({ ts: Date.now(), event: r2.event, tool: req.tool, verdict: r2.verdict, reasonCodes: r2.reasonCodes, redactionClasses: r2.redactionClasses, latencyMs: r2.latencyMs }).value;
|
|
18243
|
-
appendFileSync11(
|
|
20673
|
+
appendFileSync11(join66(dir, "hook-fires.jsonl"), JSON.stringify(entry) + "\n");
|
|
18244
20674
|
} catch {
|
|
18245
20675
|
}
|
|
18246
20676
|
if (r2.exitCode === 2) {
|
|
@@ -18647,13 +21077,13 @@ function cmdTokenCost(args) {
|
|
|
18647
21077
|
}
|
|
18648
21078
|
if (sub === "import" || sub === "validate") {
|
|
18649
21079
|
const file = arg(args, "--file");
|
|
18650
|
-
if (!file || !
|
|
21080
|
+
if (!file || !existsSync64(file)) {
|
|
18651
21081
|
process.stderr.write("Usage: avorelo token-cost " + sub + " --file <path> [--json]\n");
|
|
18652
21082
|
return 2;
|
|
18653
21083
|
}
|
|
18654
21084
|
let parsed;
|
|
18655
21085
|
try {
|
|
18656
|
-
parsed = JSON.parse(
|
|
21086
|
+
parsed = JSON.parse(readFileSync50(file, "utf8"));
|
|
18657
21087
|
} catch {
|
|
18658
21088
|
process.stderr.write("Invalid JSON file.\n");
|
|
18659
21089
|
return 1;
|
|
@@ -18794,9 +21224,9 @@ function cmdContextCheck(args) {
|
|
|
18794
21224
|
const ci = args.includes("--ci");
|
|
18795
21225
|
const wcPath = arg(args, "--work-contract");
|
|
18796
21226
|
let workContract;
|
|
18797
|
-
if (wcPath &&
|
|
21227
|
+
if (wcPath && existsSync64(wcPath)) {
|
|
18798
21228
|
try {
|
|
18799
|
-
workContract = JSON.parse(
|
|
21229
|
+
workContract = JSON.parse(readFileSync50(wcPath, "utf8"));
|
|
18800
21230
|
} catch {
|
|
18801
21231
|
}
|
|
18802
21232
|
}
|
|
@@ -18824,8 +21254,64 @@ function cmdContext(args) {
|
|
|
18824
21254
|
const asJson = args.includes("--json");
|
|
18825
21255
|
const task = args.slice(1).find((a) => !a.startsWith("--") && a !== target);
|
|
18826
21256
|
if (sub === "check") return cmdContextCheck(args.slice(1));
|
|
21257
|
+
if (sub === "stats") {
|
|
21258
|
+
const latest = loadLatestRuntimeSession(target);
|
|
21259
|
+
if (!latest?.contextControl) {
|
|
21260
|
+
process.stderr.write("No context control data yet. Run a session first.\n");
|
|
21261
|
+
return 1;
|
|
21262
|
+
}
|
|
21263
|
+
const cc = latest.contextControl;
|
|
21264
|
+
process.stdout.write(renderContextStats({
|
|
21265
|
+
contextControlStatus: cc.status === "optimized" ? "ACTIVE" : cc.status === "blocked" ? "BLOCKED" : "ACTIVE",
|
|
21266
|
+
estimatedContextBefore: cc.estimatedContextBefore,
|
|
21267
|
+
estimatedContextSent: cc.estimatedContextAfter,
|
|
21268
|
+
reductionPercent: cc.reductionPercent,
|
|
21269
|
+
evidenceRefCount: cc.evidenceRefCount,
|
|
21270
|
+
blockedItemsCount: cc.blockedItemsCount,
|
|
21271
|
+
retrievalAvailable: cc.retrievalAvailable,
|
|
21272
|
+
proofImpact: cc.proofImpact,
|
|
21273
|
+
topReasonCodes: cc.reasonCodes,
|
|
21274
|
+
summary: `Context control ${cc.status}`
|
|
21275
|
+
}) + "\n");
|
|
21276
|
+
return 0;
|
|
21277
|
+
}
|
|
21278
|
+
if (sub === "explain") {
|
|
21279
|
+
const latest = loadLatestRuntimeSession(target);
|
|
21280
|
+
if (!latest?.contextControl) {
|
|
21281
|
+
process.stderr.write("No context control data yet. Run a session first.\n");
|
|
21282
|
+
return 1;
|
|
21283
|
+
}
|
|
21284
|
+
const cc = latest.contextControl;
|
|
21285
|
+
process.stdout.write(renderContextExplain({
|
|
21286
|
+
contextControlStatus: cc.status === "optimized" ? "ACTIVE" : cc.status === "blocked" ? "BLOCKED" : "ACTIVE",
|
|
21287
|
+
estimatedContextBefore: cc.estimatedContextBefore,
|
|
21288
|
+
estimatedContextSent: cc.estimatedContextAfter,
|
|
21289
|
+
reductionPercent: cc.reductionPercent,
|
|
21290
|
+
evidenceRefCount: cc.evidenceRefCount,
|
|
21291
|
+
blockedItemsCount: cc.blockedItemsCount,
|
|
21292
|
+
retrievalAvailable: cc.retrievalAvailable,
|
|
21293
|
+
proofImpact: cc.proofImpact,
|
|
21294
|
+
topReasonCodes: cc.reasonCodes,
|
|
21295
|
+
summary: `Context control ${cc.status}`
|
|
21296
|
+
}, []) + "\n");
|
|
21297
|
+
return 0;
|
|
21298
|
+
}
|
|
21299
|
+
if (sub === "learn") {
|
|
21300
|
+
if (!args.includes("--dry-run")) {
|
|
21301
|
+
process.stderr.write("Usage: avorelo context learn --dry-run [--target <dir>]\nLearning candidates are dry-run only.\n");
|
|
21302
|
+
return 2;
|
|
21303
|
+
}
|
|
21304
|
+
const result3 = generateLearningCandidates({ failurePatterns: [], receipts: [] });
|
|
21305
|
+
process.stdout.write(`Learning candidates (dry-run): ${result3.candidates.length}
|
|
21306
|
+
`);
|
|
21307
|
+
for (const c of result3.candidates) {
|
|
21308
|
+
process.stdout.write(` - [${c.priority}] ${c.label}: ${c.description}
|
|
21309
|
+
`);
|
|
21310
|
+
}
|
|
21311
|
+
return 0;
|
|
21312
|
+
}
|
|
18827
21313
|
if (sub !== "compile") {
|
|
18828
|
-
process.stderr.write("Usage: avorelo context <compile|check> [args]\n");
|
|
21314
|
+
process.stderr.write("Usage: avorelo context <compile|check|stats|explain|learn> [args]\n");
|
|
18829
21315
|
return 2;
|
|
18830
21316
|
}
|
|
18831
21317
|
if (!task) {
|
|
@@ -18854,12 +21340,68 @@ function cmdContext(args) {
|
|
|
18854
21340
|
].filter(Boolean).join("\n"));
|
|
18855
21341
|
return 0;
|
|
18856
21342
|
}
|
|
21343
|
+
function cmdGuard(args) {
|
|
21344
|
+
const sub = args[0];
|
|
21345
|
+
const target = arg(args, "--target", process.cwd());
|
|
21346
|
+
const asJson = args.includes("--json");
|
|
21347
|
+
const ci = args.includes("--ci");
|
|
21348
|
+
if (sub === "scan") {
|
|
21349
|
+
const output = handleCli(
|
|
21350
|
+
{ command: "guard", json: asJson, verbose: false, guardSubcommand: "scan", ci },
|
|
21351
|
+
target
|
|
21352
|
+
);
|
|
21353
|
+
process.stdout.write(output.stdout + "\n");
|
|
21354
|
+
return output.exitCode;
|
|
21355
|
+
}
|
|
21356
|
+
if (sub === "explain") {
|
|
21357
|
+
const ruleId = args[1];
|
|
21358
|
+
const output = handleCli(
|
|
21359
|
+
{ command: "guard", json: false, verbose: false, guardSubcommand: "explain", guardArgs: [ruleId] },
|
|
21360
|
+
target
|
|
21361
|
+
);
|
|
21362
|
+
process.stdout.write(output.stdout + "\n");
|
|
21363
|
+
return output.exitCode;
|
|
21364
|
+
}
|
|
21365
|
+
process.stderr.write("Usage: avorelo guard <scan|explain> [args]\n");
|
|
21366
|
+
return 2;
|
|
21367
|
+
}
|
|
21368
|
+
function cmdReceipt(args) {
|
|
21369
|
+
const sub = args[0];
|
|
21370
|
+
const target = arg(args, "--target", process.cwd());
|
|
21371
|
+
const asJson = args.includes("--json");
|
|
21372
|
+
if (sub === "latest" || sub === void 0) {
|
|
21373
|
+
const output = handleCli(
|
|
21374
|
+
{ command: "receipt", json: asJson, verbose: false, receiptSubcommand: "latest" },
|
|
21375
|
+
target
|
|
21376
|
+
);
|
|
21377
|
+
process.stdout.write(output.stdout + "\n");
|
|
21378
|
+
const latest = loadLatestRuntimeSession(target);
|
|
21379
|
+
if (latest?.contextControl && !asJson) {
|
|
21380
|
+
const cc = latest.contextControl;
|
|
21381
|
+
process.stdout.write([
|
|
21382
|
+
"",
|
|
21383
|
+
"Context Control:",
|
|
21384
|
+
` Status: ${cc.status}`,
|
|
21385
|
+
` Context before: ${cc.estimatedContextBefore}`,
|
|
21386
|
+
` Context after: ${cc.estimatedContextAfter}`,
|
|
21387
|
+
` Reduction: ${cc.reductionPercent}%`,
|
|
21388
|
+
` Evidence refs: ${cc.evidenceRefCount}`,
|
|
21389
|
+
` Blocked items: ${cc.blockedItemsCount}`,
|
|
21390
|
+
` Proof impact: ${cc.proofImpact}`,
|
|
21391
|
+
""
|
|
21392
|
+
].join("\n"));
|
|
21393
|
+
}
|
|
21394
|
+
return output.exitCode;
|
|
21395
|
+
}
|
|
21396
|
+
process.stderr.write("Usage: avorelo receipt latest [--target <dir>] [--json]\n");
|
|
21397
|
+
return 2;
|
|
21398
|
+
}
|
|
18857
21399
|
function cmdSecretBoundary(args) {
|
|
18858
21400
|
const sub = args[0];
|
|
18859
21401
|
const asJson = args.includes("--json");
|
|
18860
21402
|
let content = arg(args, "--content");
|
|
18861
21403
|
const file = arg(args, "--file");
|
|
18862
|
-
if (file &&
|
|
21404
|
+
if (file && existsSync64(file)) content = readFileSync50(file, "utf8");
|
|
18863
21405
|
if (content === void 0) content = "";
|
|
18864
21406
|
if (sub === "scan" || sub === void 0) {
|
|
18865
21407
|
const r2 = scanContent({ content, sourceKind: file ? "file" : "tool_output" });
|
|
@@ -18926,7 +21468,7 @@ function cmdExplain(args) {
|
|
|
18926
21468
|
const changedFiles = [];
|
|
18927
21469
|
for (const { adapter } of detected) {
|
|
18928
21470
|
const surface = adapter.getInstructionSurface(target);
|
|
18929
|
-
if (surface &&
|
|
21471
|
+
if (surface && existsSync64(surface)) changedFiles.push(surface);
|
|
18930
21472
|
}
|
|
18931
21473
|
if (changedFiles.length > 0) {
|
|
18932
21474
|
lines.push(" Changed:");
|
|
@@ -18940,7 +21482,7 @@ function cmdExplain(args) {
|
|
|
18940
21482
|
lines.push("");
|
|
18941
21483
|
lines.push(` Hooks: ${hookValidation.installed ? "installed" : "not installed"}`);
|
|
18942
21484
|
lines.push(" Cloud: off (local-first)");
|
|
18943
|
-
lines.push(` Receipts: ${
|
|
21485
|
+
lines.push(` Receipts: ${join66(target, ".avorelo", "receipts")}`);
|
|
18944
21486
|
const sessionStatus = getSessionStatus(target);
|
|
18945
21487
|
if (sessionStatus) {
|
|
18946
21488
|
lines.push("");
|
|
@@ -19100,7 +21642,7 @@ function cmdFeedback(args) {
|
|
|
19100
21642
|
}
|
|
19101
21643
|
if (sub === "share") {
|
|
19102
21644
|
const file = arg(args, "--file");
|
|
19103
|
-
if (!file || !
|
|
21645
|
+
if (!file || !existsSync64(file)) {
|
|
19104
21646
|
process.stderr.write("Usage: avorelo feedback share --file <bundle-path>\n");
|
|
19105
21647
|
return 2;
|
|
19106
21648
|
}
|
|
@@ -19404,7 +21946,7 @@ No loop metadata found for ${loopId}.
|
|
|
19404
21946
|
if (sub === "doctor") {
|
|
19405
21947
|
const issues = [];
|
|
19406
21948
|
const ok = [];
|
|
19407
|
-
if (
|
|
21949
|
+
if (existsSync64(join66(target, ".git"))) {
|
|
19408
21950
|
ok.push("Git repository detected");
|
|
19409
21951
|
} else {
|
|
19410
21952
|
issues.push("Not a git repository \u2014 loop needs git for drift detection");
|
|
@@ -19414,12 +21956,12 @@ No loop metadata found for ${loopId}.
|
|
|
19414
21956
|
} else {
|
|
19415
21957
|
issues.push("Claude Code CLI not found \u2014 install it or ensure `claude` is on PATH");
|
|
19416
21958
|
}
|
|
19417
|
-
const testDir =
|
|
21959
|
+
const testDir = join66(target, ".avorelo", "loops");
|
|
19418
21960
|
try {
|
|
19419
|
-
|
|
19420
|
-
const testFile =
|
|
19421
|
-
|
|
19422
|
-
|
|
21961
|
+
mkdirSync40(testDir, { recursive: true });
|
|
21962
|
+
const testFile = join66(testDir, ".doctor_probe");
|
|
21963
|
+
writeFileSync38(testFile, "probe");
|
|
21964
|
+
unlinkSync5(testFile);
|
|
19423
21965
|
ok.push("Storage writable (.avorelo/loops/)");
|
|
19424
21966
|
} catch {
|
|
19425
21967
|
issues.push("Cannot write to .avorelo/loops/ \u2014 check permissions");
|
|
@@ -19460,11 +22002,11 @@ function cmdUninstallAll(args) {
|
|
|
19460
22002
|
const target = arg(args, "--target", process.cwd());
|
|
19461
22003
|
const adapterResult = uninstallAll(target);
|
|
19462
22004
|
const hookResult = uninstall(target);
|
|
19463
|
-
const removed = [...adapterResult.removed, ...hookResult.restored ? [
|
|
22005
|
+
const removed = [...adapterResult.removed, ...hookResult.restored ? [join66(target, ".claude", "settings.json")] : []];
|
|
19464
22006
|
const preserved = adapterResult.preserved;
|
|
19465
|
-
const avoreloDir2 =
|
|
22007
|
+
const avoreloDir2 = join66(target, ".avorelo");
|
|
19466
22008
|
try {
|
|
19467
|
-
if (
|
|
22009
|
+
if (existsSync64(avoreloDir2)) {
|
|
19468
22010
|
rmSync3(avoreloDir2, { recursive: true, force: true });
|
|
19469
22011
|
removed.push(avoreloDir2);
|
|
19470
22012
|
}
|
|
@@ -20057,7 +22599,7 @@ function cmdConfig(args) {
|
|
|
20057
22599
|
function avoreloVersion() {
|
|
20058
22600
|
for (const rel of ["../package.json", "../../package.json", "../../../package.json", "../../../../package.json"]) {
|
|
20059
22601
|
try {
|
|
20060
|
-
const pkg = JSON.parse(
|
|
22602
|
+
const pkg = JSON.parse(readFileSync50(join66(import.meta.dirname, rel), "utf8"));
|
|
20061
22603
|
if (pkg.name === "avorelo" && pkg.version) return pkg.version;
|
|
20062
22604
|
} catch {
|
|
20063
22605
|
}
|
|
@@ -20168,6 +22710,10 @@ function main(argv) {
|
|
|
20168
22710
|
return cmdReadiness(rest);
|
|
20169
22711
|
case "loop":
|
|
20170
22712
|
return cmdLoop(rest);
|
|
22713
|
+
case "guard":
|
|
22714
|
+
return cmdGuard(rest);
|
|
22715
|
+
case "receipt":
|
|
22716
|
+
return cmdReceipt(rest);
|
|
20171
22717
|
default:
|
|
20172
22718
|
return help();
|
|
20173
22719
|
}
|
|
@@ -20348,6 +22894,7 @@ async function finalize(exitCode) {
|
|
|
20348
22894
|
} catch {
|
|
20349
22895
|
}
|
|
20350
22896
|
if (_cmd !== "serve" && _cmd !== "webhook" && _cmd !== "sync") {
|
|
22897
|
+
process.exitCode = exitCode;
|
|
20351
22898
|
setTimeout(() => process.exit(exitCode), 50).unref();
|
|
20352
22899
|
}
|
|
20353
22900
|
}
|