@guilz-dev/belay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +268 -0
- package/agent-belay-logo.png +0 -0
- package/dist/adapters/claude/adapter.d.ts +7 -0
- package/dist/adapters/claude/adapter.js +114 -0
- package/dist/adapters/claude/hooks.d.ts +13 -0
- package/dist/adapters/claude/hooks.js +49 -0
- package/dist/adapters/claude/runtime-entry.d.ts +4 -0
- package/dist/adapters/claude/runtime-entry.js +260 -0
- package/dist/adapters/codex/adapter.d.ts +7 -0
- package/dist/adapters/codex/adapter.js +73 -0
- package/dist/adapters/codex/hooks.d.ts +21 -0
- package/dist/adapters/codex/hooks.js +78 -0
- package/dist/adapters/codex/runtime-entry.d.ts +4 -0
- package/dist/adapters/codex/runtime-entry.js +237 -0
- package/dist/adapters/cursor/adapter.d.ts +7 -0
- package/dist/adapters/cursor/adapter.js +29 -0
- package/dist/adapters/cursor/hooks.d.ts +2 -0
- package/dist/adapters/cursor/hooks.js +26 -0
- package/dist/adapters/cursor/runtime-entry.d.ts +4 -0
- package/dist/adapters/cursor/runtime-entry.js +143 -0
- package/dist/adapters/layouts/claude.d.ts +2 -0
- package/dist/adapters/layouts/claude.js +40 -0
- package/dist/adapters/layouts/codex.d.ts +2 -0
- package/dist/adapters/layouts/codex.js +43 -0
- package/dist/adapters/layouts/cursor.d.ts +2 -0
- package/dist/adapters/layouts/cursor.js +40 -0
- package/dist/adapters/layouts/index.d.ts +7 -0
- package/dist/adapters/layouts/index.js +23 -0
- package/dist/adapters/layouts/protected-paths.d.ts +3 -0
- package/dist/adapters/layouts/protected-paths.js +15 -0
- package/dist/adapters/layouts/scope.d.ts +19 -0
- package/dist/adapters/layouts/scope.js +86 -0
- package/dist/adapters/layouts/types.d.ts +14 -0
- package/dist/adapters/layouts/types.js +1 -0
- package/dist/adapters/registry.d.ts +4 -0
- package/dist/adapters/registry.js +14 -0
- package/dist/adapters/shared/gate-runtime.d.ts +51 -0
- package/dist/adapters/shared/gate-runtime.js +518 -0
- package/dist/adapters/shared/repo-root.d.ts +2 -0
- package/dist/adapters/shared/repo-root.js +17 -0
- package/dist/adapters/types.d.ts +19 -0
- package/dist/adapters/types.js +1 -0
- package/dist/branding.d.ts +3 -0
- package/dist/branding.js +3 -0
- package/dist/bundle/claude-runtime.mjs +5323 -0
- package/dist/bundle/codex-runtime.mjs +5310 -0
- package/dist/bundle/cursor-runtime.mjs +5208 -0
- package/dist/cleanup-orphans.d.ts +7 -0
- package/dist/cleanup-orphans.js +59 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +631 -0
- package/dist/commands/approve.d.ts +14 -0
- package/dist/commands/approve.js +65 -0
- package/dist/commands/audit.d.ts +59 -0
- package/dist/commands/audit.js +132 -0
- package/dist/commands/classify-for-report.d.ts +9 -0
- package/dist/commands/classify-for-report.js +85 -0
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.js +366 -0
- package/dist/commands/dogfood.d.ts +5 -0
- package/dist/commands/dogfood.js +71 -0
- package/dist/commands/explain.d.ts +3 -0
- package/dist/commands/explain.js +133 -0
- package/dist/commands/health-snapshot.d.ts +2 -0
- package/dist/commands/health-snapshot.js +166 -0
- package/dist/commands/init-wizard.d.ts +16 -0
- package/dist/commands/init-wizard.js +50 -0
- package/dist/commands/metrics.d.ts +7 -0
- package/dist/commands/metrics.js +89 -0
- package/dist/commands/recover.d.ts +3 -0
- package/dist/commands/recover.js +105 -0
- package/dist/commands/report.d.ts +3 -0
- package/dist/commands/report.js +65 -0
- package/dist/commands/revoke.d.ts +5 -0
- package/dist/commands/revoke.js +22 -0
- package/dist/commands/simulate.d.ts +14 -0
- package/dist/commands/simulate.js +55 -0
- package/dist/commands/status.d.ts +5 -0
- package/dist/commands/status.js +107 -0
- package/dist/config-io.d.ts +23 -0
- package/dist/config-io.js +180 -0
- package/dist/conformance/guarantee-table.d.ts +14 -0
- package/dist/conformance/guarantee-table.js +95 -0
- package/dist/conformance/types.d.ts +6 -0
- package/dist/conformance/types.js +1 -0
- package/dist/core/approval-service.d.ts +26 -0
- package/dist/core/approval-service.js +41 -0
- package/dist/core/approval-token.d.ts +11 -0
- package/dist/core/approval-token.js +61 -0
- package/dist/core/approval.d.ts +19 -0
- package/dist/core/approval.js +58 -0
- package/dist/core/audit-analysis.d.ts +10 -0
- package/dist/core/audit-analysis.js +147 -0
- package/dist/core/audit-metrics.d.ts +51 -0
- package/dist/core/audit-metrics.js +155 -0
- package/dist/core/audit-query.d.ts +11 -0
- package/dist/core/audit-query.js +142 -0
- package/dist/core/audit-summary.d.ts +33 -0
- package/dist/core/audit-summary.js +111 -0
- package/dist/core/audit-types.d.ts +65 -0
- package/dist/core/audit-types.js +2 -0
- package/dist/core/capability/allowlist.d.ts +10 -0
- package/dist/core/capability/allowlist.js +53 -0
- package/dist/core/capability/broker.d.ts +17 -0
- package/dist/core/capability/broker.js +29 -0
- package/dist/core/capability/index.d.ts +5 -0
- package/dist/core/capability/index.js +4 -0
- package/dist/core/capability/paths.d.ts +1 -0
- package/dist/core/capability/paths.js +20 -0
- package/dist/core/capability/reasons.d.ts +2 -0
- package/dist/core/capability/reasons.js +4 -0
- package/dist/core/capability/types.d.ts +10 -0
- package/dist/core/capability/types.js +1 -0
- package/dist/core/capability-approval.d.ts +28 -0
- package/dist/core/capability-approval.js +43 -0
- package/dist/core/classify-subagent.d.ts +2 -0
- package/dist/core/classify-subagent.js +69 -0
- package/dist/core/classify-tool.d.ts +3 -0
- package/dist/core/classify-tool.js +311 -0
- package/dist/core/config-layers.d.ts +23 -0
- package/dist/core/config-layers.js +59 -0
- package/dist/core/config.d.ts +219 -0
- package/dist/core/config.js +720 -0
- package/dist/core/control-plane-isolation.d.ts +10 -0
- package/dist/core/control-plane-isolation.js +83 -0
- package/dist/core/custom-command-match.d.ts +2 -0
- package/dist/core/custom-command-match.js +8 -0
- package/dist/core/egress/allowlist.d.ts +7 -0
- package/dist/core/egress/allowlist.js +33 -0
- package/dist/core/egress/env.d.ts +3 -0
- package/dist/core/egress/env.js +17 -0
- package/dist/core/egress/fingerprint.d.ts +3 -0
- package/dist/core/egress/fingerprint.js +35 -0
- package/dist/core/egress/policy.d.ts +8 -0
- package/dist/core/egress/policy.js +47 -0
- package/dist/core/egress/proxy-server.d.ts +21 -0
- package/dist/core/egress/proxy-server.js +263 -0
- package/dist/core/egress/types.d.ts +25 -0
- package/dist/core/egress/types.js +1 -0
- package/dist/core/egress-approval.d.ts +48 -0
- package/dist/core/egress-approval.js +129 -0
- package/dist/core/fingerprint.d.ts +6 -0
- package/dist/core/fingerprint.js +24 -0
- package/dist/core/gate-contract.d.ts +48 -0
- package/dist/core/gate-contract.js +50 -0
- package/dist/core/gate-engine.d.ts +20 -0
- package/dist/core/gate-engine.js +172 -0
- package/dist/core/glob.d.ts +1 -0
- package/dist/core/glob.js +39 -0
- package/dist/core/index.d.ts +19 -0
- package/dist/core/index.js +15 -0
- package/dist/core/integrity.d.ts +15 -0
- package/dist/core/integrity.js +68 -0
- package/dist/core/judge-api-key.d.ts +4 -0
- package/dist/core/judge-api-key.js +11 -0
- package/dist/core/judge-config.d.ts +29 -0
- package/dist/core/judge-config.js +85 -0
- package/dist/core/judge-doctor.d.ts +7 -0
- package/dist/core/judge-doctor.js +124 -0
- package/dist/core/judgment.d.ts +6 -0
- package/dist/core/judgment.js +38 -0
- package/dist/core/notify.d.ts +13 -0
- package/dist/core/notify.js +44 -0
- package/dist/core/path-utils.d.ts +12 -0
- package/dist/core/path-utils.js +107 -0
- package/dist/core/reclassify.d.ts +15 -0
- package/dist/core/reclassify.js +82 -0
- package/dist/core/recover-advice.d.ts +30 -0
- package/dist/core/recover-advice.js +177 -0
- package/dist/core/recover-git-probe.d.ts +8 -0
- package/dist/core/recover-git-probe.js +50 -0
- package/dist/core/recover-select.d.ts +10 -0
- package/dist/core/recover-select.js +60 -0
- package/dist/core/scrub.d.ts +3 -0
- package/dist/core/scrub.js +87 -0
- package/dist/core/shell-substitution.d.ts +6 -0
- package/dist/core/shell-substitution.js +130 -0
- package/dist/core/shell-tokenizer.d.ts +5 -0
- package/dist/core/shell-tokenizer.js +129 -0
- package/dist/core/shell-unparseable.d.ts +4 -0
- package/dist/core/shell-unparseable.js +96 -0
- package/dist/core/transactional/diff-evaluator.d.ts +2 -0
- package/dist/core/transactional/diff-evaluator.js +84 -0
- package/dist/core/transactional/eligibility.d.ts +4 -0
- package/dist/core/transactional/eligibility.js +44 -0
- package/dist/core/transactional/git-worktree.d.ts +13 -0
- package/dist/core/transactional/git-worktree.js +189 -0
- package/dist/core/transactional/index.d.ts +5 -0
- package/dist/core/transactional/index.js +4 -0
- package/dist/core/transactional/reasons.d.ts +4 -0
- package/dist/core/transactional/reasons.js +8 -0
- package/dist/core/transactional/runner.d.ts +2 -0
- package/dist/core/transactional/runner.js +113 -0
- package/dist/core/transactional/types.d.ts +46 -0
- package/dist/core/transactional/types.js +1 -0
- package/dist/core/types.d.ts +90 -0
- package/dist/core/types.js +1 -0
- package/dist/core/v2/adapter.d.ts +14 -0
- package/dist/core/v2/adapter.js +118 -0
- package/dist/core/v2/containment.d.ts +19 -0
- package/dist/core/v2/containment.js +91 -0
- package/dist/core/v2/egress-classify.d.ts +7 -0
- package/dist/core/v2/egress-classify.js +216 -0
- package/dist/core/v2/fingerprint.d.ts +1 -0
- package/dist/core/v2/fingerprint.js +4 -0
- package/dist/core/v2/index.d.ts +12 -0
- package/dist/core/v2/index.js +10 -0
- package/dist/core/v2/judge-audit.d.ts +2 -0
- package/dist/core/v2/judge-audit.js +15 -0
- package/dist/core/v2/judge-factory.d.ts +25 -0
- package/dist/core/v2/judge-factory.js +75 -0
- package/dist/core/v2/judge-outbound.d.ts +12 -0
- package/dist/core/v2/judge-outbound.js +73 -0
- package/dist/core/v2/judge.d.ts +47 -0
- package/dist/core/v2/judge.js +264 -0
- package/dist/core/v2/launcher-resolve.d.ts +12 -0
- package/dist/core/v2/launcher-resolve.js +190 -0
- package/dist/core/v2/overrides.d.ts +7 -0
- package/dist/core/v2/overrides.js +37 -0
- package/dist/core/v2/parser.d.ts +21 -0
- package/dist/core/v2/parser.js +213 -0
- package/dist/core/v2/types.d.ts +67 -0
- package/dist/core/v2/types.js +1 -0
- package/dist/core/v2/verdict.d.ts +2 -0
- package/dist/core/v2/verdict.js +699 -0
- package/dist/corpus/evaluate.d.ts +24 -0
- package/dist/corpus/evaluate.js +69 -0
- package/dist/defaults.d.ts +18 -0
- package/dist/defaults.js +155 -0
- package/dist/egress-daemon.d.ts +1 -0
- package/dist/egress-daemon.js +52 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +15 -0
- package/dist/installer/bootstrap.d.ts +5 -0
- package/dist/installer/bootstrap.js +61 -0
- package/dist/installer/runtime-artifacts.d.ts +3 -0
- package/dist/installer/runtime-artifacts.js +23 -0
- package/dist/installer/scope-config.d.ts +8 -0
- package/dist/installer/scope-config.js +25 -0
- package/dist/installer.d.ts +22 -0
- package/dist/installer.js +169 -0
- package/dist/node-resolution.d.ts +8 -0
- package/dist/node-resolution.js +237 -0
- package/dist/operational-insights.d.ts +19 -0
- package/dist/operational-insights.js +24 -0
- package/dist/presets.d.ts +4 -0
- package/dist/presets.js +95 -0
- package/dist/services/egress-service.d.ts +57 -0
- package/dist/services/egress-service.js +334 -0
- package/dist/services/sandbox-service.d.ts +38 -0
- package/dist/services/sandbox-service.js +95 -0
- package/dist/templates.d.ts +7 -0
- package/dist/templates.js +56 -0
- package/dist/types.d.ts +230 -0
- package/dist/types.js +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +1 -0
- package/package.json +65 -0
- package/skills/belay/SKILL.md +52 -0
- package/skills/belay/belay-approve.md +7 -0
- package/skills/belay/belay-explain.md +11 -0
- package/skills/belay/belay-recover.md +13 -0
- package/skills/belay/belay-report.md +7 -0
- package/skills/belay/belay-status.md +9 -0
- package/skills/belay/belay-why.md +11 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { approvedApprovalsPath, loadApprovalState, loadConfigFile, pendingApprovalsPath, saveApprovalState, } from '../config-io.js';
|
|
3
|
+
import { recordApproval } from '../core/approval-service.js';
|
|
4
|
+
import { recordCapabilityApproval } from '../core/capability-approval.js';
|
|
5
|
+
import { recordEgressApproval } from '../core/egress-approval.js';
|
|
6
|
+
import { createEgressApprovalStore } from '../services/egress-service.js';
|
|
7
|
+
import { createCapabilityApprovalStore } from '../services/sandbox-service.js';
|
|
8
|
+
export async function approvePending(options) {
|
|
9
|
+
const repoRoot = path.resolve(options.targetDir ?? process.cwd());
|
|
10
|
+
const config = await loadConfigFile(repoRoot);
|
|
11
|
+
const pending = await loadApprovalState(repoRoot, 'pending-approvals.json', config);
|
|
12
|
+
const match = pending.approvals.find((approval) => approval.approvalId === options.approvalId);
|
|
13
|
+
if (match?.kind === 'egress') {
|
|
14
|
+
const result = await recordEgressApproval({
|
|
15
|
+
approvalId: options.approvalId,
|
|
16
|
+
config,
|
|
17
|
+
scope: (options.scope === 'domain' ? 'domain' : 'once'),
|
|
18
|
+
token: options.token,
|
|
19
|
+
requireSignedToken: config.approvalSigning.required,
|
|
20
|
+
store: createEgressApprovalStore(repoRoot, config),
|
|
21
|
+
});
|
|
22
|
+
return { ok: result.ok, message: result.message };
|
|
23
|
+
}
|
|
24
|
+
if (options.scope === 'path') {
|
|
25
|
+
const result = await recordCapabilityApproval({
|
|
26
|
+
approvalId: options.approvalId,
|
|
27
|
+
config,
|
|
28
|
+
scope: 'path',
|
|
29
|
+
scopePath: options.scopePath,
|
|
30
|
+
token: options.token,
|
|
31
|
+
requireSignedToken: config.approvalSigning.required,
|
|
32
|
+
store: createCapabilityApprovalStore(repoRoot, config),
|
|
33
|
+
});
|
|
34
|
+
return { ok: result.ok, message: result.message };
|
|
35
|
+
}
|
|
36
|
+
const result = await recordApproval({
|
|
37
|
+
approvalId: options.approvalId,
|
|
38
|
+
config,
|
|
39
|
+
token: options.token,
|
|
40
|
+
requireSignedToken: config.approvalSigning.required,
|
|
41
|
+
store: {
|
|
42
|
+
async loadPending() {
|
|
43
|
+
const filePath = pendingApprovalsPath(repoRoot, config);
|
|
44
|
+
return {
|
|
45
|
+
filePath,
|
|
46
|
+
state: await loadApprovalState(repoRoot, 'pending-approvals.json', config),
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
async loadApproved() {
|
|
50
|
+
const filePath = approvedApprovalsPath(repoRoot, config);
|
|
51
|
+
return {
|
|
52
|
+
filePath,
|
|
53
|
+
state: await loadApprovalState(repoRoot, 'approved-approvals.json', config),
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
async writePending(_filePath, state) {
|
|
57
|
+
await saveApprovalState(repoRoot, 'pending-approvals.json', state, config);
|
|
58
|
+
},
|
|
59
|
+
async writeApproved(_filePath, state) {
|
|
60
|
+
await saveApprovalState(repoRoot, 'approved-approvals.json', state, config);
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
return { ok: result.ok, message: result.message };
|
|
65
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { AuditRecord } from '../core/audit-types.js';
|
|
2
|
+
export type AuditSubcommand = 'query' | 'summarize' | 'replay';
|
|
3
|
+
export interface AuditOptions {
|
|
4
|
+
targetDir?: string;
|
|
5
|
+
subcommand: AuditSubcommand;
|
|
6
|
+
json?: boolean;
|
|
7
|
+
since?: string;
|
|
8
|
+
until?: string;
|
|
9
|
+
verdict?: string;
|
|
10
|
+
reason?: string;
|
|
11
|
+
kind?: string;
|
|
12
|
+
fingerprint?: string;
|
|
13
|
+
event?: string;
|
|
14
|
+
location?: string;
|
|
15
|
+
opacity?: string;
|
|
16
|
+
effect?: string;
|
|
17
|
+
confidence?: string;
|
|
18
|
+
limit?: number;
|
|
19
|
+
configPath?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function loadAuditRecords(repoRoot: string): Promise<AuditRecord[]>;
|
|
22
|
+
export declare function auditProject(options: AuditOptions): Promise<{
|
|
23
|
+
subcommand: string;
|
|
24
|
+
records: AuditRecord[];
|
|
25
|
+
count: number;
|
|
26
|
+
roundTrips?: undefined;
|
|
27
|
+
lines?: undefined;
|
|
28
|
+
bypassAttempts?: undefined;
|
|
29
|
+
noisyRules?: undefined;
|
|
30
|
+
candidateConfigPath?: undefined;
|
|
31
|
+
configWarning?: undefined;
|
|
32
|
+
changedCount?: undefined;
|
|
33
|
+
diffs?: undefined;
|
|
34
|
+
} | {
|
|
35
|
+
subcommand: string;
|
|
36
|
+
roundTrips: import("../core/audit-types.js").ApprovalRoundTrip[];
|
|
37
|
+
lines: string[];
|
|
38
|
+
bypassAttempts: import("../core/audit-types.js").BypassAttempt[];
|
|
39
|
+
noisyRules: import("../core/audit-types.js").NoisyRuleCandidate[];
|
|
40
|
+
records?: undefined;
|
|
41
|
+
count?: undefined;
|
|
42
|
+
candidateConfigPath?: undefined;
|
|
43
|
+
configWarning?: undefined;
|
|
44
|
+
changedCount?: undefined;
|
|
45
|
+
diffs?: undefined;
|
|
46
|
+
} | {
|
|
47
|
+
subcommand: string;
|
|
48
|
+
candidateConfigPath: string | null;
|
|
49
|
+
configWarning: string | undefined;
|
|
50
|
+
changedCount: number;
|
|
51
|
+
diffs: import("../core/reclassify.js").ReclassifyDiff[];
|
|
52
|
+
records?: undefined;
|
|
53
|
+
count?: undefined;
|
|
54
|
+
roundTrips?: undefined;
|
|
55
|
+
lines?: undefined;
|
|
56
|
+
bypassAttempts?: undefined;
|
|
57
|
+
noisyRules?: undefined;
|
|
58
|
+
}>;
|
|
59
|
+
export declare function formatAuditReport(report: Awaited<ReturnType<typeof auditProject>>): string;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { loadConfigFile } from '../config-io.js';
|
|
5
|
+
import { detectBypassAttempts, detectNoisyRules } from '../core/audit-analysis.js';
|
|
6
|
+
import { parseAuditNdjson, toAuditRecord } from '../core/audit-metrics.js';
|
|
7
|
+
import { buildApprovalRoundTrips, filterAuditRecords, summarizeRoundTrips, } from '../core/audit-query.js';
|
|
8
|
+
import { mergeConfig } from '../core/config.js';
|
|
9
|
+
import { diffReclassification } from '../core/reclassify.js';
|
|
10
|
+
export async function loadAuditRecords(repoRoot) {
|
|
11
|
+
const config = await loadConfigFile(repoRoot);
|
|
12
|
+
const auditLogPath = path.join(repoRoot, config.audit.logPath);
|
|
13
|
+
let raw = '';
|
|
14
|
+
try {
|
|
15
|
+
raw = await readFile(auditLogPath, 'utf8');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
raw = '';
|
|
19
|
+
}
|
|
20
|
+
return parseAuditNdjson(raw).map(toAuditRecord);
|
|
21
|
+
}
|
|
22
|
+
export async function auditProject(options) {
|
|
23
|
+
const repoRoot = path.resolve(options.targetDir ?? process.cwd());
|
|
24
|
+
const records = await loadAuditRecords(repoRoot);
|
|
25
|
+
const filter = {
|
|
26
|
+
since: options.since,
|
|
27
|
+
until: options.until,
|
|
28
|
+
verdict: options.verdict,
|
|
29
|
+
reason: options.reason,
|
|
30
|
+
kind: options.kind,
|
|
31
|
+
fingerprint: options.fingerprint,
|
|
32
|
+
event: options.event,
|
|
33
|
+
location: options.location,
|
|
34
|
+
opacity: options.opacity,
|
|
35
|
+
effect: options.effect,
|
|
36
|
+
confidence: options.confidence,
|
|
37
|
+
limit: options.limit,
|
|
38
|
+
};
|
|
39
|
+
if (options.subcommand === 'query') {
|
|
40
|
+
const filtered = filterAuditRecords(records, filter);
|
|
41
|
+
return { subcommand: 'query', records: filtered, count: filtered.length };
|
|
42
|
+
}
|
|
43
|
+
if (options.subcommand === 'summarize') {
|
|
44
|
+
const filtered = filterAuditRecords(records, filter);
|
|
45
|
+
const trips = buildApprovalRoundTrips(filtered);
|
|
46
|
+
const bypassAttempts = detectBypassAttempts(filtered);
|
|
47
|
+
const noisyRules = detectNoisyRules(filtered, trips);
|
|
48
|
+
return {
|
|
49
|
+
subcommand: 'summarize',
|
|
50
|
+
roundTrips: trips,
|
|
51
|
+
lines: summarizeRoundTrips(trips),
|
|
52
|
+
bypassAttempts,
|
|
53
|
+
noisyRules,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const config = await loadConfigFile(repoRoot);
|
|
57
|
+
let candidateConfig = config;
|
|
58
|
+
let configWarning;
|
|
59
|
+
if (options.configPath) {
|
|
60
|
+
if (!existsSync(options.configPath)) {
|
|
61
|
+
configWarning = `Candidate config not found: ${options.configPath}`;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const raw = JSON.parse(await readFile(options.configPath, 'utf8'));
|
|
65
|
+
candidateConfig = mergeConfig(raw, config);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const filtered = filterAuditRecords(records, filter);
|
|
69
|
+
const diffs = (await Promise.all(filtered.map((record) => diffReclassification(record, candidateConfig, repoRoot)))).filter((diff) => diff !== null);
|
|
70
|
+
return {
|
|
71
|
+
subcommand: 'replay',
|
|
72
|
+
candidateConfigPath: options.configPath ?? null,
|
|
73
|
+
configWarning,
|
|
74
|
+
changedCount: diffs.length,
|
|
75
|
+
diffs,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export function formatAuditReport(report) {
|
|
79
|
+
if (report.subcommand === 'query') {
|
|
80
|
+
const records = report.records ?? [];
|
|
81
|
+
const count = report.count ?? records.length;
|
|
82
|
+
const lines = [`audit query: ${count} record(s)`];
|
|
83
|
+
for (const record of records.slice(0, 50)) {
|
|
84
|
+
const v2Axes = typeof record.location === 'string'
|
|
85
|
+
? ` location=${record.location} opacity=${record.opacity ?? '?'} effect=${record.effect ?? '?'} confidence=${record.confidence ?? '?'}`
|
|
86
|
+
: '';
|
|
87
|
+
lines.push(`- ${record.timestamp ?? '?'} [${record.event ?? '?'}] ${record.verdict ?? '?'} (${record.reason ?? '?'})${v2Axes} ${record.summary ?? ''}`);
|
|
88
|
+
}
|
|
89
|
+
if (count > 50) {
|
|
90
|
+
lines.push(`... ${count - 50} more`);
|
|
91
|
+
}
|
|
92
|
+
return `${lines.join('\n')}\n`;
|
|
93
|
+
}
|
|
94
|
+
if (report.subcommand === 'summarize') {
|
|
95
|
+
const lines = ['audit summarize:', ''];
|
|
96
|
+
const summaryLines = report.lines ?? [];
|
|
97
|
+
const bypassAttempts = report.bypassAttempts ?? [];
|
|
98
|
+
const noisyRules = report.noisyRules ?? [];
|
|
99
|
+
if (summaryLines.length === 0) {
|
|
100
|
+
lines.push('No deny → approve → execute round-trips found.');
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
lines.push('Round-trips:');
|
|
104
|
+
for (const line of summaryLines) {
|
|
105
|
+
lines.push(`- ${line}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (bypassAttempts.length > 0) {
|
|
109
|
+
lines.push('', `Bypass attempts (${bypassAttempts.length}):`);
|
|
110
|
+
for (const attempt of bypassAttempts.slice(0, 10)) {
|
|
111
|
+
lines.push(`- [${attempt.signal}] denied "${attempt.denySummary}" → tried "${attempt.attemptSummary}"`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (noisyRules.length > 0) {
|
|
115
|
+
lines.push('', 'Noisy rule candidates:');
|
|
116
|
+
for (const rule of noisyRules) {
|
|
117
|
+
lines.push(`- ${rule.reason}: ${(rule.approvalRate * 100).toFixed(0)}% approved after deny (${rule.approvedCount}/${rule.denyCount})`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return `${lines.join('\n')}\n`;
|
|
121
|
+
}
|
|
122
|
+
const lines = [
|
|
123
|
+
`audit replay: ${report.changedCount} verdict change(s)`,
|
|
124
|
+
report.candidateConfigPath ? `Candidate config: ${report.candidateConfigPath}` : '',
|
|
125
|
+
report.configWarning ?? '',
|
|
126
|
+
].filter(Boolean);
|
|
127
|
+
const diffs = report.diffs ?? [];
|
|
128
|
+
for (const diff of diffs.slice(0, 30)) {
|
|
129
|
+
lines.push(`- ${diff.summary ?? diff.fingerprint}: ${diff.previousVerdict}/${diff.previousReason} → ${diff.nextVerdict}/${diff.nextReason}`);
|
|
130
|
+
}
|
|
131
|
+
return `${lines.join('\n')}\n`;
|
|
132
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ClassifyForReportResult, ExplainKind } from '../types.js';
|
|
2
|
+
export declare function classifyForReport(params: {
|
|
3
|
+
targetDir?: string;
|
|
4
|
+
cwd?: string;
|
|
5
|
+
kind?: ExplainKind;
|
|
6
|
+
command?: string;
|
|
7
|
+
toolName?: string;
|
|
8
|
+
payload?: Record<string, unknown>;
|
|
9
|
+
}): Promise<ClassifyForReportResult>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { getAdapter } from '../adapters/registry.js';
|
|
3
|
+
import { repoShellClassifierOptions } from '../adapters/shared/gate-runtime.js';
|
|
4
|
+
import { detectAdapterName, loadConfigFile } from '../config-io.js';
|
|
5
|
+
import { isCapabilityBrokerDemotionActive } from '../core/capability/broker.js';
|
|
6
|
+
import { classifySubagent, classifyToolUse } from '../core/index.js';
|
|
7
|
+
import { isTransactionalEligible } from '../core/transactional/index.js';
|
|
8
|
+
import { classifyShell } from '../core/v2/adapter.js';
|
|
9
|
+
import { egressStatus } from '../services/egress-service.js';
|
|
10
|
+
import { sandboxStatus } from '../services/sandbox-service.js';
|
|
11
|
+
export async function classifyForReport(params) {
|
|
12
|
+
const repoRoot = path.resolve(params.targetDir ?? process.cwd());
|
|
13
|
+
const cwd = params.cwd ? path.resolve(params.cwd) : repoRoot;
|
|
14
|
+
const config = await loadConfigFile(repoRoot);
|
|
15
|
+
const egress = await egressStatus({ targetDir: repoRoot });
|
|
16
|
+
const sandbox = await sandboxStatus({ targetDir: repoRoot });
|
|
17
|
+
const adapter = getAdapter(config.adapter ?? detectAdapterName(repoRoot));
|
|
18
|
+
const classifierOptions = repoShellClassifierOptions(config, repoRoot, adapter.layout, {
|
|
19
|
+
brokerFsScope: isCapabilityBrokerDemotionActive(config),
|
|
20
|
+
});
|
|
21
|
+
const kind = params.kind ?? 'shell';
|
|
22
|
+
let input = params.command ?? '';
|
|
23
|
+
let result;
|
|
24
|
+
if (kind === 'shell') {
|
|
25
|
+
if (!params.command) {
|
|
26
|
+
throw new Error('classify-for-report requires a command for shell classification.');
|
|
27
|
+
}
|
|
28
|
+
result = await classifyShell(params.command, cwd, repoRoot, config, classifierOptions);
|
|
29
|
+
input = params.command;
|
|
30
|
+
}
|
|
31
|
+
else if (kind === 'subagent') {
|
|
32
|
+
const payload = params.payload ?? {
|
|
33
|
+
tool_name: 'Task',
|
|
34
|
+
tool_input: { description: params.command ?? '' },
|
|
35
|
+
};
|
|
36
|
+
if (!params.command && !params.payload) {
|
|
37
|
+
throw new Error('classify-for-report requires command or payload for subagent classification.');
|
|
38
|
+
}
|
|
39
|
+
result = classifySubagent(payload, repoRoot, classifierOptions);
|
|
40
|
+
input = params.command ?? JSON.stringify(payload);
|
|
41
|
+
}
|
|
42
|
+
else if (kind === 'tool') {
|
|
43
|
+
const payload = params.payload ??
|
|
44
|
+
{
|
|
45
|
+
tool_name: params.toolName ?? 'Shell',
|
|
46
|
+
tool_input: params.toolName === 'Shell'
|
|
47
|
+
? { command: params.command ?? '' }
|
|
48
|
+
: { path: params.command ?? '' },
|
|
49
|
+
};
|
|
50
|
+
if (!params.command && !params.payload) {
|
|
51
|
+
throw new Error('classify-for-report requires command or payload for tool classification.');
|
|
52
|
+
}
|
|
53
|
+
result = await classifyToolUse(payload, repoRoot, cwd, config, classifierOptions);
|
|
54
|
+
input = params.command ?? JSON.stringify(payload);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error(`Unknown classify kind: ${kind}`);
|
|
58
|
+
}
|
|
59
|
+
const transactionalEligible = kind === 'shell' && isTransactionalEligible(config, 'shell', result);
|
|
60
|
+
const permission = result.v2?.would ??
|
|
61
|
+
(result.verdict === 'allow' || result.verdict === 'allow_flagged' ? 'allow' : 'ask');
|
|
62
|
+
const tier = result.reason.startsWith('tier0_') || result.reason === 'external_effect'
|
|
63
|
+
? 'Tier0'
|
|
64
|
+
: result.v2?.confidence === 'llm' || result.reason === 'unknown_local_effect'
|
|
65
|
+
? 'Tier1'
|
|
66
|
+
: 'deterministic';
|
|
67
|
+
return {
|
|
68
|
+
repoRoot,
|
|
69
|
+
kind,
|
|
70
|
+
input,
|
|
71
|
+
cwd,
|
|
72
|
+
config,
|
|
73
|
+
policy: config.policy,
|
|
74
|
+
overrides: config.overrides,
|
|
75
|
+
egress: config.egress,
|
|
76
|
+
egressProxyRunning: egress.running && !egress.foreignProxy && !egress.repoRootMismatch,
|
|
77
|
+
sandbox: config.sandbox,
|
|
78
|
+
sandboxBrokerActive: classifierOptions.brokerFsScope === true,
|
|
79
|
+
l1FullActive: sandbox.l1FullActive,
|
|
80
|
+
transactionalEligible,
|
|
81
|
+
permission,
|
|
82
|
+
tier,
|
|
83
|
+
result,
|
|
84
|
+
};
|
|
85
|
+
}
|