@agentbridge1/cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/bin/agentbridge.js +11 -0
  2. package/dist/acceptance-block.js +21 -0
  3. package/dist/acceptance-preflight.js +91 -0
  4. package/dist/api-client.js +6 -0
  5. package/dist/authority-request.js +25 -0
  6. package/dist/briefing.js +26 -0
  7. package/dist/bug-registry.js +350 -0
  8. package/dist/build-info.json +6 -0
  9. package/dist/canonical-state.js +11 -0
  10. package/dist/claimed-paths.js +42 -0
  11. package/dist/cli-failure-log.js +34 -0
  12. package/dist/commands/accept.js +241 -0
  13. package/dist/commands/attention.js +85 -0
  14. package/dist/commands/autopilot.js +93 -0
  15. package/dist/commands/bug.js +106 -0
  16. package/dist/commands/check.js +283 -0
  17. package/dist/commands/connect.js +159 -0
  18. package/dist/commands/dist-freshness.js +105 -0
  19. package/dist/commands/doctor.js +300 -0
  20. package/dist/commands/done.js +292 -0
  21. package/dist/commands/handoff.js +189 -0
  22. package/dist/commands/handshake.js +78 -0
  23. package/dist/commands/health.js +154 -0
  24. package/dist/commands/identity.js +57 -0
  25. package/dist/commands/init.js +5 -0
  26. package/dist/commands/memory.js +400 -0
  27. package/dist/commands/next.js +21 -0
  28. package/dist/commands/precommit-check.js +17 -0
  29. package/dist/commands/recover.js +116 -0
  30. package/dist/commands/session.js +229 -0
  31. package/dist/commands/setup-mcp.js +56 -0
  32. package/dist/commands/start.js +626 -0
  33. package/dist/commands/status.js +486 -0
  34. package/dist/commands/use.js +13 -0
  35. package/dist/commands/verify.js +264 -0
  36. package/dist/commands/version.js +32 -0
  37. package/dist/commands/watch.js +1718 -0
  38. package/dist/config.js +55 -0
  39. package/dist/domain-resolution.js +63 -0
  40. package/dist/error-catalog.js +494 -0
  41. package/dist/errors.js +276 -0
  42. package/dist/file-fingerprints.js +45 -0
  43. package/dist/gates.js +200 -0
  44. package/dist/git-evidence.js +285 -0
  45. package/dist/git-status.js +81 -0
  46. package/dist/http.js +151 -0
  47. package/dist/index.js +622 -0
  48. package/dist/init.js +458 -0
  49. package/dist/memory-context-render.js +51 -0
  50. package/dist/operator-snapshot.js +99 -0
  51. package/dist/precommit.js +72 -0
  52. package/dist/preflight-changed-files.js +109 -0
  53. package/dist/proof-guidance.js +110 -0
  54. package/dist/redact-secrets.js +15 -0
  55. package/dist/revert-crossing.js +73 -0
  56. package/dist/server-sync.js +433 -0
  57. package/dist/session-state.js +138 -0
  58. package/dist/session.js +89 -0
  59. package/dist/supervision.js +212 -0
  60. package/dist/terminal-ui.js +18 -0
  61. package/dist/test-runner.js +62 -0
  62. package/dist/types.js +2 -0
  63. package/dist/verification-conditions.js +185 -0
  64. package/dist/watch-core.js +208 -0
  65. package/dist/watch-packet-handshake.js +71 -0
  66. package/dist/watcher.js +62 -0
  67. package/dist/work-context-resolver.js +412 -0
  68. package/dist/work-contract.js +110 -0
  69. package/package.json +44 -0
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.inferSupervisionWorkType = inferSupervisionWorkType;
4
+ exports.requiredProofHints = requiredProofHints;
5
+ exports.computeScopeDriftAlerts = computeScopeDriftAlerts;
6
+ exports.supervisionFromAcceptance = supervisionFromAcceptance;
7
+ exports.fallbackSupervisionSnapshot = fallbackSupervisionSnapshot;
8
+ exports.supervisionSignature = supervisionSignature;
9
+ exports.renderSupervisionSummary = renderSupervisionSummary;
10
+ const domain_resolution_1 = require("./domain-resolution");
11
+ const memory_context_render_1 = require("./memory-context-render");
12
+ const CLI_GIT_REQUIRED_PROOF = [
13
+ "tracked modified file handled",
14
+ "untracked new file handled",
15
+ "staged-new file handled",
16
+ "tracked deleted file handled",
17
+ "missing file handled safely",
18
+ "watcher does not crash",
19
+ "precommit enforces unresolved Tier A/B crossings",
20
+ ];
21
+ function uniqueSorted(values) {
22
+ return [...new Set(values.map((v) => v.trim()).filter(Boolean))].sort();
23
+ }
24
+ function inferSupervisionWorkType(changedFiles) {
25
+ if (changedFiles.length === 0)
26
+ return "unknown";
27
+ const lower = changedFiles.map((file) => file.toLowerCase());
28
+ const cliSignal = lower.some((file) => file.includes("cli/src/") &&
29
+ (file.includes("watch") || file.includes("precommit") || file.includes("revert-crossing")));
30
+ if (cliSignal)
31
+ return "cli_git_enforcement";
32
+ const docsOnly = lower.every((file) => file.startsWith("docs/") || file.endsWith(".md") || file.endsWith(".mdx") || file.includes("/docs/"));
33
+ if (docsOnly)
34
+ return "documentation";
35
+ return "general";
36
+ }
37
+ function requiredProofHints(workType) {
38
+ if (workType === "cli_git_enforcement")
39
+ return [...CLI_GIT_REQUIRED_PROOF];
40
+ if (workType === "documentation")
41
+ return ["explicit verification evidence for changed docs/files"];
42
+ return [
43
+ "explicit verification evidence for changed files",
44
+ "run agentbridge check for full acceptance state",
45
+ ];
46
+ }
47
+ function escapeRegex(text) {
48
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
49
+ }
50
+ function globToRegex(glob) {
51
+ const normalized = glob.replaceAll("\\", "/");
52
+ const withDouble = normalized.replaceAll("**", "__DOUBLE_STAR__");
53
+ const withSingle = withDouble.replaceAll("*", "__SINGLE_STAR__");
54
+ const escaped = escapeRegex(withSingle)
55
+ .replaceAll("__DOUBLE_STAR__", ".*")
56
+ .replaceAll("__SINGLE_STAR__", "[^/]*");
57
+ return new RegExp(`^${escaped}$`);
58
+ }
59
+ function fileWithinClaimedPaths(file, claimedPaths) {
60
+ if (claimedPaths.length === 0)
61
+ return false;
62
+ return claimedPaths.some((claim) => {
63
+ const normalized = claim.trim().replaceAll("\\", "/");
64
+ if (!normalized)
65
+ return false;
66
+ if (normalized === file)
67
+ return true;
68
+ if (normalized.endsWith("/**")) {
69
+ const prefix = normalized.slice(0, -3);
70
+ return file.startsWith(prefix);
71
+ }
72
+ if (normalized.includes("*")) {
73
+ return globToRegex(normalized).test(file);
74
+ }
75
+ return file.startsWith(normalized.endsWith("/") ? normalized : `${normalized}/`);
76
+ });
77
+ }
78
+ function toAlertSeverity(tier) {
79
+ return tier === "tier_d" ? "informational" : "review";
80
+ }
81
+ function computeScopeDriftAlerts(input) {
82
+ const claimed = uniqueSorted(input.claimedPaths);
83
+ if (claimed.length === 0)
84
+ return [];
85
+ const alerts = [];
86
+ for (const file of uniqueSorted(input.changedFiles)) {
87
+ if (fileWithinClaimedPaths(file, claimed))
88
+ continue;
89
+ const resolved = (0, domain_resolution_1.resolveDomainForFile)(file, input.domains);
90
+ if (resolved.tier === "tier_a" || resolved.tier === "tier_b")
91
+ continue;
92
+ alerts.push({
93
+ file,
94
+ claimedPaths: claimed,
95
+ likelyDomain: resolved.domain ?? "unknown",
96
+ severity: toAlertSeverity(resolved.tier),
97
+ });
98
+ }
99
+ return alerts;
100
+ }
101
+ function supervisionFromAcceptance(report, domains) {
102
+ const changedFiles = uniqueSorted(report.changed_files);
103
+ const workType = report.detected_work_type;
104
+ let requiredProof = report.required_proof.length > 0 ? uniqueSorted(report.required_proof) : requiredProofHints(workType);
105
+ if (workType === "documentation" &&
106
+ requiredProof.includes("explicit verification evidence for the changed files")) {
107
+ requiredProof = ["explicit verification evidence for changed docs/files"];
108
+ }
109
+ const claimedPaths = report.claimed_paths ?? [];
110
+ const driftAlerts = report.scope_status === "drift"
111
+ ? computeScopeDriftAlerts({ changedFiles, claimedPaths, domains })
112
+ : [];
113
+ return {
114
+ workSessionId: report.work_session_id,
115
+ changeRequestId: report.change_request_id,
116
+ changedFiles,
117
+ workType,
118
+ scopeStatus: report.scope_status,
119
+ boundaryStatus: report.boundary_status,
120
+ decision: report.decision,
121
+ requiredProof,
122
+ knownTraps: report.known_traps.slice(0, 3),
123
+ memoryContext: report.memory_context,
124
+ nextAction: "Run verification command(s), then run agentbridge check.",
125
+ driftAlerts,
126
+ };
127
+ }
128
+ function fallbackSupervisionSnapshot(input) {
129
+ const changedFiles = uniqueSorted(input.changedFiles);
130
+ const workType = inferSupervisionWorkType(changedFiles);
131
+ const requiredProof = requiredProofHints(workType);
132
+ const driftAlerts = computeScopeDriftAlerts({
133
+ changedFiles,
134
+ claimedPaths: [],
135
+ domains: input.domains,
136
+ });
137
+ return {
138
+ workSessionId: input.workSessionId,
139
+ changeRequestId: input.changeRequestId,
140
+ changedFiles,
141
+ workType,
142
+ scopeStatus: driftAlerts.length > 0 ? "drift" : changedFiles.length > 0 ? "clean" : "unknown",
143
+ boundaryStatus: input.unresolvedProtectedCrossing || input.blocked ? "unresolved_crossing" : "clean",
144
+ decision: input.unresolvedProtectedCrossing || input.blocked ? "failed" : "needs_proof",
145
+ requiredProof,
146
+ knownTraps: [],
147
+ nextAction: "Run verification command(s), then run agentbridge check.",
148
+ driftAlerts,
149
+ serverAcceptanceUnavailable: input.serverAcceptanceUnavailable,
150
+ };
151
+ }
152
+ function supervisionSignature(snapshot) {
153
+ return JSON.stringify({
154
+ workSessionId: snapshot.workSessionId,
155
+ changedFiles: snapshot.changedFiles,
156
+ workType: snapshot.workType,
157
+ scopeStatus: snapshot.scopeStatus,
158
+ boundaryStatus: snapshot.boundaryStatus,
159
+ decision: snapshot.decision,
160
+ requiredProof: snapshot.requiredProof,
161
+ drift: snapshot.driftAlerts.map((a) => `${a.file}:${a.severity}:${a.likelyDomain}`),
162
+ serverAcceptanceUnavailable: snapshot.serverAcceptanceUnavailable ? "yes" : "no",
163
+ });
164
+ }
165
+ function renderSupervisionSummary(snapshot) {
166
+ const lines = [];
167
+ lines.push("AgentBridge supervision:");
168
+ lines.push(`Current run: ${snapshot.workSessionId}${snapshot.changeRequestId ? ` / task ${snapshot.changeRequestId}` : ""}`);
169
+ lines.push(`Changed files: ${snapshot.changedFiles.length}`);
170
+ lines.push(`Detected work type: ${snapshot.workType}`);
171
+ lines.push(`Scope: ${snapshot.scopeStatus}`);
172
+ lines.push(`Boundary: ${snapshot.boundaryStatus}`);
173
+ lines.push(`Watch result: ${snapshot.decision}`);
174
+ if (snapshot.requiredProof.length > 0) {
175
+ lines.push("");
176
+ lines.push("Likely required proof:");
177
+ for (const proof of snapshot.requiredProof) {
178
+ lines.push(`- ${proof}`);
179
+ }
180
+ }
181
+ if (snapshot.knownTraps.length > 0) {
182
+ lines.push("");
183
+ lines.push("Known traps:");
184
+ for (const trap of snapshot.knownTraps.slice(0, 3)) {
185
+ lines.push(`- ${trap}`);
186
+ }
187
+ }
188
+ const watchMemoryLines = (0, memory_context_render_1.renderWatchMemoryContext)(snapshot.memoryContext);
189
+ if (watchMemoryLines.length > 0) {
190
+ lines.push("");
191
+ lines.push(...watchMemoryLines);
192
+ }
193
+ if (snapshot.driftAlerts.length > 0) {
194
+ for (const alert of snapshot.driftAlerts) {
195
+ lines.push("");
196
+ lines.push("Files outside scope detected:");
197
+ lines.push(`- file: ${alert.file}`);
198
+ lines.push(`- claimed paths: [${alert.claimedPaths.join(", ")}]`);
199
+ lines.push(`- likely domain: ${alert.likelyDomain}`);
200
+ lines.push(`- severity: ${alert.severity}`);
201
+ lines.push("- suggested action: update scope or confirm this belongs to the current work");
202
+ }
203
+ }
204
+ if (snapshot.serverAcceptanceUnavailable) {
205
+ lines.push("");
206
+ lines.push(`Watch result unavailable: ${snapshot.serverAcceptanceUnavailable}`);
207
+ }
208
+ lines.push("");
209
+ lines.push("Before saying done:");
210
+ lines.push(`- ${snapshot.nextAction}`);
211
+ return lines.join("\n");
212
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.promptLine = promptLine;
4
+ exports.info = info;
5
+ const promises_1 = require("node:readline/promises");
6
+ const node_process_1 = require("node:process");
7
+ async function promptLine(question) {
8
+ const rl = (0, promises_1.createInterface)({ input: node_process_1.stdin, output: node_process_1.stdout });
9
+ try {
10
+ return await rl.question(`${question}\n> `);
11
+ }
12
+ finally {
13
+ rl.close();
14
+ }
15
+ }
16
+ function info(message) {
17
+ node_process_1.stdout.write(`${message}\n`);
18
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.detectTestCommand = detectTestCommand;
4
+ exports.runDetectedTests = runDetectedTests;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_path_1 = require("node:path");
7
+ const node_child_process_1 = require("node:child_process");
8
+ function detectTestCommand(cwd = process.cwd()) {
9
+ const packageJsonPath = (0, node_path_1.resolve)(cwd, "package.json");
10
+ if (!(0, node_fs_1.existsSync)(packageJsonPath))
11
+ return null;
12
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, "utf8"));
13
+ if (!pkg.scripts)
14
+ return null;
15
+ if (pkg.scripts["test:unit"])
16
+ return "npm run test:unit";
17
+ if (pkg.scripts.test)
18
+ return "npm run test";
19
+ return null;
20
+ }
21
+ async function runDetectedTests(timeoutMs = 5 * 60 * 1000) {
22
+ const command = detectTestCommand();
23
+ if (!command)
24
+ return null;
25
+ const started = Date.now();
26
+ return new Promise((resolveResult) => {
27
+ const child = (0, node_child_process_1.spawn)(command, {
28
+ shell: true,
29
+ cwd: process.cwd(),
30
+ env: process.env,
31
+ stdio: ["ignore", "pipe", "pipe"],
32
+ });
33
+ let stdout = "";
34
+ let stderr = "";
35
+ child.stdout?.on("data", (chunk) => {
36
+ stdout += chunk.toString();
37
+ });
38
+ child.stderr?.on("data", (chunk) => {
39
+ stderr += chunk.toString();
40
+ });
41
+ const timeout = setTimeout(() => {
42
+ child.kill("SIGTERM");
43
+ resolveResult({
44
+ command,
45
+ passed: false,
46
+ stdout,
47
+ stderr: `${stderr}\nTimed out after ${timeoutMs}ms`,
48
+ durationMs: Date.now() - started,
49
+ });
50
+ }, timeoutMs);
51
+ child.on("close", (code) => {
52
+ clearTimeout(timeout);
53
+ resolveResult({
54
+ command,
55
+ passed: code === 0,
56
+ stdout,
57
+ stderr,
58
+ durationMs: Date.now() - started,
59
+ });
60
+ });
61
+ });
62
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,185 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.analyzeVerificationCommand = analyzeVerificationCommand;
4
+ exports.buildVerificationConfidence = buildVerificationConfidence;
5
+ exports.renderConfidenceSummary = renderConfidenceSummary;
6
+ exports.renderMockHygieneWarning = renderMockHygieneWarning;
7
+ const node_fs_1 = require("node:fs");
8
+ const node_path_1 = require("node:path");
9
+ const REPEAT_PATTERNS = [
10
+ /--repeat(?:=|\s|$)/i,
11
+ /\bfor\s+\(?\s*\w+\s+in\s+/i,
12
+ /\bwhile\s+\(/i,
13
+ /\bseq\s+\d+/i,
14
+ ];
15
+ const SHUFFLE_PATTERNS = [
16
+ /--sequence\.shuffle/i,
17
+ /--shuffle(?:=|\s|$)/i,
18
+ /--random(?:=|\s|$)/i,
19
+ /\bvitest\s+run\b.*--sequence\.shuffle/i,
20
+ /\bnpm\s+test\b.*--sequence\.shuffle/i,
21
+ ];
22
+ const SEED_PATTERNS = [/--seed(?:=|\s+)(\S+)/i, /\bVITEST_SEED=(\S+)/i, /\bSEED=(\S+)/i];
23
+ function isTestFile(path) {
24
+ const normalized = path.replaceAll("\\", "/").toLowerCase();
25
+ return (normalized.endsWith(".test.ts") ||
26
+ normalized.endsWith(".test.tsx") ||
27
+ normalized.endsWith(".spec.ts") ||
28
+ normalized.endsWith(".spec.tsx"));
29
+ }
30
+ function inferIntegrationLevel(command) {
31
+ const lower = command.toLowerCase();
32
+ if (/\b(dogfood|browse|playwright|e2e|cypress)\b/.test(lower))
33
+ return "dogfood";
34
+ if (/\b(integration|acceptance-check|work-sessions)\b/.test(lower))
35
+ return "integration";
36
+ if (/\b(vitest|jest|mocha|npm test|pnpm test|yarn test)\b/.test(lower))
37
+ return "unit";
38
+ if (/\b(test -f|test -d|manual)\b/.test(lower))
39
+ return "manual";
40
+ return "unknown";
41
+ }
42
+ function parseSeed(command) {
43
+ for (const pattern of SEED_PATTERNS) {
44
+ const match = command.match(pattern);
45
+ if (match?.[1])
46
+ return match[1];
47
+ }
48
+ return undefined;
49
+ }
50
+ function detectRepeated(command) {
51
+ return REPEAT_PATTERNS.some((pattern) => pattern.test(command));
52
+ }
53
+ function detectRandomizedOrShuffled(command) {
54
+ return SHUFFLE_PATTERNS.some((pattern) => pattern.test(command));
55
+ }
56
+ function scanFileForMockHygiene(filePath, cwd) {
57
+ let content = "";
58
+ try {
59
+ content = (0, node_fs_1.readFileSync)((0, node_path_1.join)(cwd, filePath), "utf8");
60
+ }
61
+ catch {
62
+ return { risk: "unknown" };
63
+ }
64
+ const clearAllMocks_used = /\bclearAllMocks\s*\(/.test(content);
65
+ const resetAllMocks_used = /\bresetAllMocks\s*\(/.test(content);
66
+ const mockImplementation_used = /\bmockImplementation\s*\(/.test(content);
67
+ let risk = "none";
68
+ if (clearAllMocks_used || resetAllMocks_used || mockImplementation_used) {
69
+ if (clearAllMocks_used && mockImplementation_used && !resetAllMocks_used) {
70
+ risk = "high";
71
+ }
72
+ else if (clearAllMocks_used && !resetAllMocks_used) {
73
+ risk = "medium";
74
+ }
75
+ else {
76
+ risk = "low";
77
+ }
78
+ }
79
+ return {
80
+ clearAllMocks_used,
81
+ resetAllMocks_used,
82
+ mockImplementation_used,
83
+ risk,
84
+ };
85
+ }
86
+ function aggregateMockHygiene(scopes, cwd) {
87
+ const testFiles = scopes.filter(isTestFile);
88
+ if (testFiles.length === 0)
89
+ return undefined;
90
+ const scans = testFiles.map((file) => scanFileForMockHygiene(file, cwd));
91
+ const clearAllMocks_used = scans.some((s) => s.clearAllMocks_used);
92
+ const resetAllMocks_used = scans.some((s) => s.resetAllMocks_used);
93
+ const mockImplementation_used = scans.some((s) => s.mockImplementation_used);
94
+ const riskOrder = ["high", "medium", "low", "unknown", "none"];
95
+ const observed = new Set(scans.map((scan) => scan.risk ?? "unknown"));
96
+ const risk = riskOrder.find((candidate) => observed.has(candidate)) ?? "none";
97
+ return {
98
+ clearAllMocks_used,
99
+ resetAllMocks_used,
100
+ mockImplementation_used,
101
+ risk,
102
+ };
103
+ }
104
+ function analyzeVerificationCommand(input) {
105
+ const repeated = detectRepeated(input.command);
106
+ const randomized_or_shuffled = detectRandomizedOrShuffled(input.command);
107
+ const run_count = repeated ? 2 : 1;
108
+ return {
109
+ run_count,
110
+ repeated,
111
+ randomized_or_shuffled,
112
+ seed: parseSeed(input.command),
113
+ integration_level: inferIntegrationLevel(input.command),
114
+ server_backed: input.serverBacked,
115
+ proof_scope_source: input.proofScopeSource,
116
+ mock_hygiene: aggregateMockHygiene(input.proofScopeFiles, input.cwd ?? process.cwd()),
117
+ };
118
+ }
119
+ function buildVerificationConfidence(input) {
120
+ const reasons = [];
121
+ const missing_robustness = [];
122
+ if (input.status === "failed") {
123
+ return {
124
+ level: "low",
125
+ reasons: ["verification command exited non-zero"],
126
+ missing_robustness: ["passing verification run"],
127
+ };
128
+ }
129
+ reasons.push("single targeted verification run passed");
130
+ if (input.conditions.repeated) {
131
+ reasons.push("command indicates repeated execution");
132
+ }
133
+ else {
134
+ missing_robustness.push("repeated run");
135
+ }
136
+ if (input.conditions.randomized_or_shuffled) {
137
+ reasons.push("command indicates randomized or shuffled execution");
138
+ }
139
+ else {
140
+ missing_robustness.push("randomized_or_shuffled proof");
141
+ }
142
+ const hasRepeated = Boolean(input.conditions.repeated);
143
+ const hasShuffle = Boolean(input.conditions.randomized_or_shuffled);
144
+ if (hasRepeated && hasShuffle) {
145
+ return {
146
+ level: "high",
147
+ reasons: [...reasons, "repeated and shuffled proof conditions met"],
148
+ missing_robustness: [],
149
+ };
150
+ }
151
+ return {
152
+ level: "medium",
153
+ reasons,
154
+ missing_robustness,
155
+ };
156
+ }
157
+ function renderConfidenceSummary(confidence) {
158
+ const lines = [`Proof confidence: ${confidence.level}`];
159
+ if (confidence.reasons.length > 0) {
160
+ lines.push("Reasons:");
161
+ for (const reason of confidence.reasons) {
162
+ lines.push(` - ${reason}`);
163
+ }
164
+ }
165
+ if (confidence.missing_robustness.length > 0) {
166
+ lines.push("Missing robustness:");
167
+ for (const item of confidence.missing_robustness) {
168
+ lines.push(` - ${item}`);
169
+ }
170
+ }
171
+ return lines;
172
+ }
173
+ function renderMockHygieneWarning(hygiene) {
174
+ if (!hygiene || !hygiene.risk || hygiene.risk === "none" || hygiene.risk === "low") {
175
+ return null;
176
+ }
177
+ if (hygiene.risk === "high") {
178
+ return ("Mock hygiene: high risk — clearAllMocks and mockImplementation without resetAllMocks " +
179
+ "in proof-scope test files.");
180
+ }
181
+ if (hygiene.risk === "medium") {
182
+ return "Mock hygiene: medium risk — clearAllMocks without resetAllMocks in proof-scope test files.";
183
+ }
184
+ return "Mock hygiene: review mock reset patterns in proof-scope test files.";
185
+ }
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applyFileChange = applyFileChange;
4
+ exports.applyBoundaryDecision = applyBoundaryDecision;
5
+ exports.canCloseSession = canCloseSession;
6
+ exports.inferTierFromDomainName = inferTierFromDomainName;
7
+ exports.toDomainOwnershipInput = toDomainOwnershipInput;
8
+ const domain_resolution_1 = require("./domain-resolution");
9
+ const claimed_paths_1 = require("./claimed-paths");
10
+ function findCrossing(state, file) {
11
+ return state.crossings.find((crossing) => crossing.file === file);
12
+ }
13
+ function applyFileChange(state, file) {
14
+ if (!state.changedFiles.includes(file)) {
15
+ state.changedFiles.push(file);
16
+ }
17
+ const resolved = (0, domain_resolution_1.resolveDomainForFile)(file, state.domains);
18
+ if (!state.laneDomain) {
19
+ return {
20
+ requiresAuthorityPrompt: false,
21
+ requestType: null,
22
+ crossingDomain: resolved.domain,
23
+ crossingTier: resolved.tier,
24
+ };
25
+ }
26
+ if (!resolved.domain) {
27
+ const claimedPaths = state.claimedPaths ?? [];
28
+ if (claimedPaths.length > 0 && (0, claimed_paths_1.fileWithinClaimedPaths)(file, claimedPaths)) {
29
+ return {
30
+ requiresAuthorityPrompt: false,
31
+ requestType: null,
32
+ crossingDomain: null,
33
+ crossingTier: "tier_d",
34
+ };
35
+ }
36
+ const existing = findCrossing(state, file);
37
+ if (!existing) {
38
+ state.crossings.push({
39
+ file,
40
+ domain: null,
41
+ tier: "tier_d",
42
+ status: "unresolved",
43
+ });
44
+ }
45
+ return {
46
+ requiresAuthorityPrompt: true,
47
+ requestType: "handshake",
48
+ crossingDomain: null,
49
+ crossingTier: "tier_d",
50
+ };
51
+ }
52
+ if (resolved.domain === state.laneDomain) {
53
+ return {
54
+ requiresAuthorityPrompt: false,
55
+ requestType: null,
56
+ crossingDomain: resolved.domain,
57
+ crossingTier: resolved.tier,
58
+ };
59
+ }
60
+ const existing = findCrossing(state, file);
61
+ if (existing &&
62
+ existing.status !== "unresolved" &&
63
+ existing.status !== "pending_handshake") {
64
+ return {
65
+ requiresAuthorityPrompt: false,
66
+ requestType: null,
67
+ crossingDomain: existing.domain,
68
+ crossingTier: existing.tier,
69
+ };
70
+ }
71
+ if (existing?.status === "pending_handshake") {
72
+ return {
73
+ requiresAuthorityPrompt: false,
74
+ requestType: null,
75
+ crossingDomain: existing.domain,
76
+ crossingTier: existing.tier,
77
+ };
78
+ }
79
+ if (!existing) {
80
+ state.crossings.push({
81
+ file,
82
+ domain: resolved.domain,
83
+ tier: resolved.tier,
84
+ status: resolved.tier === "tier_d" ? "logged" : "unresolved",
85
+ });
86
+ }
87
+ if (resolved.tier === "tier_a" || resolved.tier === "tier_b") {
88
+ state.blockedAt = new Date().toISOString();
89
+ state.status = "blocked";
90
+ return {
91
+ requiresAuthorityPrompt: true,
92
+ requestType: "authority",
93
+ crossingDomain: resolved.domain,
94
+ crossingTier: resolved.tier,
95
+ };
96
+ }
97
+ if (resolved.tier === "tier_c") {
98
+ return {
99
+ requiresAuthorityPrompt: true,
100
+ requestType: "handshake",
101
+ crossingDomain: resolved.domain,
102
+ crossingTier: resolved.tier,
103
+ };
104
+ }
105
+ return {
106
+ requiresAuthorityPrompt: false,
107
+ requestType: null,
108
+ crossingDomain: resolved.domain,
109
+ crossingTier: resolved.tier,
110
+ };
111
+ }
112
+ function clearBlockedIfResolved(state) {
113
+ const unresolvedProtected = state.crossings.some((crossing) => crossing.status === "unresolved" &&
114
+ (crossing.tier === "tier_a" || crossing.tier === "tier_b"));
115
+ if (!unresolvedProtected) {
116
+ state.blockedAt = undefined;
117
+ if (state.status !== "closed") {
118
+ state.status = "active";
119
+ }
120
+ }
121
+ }
122
+ function applyBoundaryDecision(state, file, decision, handlers, options) {
123
+ const crossing = findCrossing(state, file);
124
+ if (!crossing) {
125
+ throw new Error(`No crossing found for file ${file}`);
126
+ }
127
+ switch (decision) {
128
+ case "approve":
129
+ crossing.status = "approved";
130
+ state.approvals.push({
131
+ domain: crossing.domain ?? "unknown",
132
+ file,
133
+ mode: "file_only",
134
+ createdAt: new Date().toISOString(),
135
+ });
136
+ clearBlockedIfResolved(state);
137
+ return;
138
+ case "deny":
139
+ handlers.revertFile(file);
140
+ crossing.status = "denied";
141
+ clearBlockedIfResolved(state);
142
+ return;
143
+ case "limit_scope":
144
+ crossing.status = "limited";
145
+ state.approvals.push({
146
+ domain: crossing.domain ?? "unknown",
147
+ file,
148
+ mode: options?.limitMode ?? "file_only",
149
+ createdAt: new Date().toISOString(),
150
+ });
151
+ clearBlockedIfResolved(state);
152
+ return;
153
+ case "handoff":
154
+ crossing.status = "handoff";
155
+ state.status = "closed";
156
+ state.closeReason = "handoff_required";
157
+ return;
158
+ case "abandon":
159
+ handlers.revertAll([...state.changedFiles]);
160
+ crossing.status = "abandoned";
161
+ state.status = "closed";
162
+ state.closeReason = "abandoned";
163
+ state.blockedAt = undefined;
164
+ return;
165
+ default:
166
+ return;
167
+ }
168
+ }
169
+ function canCloseSession(state) {
170
+ const unresolved = state.crossings.some((crossing) => crossing.status === "unresolved");
171
+ if (unresolved) {
172
+ return { ok: false, reason: "Session has unresolved authority crossings." };
173
+ }
174
+ return { ok: true };
175
+ }
176
+ function inferTierFromDomainName(domain) {
177
+ const value = domain.toLowerCase();
178
+ if (value.includes("auth") ||
179
+ value.includes("billing") ||
180
+ value.includes("payment") ||
181
+ value.includes("webhook") ||
182
+ value.includes("database") ||
183
+ value.includes("schema") ||
184
+ value.includes("secret") ||
185
+ value.includes("permission") ||
186
+ value.includes("middleware") ||
187
+ value.includes("deploy")) {
188
+ return "tier_a";
189
+ }
190
+ if (value.includes("api") ||
191
+ value.includes("backend") ||
192
+ value.includes("frontend") ||
193
+ value.includes("service")) {
194
+ return "tier_b";
195
+ }
196
+ if (value.includes("shared") || value.includes("infra") || value.includes("test") || value.includes("util")) {
197
+ return "tier_c";
198
+ }
199
+ return "tier_d";
200
+ }
201
+ function toDomainOwnershipInput(domains) {
202
+ return domains.map((domain) => ({
203
+ domain: domain.domain,
204
+ pathPatterns: domain.pathPatterns,
205
+ ownerAgentId: domain.ownerAgentId ?? `${domain.domain} Agent`,
206
+ tier: domain.tier ?? inferTierFromDomainName(domain.domain),
207
+ }));
208
+ }