monora-ai 2.0.0 → 2.1.3

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 (202) hide show
  1. package/README.md +441 -150
  2. package/dist/aims_governance.d.ts +238 -0
  3. package/dist/aims_governance.d.ts.map +1 -0
  4. package/dist/aims_governance.js +922 -0
  5. package/dist/alerts.d.ts +16 -0
  6. package/dist/alerts.d.ts.map +1 -1
  7. package/dist/alerts.js +16 -0
  8. package/dist/api.d.ts +6 -0
  9. package/dist/api.d.ts.map +1 -1
  10. package/dist/api.js +6 -0
  11. package/dist/assessment.d.ts +269 -0
  12. package/dist/assessment.d.ts.map +1 -0
  13. package/dist/assessment.js +1232 -0
  14. package/dist/attestation.js +23 -1
  15. package/dist/attribution.d.ts +349 -0
  16. package/dist/attribution.d.ts.map +1 -0
  17. package/dist/attribution.js +987 -0
  18. package/dist/autodetect.d.ts +69 -1
  19. package/dist/autodetect.d.ts.map +1 -1
  20. package/dist/autodetect.js +644 -1
  21. package/dist/bias.d.ts +130 -0
  22. package/dist/bias.d.ts.map +1 -0
  23. package/dist/bias.js +223 -0
  24. package/dist/circuit_breaker.js +3 -3
  25. package/dist/cli/diagnostics.d.ts +5 -1
  26. package/dist/cli/diagnostics.d.ts.map +1 -1
  27. package/dist/cli/diagnostics.js +31 -8
  28. package/dist/cli/doctor.d.ts +25 -0
  29. package/dist/cli/doctor.d.ts.map +1 -0
  30. package/dist/cli/doctor.js +381 -0
  31. package/dist/cli/fix.d.ts +16 -0
  32. package/dist/cli/fix.d.ts.map +1 -0
  33. package/dist/cli/fix.js +284 -0
  34. package/dist/cli/init.d.ts +57 -0
  35. package/dist/cli/init.d.ts.map +1 -0
  36. package/dist/cli/init.js +205 -0
  37. package/dist/cli.js +1611 -126
  38. package/dist/complianceTargets.d.ts +111 -0
  39. package/dist/complianceTargets.d.ts.map +1 -0
  40. package/dist/complianceTargets.js +521 -0
  41. package/dist/config.d.ts +301 -17
  42. package/dist/config.d.ts.map +1 -1
  43. package/dist/config.js +428 -36
  44. package/dist/config_migrations.d.ts +41 -0
  45. package/dist/config_migrations.d.ts.map +1 -1
  46. package/dist/config_migrations.js +205 -0
  47. package/dist/config_schema.d.ts +2900 -731
  48. package/dist/config_schema.d.ts.map +1 -1
  49. package/dist/config_schema.js +257 -55
  50. package/dist/context.d.ts +34 -0
  51. package/dist/context.d.ts.map +1 -1
  52. package/dist/context.js +118 -7
  53. package/dist/control_backbone.d.ts +122 -0
  54. package/dist/control_backbone.d.ts.map +1 -0
  55. package/dist/control_backbone.js +698 -0
  56. package/dist/data-governance.d.ts +187 -0
  57. package/dist/data-governance.d.ts.map +1 -0
  58. package/dist/data-governance.js +424 -0
  59. package/dist/dataResidency.d.ts +44 -0
  60. package/dist/dataResidency.d.ts.map +1 -0
  61. package/dist/dataResidency.js +203 -0
  62. package/dist/dispatcher.d.ts +32 -0
  63. package/dist/dispatcher.d.ts.map +1 -1
  64. package/dist/dispatcher.js +91 -4
  65. package/dist/events.d.ts.map +1 -1
  66. package/dist/events.js +38 -0
  67. package/dist/evidence_store.d.ts +103 -0
  68. package/dist/evidence_store.d.ts.map +1 -0
  69. package/dist/evidence_store.js +459 -0
  70. package/dist/executiveSummary.d.ts +65 -8
  71. package/dist/executiveSummary.d.ts.map +1 -1
  72. package/dist/executiveSummary.js +289 -26
  73. package/dist/identity.d.ts +143 -0
  74. package/dist/identity.d.ts.map +1 -0
  75. package/dist/identity.js +231 -0
  76. package/dist/impact-assessment.d.ts +350 -0
  77. package/dist/impact-assessment.d.ts.map +1 -0
  78. package/dist/impact-assessment.js +580 -0
  79. package/dist/index.d.ts +25 -5
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +300 -4
  82. package/dist/instrumentation.d.ts +1 -1
  83. package/dist/instrumentation.d.ts.map +1 -1
  84. package/dist/instrumentation.js +243 -27
  85. package/dist/integrations/anthropic.d.ts +3 -0
  86. package/dist/integrations/anthropic.d.ts.map +1 -1
  87. package/dist/integrations/anthropic.js +284 -79
  88. package/dist/integrations/governance.d.ts +33 -0
  89. package/dist/integrations/governance.d.ts.map +1 -0
  90. package/dist/integrations/governance.js +208 -0
  91. package/dist/integrations/langchain.d.ts +7 -0
  92. package/dist/integrations/langchain.d.ts.map +1 -1
  93. package/dist/integrations/langchain.js +387 -143
  94. package/dist/integrations/openai.d.ts +9 -0
  95. package/dist/integrations/openai.d.ts.map +1 -1
  96. package/dist/integrations/openai.js +673 -73
  97. package/dist/iso42001_consolidation.d.ts +16 -0
  98. package/dist/iso42001_consolidation.d.ts.map +1 -0
  99. package/dist/iso42001_consolidation.js +413 -0
  100. package/dist/iso42001_workflows.d.ts +263 -0
  101. package/dist/iso42001_workflows.d.ts.map +1 -0
  102. package/dist/iso42001_workflows.js +781 -0
  103. package/dist/lifecycle.d.ts +299 -0
  104. package/dist/lifecycle.d.ts.map +1 -0
  105. package/dist/lifecycle.js +624 -0
  106. package/dist/lineage.d.ts +2 -2
  107. package/dist/lineage.d.ts.map +1 -1
  108. package/dist/lineage.js +12 -17
  109. package/dist/middleware/express.d.ts.map +1 -1
  110. package/dist/middleware/express.js +33 -3
  111. package/dist/middleware/nextjs.d.ts.map +1 -1
  112. package/dist/middleware/nextjs.js +42 -68
  113. package/dist/model.d.ts +143 -0
  114. package/dist/model.d.ts.map +1 -0
  115. package/dist/model.js +371 -0
  116. package/dist/onboarding.d.ts +42 -0
  117. package/dist/onboarding.d.ts.map +1 -0
  118. package/dist/onboarding.js +1022 -0
  119. package/dist/oversight.d.ts +264 -0
  120. package/dist/oversight.d.ts.map +1 -0
  121. package/dist/oversight.js +497 -0
  122. package/dist/pdf_report.d.ts.map +1 -1
  123. package/dist/pdf_report.js +42 -21
  124. package/dist/presets.d.ts +88 -0
  125. package/dist/presets.d.ts.map +1 -0
  126. package/dist/presets.js +520 -0
  127. package/dist/propagation.d.ts.map +1 -1
  128. package/dist/propagation.js +34 -2
  129. package/dist/quotas.d.ts +171 -0
  130. package/dist/quotas.d.ts.map +1 -0
  131. package/dist/quotas.js +259 -0
  132. package/dist/register.d.ts +13 -0
  133. package/dist/register.d.ts.map +1 -0
  134. package/dist/register.js +99 -0
  135. package/dist/registry.d.ts +1 -0
  136. package/dist/registry.d.ts.map +1 -1
  137. package/dist/registry.js +7 -0
  138. package/dist/registryData.json +43 -6
  139. package/dist/report.d.ts +2 -1
  140. package/dist/report.d.ts.map +1 -1
  141. package/dist/report.js +189 -2
  142. package/dist/reporting.d.ts +125 -0
  143. package/dist/reporting.d.ts.map +1 -1
  144. package/dist/reporting.js +196 -5
  145. package/dist/resources.d.ts +285 -0
  146. package/dist/resources.d.ts.map +1 -0
  147. package/dist/resources.js +643 -0
  148. package/dist/risk.d.ts +120 -0
  149. package/dist/risk.d.ts.map +1 -0
  150. package/dist/risk.js +220 -0
  151. package/dist/runtime.d.ts +74 -1
  152. package/dist/runtime.d.ts.map +1 -1
  153. package/dist/runtime.js +598 -22
  154. package/dist/schemaInference.d.ts +92 -0
  155. package/dist/schemaInference.d.ts.map +1 -0
  156. package/dist/schemaInference.js +466 -0
  157. package/dist/schema_validation.js +2 -2
  158. package/dist/schemas/config.schema.json +169 -6
  159. package/dist/schemas/event.schema.json +4 -0
  160. package/dist/security_report.js +4 -4
  161. package/dist/signing.d.ts +1 -1
  162. package/dist/signing.d.ts.map +1 -1
  163. package/dist/signing.js +4 -0
  164. package/dist/sinks/file.d.ts +19 -1
  165. package/dist/sinks/file.d.ts.map +1 -1
  166. package/dist/sinks/file.js +82 -13
  167. package/dist/sinks/https.d.ts +10 -0
  168. package/dist/sinks/https.d.ts.map +1 -1
  169. package/dist/sinks/https.js +76 -16
  170. package/dist/sinks/stdout.d.ts +1 -0
  171. package/dist/sinks/stdout.d.ts.map +1 -1
  172. package/dist/sinks/stdout.js +12 -1
  173. package/dist/spec.d.ts +159 -0
  174. package/dist/spec.d.ts.map +1 -0
  175. package/dist/spec.js +391 -0
  176. package/dist/stakeholders.d.ts +199 -0
  177. package/dist/stakeholders.d.ts.map +1 -0
  178. package/dist/stakeholders.js +398 -0
  179. package/dist/standards.d.ts.map +1 -1
  180. package/dist/standards.js +160 -2
  181. package/dist/standards_ingest.d.ts +2 -2
  182. package/dist/standards_ingest.d.ts.map +1 -1
  183. package/dist/standards_ingest.js +105 -23
  184. package/dist/streaming.d.ts.map +1 -1
  185. package/dist/streaming.js +7 -2
  186. package/dist/telemetry.d.ts +16 -2
  187. package/dist/telemetry.d.ts.map +1 -1
  188. package/dist/telemetry.js +79 -14
  189. package/dist/templates/controls/iso42001_control_catalog.json +1443 -0
  190. package/dist/traced_emitter.d.ts +3 -0
  191. package/dist/traced_emitter.d.ts.map +1 -1
  192. package/dist/traced_emitter.js +142 -25
  193. package/dist/trust_package.d.ts +21 -1
  194. package/dist/trust_package.d.ts.map +1 -1
  195. package/dist/trust_package.js +101 -4
  196. package/dist/verify.d.ts.map +1 -1
  197. package/dist/verify.js +9 -2
  198. package/dist/wal.d.ts.map +1 -1
  199. package/dist/wal.js +2 -1
  200. package/package.json +14 -1
  201. package/scripts/postinstall.js +119 -97
  202. package/templates/controls/iso42001_control_catalog.json +1443 -0
