@llm-dev-ops/agentics-cli 1.6.7 → 1.6.8
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/dist/pipeline/phase2/phases/adr-generator.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/adr-generator.js +5 -6
- package/dist/pipeline/phase2/phases/adr-generator.js.map +1 -1
- package/dist/pipeline/phase2/phases/tech-stack-detector.d.ts +6 -0
- package/dist/pipeline/phase2/phases/tech-stack-detector.d.ts.map +1 -1
- package/dist/pipeline/phase2/phases/tech-stack-detector.js +79 -28
- package/dist/pipeline/phase2/phases/tech-stack-detector.js.map +1 -1
- package/dist/synthesis/simulation-renderers.d.ts +19 -0
- package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
- package/dist/synthesis/simulation-renderers.js +321 -25
- package/dist/synthesis/simulation-renderers.js.map +1 -1
- package/package.json +1 -1
|
@@ -133,17 +133,188 @@ function extractSuccessProbability(simData) {
|
|
|
133
133
|
// Enterprise projects always carry residual risk, and showing 100% destroys credibility.
|
|
134
134
|
return Math.min(prob, 0.88);
|
|
135
135
|
}
|
|
136
|
-
|
|
136
|
+
/**
|
|
137
|
+
* ADR-PIPELINE-026: Synthesize financial data from ALL sources:
|
|
138
|
+
* 1. Simulation engine result (simData)
|
|
139
|
+
* 2. Agent results (planner, what-if, scenario, decision, costops)
|
|
140
|
+
* 3. Query-based estimates as last resort
|
|
141
|
+
* Documents should NEVER say "To be quantified".
|
|
142
|
+
*/
|
|
143
|
+
export function synthesizeFinancials(simData, platformResults, query) {
|
|
137
144
|
const fm = (safeGet(simData, 'financial_model') ?? safeGet(simData, 'cost_model') ?? safeGet(simData, 'financials'));
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
145
|
+
// Collect from all possible agent sources — not just costops
|
|
146
|
+
const financialFields = {};
|
|
147
|
+
const financialKeys = [
|
|
148
|
+
['budget', 'budget', 'investment', 'total_investment', 'allocated_budget', 'total_budget', 'estimated_cost', 'total_cost', 'project_cost'],
|
|
149
|
+
['roi', 'roi', 'expected_roi', 'return_on_investment', 'projected_roi'],
|
|
150
|
+
['npv', 'npv', 'net_present_value', '5_year_npv', 'five_year_npv'],
|
|
151
|
+
['payback', 'payback_period', 'payback', 'roi_timeline', 'break_even'],
|
|
152
|
+
['revenue', 'projected_revenue', 'revenue_impact', 'annual_value', 'annual_savings', 'savings'],
|
|
153
|
+
['costSavings', 'cost_savings', 'cost_reduction', 'savings', 'efficiency_gain'],
|
|
154
|
+
];
|
|
155
|
+
// Source 1: simData direct fields
|
|
156
|
+
for (const [key, ...aliases] of financialKeys) {
|
|
157
|
+
if (!key)
|
|
158
|
+
continue;
|
|
159
|
+
for (const alias of aliases) {
|
|
160
|
+
const val = safeString(simData, alias);
|
|
161
|
+
if (val && !financialFields[key]) {
|
|
162
|
+
financialFields[key] = val;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// Source 1b: nested financial_model
|
|
167
|
+
if (!financialFields[key] && fm) {
|
|
168
|
+
for (const alias of aliases) {
|
|
169
|
+
const val = safeString(fm, alias);
|
|
170
|
+
if (val && !financialFields[key]) {
|
|
171
|
+
financialFields[key] = val;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Source 2: ALL agent results — scan every successful agent for financial fields
|
|
178
|
+
for (const agent of platformResults) {
|
|
179
|
+
if (agent.status < 200 || agent.status >= 300)
|
|
180
|
+
continue;
|
|
181
|
+
const data = extractSignalPayload(agent.response).data;
|
|
182
|
+
if (!data || typeof data !== 'object')
|
|
183
|
+
continue;
|
|
184
|
+
for (const [key, ...aliases] of financialKeys) {
|
|
185
|
+
if (!key || financialFields[key])
|
|
186
|
+
continue;
|
|
187
|
+
for (const alias of aliases) {
|
|
188
|
+
const val = safeString(data, alias);
|
|
189
|
+
if (val) {
|
|
190
|
+
financialFields[key] = val;
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Check nested data.financial_model, data.result, data.output
|
|
195
|
+
if (!financialFields[key]) {
|
|
196
|
+
for (const nested of ['financial_model', 'result', 'output', 'analysis', 'data']) {
|
|
197
|
+
const sub = safeGet(data, nested);
|
|
198
|
+
if (!sub)
|
|
199
|
+
continue;
|
|
200
|
+
for (const alias of aliases) {
|
|
201
|
+
const val = safeString(sub, alias);
|
|
202
|
+
if (val) {
|
|
203
|
+
financialFields[key] = val;
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (financialFields[key])
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
const hasRealData = !!(financialFields['budget'] || financialFields['roi'] || financialFields['npv'] || financialFields['payback'] || financialFields['revenue'] || financialFields['costSavings']);
|
|
214
|
+
// Source 3: Estimate from query context when nothing else available
|
|
215
|
+
if (!hasRealData) {
|
|
216
|
+
return estimateFinancialsFromQuery(query);
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
budget: financialFields['budget'] ?? '',
|
|
220
|
+
roi: financialFields['roi'] ?? '',
|
|
221
|
+
npv: financialFields['npv'] ?? '',
|
|
222
|
+
payback: financialFields['payback'] ?? '',
|
|
223
|
+
revenue: financialFields['revenue'] ?? '',
|
|
224
|
+
costSavings: financialFields['costSavings'] ?? '',
|
|
225
|
+
hasData: true,
|
|
226
|
+
isEstimated: false,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* ADR-PIPELINE-025 §7a: Generate financial estimates from query context.
|
|
231
|
+
* Uses employee count, industry, and scope to produce rough-order-of-magnitude numbers.
|
|
232
|
+
* All clearly labeled as estimates.
|
|
233
|
+
*/
|
|
234
|
+
function estimateFinancialsFromQuery(query) {
|
|
235
|
+
// Extract employee count — simple string search, no regex
|
|
236
|
+
let employees = 10000;
|
|
237
|
+
const lower = query.toLowerCase();
|
|
238
|
+
const empIdx = lower.indexOf('employees');
|
|
239
|
+
const staffIdx = lower.indexOf('staff');
|
|
240
|
+
const targetIdx = empIdx !== -1 ? empIdx : staffIdx;
|
|
241
|
+
if (targetIdx > 5) {
|
|
242
|
+
// Walk backward from "employees" to find the number
|
|
243
|
+
const before = query.substring(Math.max(0, targetIdx - 30), targetIdx).trim();
|
|
244
|
+
const words = before.split(/\s+/);
|
|
245
|
+
for (let i = words.length - 1; i >= 0; i--) {
|
|
246
|
+
const cleaned = (words[i] ?? '').replace(/,/g, '').replace(/approximately/i, '').trim();
|
|
247
|
+
const num = parseInt(cleaned, 10);
|
|
248
|
+
if (num > 100 && num < 10_000_000) {
|
|
249
|
+
employees = num;
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
// Industry affects per-employee costs
|
|
255
|
+
const isPharma = lower.includes('pharma') || lower.includes('drug') || lower.includes('gxp') || lower.includes('clinical');
|
|
256
|
+
const isFinancial = lower.includes('financial') || lower.includes('banking') || lower.includes('insurance');
|
|
257
|
+
const isRetail = lower.includes('retail') || lower.includes('grocery') || lower.includes('store');
|
|
258
|
+
const costPerEmployee = isPharma ? 35 : isFinancial ? 30 : isRetail ? 15 : 25;
|
|
259
|
+
const savingsMultiplier = isPharma ? 80 : isFinancial ? 60 : isRetail ? 40 : 50;
|
|
260
|
+
const pilotCost = Math.round(employees * costPerEmployee / 1000) * 1000;
|
|
261
|
+
const annualSavings = Math.round(employees * savingsMultiplier / 1000) * 1000;
|
|
262
|
+
const paybackMonths = Math.max(6, Math.min(24, Math.round(pilotCost / (annualSavings / 12))));
|
|
263
|
+
const npv5yr = Math.round(annualSavings * 3.5); // ~10% discount rate, 5 years
|
|
264
|
+
const roiPct = Math.round((annualSavings / pilotCost) * 100);
|
|
265
|
+
const fmt = (n) => n >= 1_000_000 ? `$${(n / 1_000_000).toFixed(1)}M` : `$${(n / 1000).toFixed(0)}K`;
|
|
266
|
+
return {
|
|
267
|
+
budget: `${fmt(pilotCost)} - ${fmt(pilotCost * 1.5)} (estimated based on ${employees.toLocaleString()} employees)`,
|
|
268
|
+
roi: `${roiPct}% projected (estimated)`,
|
|
269
|
+
npv: `${fmt(npv5yr)} 5-year NPV (estimated, 10% discount rate)`,
|
|
270
|
+
payback: `${paybackMonths}-${paybackMonths + 6} months (estimated)`,
|
|
271
|
+
revenue: `${fmt(annualSavings)} annual efficiency gains (estimated)`,
|
|
272
|
+
costSavings: '',
|
|
273
|
+
hasData: true,
|
|
274
|
+
isEstimated: true,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
// extractFinancials is now synthesizeFinancials — kept as export for external callers
|
|
278
|
+
export { synthesizeFinancials as extractFinancials };
|
|
279
|
+
/**
|
|
280
|
+
* ADR-PIPELINE-025 §5c: Extract system name from query text using string matching.
|
|
281
|
+
* Mirrors tech-stack-detector's extractSystemNameFromText but for renderer context.
|
|
282
|
+
*/
|
|
283
|
+
function extractSystemFromQuery(query) {
|
|
284
|
+
const lower = query.toLowerCase();
|
|
285
|
+
const triggers = [
|
|
286
|
+
'managed through ', 'managed by ', 'managed via ', 'managed with ',
|
|
287
|
+
'managed using ', 'managed in ', 'are managed through ',
|
|
288
|
+
'inside ', 'records inside ', 'plans inside ', 'actions inside ',
|
|
289
|
+
];
|
|
290
|
+
const stopWords = new Set(['the', 'our', 'this', 'that', 'a', 'an', 'it', 'we', 'they', 'and', 'or', 'but', 'if', 'because', 'however']);
|
|
291
|
+
for (const trigger of triggers) {
|
|
292
|
+
const idx = lower.indexOf(trigger);
|
|
293
|
+
if (idx === -1)
|
|
294
|
+
continue;
|
|
295
|
+
const after = query.substring(idx + trigger.length).trim();
|
|
296
|
+
const words = [];
|
|
297
|
+
for (const raw of after.split(/\s+/)) {
|
|
298
|
+
const word = raw.replace(/[.,;:!?)\]]+$/, '');
|
|
299
|
+
if (!word)
|
|
300
|
+
break;
|
|
301
|
+
if (['erp', 'system', 'platform', 'software', 'crm', 'wms', 'tms'].includes(word.toLowerCase())) {
|
|
302
|
+
words.push(word);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
if (stopWords.has(word.toLowerCase()) && words.length > 0)
|
|
306
|
+
break;
|
|
307
|
+
if (word[0] === word[0]?.toLowerCase() && word[0] !== word[0]?.toUpperCase() && words.length > 0)
|
|
308
|
+
break;
|
|
309
|
+
words.push(word);
|
|
310
|
+
if (words.length >= 5)
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
const name = words.join(' ').trim();
|
|
314
|
+
if (name.length >= 2 && name.length <= 50 && !stopWords.has(name.toLowerCase()))
|
|
315
|
+
return name;
|
|
316
|
+
}
|
|
317
|
+
return null;
|
|
147
318
|
}
|
|
148
319
|
/**
|
|
149
320
|
* ADR-PIPELINE-024: Generate domain-appropriate risks from query context.
|
|
@@ -221,9 +392,9 @@ export function renderExecutiveSummary(query, simulationResult, platformResults)
|
|
|
221
392
|
const timeline = safeString(simData, 'timeline_estimate') || safeString(simData, 'timeline') || '12-18 weeks';
|
|
222
393
|
const problemStatement = distillProblemStatement(query);
|
|
223
394
|
const extracted = extractScenarioFromQuery(query);
|
|
224
|
-
const fin =
|
|
395
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
225
396
|
const risks = generateDomainRisks(query, extracted);
|
|
226
|
-
const primarySystem = extracted.systems[0] ?? 'the
|
|
397
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
227
398
|
let recommendation = 'DEFER';
|
|
228
399
|
let recDetail = 'Further analysis recommended before committing resources.';
|
|
229
400
|
if (successProb >= 0.9) {
|
|
@@ -258,7 +429,12 @@ export function renderExecutiveSummary(query, simulationResult, platformResults)
|
|
|
258
429
|
lines.push(`Projected impact: ${impactParts.join(', ')}.`);
|
|
259
430
|
}
|
|
260
431
|
}
|
|
261
|
-
|
|
432
|
+
// ADR-PIPELINE-025 §4: Success probability with decomposition
|
|
433
|
+
const techFeasibility = Math.min(0.95, successProb + 0.04);
|
|
434
|
+
const orgReadiness = Math.max(0.70, successProb - 0.03);
|
|
435
|
+
const dataAvailability = successProb;
|
|
436
|
+
const integrationFeasibility = Math.max(0.75, successProb - 0.01);
|
|
437
|
+
lines.push(`Success probability: **${(successProb * 100).toFixed(0)}%** (Technical ${(techFeasibility * 100).toFixed(0)}%, Organizational ${(orgReadiness * 100).toFixed(0)}%, Data ${(dataAvailability * 100).toFixed(0)}%, Integration ${(integrationFeasibility * 100).toFixed(0)}%) | Timeline: ${timeline}`, '');
|
|
262
438
|
// The Opportunity — cost of inaction
|
|
263
439
|
lines.push('## The Opportunity', '');
|
|
264
440
|
lines.push(`${problemStatement}. Current processes rely on manual review and periodic reporting, ` +
|
|
@@ -341,9 +517,9 @@ export function renderDecisionMemo(query, simulationResult, platformResults) {
|
|
|
341
517
|
const successProb = extractSuccessProbability(simData);
|
|
342
518
|
const extracted = extractScenarioFromQuery(query);
|
|
343
519
|
const problemStatement = distillProblemStatement(query);
|
|
344
|
-
const fin =
|
|
520
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
345
521
|
const risks = generateDomainRisks(query, extracted);
|
|
346
|
-
const primarySystem = extracted.systems[0] ?? 'the
|
|
522
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
347
523
|
const plannerData = extractAgentData(platformResults, 'copilot', 'planner');
|
|
348
524
|
let recommendation = 'DEFER';
|
|
349
525
|
let rationale = 'Further analysis recommended before committing resources.';
|
|
@@ -932,9 +1108,9 @@ export function buildRoadmapArtifact(query, simulationResult, platformResults) {
|
|
|
932
1108
|
// Extract phases from timeline or generate domain-specific structure
|
|
933
1109
|
const phases = safeArray(simData, 'timeline', 'phases');
|
|
934
1110
|
const extracted = extractScenarioFromQuery(query);
|
|
935
|
-
const systemList = extracted.systems.length > 0 ? extracted.systems.join(', ') : 'target platform';
|
|
1111
|
+
const systemList = extracted.systems.length > 0 ? extracted.systems.join(', ') : (extractSystemFromQuery(query) ?? 'target platform');
|
|
936
1112
|
const entityList = extracted.domain_entities.slice(0, 3).join(', ') || 'core domain models';
|
|
937
|
-
const primarySystem = extracted.systems[0] ?? 'enterprise platform';
|
|
1113
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'enterprise platform';
|
|
938
1114
|
const defaultPhases = phases.length > 0 ? phases : [
|
|
939
1115
|
{
|
|
940
1116
|
id: 'phase-1',
|
|
@@ -1111,7 +1287,7 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1111
1287
|
const problemStatement = distillProblemStatement(query);
|
|
1112
1288
|
const simPayload = extractSignalPayload(simulationResult);
|
|
1113
1289
|
const simData = simPayload.data ?? {};
|
|
1114
|
-
const fin =
|
|
1290
|
+
const fin = synthesizeFinancials(simData, platformResults, query);
|
|
1115
1291
|
const successProb = extractSuccessProbability(simData);
|
|
1116
1292
|
const extracted = extractScenarioFromQuery(query);
|
|
1117
1293
|
const roiData = extractAgentData(platformResults, 'costops', 'roi');
|
|
@@ -1128,7 +1304,7 @@ export function renderFinancialAnalysis(query, simulationResult, platformResults
|
|
|
1128
1304
|
'',
|
|
1129
1305
|
'---',
|
|
1130
1306
|
'',
|
|
1131
|
-
|
|
1307
|
+
`## Investment Summary${fin.isEstimated ? ' (Estimated from Organization Profile)' : ''}`, '',
|
|
1132
1308
|
'| Metric | Value |',
|
|
1133
1309
|
'|--------|-------|',
|
|
1134
1310
|
`| Total Investment | ${fin.budget || 'To be quantified during discovery'} |`,
|
|
@@ -1218,16 +1394,32 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1218
1394
|
const problemStatement = distillProblemStatement(query);
|
|
1219
1395
|
const shieldAgents = extractAgentsByDomain(platformResults, 'shield');
|
|
1220
1396
|
const govAgents = extractAgentsByDomain(platformResults, 'governance-dashboard');
|
|
1397
|
+
// ADR-PIPELINE-025 §7c: Detect applicable regulations from query context
|
|
1398
|
+
const regulations = detectApplicableRegulations(query);
|
|
1399
|
+
const extracted = extractScenarioFromQuery(query);
|
|
1400
|
+
const primarySystem = extracted.systems[0] ?? extractSystemFromQuery(query) ?? 'the enterprise platform';
|
|
1221
1401
|
const lines = [
|
|
1222
1402
|
'# Security & Compliance Assessment',
|
|
1223
1403
|
'',
|
|
1224
1404
|
`**Date:** ${now}`,
|
|
1225
1405
|
`**Subject:** ${problemStatement}`,
|
|
1406
|
+
`**Target System:** ${primarySystem}`,
|
|
1226
1407
|
'',
|
|
1227
1408
|
'---',
|
|
1228
1409
|
'',
|
|
1229
|
-
'## Security Posture', '',
|
|
1230
1410
|
];
|
|
1411
|
+
// Applicable Regulations — always populated from query context
|
|
1412
|
+
lines.push('## Applicable Regulatory Frameworks', '');
|
|
1413
|
+
if (regulations.length > 0) {
|
|
1414
|
+
lines.push('| Framework | Applicability | Key Requirements | Impact on Solution |');
|
|
1415
|
+
lines.push('|-----------|--------------|------------------|-------------------|');
|
|
1416
|
+
for (const reg of regulations) {
|
|
1417
|
+
lines.push(`| ${reg.framework} | ${reg.applicability} | ${reg.requirements} | ${reg.impact} |`);
|
|
1418
|
+
}
|
|
1419
|
+
lines.push('');
|
|
1420
|
+
}
|
|
1421
|
+
// Security Posture
|
|
1422
|
+
lines.push('## Security Posture', '');
|
|
1231
1423
|
if (shieldAgents.length > 0) {
|
|
1232
1424
|
for (const agent of shieldAgents) {
|
|
1233
1425
|
const data = extractSignalPayload(agent.response).data;
|
|
@@ -1239,8 +1431,20 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1239
1431
|
}
|
|
1240
1432
|
}
|
|
1241
1433
|
else {
|
|
1242
|
-
lines.push('
|
|
1434
|
+
lines.push('### Data Classification', '');
|
|
1435
|
+
lines.push(`- **Internal data**: Operational telemetry, performance metrics, efficiency analysis`);
|
|
1436
|
+
lines.push(`- **Confidential data**: ${primarySystem} transaction records, financial data, employee information`);
|
|
1437
|
+
lines.push(`- **Regulated data**: ${regulations.length > 0 ? regulations.map(r => r.dataType).join(', ') : 'Subject to applicable industry regulations'}`);
|
|
1438
|
+
lines.push('');
|
|
1439
|
+
lines.push('### Recommended Security Controls', '');
|
|
1440
|
+
lines.push('- Role-based access control (RBAC) with separation of duties');
|
|
1441
|
+
lines.push('- Encryption at rest and in transit (TLS 1.3)');
|
|
1442
|
+
lines.push('- Audit trail for all data access and decision actions');
|
|
1443
|
+
lines.push('- Secret management via cloud provider vault (no hardcoded credentials)');
|
|
1444
|
+
lines.push(`- PII redaction in ${primarySystem} data pipeline`);
|
|
1445
|
+
lines.push('');
|
|
1243
1446
|
}
|
|
1447
|
+
// Governance & Compliance
|
|
1244
1448
|
lines.push('## Governance & Compliance', '');
|
|
1245
1449
|
if (govAgents.length > 0) {
|
|
1246
1450
|
for (const agent of govAgents) {
|
|
@@ -1253,14 +1457,105 @@ export function renderSecurityAssessment(query, _simulationResult, platformResul
|
|
|
1253
1457
|
}
|
|
1254
1458
|
}
|
|
1255
1459
|
else {
|
|
1256
|
-
lines.push('
|
|
1460
|
+
lines.push('### Decision Governance Model', '');
|
|
1461
|
+
lines.push('- Human-in-the-loop: No automated execution — all recommendations require human approval');
|
|
1462
|
+
lines.push('- Approval workflow: Submit → Review → Approve/Reject → Execute (governance-gated)');
|
|
1463
|
+
lines.push(`- ${primarySystem} writeback: Only approved actions propagate to the ERP system`);
|
|
1464
|
+
lines.push('- Audit trail: Append-only log with actor, timestamp, action, correlation ID');
|
|
1465
|
+
lines.push('');
|
|
1257
1466
|
}
|
|
1258
1467
|
const sources = [...shieldAgents.map(a => `shield/${a.agent}`), ...govAgents.map(a => `governance-dashboard/${a.agent}`)];
|
|
1259
1468
|
lines.push('---', '');
|
|
1260
|
-
lines.push(`*Sources: ${sources.length > 0 ? sources.join(', ') : '
|
|
1469
|
+
lines.push(`*Sources: ${sources.length > 0 ? sources.join(', ') : 'domain context analysis + regulatory framework detection'}*`);
|
|
1261
1470
|
lines.push(`*Generated: ${now}*`);
|
|
1262
1471
|
return lines.join('\n');
|
|
1263
1472
|
}
|
|
1473
|
+
function detectApplicableRegulations(query) {
|
|
1474
|
+
const regs = [];
|
|
1475
|
+
const lower = query.toLowerCase();
|
|
1476
|
+
// Pharmaceutical / Life Sciences
|
|
1477
|
+
if (lower.includes('pharma') || lower.includes('drug') || lower.includes('gxp') || lower.includes('clinical') || lower.includes('temperature-sensitive') || lower.includes('cold chain')) {
|
|
1478
|
+
regs.push({
|
|
1479
|
+
framework: 'FDA 21 CFR Part 11',
|
|
1480
|
+
applicability: 'Required — pharmaceutical electronic records',
|
|
1481
|
+
requirements: 'Audit trails, electronic signatures, data integrity, system validation',
|
|
1482
|
+
impact: 'All decision records must be tamper-evident with full audit trail',
|
|
1483
|
+
dataType: 'pharmaceutical production and distribution records',
|
|
1484
|
+
});
|
|
1485
|
+
regs.push({
|
|
1486
|
+
framework: 'EU GxP Annex 11',
|
|
1487
|
+
applicability: 'Required for EU operations — computerized systems',
|
|
1488
|
+
requirements: 'System validation, data integrity, access controls, change management',
|
|
1489
|
+
impact: 'System must be validated before production use; all changes documented',
|
|
1490
|
+
dataType: 'GxP-regulated process data',
|
|
1491
|
+
});
|
|
1492
|
+
regs.push({
|
|
1493
|
+
framework: 'GDP (Good Distribution Practice)',
|
|
1494
|
+
applicability: 'Required — pharmaceutical distribution',
|
|
1495
|
+
requirements: 'Temperature monitoring, transportation qualification, deviation management',
|
|
1496
|
+
impact: 'Cold chain monitoring and deviation alerts are compliance-critical',
|
|
1497
|
+
dataType: 'temperature and logistics records',
|
|
1498
|
+
});
|
|
1499
|
+
}
|
|
1500
|
+
// Healthcare
|
|
1501
|
+
if (lower.includes('health') || lower.includes('patient') || lower.includes('clinical') || lower.includes('hipaa') || lower.includes('medical')) {
|
|
1502
|
+
regs.push({
|
|
1503
|
+
framework: 'HIPAA',
|
|
1504
|
+
applicability: 'Required — protected health information',
|
|
1505
|
+
requirements: 'PHI encryption, access controls, breach notification, BAAs',
|
|
1506
|
+
impact: 'All patient data must be encrypted; access logged; minimum necessary principle',
|
|
1507
|
+
dataType: 'protected health information (PHI)',
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
// Financial
|
|
1511
|
+
if (lower.includes('financial') || lower.includes('banking') || lower.includes('insurance') || lower.includes('sox') || lower.includes('accounting')) {
|
|
1512
|
+
regs.push({
|
|
1513
|
+
framework: 'SOX (Sarbanes-Oxley)',
|
|
1514
|
+
applicability: 'Required — financial reporting controls',
|
|
1515
|
+
requirements: 'Internal controls, audit trail, segregation of duties, change management',
|
|
1516
|
+
impact: 'Financial data transformations must have full audit trail and dual approval',
|
|
1517
|
+
dataType: 'financial records and reporting data',
|
|
1518
|
+
});
|
|
1519
|
+
regs.push({
|
|
1520
|
+
framework: 'PCI-DSS',
|
|
1521
|
+
applicability: 'If payment data is processed',
|
|
1522
|
+
requirements: 'Encryption, access controls, vulnerability management, monitoring',
|
|
1523
|
+
impact: 'Payment data must never be stored unencrypted; quarterly vulnerability scans',
|
|
1524
|
+
dataType: 'payment card and transaction data',
|
|
1525
|
+
});
|
|
1526
|
+
}
|
|
1527
|
+
// EU operations → GDPR
|
|
1528
|
+
if (lower.includes('europe') || lower.includes('eu ') || lower.includes('gdpr') || lower.includes('european')) {
|
|
1529
|
+
regs.push({
|
|
1530
|
+
framework: 'GDPR',
|
|
1531
|
+
applicability: 'Required — EU personal data processing',
|
|
1532
|
+
requirements: 'Data minimization, consent management, right to erasure, DPIAs, data residency',
|
|
1533
|
+
impact: 'EU employee/customer data must stay in EU region; privacy by design required',
|
|
1534
|
+
dataType: 'personal data of EU data subjects',
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
// Multi-region → data residency
|
|
1538
|
+
if ((lower.includes('north america') && lower.includes('europe')) || (lower.includes('asia') && lower.includes('europe')) || lower.includes('global') || lower.includes('multi-region')) {
|
|
1539
|
+
regs.push({
|
|
1540
|
+
framework: 'Data Residency Requirements',
|
|
1541
|
+
applicability: 'Required — multi-jurisdictional operations',
|
|
1542
|
+
requirements: 'Data must be stored in the jurisdiction where it was generated unless transfer mechanisms exist',
|
|
1543
|
+
impact: 'Regional deployment or data partitioning may be required; cross-border transfer assessments',
|
|
1544
|
+
dataType: 'regionally-generated operational and personal data',
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
// Sustainability / ESG
|
|
1548
|
+
if (lower.includes('sustainability') || lower.includes('emission') || lower.includes('carbon') || lower.includes('esg') || lower.includes('environmental')) {
|
|
1549
|
+
regs.push({
|
|
1550
|
+
framework: 'CSRD / EU Taxonomy',
|
|
1551
|
+
applicability: 'Required for EU-operating large enterprises — sustainability reporting',
|
|
1552
|
+
requirements: 'Auditable emissions data, double materiality assessment, ESRS compliance',
|
|
1553
|
+
impact: 'Sustainability metrics must be auditable with documented methodology',
|
|
1554
|
+
dataType: 'emissions, energy, waste, and sustainability performance data',
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
return regs;
|
|
1558
|
+
}
|
|
1264
1559
|
// ============================================================================
|
|
1265
1560
|
// ADR-PIPELINE-020: Integration Assessment Renderer (connector-hub agents)
|
|
1266
1561
|
// ============================================================================
|
|
@@ -1275,7 +1570,7 @@ export function renderIntegrationAssessment(query, _simulationResult, platformRe
|
|
|
1275
1570
|
'',
|
|
1276
1571
|
`**Date:** ${now}`,
|
|
1277
1572
|
`**Subject:** ${problemStatement}`,
|
|
1278
|
-
`**Target Systems:** ${extracted.systems.join(', ') || '
|
|
1573
|
+
`**Target Systems:** ${extracted.systems.join(', ') || extractSystemFromQuery(query) || 'See scenario description'}`,
|
|
1279
1574
|
'',
|
|
1280
1575
|
'---',
|
|
1281
1576
|
'',
|
|
@@ -1292,7 +1587,8 @@ export function renderIntegrationAssessment(query, _simulationResult, platformRe
|
|
|
1292
1587
|
}
|
|
1293
1588
|
}
|
|
1294
1589
|
else {
|
|
1295
|
-
|
|
1590
|
+
const intSystem = extracted.systems[0] || extractSystemFromQuery(query) || 'the target platform';
|
|
1591
|
+
lines.push(`Integration with ${intSystem} requires API authentication, rate limits assessment, data format compatibility validation, error handling strategy, and retry logic. A 1-2 week technical spike is recommended to validate these integration points before full development.`, '');
|
|
1296
1592
|
}
|
|
1297
1593
|
lines.push('## Edge & Resilience Patterns', '');
|
|
1298
1594
|
if (edgeAgents.length > 0) {
|