@backendkit-labs/agent-enterprise 0.3.2 → 0.4.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/README.md +2 -0
- package/dist/__tests__/integration/EventPublisher.integration.test.d.ts +2 -0
- package/dist/__tests__/integration/EventPublisher.integration.test.d.ts.map +1 -0
- package/dist/__tests__/integration/EventPublisher.integration.test.js +122 -0
- package/dist/__tests__/integration/EventPublisher.integration.test.js.map +1 -0
- package/dist/__tests__/unit/ComplianceGate.test.d.ts +2 -0
- package/dist/__tests__/unit/ComplianceGate.test.d.ts.map +1 -0
- package/dist/__tests__/unit/ComplianceGate.test.js +271 -0
- package/dist/__tests__/unit/ComplianceGate.test.js.map +1 -0
- package/dist/__tests__/unit/EventPublisher.test.d.ts +2 -0
- package/dist/__tests__/unit/EventPublisher.test.d.ts.map +1 -0
- package/dist/__tests__/unit/EventPublisher.test.js +160 -0
- package/dist/__tests__/unit/EventPublisher.test.js.map +1 -0
- package/dist/__tests__/unit/FlowLoader.test.d.ts +2 -0
- package/dist/__tests__/unit/FlowLoader.test.d.ts.map +1 -0
- package/dist/__tests__/unit/FlowLoader.test.js +283 -0
- package/dist/__tests__/unit/FlowLoader.test.js.map +1 -0
- package/dist/__tests__/unit/FlowRunner.test.d.ts +2 -0
- package/dist/__tests__/unit/FlowRunner.test.d.ts.map +1 -0
- package/dist/__tests__/unit/FlowRunner.test.js +386 -0
- package/dist/__tests__/unit/FlowRunner.test.js.map +1 -0
- package/dist/__tests__/unit/PolicySeeder.test.d.ts +2 -0
- package/dist/__tests__/unit/PolicySeeder.test.d.ts.map +1 -0
- package/dist/__tests__/unit/PolicySeeder.test.js +146 -0
- package/dist/__tests__/unit/PolicySeeder.test.js.map +1 -0
- package/dist/__tests__/unit/ProfilePolicies.test.d.ts +2 -0
- package/dist/__tests__/unit/ProfilePolicies.test.d.ts.map +1 -0
- package/dist/__tests__/unit/ProfilePolicies.test.js +70 -0
- package/dist/__tests__/unit/ProfilePolicies.test.js.map +1 -0
- package/dist/__tests__/unit/ReconciliationSweeper.test.d.ts +2 -0
- package/dist/__tests__/unit/ReconciliationSweeper.test.d.ts.map +1 -0
- package/dist/__tests__/unit/ReconciliationSweeper.test.js +235 -0
- package/dist/__tests__/unit/ReconciliationSweeper.test.js.map +1 -0
- package/dist/__tests__/unit/n8nTriggerTool.test.d.ts +2 -0
- package/dist/__tests__/unit/n8nTriggerTool.test.d.ts.map +1 -0
- package/dist/__tests__/unit/n8nTriggerTool.test.js +103 -0
- package/dist/__tests__/unit/n8nTriggerTool.test.js.map +1 -0
- package/dist/events/EventPublisher.d.ts +47 -0
- package/dist/events/EventPublisher.d.ts.map +1 -0
- package/dist/events/EventPublisher.js +75 -0
- package/dist/events/EventPublisher.js.map +1 -0
- package/dist/events/ReconciliationSweeper.d.ts +37 -0
- package/dist/events/ReconciliationSweeper.d.ts.map +1 -0
- package/dist/events/ReconciliationSweeper.js +151 -0
- package/dist/events/ReconciliationSweeper.js.map +1 -0
- package/dist/events/index.d.ts +6 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +8 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/types.d.ts +48 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +3 -0
- package/dist/events/types.js.map +1 -0
- package/dist/factory.d.ts +6 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +11 -5
- package/dist/factory.js.map +1 -1
- package/dist/flow/ComplianceGate.d.ts +90 -0
- package/dist/flow/ComplianceGate.d.ts.map +1 -0
- package/dist/flow/ComplianceGate.js +214 -0
- package/dist/flow/ComplianceGate.js.map +1 -0
- package/dist/flow/FlowLoader.d.ts +22 -0
- package/dist/flow/FlowLoader.d.ts.map +1 -0
- package/dist/flow/FlowLoader.js +170 -0
- package/dist/flow/FlowLoader.js.map +1 -0
- package/dist/flow/FlowRunner.d.ts +73 -0
- package/dist/flow/FlowRunner.d.ts.map +1 -0
- package/dist/flow/FlowRunner.js +162 -0
- package/dist/flow/FlowRunner.js.map +1 -0
- package/dist/flow/index.d.ts +8 -0
- package/dist/flow/index.d.ts.map +1 -0
- package/dist/flow/index.js +21 -0
- package/dist/flow/index.js.map +1 -0
- package/dist/flow/types.d.ts +306 -0
- package/dist/flow/types.d.ts.map +1 -0
- package/dist/flow/types.js +63 -0
- package/dist/flow/types.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/index.d.ts +3 -0
- package/dist/integrations/index.d.ts.map +1 -0
- package/dist/integrations/index.js +6 -0
- package/dist/integrations/index.js.map +1 -0
- package/dist/integrations/n8nTriggerTool.d.ts +12 -0
- package/dist/integrations/n8nTriggerTool.d.ts.map +1 -0
- package/dist/integrations/n8nTriggerTool.js +45 -0
- package/dist/integrations/n8nTriggerTool.js.map +1 -0
- package/dist/policies/PolicySeeder.d.ts +31 -0
- package/dist/policies/PolicySeeder.d.ts.map +1 -0
- package/dist/policies/PolicySeeder.js +270 -0
- package/dist/policies/PolicySeeder.js.map +1 -0
- package/dist/policies/index.d.ts +3 -0
- package/dist/policies/index.d.ts.map +1 -0
- package/dist/policies/index.js +6 -0
- package/dist/policies/index.js.map +1 -0
- package/dist/profiles/constants.d.ts +4 -0
- package/dist/profiles/constants.d.ts.map +1 -1
- package/dist/profiles/constants.js +4 -0
- package/dist/profiles/constants.js.map +1 -1
- package/dist/profiles/finance.d.ts.map +1 -1
- package/dist/profiles/finance.js +7 -8
- package/dist/profiles/finance.js.map +1 -1
- package/dist/profiles/general.d.ts.map +1 -1
- package/dist/profiles/general.js +1 -0
- package/dist/profiles/general.js.map +1 -1
- package/dist/profiles/hr.d.ts.map +1 -1
- package/dist/profiles/hr.js +6 -8
- package/dist/profiles/hr.js.map +1 -1
- package/dist/profiles/ops.d.ts.map +1 -1
- package/dist/profiles/ops.js +6 -10
- package/dist/profiles/ops.js.map +1 -1
- package/dist/profiles/sales.d.ts.map +1 -1
- package/dist/profiles/sales.js +3 -1
- package/dist/profiles/sales.js.map +1 -1
- package/dist/profiles/support.d.ts.map +1 -1
- package/dist/profiles/support.js +2 -4
- package/dist/profiles/support.js.map +1 -1
- package/dist/rag/OllamaEmbedder.d.ts +6 -0
- package/dist/rag/OllamaEmbedder.d.ts.map +1 -1
- package/dist/rag/OllamaEmbedder.js +75 -16
- package/dist/rag/OllamaEmbedder.js.map +1 -1
- package/dist/rag/VaultWriter.d.ts +3 -0
- package/dist/rag/VaultWriter.d.ts.map +1 -1
- package/dist/rag/VaultWriter.js +63 -17
- package/dist/rag/VaultWriter.js.map +1 -1
- package/dist/reflection/EnterpriseReflection.d.ts +108 -0
- package/dist/reflection/EnterpriseReflection.d.ts.map +1 -0
- package/dist/reflection/EnterpriseReflection.js +296 -0
- package/dist/reflection/EnterpriseReflection.js.map +1 -0
- package/dist/reflection/GateReflectionBridge.d.ts +21 -0
- package/dist/reflection/GateReflectionBridge.d.ts.map +1 -0
- package/dist/reflection/GateReflectionBridge.js +120 -0
- package/dist/reflection/GateReflectionBridge.js.map +1 -0
- package/dist/reflection/domains.d.ts +6 -0
- package/dist/reflection/domains.d.ts.map +1 -0
- package/dist/reflection/domains.js +176 -0
- package/dist/reflection/domains.js.map +1 -0
- package/dist/reflection/index.d.ts +6 -0
- package/dist/reflection/index.d.ts.map +1 -0
- package/dist/reflection/index.js +11 -0
- package/dist/reflection/index.js.map +1 -0
- package/package.json +6 -2
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Structural compliance test for Fase 4 — Policies-in-vault (ADR-4).
|
|
5
|
+
*
|
|
6
|
+
* Criterio de salida: grep of hardcoded thresholds/matrices in ALL system prompts → 0 matches.
|
|
7
|
+
* The prompts must instruct agents to retrieve current policies via their search tool at runtime.
|
|
8
|
+
*/
|
|
9
|
+
const profiles_1 = require("../../profiles");
|
|
10
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
11
|
+
function violations(prompt, pattern) {
|
|
12
|
+
const matches = [];
|
|
13
|
+
for (const line of prompt.split('\n')) {
|
|
14
|
+
if (pattern.test(line))
|
|
15
|
+
matches.push(line.trim());
|
|
16
|
+
}
|
|
17
|
+
return matches;
|
|
18
|
+
}
|
|
19
|
+
// ── Criterio de salida: zero hardcoded thresholds ─────────────────────────────
|
|
20
|
+
describe('Fase 4 — criterio de salida: no hardcoded business rules in system prompts', () => {
|
|
21
|
+
for (const profile of profiles_1.ALL_ENTERPRISE_PROFILES) {
|
|
22
|
+
const prompt = profile.systemPrompt ?? '';
|
|
23
|
+
it(`[${profile.id}] must not contain hardcoded currency amounts ($NNN)`, () => {
|
|
24
|
+
const found = violations(prompt, /\$\d/);
|
|
25
|
+
expect(found).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
it(`[${profile.id}] must not contain hardcoded percentage thresholds (NN%)`, () => {
|
|
28
|
+
// Exclude "100%" which is never a business threshold
|
|
29
|
+
const found = violations(prompt, /\b(?!100)\d+%/);
|
|
30
|
+
expect(found).toEqual([]);
|
|
31
|
+
});
|
|
32
|
+
it(`[${profile.id}] must not contain hardcoded severity codes (P1:, P2:, P3:, P4:)`, () => {
|
|
33
|
+
const found = violations(prompt, /\bP[1-4]:/);
|
|
34
|
+
expect(found).toEqual([]);
|
|
35
|
+
});
|
|
36
|
+
it(`[${profile.id}] must not contain hardcoded escalation tiers (Tier N:)`, () => {
|
|
37
|
+
const found = violations(prompt, /\bTier\s+[123]:/i);
|
|
38
|
+
expect(found).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
// ── Prompts must reference their search tool for policy lookups ───────────────
|
|
43
|
+
describe('Profiles — prompts instruct to use search tool for policy lookups', () => {
|
|
44
|
+
const POLICY_AGENTS = [
|
|
45
|
+
{ id: 'finance', tool: 'search_finance_kb', keyword: 'approval' },
|
|
46
|
+
{ id: 'hr', tool: 'search_hr_kb', keyword: 'privacy' },
|
|
47
|
+
{ id: 'sales', tool: 'search_sales_kb', keyword: 'discount' },
|
|
48
|
+
{ id: 'ops', tool: 'search_ops_kb', keyword: 'severity' },
|
|
49
|
+
{ id: 'support', tool: 'search_support_kb', keyword: 'escalation' },
|
|
50
|
+
];
|
|
51
|
+
for (const { id, tool, keyword } of POLICY_AGENTS) {
|
|
52
|
+
const profile = profiles_1.ALL_ENTERPRISE_PROFILES.find(p => p.id === id);
|
|
53
|
+
it(`[${id}] prompt references its domain search tool (${tool})`, () => {
|
|
54
|
+
expect(profile.systemPrompt).toContain(tool);
|
|
55
|
+
});
|
|
56
|
+
it(`[${id}] prompt contains a policy search instruction related to '${keyword}'`, () => {
|
|
57
|
+
expect(profile.systemPrompt?.toLowerCase()).toContain(keyword);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
// ── All profiles have non-empty systemPrompts ─────────────────────────────────
|
|
62
|
+
describe('All profiles have well-formed system prompts', () => {
|
|
63
|
+
for (const profile of profiles_1.ALL_ENTERPRISE_PROFILES) {
|
|
64
|
+
it(`[${profile.id}] has a non-empty systemPrompt`, () => {
|
|
65
|
+
expect(typeof profile.systemPrompt).toBe('string');
|
|
66
|
+
expect((profile.systemPrompt ?? '').length).toBeGreaterThan(50);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=ProfilePolicies.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProfilePolicies.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/ProfilePolicies.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReconciliationSweeper.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/ReconciliationSweeper.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
const fs = __importStar(require("fs/promises"));
|
|
37
|
+
const path = __importStar(require("path"));
|
|
38
|
+
const os = __importStar(require("os"));
|
|
39
|
+
const ReconciliationSweeper_1 = require("../../events/ReconciliationSweeper");
|
|
40
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
41
|
+
async function makeTmpVault() {
|
|
42
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), 'sweeper-'));
|
|
43
|
+
await fs.mkdir(path.join(dir, 'eventos'), { recursive: true });
|
|
44
|
+
await fs.mkdir(path.join(dir, 'auditoria'), { recursive: true });
|
|
45
|
+
return dir;
|
|
46
|
+
}
|
|
47
|
+
function eventNote(eventId, type = 'contratacion', source = 'hr-system') {
|
|
48
|
+
return [
|
|
49
|
+
'---',
|
|
50
|
+
`title: "Event: ${type} (${eventId})"`,
|
|
51
|
+
`author: "agent/${source}"`,
|
|
52
|
+
`date: 2026-06-11`,
|
|
53
|
+
`tags: ["evento"]`,
|
|
54
|
+
'---',
|
|
55
|
+
'',
|
|
56
|
+
`## Event: ${type}`,
|
|
57
|
+
'',
|
|
58
|
+
'| Field | Value |',
|
|
59
|
+
'|-------|-------|',
|
|
60
|
+
`| ID | \`${eventId}\` |`,
|
|
61
|
+
`| Type | \`${type}\` |`,
|
|
62
|
+
`| Occurred | 2026-06-11T10:00:00.000Z |`,
|
|
63
|
+
`| Source | \`${source}\` |`,
|
|
64
|
+
`| Version | 1 |`,
|
|
65
|
+
].join('\n');
|
|
66
|
+
}
|
|
67
|
+
function auditNote(eventId) {
|
|
68
|
+
return `# Audit\n\nProcessed event ${eventId} successfully.\n`;
|
|
69
|
+
}
|
|
70
|
+
async function writeEventNote(vault, eventId, type = 'contratacion') {
|
|
71
|
+
const file = path.join(vault, 'eventos', `${eventId}.md`);
|
|
72
|
+
await fs.writeFile(file, eventNote(eventId, type), 'utf-8');
|
|
73
|
+
}
|
|
74
|
+
async function writeAuditNote(vault, eventId, subfolder = '') {
|
|
75
|
+
const dir = subfolder
|
|
76
|
+
? path.join(vault, 'auditoria', subfolder)
|
|
77
|
+
: path.join(vault, 'auditoria');
|
|
78
|
+
await fs.mkdir(dir, { recursive: true });
|
|
79
|
+
await fs.writeFile(path.join(dir, `${eventId}-audit.md`), auditNote(eventId), 'utf-8');
|
|
80
|
+
}
|
|
81
|
+
// ── sweep — empty / missing folders ──────────────────────────────────────────
|
|
82
|
+
describe('ReconciliationSweeper — empty vault', () => {
|
|
83
|
+
it('returns empty report when eventos folder is missing', async () => {
|
|
84
|
+
const vault = await fs.mkdtemp(path.join(os.tmpdir(), 'sw-'));
|
|
85
|
+
try {
|
|
86
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
87
|
+
const report = await sweeper.sweep();
|
|
88
|
+
expect(report.checked).toBe(0);
|
|
89
|
+
expect(report.orphaned).toEqual([]);
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await fs.rm(vault, { recursive: true, force: true });
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
it('returns empty report when both folders are empty', async () => {
|
|
96
|
+
const vault = await makeTmpVault();
|
|
97
|
+
try {
|
|
98
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
99
|
+
const report = await sweeper.sweep();
|
|
100
|
+
expect(report.checked).toBe(0);
|
|
101
|
+
expect(report.matched).toBe(0);
|
|
102
|
+
}
|
|
103
|
+
finally {
|
|
104
|
+
await fs.rm(vault, { recursive: true, force: true });
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
// ── sweep — matching ──────────────────────────────────────────────────────────
|
|
109
|
+
describe('ReconciliationSweeper — matched events', () => {
|
|
110
|
+
let vault;
|
|
111
|
+
beforeEach(async () => { vault = await makeTmpVault(); });
|
|
112
|
+
afterEach(async () => { await fs.rm(vault, { recursive: true, force: true }); });
|
|
113
|
+
it('reports checked=1, matched=1, orphaned=[] when event has an audit note', async () => {
|
|
114
|
+
const id = 'evt-abc-001';
|
|
115
|
+
await writeEventNote(vault, id);
|
|
116
|
+
await writeAuditNote(vault, id);
|
|
117
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
118
|
+
const report = await sweeper.sweep();
|
|
119
|
+
expect(report.checked).toBe(1);
|
|
120
|
+
expect(report.matched).toBe(1);
|
|
121
|
+
expect(report.orphaned).toEqual([]);
|
|
122
|
+
});
|
|
123
|
+
it('matches event IDs in nested audit subfolders', async () => {
|
|
124
|
+
const id = 'evt-abc-002';
|
|
125
|
+
await writeEventNote(vault, id);
|
|
126
|
+
await writeAuditNote(vault, id, 'onboarding/run-xyz');
|
|
127
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
128
|
+
const report = await sweeper.sweep();
|
|
129
|
+
expect(report.matched).toBe(1);
|
|
130
|
+
expect(report.orphaned).toEqual([]);
|
|
131
|
+
});
|
|
132
|
+
it('handles multiple events — all with audit notes', async () => {
|
|
133
|
+
for (let i = 0; i < 3; i++) {
|
|
134
|
+
const id = `evt-abc-${i}`;
|
|
135
|
+
await writeEventNote(vault, id);
|
|
136
|
+
await writeAuditNote(vault, id);
|
|
137
|
+
}
|
|
138
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
139
|
+
const report = await sweeper.sweep();
|
|
140
|
+
expect(report.checked).toBe(3);
|
|
141
|
+
expect(report.matched).toBe(3);
|
|
142
|
+
expect(report.orphaned).toHaveLength(0);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
// ── sweep — orphaned events ───────────────────────────────────────────────────
|
|
146
|
+
describe('ReconciliationSweeper — orphaned events', () => {
|
|
147
|
+
let vault;
|
|
148
|
+
beforeEach(async () => { vault = await makeTmpVault(); });
|
|
149
|
+
afterEach(async () => { await fs.rm(vault, { recursive: true, force: true }); });
|
|
150
|
+
it('reports an orphaned event when no audit note exists', async () => {
|
|
151
|
+
const id = 'evt-orphan-001';
|
|
152
|
+
await writeEventNote(vault, id, 'contratacion');
|
|
153
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
154
|
+
const report = await sweeper.sweep();
|
|
155
|
+
expect(report.checked).toBe(1);
|
|
156
|
+
expect(report.matched).toBe(0);
|
|
157
|
+
expect(report.orphaned).toHaveLength(1);
|
|
158
|
+
expect(report.orphaned[0].eventId).toBe(id);
|
|
159
|
+
expect(report.orphaned[0].type).toBe('contratacion');
|
|
160
|
+
});
|
|
161
|
+
it('reports partial orphans: 1 matched, 1 orphaned', async () => {
|
|
162
|
+
const matched = 'evt-matched-001';
|
|
163
|
+
const orphan = 'evt-orphan-002';
|
|
164
|
+
await writeEventNote(vault, matched);
|
|
165
|
+
await writeAuditNote(vault, matched);
|
|
166
|
+
await writeEventNote(vault, orphan);
|
|
167
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
168
|
+
const report = await sweeper.sweep();
|
|
169
|
+
expect(report.checked).toBe(2);
|
|
170
|
+
expect(report.matched).toBe(1);
|
|
171
|
+
expect(report.orphaned.map(o => o.eventId)).toEqual([orphan]);
|
|
172
|
+
});
|
|
173
|
+
it('includes notePath pointing to the event note file', async () => {
|
|
174
|
+
const id = 'evt-orphan-path';
|
|
175
|
+
await writeEventNote(vault, id);
|
|
176
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
177
|
+
const report = await sweeper.sweep();
|
|
178
|
+
expect(report.orphaned[0].notePath).toContain('eventos');
|
|
179
|
+
expect(report.orphaned[0].notePath).toContain('.md');
|
|
180
|
+
});
|
|
181
|
+
it('report.sweptAt is a valid ISO timestamp', async () => {
|
|
182
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
183
|
+
const report = await sweeper.sweep();
|
|
184
|
+
expect(report.sweptAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
// ── Custom folders ────────────────────────────────────────────────────────────
|
|
188
|
+
describe('ReconciliationSweeper — custom folders', () => {
|
|
189
|
+
it('respects custom eventsFolder and auditFolder options', async () => {
|
|
190
|
+
const vault = await fs.mkdtemp(path.join(os.tmpdir(), 'sw-custom-'));
|
|
191
|
+
try {
|
|
192
|
+
const id = 'evt-custom-001';
|
|
193
|
+
await fs.mkdir(path.join(vault, 'pub'), { recursive: true });
|
|
194
|
+
await fs.mkdir(path.join(vault, 'logs'), { recursive: true });
|
|
195
|
+
await fs.writeFile(path.join(vault, 'pub', `${id}.md`), eventNote(id), 'utf-8');
|
|
196
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({
|
|
197
|
+
vaultPath: vault,
|
|
198
|
+
eventsFolder: 'pub',
|
|
199
|
+
auditFolder: 'logs',
|
|
200
|
+
});
|
|
201
|
+
const report = await sweeper.sweep();
|
|
202
|
+
expect(report.checked).toBe(1);
|
|
203
|
+
expect(report.orphaned).toHaveLength(1);
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
await fs.rm(vault, { recursive: true, force: true });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// ── Criterio de salida fase 3 ─────────────────────────────────────────────────
|
|
211
|
+
describe('Fase 3 — criterio de salida (sweeper detects injected orphan)', () => {
|
|
212
|
+
it('detects an event without an audit note injected on purpose', async () => {
|
|
213
|
+
const vault = await makeTmpVault();
|
|
214
|
+
try {
|
|
215
|
+
// 2 events with audit notes (normal flow)
|
|
216
|
+
for (const id of ['evt-ok-1', 'evt-ok-2']) {
|
|
217
|
+
await writeEventNote(vault, id);
|
|
218
|
+
await writeAuditNote(vault, id);
|
|
219
|
+
}
|
|
220
|
+
// 1 orphan injected on purpose — no audit note
|
|
221
|
+
await writeEventNote(vault, 'evt-orphan-injected', 'solicitud-compra');
|
|
222
|
+
const sweeper = new ReconciliationSweeper_1.ReconciliationSweeper({ vaultPath: vault });
|
|
223
|
+
const report = await sweeper.sweep();
|
|
224
|
+
expect(report.checked).toBe(3);
|
|
225
|
+
expect(report.matched).toBe(2);
|
|
226
|
+
expect(report.orphaned).toHaveLength(1);
|
|
227
|
+
expect(report.orphaned[0].eventId).toBe('evt-orphan-injected');
|
|
228
|
+
expect(report.orphaned[0].type).toBe('solicitud-compra');
|
|
229
|
+
}
|
|
230
|
+
finally {
|
|
231
|
+
await fs.rm(vault, { recursive: true, force: true });
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
//# sourceMappingURL=ReconciliationSweeper.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReconciliationSweeper.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/ReconciliationSweeper.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n8nTriggerTool.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/unit/n8nTriggerTool.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const n8nTriggerTool_1 = require("../../integrations/n8nTriggerTool");
|
|
4
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
5
|
+
function mockFetch(ok, text, status = 200) {
|
|
6
|
+
return jest.fn().mockResolvedValue({ ok, status, text: jest.fn().mockResolvedValue(text) });
|
|
7
|
+
}
|
|
8
|
+
const WEBHOOK = 'https://n8n.internal/webhook/abc123';
|
|
9
|
+
// ── Construction ──────────────────────────────────────────────────────────────
|
|
10
|
+
describe('createN8nTriggerTool — construction', () => {
|
|
11
|
+
it('throws when no URL is configured', () => {
|
|
12
|
+
delete process.env.N8N_WEBHOOK_URL;
|
|
13
|
+
expect(() => (0, n8nTriggerTool_1.createN8nTriggerTool)()).toThrow('N8N_WEBHOOK_URL');
|
|
14
|
+
});
|
|
15
|
+
it('accepts URL via opts.webhookUrl', () => {
|
|
16
|
+
expect(() => (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK })).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
it('falls back to N8N_WEBHOOK_URL env var', () => {
|
|
19
|
+
process.env.N8N_WEBHOOK_URL = WEBHOOK;
|
|
20
|
+
expect(() => (0, n8nTriggerTool_1.createN8nTriggerTool)()).not.toThrow();
|
|
21
|
+
delete process.env.N8N_WEBHOOK_URL;
|
|
22
|
+
});
|
|
23
|
+
it('returns a tool with name "n8n_trigger"', () => {
|
|
24
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
25
|
+
expect(tool.name).toBe('n8n_trigger');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
// ── execute — happy path ──────────────────────────────────────────────────────
|
|
29
|
+
describe('createN8nTriggerTool — execute', () => {
|
|
30
|
+
let fetchMock;
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
fetchMock = mockFetch(true, 'ok');
|
|
33
|
+
global.fetch = fetchMock;
|
|
34
|
+
});
|
|
35
|
+
afterEach(() => { jest.restoreAllMocks(); });
|
|
36
|
+
it('calls fetch with POST, correct URL, and Content-Type header', async () => {
|
|
37
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
38
|
+
await tool.execute({ event: 'test.event', payload: {}, correlation_id: 'cid-1' });
|
|
39
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
40
|
+
const [url, init] = fetchMock.mock.calls[0];
|
|
41
|
+
expect(url).toBe(WEBHOOK);
|
|
42
|
+
expect(init.method).toBe('POST');
|
|
43
|
+
expect(init.headers['Content-Type']).toBe('application/json');
|
|
44
|
+
});
|
|
45
|
+
it('serializes event + payload + correlationId in body', async () => {
|
|
46
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
47
|
+
await tool.execute({
|
|
48
|
+
event: 'employee.onboarded',
|
|
49
|
+
payload: { employee_id: 42, name: 'Ana' },
|
|
50
|
+
correlation_id: 'cid-2',
|
|
51
|
+
});
|
|
52
|
+
const body = JSON.parse(fetchMock.mock.calls[0][1].body);
|
|
53
|
+
expect(body.event).toBe('employee.onboarded');
|
|
54
|
+
expect(body.correlationId).toBe('cid-2');
|
|
55
|
+
expect(body.employee_id).toBe(42);
|
|
56
|
+
expect(body.name).toBe('Ana');
|
|
57
|
+
});
|
|
58
|
+
it('auto-generates a UUID correlation_id when omitted', async () => {
|
|
59
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
60
|
+
await tool.execute({ event: 'x', payload: {} });
|
|
61
|
+
const body = JSON.parse(fetchMock.mock.calls[0][1].body);
|
|
62
|
+
expect(body.correlationId).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
63
|
+
});
|
|
64
|
+
it('returns the event name and correlation_id in the result', async () => {
|
|
65
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
66
|
+
const result = await tool.execute({
|
|
67
|
+
event: 'invoice.approved',
|
|
68
|
+
payload: {},
|
|
69
|
+
correlation_id: 'trace-99',
|
|
70
|
+
});
|
|
71
|
+
expect(result).toContain('invoice.approved');
|
|
72
|
+
expect(result).toContain('trace-99');
|
|
73
|
+
});
|
|
74
|
+
it('includes n8n response text in result when non-empty', async () => {
|
|
75
|
+
fetchMock = mockFetch(true, '{"workflowId":"wf-1"}');
|
|
76
|
+
global.fetch = fetchMock;
|
|
77
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
78
|
+
const result = await tool.execute({ event: 'x', payload: {} });
|
|
79
|
+
expect(result).toContain('wf-1');
|
|
80
|
+
});
|
|
81
|
+
it('throws on non-OK HTTP response', async () => {
|
|
82
|
+
fetchMock = mockFetch(false, 'Webhook not found', 404);
|
|
83
|
+
global.fetch = fetchMock;
|
|
84
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
85
|
+
await expect(tool.execute({ event: 'x', payload: {} })).rejects.toThrow('404');
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
// ── criterio de salida ─────────────────────────────────────────────────────────
|
|
89
|
+
describe('n8n_trigger — criterio de salida', () => {
|
|
90
|
+
beforeEach(() => { global.fetch = mockFetch(true, 'triggered'); });
|
|
91
|
+
afterEach(() => jest.restoreAllMocks());
|
|
92
|
+
it('two separate calls produce different correlation IDs', async () => {
|
|
93
|
+
const tool = (0, n8nTriggerTool_1.createN8nTriggerTool)({ webhookUrl: WEBHOOK });
|
|
94
|
+
const r1 = await tool.execute({ event: 'a', payload: {} });
|
|
95
|
+
const r2 = await tool.execute({ event: 'b', payload: {} });
|
|
96
|
+
const id1 = r1.match(/correlationId: (\S+)/)?.[1];
|
|
97
|
+
const id2 = r2.match(/correlationId: (\S+)/)?.[1];
|
|
98
|
+
expect(id1).toBeDefined();
|
|
99
|
+
expect(id2).toBeDefined();
|
|
100
|
+
expect(id1).not.toBe(id2);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
//# sourceMappingURL=n8nTriggerTool.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"n8nTriggerTool.test.js","sourceRoot":"","sources":["../../../src/__tests__/unit/n8nTriggerTool.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { EventEnvelope, EventSchema, PublishOptions } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Minimal subset of the BullMQ Queue API required by EventPublisher.
|
|
4
|
+
* Satisfied by a real BullMQ Queue without importing bullmq at build time.
|
|
5
|
+
*/
|
|
6
|
+
export interface QueueLike {
|
|
7
|
+
name: string;
|
|
8
|
+
add(jobName: string, data: unknown, opts?: {
|
|
9
|
+
attempts?: number;
|
|
10
|
+
backoff?: {
|
|
11
|
+
type: string;
|
|
12
|
+
delay: number;
|
|
13
|
+
};
|
|
14
|
+
removeOnComplete?: boolean | {
|
|
15
|
+
count: number;
|
|
16
|
+
};
|
|
17
|
+
removeOnFail?: boolean | {
|
|
18
|
+
count: number;
|
|
19
|
+
};
|
|
20
|
+
jobId?: string;
|
|
21
|
+
}): Promise<{
|
|
22
|
+
id?: string | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
}
|
|
25
|
+
export interface EventPublisherOptions {
|
|
26
|
+
/** BullMQ Queue (or any QueueLike). Must target the right queue name. */
|
|
27
|
+
queue: QueueLike;
|
|
28
|
+
/** Absolute path to the shared vault. Events are written to {vaultPath}/eventos/. */
|
|
29
|
+
vaultPath: string;
|
|
30
|
+
/** Publisher identity (agentId, system name) written into the envelope and vault note. */
|
|
31
|
+
source: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Publishes a typed, validated event in a single atomic call:
|
|
35
|
+
* 1. Validates payload against the EventSchema's Zod schema.
|
|
36
|
+
* 2. Creates an EventEnvelope with a UUID and ISO timestamp.
|
|
37
|
+
* 3. Adds the job to the BullMQ queue (retry config, removeOnComplete: false for reconciliation).
|
|
38
|
+
* 4. Writes a canonical vault note to /eventos/ so the event is auditable and RAG-queryable.
|
|
39
|
+
*/
|
|
40
|
+
export declare class EventPublisher {
|
|
41
|
+
private readonly opts;
|
|
42
|
+
private readonly vaultWriter;
|
|
43
|
+
constructor(opts: EventPublisherOptions);
|
|
44
|
+
publish<T>(schema: EventSchema<T>, payload: T, options?: PublishOptions): Promise<EventEnvelope<T>>;
|
|
45
|
+
private writeEventNote;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=EventPublisher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventPublisher.d.ts","sourceRoot":"","sources":["../../src/events/EventPublisher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI1E;;;GAGG;AACH,MAAM,WAAW,SAAS;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CACC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QACH,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,gBAAgB,CAAC,EAAE,OAAO,GAAG;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/C,YAAY,CAAC,EAAE,OAAO,GAAG;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC3C,KAAK,CAAC,EAAE,MAAM,CAAC;KAClB,GACF,OAAO,CAAC;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,CAAC,CAAC;CAC3C;AAID,MAAM,WAAW,qBAAqB;IAClC,yEAAyE;IACzE,KAAK,EAAE,SAAS,CAAC;IACjB,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,MAAM,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,qBAAa,cAAc;IAGX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAFjC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAEb,IAAI,EAAE,qBAAqB;IAOlD,OAAO,CAAC,CAAC,EACX,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EACtB,OAAO,EAAE,CAAC,EACV,OAAO,CAAC,EAAE,cAAc,GACzB,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;YA8Bd,cAAc;CA6B/B"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventPublisher = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const VaultWriter_1 = require("../rag/VaultWriter");
|
|
6
|
+
/**
|
|
7
|
+
* Publishes a typed, validated event in a single atomic call:
|
|
8
|
+
* 1. Validates payload against the EventSchema's Zod schema.
|
|
9
|
+
* 2. Creates an EventEnvelope with a UUID and ISO timestamp.
|
|
10
|
+
* 3. Adds the job to the BullMQ queue (retry config, removeOnComplete: false for reconciliation).
|
|
11
|
+
* 4. Writes a canonical vault note to /eventos/ so the event is auditable and RAG-queryable.
|
|
12
|
+
*/
|
|
13
|
+
class EventPublisher {
|
|
14
|
+
opts;
|
|
15
|
+
vaultWriter;
|
|
16
|
+
constructor(opts) {
|
|
17
|
+
this.opts = opts;
|
|
18
|
+
this.vaultWriter = new VaultWriter_1.VaultWriter({
|
|
19
|
+
vaultPath: opts.vaultPath,
|
|
20
|
+
agentFolder: 'eventos',
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async publish(schema, payload, options) {
|
|
24
|
+
// Validate — throws ZodError if payload doesn't match the schema
|
|
25
|
+
schema.schema.parse(payload);
|
|
26
|
+
const envelope = {
|
|
27
|
+
id: (0, crypto_1.randomUUID)(),
|
|
28
|
+
type: schema.type,
|
|
29
|
+
occurredAt: new Date().toISOString(),
|
|
30
|
+
source: this.opts.source,
|
|
31
|
+
version: schema.version,
|
|
32
|
+
payload,
|
|
33
|
+
};
|
|
34
|
+
const jobId = options?.jobId ?? envelope.id;
|
|
35
|
+
await this.opts.queue.add(schema.type, envelope, {
|
|
36
|
+
attempts: options?.attempts ?? 3,
|
|
37
|
+
backoff: { type: 'exponential', delay: options?.backoffDelay ?? 1000 },
|
|
38
|
+
removeOnComplete: false, // keep completed jobs available for reconciliation
|
|
39
|
+
removeOnFail: false,
|
|
40
|
+
jobId,
|
|
41
|
+
});
|
|
42
|
+
await this.writeEventNote(envelope);
|
|
43
|
+
return envelope;
|
|
44
|
+
}
|
|
45
|
+
// ── Private ───────────────────────────────────────────────────────────────
|
|
46
|
+
async writeEventNote(envelope) {
|
|
47
|
+
const payloadJson = JSON.stringify(envelope.payload, null, 2);
|
|
48
|
+
const content = [
|
|
49
|
+
`## Event: ${envelope.type}`,
|
|
50
|
+
'',
|
|
51
|
+
'| Field | Value |',
|
|
52
|
+
'|-------|-------|',
|
|
53
|
+
`| ID | \`${envelope.id}\` |`,
|
|
54
|
+
`| Type | \`${envelope.type}\` |`,
|
|
55
|
+
`| Occurred | ${envelope.occurredAt} |`,
|
|
56
|
+
`| Source | \`${envelope.source}\` |`,
|
|
57
|
+
`| Version | ${envelope.version} |`,
|
|
58
|
+
'',
|
|
59
|
+
'### Payload',
|
|
60
|
+
'',
|
|
61
|
+
'```json',
|
|
62
|
+
payloadJson,
|
|
63
|
+
'```',
|
|
64
|
+
].join('\n');
|
|
65
|
+
await this.vaultWriter.writeNote({
|
|
66
|
+
title: `Event: ${envelope.type} (${envelope.id})`,
|
|
67
|
+
content,
|
|
68
|
+
agentId: envelope.source,
|
|
69
|
+
tags: ['evento', `tipo/${envelope.type}`],
|
|
70
|
+
description: `Published event — type: ${envelope.type}, source: ${envelope.source}`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.EventPublisher = EventPublisher;
|
|
75
|
+
//# sourceMappingURL=EventPublisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventPublisher.js","sourceRoot":"","sources":["../../src/events/EventPublisher.ts"],"names":[],"mappings":";;;AAAA,mCAAoC;AACpC,oDAAiD;AAmCjD;;;;;;GAMG;AACH,MAAa,cAAc;IAGM;IAFZ,WAAW,CAAc;IAE1C,YAA6B,IAA2B;QAA3B,SAAI,GAAJ,IAAI,CAAuB;QACpD,IAAI,CAAC,WAAW,GAAG,IAAI,yBAAW,CAAC;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,WAAW,EAAE,SAAS;SACzB,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,OAAO,CACT,MAAsB,EACtB,OAAU,EACV,OAAwB;QAExB,iEAAiE;QACjE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAE7B,MAAM,QAAQ,GAAqB;YAC/B,EAAE,EAAE,IAAA,mBAAU,GAAE;YAChB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACpC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM;YACxB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,OAAO;SACV,CAAC;QAEF,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,QAAQ,CAAC,EAAE,CAAC;QAE5C,MAAM,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,QAAQ,EAAE;YAC7C,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,CAAC;YAChC,OAAO,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,IAAI,IAAI,EAAE;YACtE,gBAAgB,EAAE,KAAK,EAAG,mDAAmD;YAC7E,YAAY,EAAE,KAAK;YACnB,KAAK;SACR,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAEpC,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,6EAA6E;IAErE,KAAK,CAAC,cAAc,CAAI,QAA0B;QACtD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAE9D,MAAM,OAAO,GAAG;YACZ,aAAa,QAAQ,CAAC,IAAI,EAAE;YAC5B,EAAE;YACF,mBAAmB;YACnB,mBAAmB;YACnB,YAAY,QAAQ,CAAC,EAAE,MAAM;YAC7B,cAAc,QAAQ,CAAC,IAAI,MAAM;YACjC,gBAAgB,QAAQ,CAAC,UAAU,IAAI;YACvC,gBAAgB,QAAQ,CAAC,MAAM,MAAM;YACrC,eAAe,QAAQ,CAAC,OAAO,IAAI;YACnC,EAAE;YACF,aAAa;YACb,EAAE;YACF,SAAS;YACT,WAAW;YACX,KAAK;SACR,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEb,MAAM,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC;YAC7B,KAAK,EAAE,UAAU,QAAQ,CAAC,IAAI,KAAK,QAAQ,CAAC,EAAE,GAAG;YACjD,OAAO;YACP,OAAO,EAAE,QAAQ,CAAC,MAAM;YACxB,IAAI,EAAE,CAAC,QAAQ,EAAE,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC;YACzC,WAAW,EAAE,2BAA2B,QAAQ,CAAC,IAAI,aAAa,QAAQ,CAAC,MAAM,EAAE;SACtF,CAAC,CAAC;IACP,CAAC;CACJ;AAzED,wCAyEC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ReconciliationReport } from './types';
|
|
2
|
+
export interface ReconciliationSweeperOptions {
|
|
3
|
+
/** Absolute path to the shared vault. */
|
|
4
|
+
vaultPath: string;
|
|
5
|
+
/** Subfolder where EventPublisher writes event notes. Default: 'eventos' */
|
|
6
|
+
eventsFolder?: string;
|
|
7
|
+
/** Subfolder tree where consumers write audit notes. Default: 'auditoria' */
|
|
8
|
+
auditFolder?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Compares published events (vault /eventos/) against audit notes (vault /auditoria/).
|
|
12
|
+
*
|
|
13
|
+
* Each event note written by EventPublisher contains a table row:
|
|
14
|
+
* | ID | `<event-uuid>` |
|
|
15
|
+
*
|
|
16
|
+
* The sweeper extracts these IDs and checks if the text appears anywhere in
|
|
17
|
+
* the /auditoria/ subtree. Events without a matching audit note are "orphaned".
|
|
18
|
+
*
|
|
19
|
+
* This detects consistency gaps without an outbox transaction, using the vault
|
|
20
|
+
* as the source of truth (ADR-1).
|
|
21
|
+
*/
|
|
22
|
+
export declare class ReconciliationSweeper {
|
|
23
|
+
private readonly vaultPath;
|
|
24
|
+
private readonly eventsFolder;
|
|
25
|
+
private readonly auditFolder;
|
|
26
|
+
constructor(opts: ReconciliationSweeperOptions);
|
|
27
|
+
sweep(): Promise<ReconciliationReport>;
|
|
28
|
+
private readEventNotes;
|
|
29
|
+
private readAuditContent;
|
|
30
|
+
private collectContent;
|
|
31
|
+
/**
|
|
32
|
+
* Extracts a value from a Markdown table row of the form:
|
|
33
|
+
* | FieldName | `value` | or | FieldName | value |
|
|
34
|
+
*/
|
|
35
|
+
private extractTableField;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=ReconciliationSweeper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReconciliationSweeper.d.ts","sourceRoot":"","sources":["../../src/events/ReconciliationSweeper.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEnE,MAAM,WAAW,4BAA4B;IACzC,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,qBAAqB;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;gBAEzB,IAAI,EAAE,4BAA4B;IAMxC,KAAK,IAAI,OAAO,CAAC,oBAAoB,CAAC;YAyB9B,cAAc;YA6Bd,gBAAgB;YAShB,cAAc;IAmB5B;;;OAGG;IACH,OAAO,CAAC,iBAAiB;CAO5B"}
|