@@ -0,0 +1,698 @@
1
+ "use strict";
2
+ /**
3
+ * Control/evidence workflow backbone for ISO 42001 coverage reporting.
4
+ */
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.clearControlRuntime = clearControlRuntime;
40
+ exports.setControlCatalog = setControlCatalog;
41
+ exports.loadControlCatalog = loadControlCatalog;
42
+ exports.getControlCatalog = getControlCatalog;
43
+ exports.loadDefaultIso42001Catalog = loadDefaultIso42001Catalog;
44
+ exports.resolveControlIdsForEvidenceTypes = resolveControlIdsForEvidenceTypes;
45
+ exports.createWorkflowTask = createWorkflowTask;
46
+ exports.getWorkflowTask = getWorkflowTask;
47
+ exports.listWorkflowTasks = listWorkflowTasks;
48
+ exports.transitionWorkflowTask = transitionWorkflowTask;
49
+ exports.submitWorkflowTask = submitWorkflowTask;
50
+ exports.approveControlEvidence = approveControlEvidence;
51
+ exports.expireWorkflowTask = expireWorkflowTask;
52
+ exports.attachControlEvidence = attachControlEvidence;
53
+ exports.importEvidenceManifest = importEvidenceManifest;
54
+ exports.bootstrapWorkflowsFromCatalog = bootstrapWorkflowsFromCatalog;
55
+ exports.prioritizeMissingWorkflowModules = prioritizeMissingWorkflowModules;
56
+ exports.generateControlCoverageReport = generateControlCoverageReport;
57
+ exports.buildWorkflowStatePayload = buildWorkflowStatePayload;
58
+ exports.exportWorkflowState = exportWorkflowState;
59
+ exports.loadWorkflowState = loadWorkflowState;
60
+ const crypto = __importStar(require("crypto"));
61
+ const fs = __importStar(require("fs"));
62
+ const path = __importStar(require("path"));
63
+ const evidence_store_1 = require("./evidence_store");
64
+ const WORKFLOW_STATUSES = new Set(['draft', 'in_review', 'approved', 'expired']);
65
+ const STATUS_TRANSITIONS = {
66
+ draft: ['in_review', 'approved'],
67
+ in_review: ['draft', 'approved'],
68
+ approved: ['in_review', 'expired'],
69
+ expired: ['in_review'],
70
+ };
71
+ const ISO42001_WORKFLOW_MODULES = [
72
+ {
73
+ module_id: 'aims_governance',
74
+ name: 'AIMS Governance',
75
+ priority: 'P1',
76
+ impact: 'high',
77
+ controls: ['A.2.2', 'A.2.3', 'A.2.4', 'A.3.2', 'A.3.3', 'Clause4', 'Clause5', 'Clause7'],
78
+ },
79
+ {
80
+ module_id: 'risk_impact_workflow',
81
+ name: 'Risk + Impact Workflow',
82
+ priority: 'P1',
83
+ impact: 'high',
84
+ controls: ['A.5.2', 'A.5.3', 'A.5.4', 'A.5.5', 'Clause6'],
85
+ },
86
+ {
87
+ module_id: 'ai_lifecycle_assurance',
88
+ name: 'AI Lifecycle Assurance',
89
+ priority: 'P1',
90
+ impact: 'high',
91
+ controls: ['A.6.1.2', 'A.6.1.3', 'A.6.2.2', 'A.6.2.4', 'A.6.2.5', 'A.6.2.6', 'A.6.2.7', 'A.6.2.8', 'Clause8'],
92
+ },
93
+ {
94
+ module_id: 'audit_review_capa',
95
+ name: 'Audit + Management Review + CAPA',
96
+ priority: 'P2',
97
+ impact: 'high',
98
+ controls: ['Clause9', 'Clause10'],
99
+ },
100
+ {
101
+ module_id: 'data_governance_workflow',
102
+ name: 'Data Governance Workflow',
103
+ priority: 'P2',
104
+ impact: 'medium',
105
+ controls: ['A.7.2', 'A.7.3', 'A.7.4', 'A.7.5', 'A.7.6'],
106
+ },
107
+ {
108
+ module_id: 'transparency_responsible_use',
109
+ name: 'Transparency + Responsible Use',
110
+ priority: 'P2',
111
+ impact: 'medium',
112
+ controls: ['A.8.2', 'A.8.3', 'A.8.4', 'A.8.5', 'A.9.2', 'A.9.3', 'A.9.4'],
113
+ },
114
+ {
115
+ module_id: 'third_party_customer_governance',
116
+ name: 'Third-Party + Customer Governance',
117
+ priority: 'P3',
118
+ impact: 'medium',
119
+ controls: ['A.10.1', 'A.10.2', 'A.10.3'],
120
+ },
121
+ ];
122
+ const PRIORITY_ORDER = { P0: 0, P1: 1, P2: 2, P3: 3, P4: 4 };
123
+ const IMPACT_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
124
+ let runtimeCatalog = null;
125
+ let runtimeWorkflows = new Map();
126
+ let runtimeEvidence = new Map();
127
+ let defaultIso42001Catalog = null;
128
+ function utcNow() {
129
+ return new Date().toISOString();
130
+ }
131
+ function clone(value) {
132
+ return JSON.parse(JSON.stringify(value));
133
+ }
134
+ function normalizeList(value) {
135
+ if (value === null || value === undefined) {
136
+ return [];
137
+ }
138
+ if (Array.isArray(value)) {
139
+ return value
140
+ .map((item) => String(item).trim())
141
+ .filter(Boolean);
142
+ }
143
+ return String(value)
144
+ .replace(/;/g, ',')
145
+ .split(',')
146
+ .map((item) => item.trim())
147
+ .filter(Boolean);
148
+ }
149
+ function buildId(prefix, ...parts) {
150
+ const seed = `${prefix}:${parts.join(':')}:${utcNow()}`;
151
+ const digest = crypto.createHash('sha256').update(seed).digest('hex').slice(0, 12);
152
+ return `${prefix}_${digest}`;
153
+ }
154
+ function resolveCatalog(catalog) {
155
+ if (typeof catalog === 'string') {
156
+ try {
157
+ return JSON.parse(fs.readFileSync(catalog, 'utf-8'));
158
+ }
159
+ catch (error) {
160
+ const message = error instanceof Error ? error.message : String(error);
161
+ console.warn(`Failed to load catalog from ${catalog}: ${message}`);
162
+ if (runtimeCatalog) {
163
+ return clone(runtimeCatalog);
164
+ }
165
+ return loadDefaultIso42001Catalog();
166
+ }
167
+ }
168
+ if (catalog) {
169
+ return catalog;
170
+ }
171
+ if (runtimeCatalog) {
172
+ return clone(runtimeCatalog);
173
+ }
174
+ return loadDefaultIso42001Catalog();
175
+ }
176
+ function ensureEvidenceRecord(payload, controlId) {
177
+ const evidence = clone(payload);
178
+ if (!evidence.evidence_id) {
179
+ evidence.evidence_id = buildId('evd', controlId);
180
+ }
181
+ if (!evidence.title) {
182
+ evidence.title = `Evidence for ${controlId}`;
183
+ }
184
+ if (!evidence.source) {
185
+ evidence.source = 'manual';
186
+ }
187
+ if (!evidence.category) {
188
+ evidence.category = 'governance';
189
+ }
190
+ if (!evidence.collection_method) {
191
+ evidence.collection_method = 'manual';
192
+ }
193
+ if (!evidence.collected_at) {
194
+ evidence.collected_at = utcNow();
195
+ }
196
+ const controlIds = new Set(normalizeList(evidence.control_ids));
197
+ controlIds.add(controlId);
198
+ evidence.control_ids = Array.from(controlIds).sort();
199
+ if (!evidence.metadata || typeof evidence.metadata !== 'object') {
200
+ evidence.metadata = {};
201
+ }
202
+ if (!evidence.status) {
203
+ evidence.status = 'collected';
204
+ }
205
+ return evidence;
206
+ }
207
+ function appendHistory(workflow, event, options) {
208
+ workflow.history.push({
209
+ timestamp: utcNow(),
210
+ event,
211
+ actor: options?.actor,
212
+ from_status: options?.fromStatus,
213
+ to_status: options?.toStatus,
214
+ note: options?.note,
215
+ metadata: options?.metadata,
216
+ });
217
+ workflow.updated_at = utcNow();
218
+ }
219
+ function findControl(controlId) {
220
+ const controls = (runtimeCatalog?.controls || []);
221
+ return controls.find((control) => String(control.control_id) === controlId) || {};
222
+ }
223
+ function clearControlRuntime() {
224
+ runtimeCatalog = null;
225
+ runtimeWorkflows = new Map();
226
+ runtimeEvidence = new Map();
227
+ (0, evidence_store_1.clearRuntimeEvidenceStore)();
228
+ }
229
+ function setControlCatalog(catalog) {
230
+ runtimeCatalog = clone(catalog);
231
+ return clone(runtimeCatalog);
232
+ }
233
+ function loadControlCatalog(filePath) {
234
+ const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
235
+ return setControlCatalog(payload);
236
+ }
237
+ function getControlCatalog() {
238
+ return runtimeCatalog ? clone(runtimeCatalog) : null;
239
+ }
240
+ function loadDefaultIso42001Catalog() {
241
+ if (defaultIso42001Catalog) {
242
+ return clone(defaultIso42001Catalog);
243
+ }
244
+ const candidatePaths = [
245
+ path.resolve(__dirname, '..', 'templates', 'controls', 'iso42001_control_catalog.json'),
246
+ path.resolve(process.cwd(), 'monora-node', 'templates', 'controls', 'iso42001_control_catalog.json'),
247
+ path.resolve(process.cwd(), 'templates', 'controls', 'iso42001_control_catalog.json'),
248
+ path.resolve(process.cwd(), 'docs', 'iso42001_control_catalog.json'),
249
+ ];
250
+ for (const candidate of candidatePaths) {
251
+ if (!fs.existsSync(candidate)) {
252
+ continue;
253
+ }
254
+ const payload = JSON.parse(fs.readFileSync(candidate, 'utf-8'));
255
+ defaultIso42001Catalog = payload;
256
+ return clone(payload);
257
+ }
258
+ throw new Error('Default ISO 42001 control catalog not found');
259
+ }
260
+ function resolveControlIdsForEvidenceTypes(evidenceTypes, catalog) {
261
+ const normalizedTypes = new Set(normalizeList(evidenceTypes));
262
+ if (normalizedTypes.size === 0) {
263
+ return [];
264
+ }
265
+ let payload = catalog || runtimeCatalog;
266
+ if (!payload) {
267
+ try {
268
+ payload = loadDefaultIso42001Catalog();
269
+ }
270
+ catch (_err) {
271
+ payload = null;
272
+ }
273
+ }
274
+ const controls = (payload?.controls || []);
275
+ const ids = new Set();
276
+ for (const control of controls) {
277
+ const controlId = String(control.control_id || '').trim();
278
+ if (!controlId) {
279
+ continue;
280
+ }
281
+ const controlTypes = new Set(normalizeList(control.evidence_types));
282
+ for (const evidenceType of normalizedTypes) {
283
+ if (controlTypes.has(evidenceType)) {
284
+ ids.add(controlId);
285
+ }
286
+ }
287
+ }
288
+ return Array.from(ids).sort();
289
+ }
290
+ function createWorkflowTask(options) {
291
+ const control = findControl(options.controlId);
292
+ const controlTitle = String(control.title || options.controlId);
293
+ const owner = options.owner || (typeof control.owner === 'string' ? control.owner.trim() || undefined : undefined);
294
+ const workflow = {
295
+ workflow_id: buildId('wf', options.controlId),
296
+ control_id: options.controlId,
297
+ title: options.title || `${options.controlId} - ${controlTitle}`,
298
+ status: 'draft',
299
+ owner,
300
+ created_at: utcNow(),
301
+ updated_at: utcNow(),
302
+ created_by: options.createdBy,
303
+ due_at: options.dueAt,
304
+ evidence_ids: [],
305
+ notes: options.notes,
306
+ metadata: options.metadata || {},
307
+ history: [],
308
+ };
309
+ appendHistory(workflow, 'created', { actor: options.createdBy, note: options.notes });
310
+ runtimeWorkflows.set(workflow.workflow_id, clone(workflow));
311
+ return clone(workflow);
312
+ }
313
+ function getWorkflowTask(workflowId) {
314
+ const workflow = runtimeWorkflows.get(workflowId);
315
+ return workflow ? clone(workflow) : null;
316
+ }
317
+ function listWorkflowTasks(options) {
318
+ const values = Array.from(runtimeWorkflows.values());
319
+ return values
320
+ .filter((workflow) => !options?.controlId || workflow.control_id === options.controlId)
321
+ .filter((workflow) => !options?.status || workflow.status === options.status)
322
+ .map((workflow) => clone(workflow));
323
+ }
324
+ function transitionWorkflowTask(workflowId, options) {
325
+ const workflow = runtimeWorkflows.get(workflowId);
326
+ if (!workflow) {
327
+ throw new Error(`Workflow not found: ${workflowId}`);
328
+ }
329
+ const fromStatus = workflow.status;
330
+ const allowed = STATUS_TRANSITIONS[workflow.status] || [];
331
+ if (!allowed.includes(options.toStatus)) {
332
+ throw new Error(`Invalid workflow transition ${workflow.status} -> ${options.toStatus}. Allowed: ${allowed.join(', ')}`);
333
+ }
334
+ workflow.status = options.toStatus;
335
+ if (options.toStatus === 'approved') {
336
+ if (workflow.evidence_ids.length === 0) {
337
+ throw new Error('Cannot approve workflow without linked evidence');
338
+ }
339
+ workflow.approved_at = utcNow();
340
+ workflow.approved_by = options.actor;
341
+ }
342
+ appendHistory(workflow, 'status_changed', {
343
+ actor: options.actor,
344
+ fromStatus,
345
+ toStatus: options.toStatus,
346
+ note: options.note,
347
+ });
348
+ runtimeWorkflows.set(workflowId, clone(workflow));
349
+ return clone(workflow);
350
+ }
351
+ function submitWorkflowTask(workflowId, options) {
352
+ return transitionWorkflowTask(workflowId, {
353
+ toStatus: 'in_review',
354
+ actor: options?.actor,
355
+ note: options?.note,
356
+ });
357
+ }
358
+ function approveControlEvidence(workflowId, options) {
359
+ return transitionWorkflowTask(workflowId, {
360
+ toStatus: 'approved',
361
+ actor: options?.actor,
362
+ note: options?.note,
363
+ });
364
+ }
365
+ function expireWorkflowTask(workflowId, options) {
366
+ return transitionWorkflowTask(workflowId, {
367
+ toStatus: 'expired',
368
+ actor: options?.actor,
369
+ note: options?.note,
370
+ });
371
+ }
372
+ function attachControlEvidence(options) {
373
+ const evidence = ensureEvidenceRecord(options.evidence, options.controlId);
374
+ if (options.evidenceType) {
375
+ evidence.metadata = evidence.metadata || {};
376
+ evidence.metadata.evidence_type = options.evidenceType;
377
+ const evidenceTypes = new Set(normalizeList(evidence.metadata.evidence_types));
378
+ evidenceTypes.add(options.evidenceType);
379
+ evidence.metadata.evidence_types = Array.from(evidenceTypes).sort();
380
+ }
381
+ if (options.workflowId) {
382
+ evidence.metadata = evidence.metadata || {};
383
+ evidence.metadata.workflow_id = options.workflowId;
384
+ }
385
+ runtimeEvidence.set(evidence.evidence_id, clone(evidence));
386
+ (0, evidence_store_1.recordRuntimeEvidence)(evidence);
387
+ if (options.workflowId) {
388
+ const workflow = runtimeWorkflows.get(options.workflowId);
389
+ if (!workflow) {
390
+ throw new Error(`Workflow not found: ${options.workflowId}`);
391
+ }
392
+ if (workflow.control_id !== options.controlId) {
393
+ throw new Error(`Workflow control mismatch: ${workflow.control_id} != ${options.controlId}`);
394
+ }
395
+ if (!workflow.evidence_ids.includes(evidence.evidence_id)) {
396
+ workflow.evidence_ids.push(evidence.evidence_id);
397
+ }
398
+ appendHistory(workflow, 'evidence_attached', {
399
+ actor: options.actor,
400
+ note: options.note,
401
+ metadata: { evidence_id: evidence.evidence_id },
402
+ });
403
+ runtimeWorkflows.set(options.workflowId, clone(workflow));
404
+ }
405
+ return clone(evidence);
406
+ }
407
+ function importEvidenceManifest(manifest, options) {
408
+ const evidenceItems = Array.isArray(manifest.evidence_items) ? manifest.evidence_items : [];
409
+ if (options?.clearExisting) {
410
+ runtimeEvidence = new Map();
411
+ (0, evidence_store_1.clearRuntimeEvidenceStore)();
412
+ }
413
+ let count = 0;
414
+ for (const rawItem of evidenceItems) {
415
+ if (!rawItem || typeof rawItem !== 'object') {
416
+ continue;
417
+ }
418
+ const evidenceId = String(rawItem.evidence_id || '').trim();
419
+ if (!evidenceId) {
420
+ continue;
421
+ }
422
+ runtimeEvidence.set(evidenceId, clone(rawItem));
423
+ (0, evidence_store_1.recordRuntimeEvidence)(rawItem);
424
+ count += 1;
425
+ }
426
+ return count;
427
+ }
428
+ function bootstrapWorkflowsFromCatalog(catalog, options) {
429
+ const payload = catalog || runtimeCatalog || loadDefaultIso42001Catalog();
430
+ const controls = Array.isArray(payload.controls) ? payload.controls : [];
431
+ const existingControlIds = new Set(Array.from(runtimeWorkflows.values()).map((w) => w.control_id));
432
+ const created = [];
433
+ for (const control of controls) {
434
+ if (!control || typeof control !== 'object') {
435
+ continue;
436
+ }
437
+ const controlId = String(control.control_id || '').trim();
438
+ if (!controlId || existingControlIds.has(controlId)) {
439
+ continue;
440
+ }
441
+ const ownerValue = String(control.owner || '').trim();
442
+ const owner = ownerValue || options?.ownerFallback || 'Unassigned';
443
+ const workflow = createWorkflowTask({
444
+ controlId,
445
+ title: `${controlId} - ${String(control.title || controlId)}`,
446
+ owner,
447
+ createdBy: options?.createdBy || 'system',
448
+ metadata: { bootstrap: true },
449
+ });
450
+ created.push(workflow);
451
+ existingControlIds.add(controlId);
452
+ }
453
+ return created;
454
+ }
455
+ function extractEvidenceTypes(item) {
456
+ const out = new Set();
457
+ const metadata = item.metadata && typeof item.metadata === 'object' ? item.metadata : {};
458
+ for (const key of ['evidence_type', 'type']) {
459
+ normalizeList(metadata[key]).forEach((value) => out.add(value));
460
+ }
461
+ normalizeList(metadata.evidence_types).forEach((value) => out.add(value));
462
+ normalizeList(item.tags).forEach((value) => out.add(value));
463
+ if (item.category) {
464
+ out.add(String(item.category));
465
+ }
466
+ return out;
467
+ }
468
+ function prioritizeMissingWorkflowModules(uncoveredControls) {
469
+ const missingSet = new Set(normalizeList(uncoveredControls));
470
+ const modules = [];
471
+ for (const moduleDef of ISO42001_WORKFLOW_MODULES) {
472
+ const controls = normalizeList(moduleDef.controls);
473
+ const overlap = controls.filter((controlId) => missingSet.has(controlId)).sort();
474
+ if (overlap.length === 0) {
475
+ continue;
476
+ }
477
+ modules.push({
478
+ module_id: moduleDef.module_id,
479
+ name: moduleDef.name,
480
+ priority: moduleDef.priority,
481
+ impact: moduleDef.impact,
482
+ missing_controls: overlap,
483
+ missing_count: overlap.length,
484
+ });
485
+ }
486
+ modules.sort((a, b) => {
487
+ const p = (PRIORITY_ORDER[a.priority] ?? 99) - (PRIORITY_ORDER[b.priority] ?? 99);
488
+ if (p !== 0) {
489
+ return p;
490
+ }
491
+ const i = (IMPACT_ORDER[a.impact] ?? 99) - (IMPACT_ORDER[b.impact] ?? 99);
492
+ if (i !== 0) {
493
+ return i;
494
+ }
495
+ return Number(b.missing_count || 0) - Number(a.missing_count || 0);
496
+ });
497
+ return modules;
498
+ }
499
+ function generateControlCoverageReport(options) {
500
+ const catalog = resolveCatalog(options?.catalog || null);
501
+ const controls = Array.isArray(catalog.controls) ? catalog.controls : [];
502
+ const evidenceItems = options?.evidenceItems || Array.from(runtimeEvidence.values());
503
+ const workflows = options?.workflows || Array.from(runtimeWorkflows.values());
504
+ const targetCoverage = typeof options?.targetCoverage === 'number' ? options.targetCoverage : 0.9;
505
+ const evidenceById = new Map();
506
+ const evidenceByControl = new Map();
507
+ const approvedByControl = new Map();
508
+ for (const rawItem of evidenceItems) {
509
+ if (!rawItem || typeof rawItem !== 'object') {
510
+ continue;
511
+ }
512
+ const item = rawItem;
513
+ const evidenceId = String(item.evidence_id || '').trim();
514
+ if (!evidenceId) {
515
+ continue;
516
+ }
517
+ evidenceById.set(evidenceId, item);
518
+ for (const controlId of normalizeList(item.control_ids)) {
519
+ if (!evidenceByControl.has(controlId)) {
520
+ evidenceByControl.set(controlId, new Set());
521
+ }
522
+ evidenceByControl.get(controlId)?.add(evidenceId);
523
+ if (String(item.status || '').toLowerCase() === 'approved') {
524
+ if (!approvedByControl.has(controlId)) {
525
+ approvedByControl.set(controlId, new Set());
526
+ }
527
+ approvedByControl.get(controlId)?.add(evidenceId);
528
+ }
529
+ }
530
+ }
531
+ const workflowsByControl = new Map();
532
+ for (const rawWorkflow of workflows) {
533
+ if (!rawWorkflow || typeof rawWorkflow !== 'object') {
534
+ continue;
535
+ }
536
+ const workflow = rawWorkflow;
537
+ const controlId = String(workflow.control_id || '').trim();
538
+ const workflowId = String(workflow.workflow_id || '').trim();
539
+ if (!controlId || !workflowId) {
540
+ continue;
541
+ }
542
+ if (!workflowsByControl.has(controlId)) {
543
+ workflowsByControl.set(controlId, new Set());
544
+ }
545
+ workflowsByControl.get(controlId)?.add(workflowId);
546
+ if (workflow.status === 'approved') {
547
+ if (!approvedByControl.has(controlId)) {
548
+ approvedByControl.set(controlId, new Set());
549
+ }
550
+ for (const evidenceId of normalizeList(workflow.evidence_ids)) {
551
+ if (evidenceById.has(evidenceId)) {
552
+ approvedByControl.get(controlId)?.add(evidenceId);
553
+ }
554
+ }
555
+ }
556
+ }
557
+ const rows = [];
558
+ for (const control of controls) {
559
+ if (!control || typeof control !== 'object') {
560
+ continue;
561
+ }
562
+ const controlId = String(control.control_id || '').trim();
563
+ if (!controlId) {
564
+ continue;
565
+ }
566
+ const requiredTypes = Array.from(new Set(normalizeList(control.evidence_types))).sort();
567
+ const linkedEvidenceIds = Array.from(evidenceByControl.get(controlId) || new Set()).sort();
568
+ const approvedEvidenceIds = Array.from(approvedByControl.get(controlId) || new Set()).sort();
569
+ const collectedTypes = new Set();
570
+ for (const evidenceId of linkedEvidenceIds) {
571
+ const evidence = evidenceById.get(evidenceId);
572
+ if (!evidence) {
573
+ continue;
574
+ }
575
+ extractEvidenceTypes(evidence).forEach((value) => collectedTypes.add(value));
576
+ }
577
+ const collectedTypeList = Array.from(collectedTypes).sort();
578
+ const missingEvidenceTypes = requiredTypes.filter((item) => !collectedTypes.has(item));
579
+ const owner = String(control.owner || '').trim() || undefined;
580
+ const frequency = String(control.frequency || '').trim() || undefined;
581
+ const hasRequirements = requiredTypes.length > 0;
582
+ const hasApprovedEvidence = approvedEvidenceIds.length > 0;
583
+ const reportable = Boolean(owner && frequency && hasRequirements && hasApprovedEvidence && missingEvidenceTypes.length === 0);
584
+ const gaps = [];
585
+ if (!owner) {
586
+ gaps.push('missing_owner');
587
+ }
588
+ if (!frequency) {
589
+ gaps.push('missing_frequency');
590
+ }
591
+ if (!hasRequirements) {
592
+ gaps.push('missing_evidence_type_requirements');
593
+ }
594
+ if (linkedEvidenceIds.length === 0) {
595
+ gaps.push('missing_linked_evidence');
596
+ }
597
+ if (!hasApprovedEvidence) {
598
+ gaps.push('missing_approved_evidence');
599
+ }
600
+ if (missingEvidenceTypes.length > 0) {
601
+ gaps.push('missing_required_evidence_types');
602
+ }
603
+ rows.push({
604
+ control_id: controlId,
605
+ title: String(control.title || controlId),
606
+ clause: String(control.clause || '').trim() || undefined,
607
+ owner,
608
+ frequency,
609
+ required_evidence_types: requiredTypes,
610
+ evidence_types_collected: collectedTypeList,
611
+ missing_evidence_types: missingEvidenceTypes,
612
+ linked_evidence_ids: linkedEvidenceIds,
613
+ approved_evidence_ids: approvedEvidenceIds,
614
+ workflow_ids: Array.from(workflowsByControl.get(controlId) || new Set()).sort(),
615
+ status: reportable ? 'covered' : linkedEvidenceIds.length > 0 || approvedEvidenceIds.length > 0 ? 'partial' : 'gap',
616
+ reportable,
617
+ gaps,
618
+ });
619
+ }
620
+ const totalControls = rows.length;
621
+ const coveredControls = rows.filter((row) => row.reportable).length;
622
+ const partialControls = rows.filter((row) => row.status === 'partial').length;
623
+ const gapControls = rows.filter((row) => row.status === 'gap').length;
624
+ const coverageRatio = totalControls > 0 ? coveredControls / totalControls : 0;
625
+ const uncovered = rows.filter((row) => !row.reportable).map((row) => row.control_id);
626
+ return {
627
+ report_type: 'control_coverage',
628
+ generated_at: utcNow(),
629
+ catalog_id: catalog.catalog_id,
630
+ standard: catalog.standard,
631
+ version: catalog.version,
632
+ summary: {
633
+ total_controls: totalControls,
634
+ covered_controls: coveredControls,
635
+ partial_controls: partialControls,
636
+ gap_controls: gapControls,
637
+ coverage_ratio: Number(coverageRatio.toFixed(6)),
638
+ coverage_percent: Number((coverageRatio * 100).toFixed(2)),
639
+ target_coverage_ratio: Number(targetCoverage.toFixed(6)),
640
+ target_coverage_percent: Number((targetCoverage * 100).toFixed(2)),
641
+ target_met: coverageRatio >= targetCoverage,
642
+ },
643
+ controls: rows,
644
+ missing_workflow_modules: prioritizeMissingWorkflowModules(uncovered),
645
+ };
646
+ }
647
+ function buildWorkflowStatePayload(options) {
648
+ const catalog = resolveCatalog(options?.catalog || null);
649
+ const workflows = options?.workflows || Array.from(runtimeWorkflows.values());
650
+ const evidenceItems = options?.evidenceItems || Array.from(runtimeEvidence.values());
651
+ return {
652
+ state_type: 'control_workflow_state',
653
+ generated_at: utcNow(),
654
+ catalog_id: catalog.catalog_id,
655
+ standard: catalog.standard,
656
+ version: catalog.version,
657
+ workflows,
658
+ evidence_items: evidenceItems,
659
+ };
660
+ }
661
+ function exportWorkflowState(outputPath, options) {
662
+ const payload = buildWorkflowStatePayload(options);
663
+ fs.mkdirSync(path.dirname(outputPath), { recursive: true });
664
+ fs.writeFileSync(outputPath, JSON.stringify(payload, null, 2), 'utf-8');
665
+ return clone(payload);
666
+ }
667
+ function loadWorkflowState(filePath, options) {
668
+ const payload = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
669
+ const workflows = Array.isArray(payload.workflows) ? payload.workflows : [];
670
+ const evidenceItems = Array.isArray(payload.evidence_items) ? payload.evidence_items : [];
671
+ if (options?.replaceRuntime !== false) {
672
+ runtimeWorkflows = new Map();
673
+ runtimeEvidence = new Map();
674
+ (0, evidence_store_1.clearRuntimeEvidenceStore)();
675
+ for (const workflow of workflows) {
676
+ if (!workflow || typeof workflow !== 'object') {
677
+ continue;
678
+ }
679
+ const workflowId = String(workflow.workflow_id || '').trim();
680
+ if (!workflowId) {
681
+ continue;
682
+ }
683
+ runtimeWorkflows.set(workflowId, clone(workflow));
684
+ }
685
+ for (const evidence of evidenceItems) {
686
+ if (!evidence || typeof evidence !== 'object') {
687
+ continue;
688
+ }
689
+ const evidenceId = String(evidence.evidence_id || '').trim();
690
+ if (!evidenceId) {
691
+ continue;
692
+ }
693
+ runtimeEvidence.set(evidenceId, clone(evidence));
694
+ (0, evidence_store_1.recordRuntimeEvidence)(evidence);
695
+ }
696
+ }
697
+ return payload;
698
+ }