@llm-dev-ops/agentics-cli 1.8.2 → 1.9.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/dist/commands/erp.d.ts.map +1 -1
- package/dist/commands/erp.js +11 -0
- package/dist/commands/erp.js.map +1 -1
- package/dist/pipeline/auto-chain.d.ts.map +1 -1
- package/dist/pipeline/auto-chain.js +65 -3
- package/dist/pipeline/auto-chain.js.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.js +14 -1
- package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
- package/dist/synthesis/simulation-renderers.d.ts +6 -0
- package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
- package/dist/synthesis/simulation-renderers.js +169 -4
- package/dist/synthesis/simulation-renderers.js.map +1 -1
- package/package.json +1 -1
|
@@ -331,13 +331,17 @@ function estimateFinancialsFromQuery(query) {
|
|
|
331
331
|
savingsMultiplier = 50;
|
|
332
332
|
industryLabel = 'professional services';
|
|
333
333
|
}
|
|
334
|
-
|
|
335
|
-
|
|
334
|
+
// ── ADR-PIPELINE-038: Extract the operational unit from the query ──
|
|
335
|
+
// Instead of always using "employees", find what the system actually manages:
|
|
336
|
+
// vehicles, suppliers, patients, locations, transactions, etc.
|
|
337
|
+
const unitInfo = extractOperationalUnit(query, lower, employees);
|
|
338
|
+
const pilotCost = Math.round(unitInfo.count * costPerEmployee / 1000) * 1000;
|
|
339
|
+
const annualSavings = Math.round(unitInfo.count * savingsMultiplier / 1000) * 1000;
|
|
336
340
|
const paybackMonths = Math.max(6, Math.min(24, Math.round(pilotCost / (annualSavings / 12))));
|
|
337
341
|
const npv5yr = Math.round(annualSavings * 3.5);
|
|
338
342
|
const roiPct = Math.round((annualSavings / pilotCost) * 100);
|
|
339
|
-
const methodology = `${
|
|
340
|
-
const savingsMethod = `${
|
|
343
|
+
const methodology = `${unitInfo.count.toLocaleString()} ${unitInfo.label} × $${costPerEmployee}/${unitInfo.singular} ${industryLabel} benchmark`;
|
|
344
|
+
const savingsMethod = `${unitInfo.count.toLocaleString()} ${unitInfo.label} × $${savingsMultiplier}/${unitInfo.singular} annual efficiency gain`;
|
|
341
345
|
return {
|
|
342
346
|
budget: `${fmt(pilotCost)} - ${fmt(pilotCost * 1.5)} (${methodology})`,
|
|
343
347
|
roi: `${roiPct}% projected (${fmt(annualSavings)} savings ÷ ${fmt(pilotCost)} investment)`,
|
|
@@ -349,6 +353,40 @@ function estimateFinancialsFromQuery(query) {
|
|
|
349
353
|
isEstimated: true,
|
|
350
354
|
};
|
|
351
355
|
}
|
|
356
|
+
/**
|
|
357
|
+
* Extract the primary operational unit from the query.
|
|
358
|
+
* Instead of always defaulting to "employees", find what the system actually operates on.
|
|
359
|
+
* Returns the unit count, label (plural), and singular form.
|
|
360
|
+
*/
|
|
361
|
+
function extractOperationalUnit(query, lower, employeeCount) {
|
|
362
|
+
// Try to find an explicit count + unit in the query
|
|
363
|
+
const unitPatterns = [
|
|
364
|
+
{ pattern: /(\d[\d,]*)\s*(?:vehicles?|trucks?|vans?|cars?)/i, label: 'vehicles', singular: 'vehicle', fallbackRatio: 0.05 },
|
|
365
|
+
{ pattern: /(\d[\d,]*)\s*(?:suppliers?|vendors?)/i, label: 'suppliers', singular: 'supplier', fallbackRatio: 0.013 },
|
|
366
|
+
{ pattern: /(\d[\d,]*)\s*(?:patients?|beds?)/i, label: 'patients', singular: 'patient', fallbackRatio: 5 },
|
|
367
|
+
{ pattern: /(\d[\d,]*)\s*(?:locations?|sites?|offices?|stores?|branches?)/i, label: 'locations', singular: 'location', fallbackRatio: 0.005 },
|
|
368
|
+
{ pattern: /(\d[\d,]*)\s*(?:assets?|machines?|devices?)/i, label: 'assets', singular: 'asset', fallbackRatio: 0.1 },
|
|
369
|
+
{ pattern: /(\d[\d,]*)\s*(?:products?|skus?|items?)/i, label: 'products', singular: 'product', fallbackRatio: 0.5 },
|
|
370
|
+
{ pattern: /(\d[\d,]*)\s*(?:customers?|accounts?|clients?)/i, label: 'customers', singular: 'customer', fallbackRatio: 10 },
|
|
371
|
+
{ pattern: /(\d[\d,]*)\s*(?:shipments?|orders?|transactions?)/i, label: 'transactions', singular: 'transaction', fallbackRatio: 50 },
|
|
372
|
+
{ pattern: /(\d[\d,]*)\s*(?:contracts?|agreements?)/i, label: 'contracts', singular: 'contract', fallbackRatio: 0.02 },
|
|
373
|
+
];
|
|
374
|
+
for (const { pattern, label, singular, fallbackRatio } of unitPatterns) {
|
|
375
|
+
const match = query.match(pattern);
|
|
376
|
+
if (match?.[1]) {
|
|
377
|
+
const count = parseInt(match[1].replace(/,/g, ''), 10);
|
|
378
|
+
if (count > 0)
|
|
379
|
+
return { count, label, singular };
|
|
380
|
+
}
|
|
381
|
+
// If the unit type is mentioned but without a number, estimate from employee count
|
|
382
|
+
if (new RegExp(`\\b${label.replace(/s$/, '')}`, 'i').test(lower)) {
|
|
383
|
+
const estimated = Math.max(10, Math.round(employeeCount * fallbackRatio));
|
|
384
|
+
return { count: estimated, label, singular };
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
// Default: use employees (the original behavior)
|
|
388
|
+
return { count: employeeCount, label: 'employees', singular: 'employee' };
|
|
389
|
+
}
|
|
352
390
|
/**
|
|
353
391
|
* ADR-PIPELINE-029 §2: Domain-specific bottom-up financial models.
|
|
354
392
|
* These use query signals to build unit-economics-based estimates
|
|
@@ -794,6 +832,26 @@ function generateDomainRisks(query, extracted) {
|
|
|
794
832
|
mitigation: 'Include competitive benchmarking in board reporting; set public electrification targets aligned with or exceeding peer commitments; communicate transition progress to investors and customers',
|
|
795
833
|
});
|
|
796
834
|
}
|
|
835
|
+
// ── Generic domain-contextual risks for ANY domain not covered above ──
|
|
836
|
+
// Extract the core domain noun from the query and generate risks referencing it.
|
|
837
|
+
// This ensures every domain gets contextual risks, not just hardcoded ones.
|
|
838
|
+
if (risks.length < 5) {
|
|
839
|
+
const domainNoun = extractCoreDomainNoun(q);
|
|
840
|
+
if (domainNoun && !risks.some(r => r.risk.toLowerCase().includes(domainNoun.toLowerCase()))) {
|
|
841
|
+
risks.push({
|
|
842
|
+
risk: `${domainNoun} data may be incomplete, inconsistent, or outdated — analysis quality depends directly on input data quality from ${primarySystem}`,
|
|
843
|
+
category: 'Data', likelihood: 'Medium', impact: 'High', score: 6,
|
|
844
|
+
mitigation: `Data profiling and quality assessment during discovery phase; define minimum data completeness thresholds for ${domainNoun.toLowerCase()} analysis`,
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
if (risks.length < 5) {
|
|
848
|
+
risks.push({
|
|
849
|
+
risk: `Stakeholder alignment on ${domainNoun.toLowerCase()} priorities — different departments may have conflicting objectives for how to act on AI-generated insights`,
|
|
850
|
+
category: 'Organizational', likelihood: 'Medium', impact: 'Medium', score: 4,
|
|
851
|
+
mitigation: 'Cross-functional steering committee with clear decision authority; pilot scope agreed by all stakeholders before launch',
|
|
852
|
+
});
|
|
853
|
+
}
|
|
854
|
+
}
|
|
797
855
|
// Always include vendor dependency
|
|
798
856
|
risks.push({
|
|
799
857
|
risk: 'Technology vendor dependency — platform availability, pricing changes, or API deprecation',
|
|
@@ -802,6 +860,98 @@ function generateDomainRisks(query, extracted) {
|
|
|
802
860
|
});
|
|
803
861
|
return risks.sort((a, b) => b.score - a.score);
|
|
804
862
|
}
|
|
863
|
+
/**
|
|
864
|
+
* Extract the core domain noun from a query — "fleet electrification", "packaging waste",
|
|
865
|
+
* "water loss", "employee sustainability", etc. Used to generate contextual risks and
|
|
866
|
+
* financial language for domains not explicitly covered by hardcoded patterns.
|
|
867
|
+
*/
|
|
868
|
+
function extractCoreDomainNoun(query) {
|
|
869
|
+
// Try structured patterns first
|
|
870
|
+
const patterns = [
|
|
871
|
+
/(?:reduce|optimize|improve|manage|analyze|monitor)\s+([\w\s]{3,30}?)(?:\.|,|$|\s+(?:across|from|in|for|by|through|using|while|without))/i,
|
|
872
|
+
/(?:challenge.*?is)\s+([\w\s]{3,30}?)(?:\.|,|$)/i,
|
|
873
|
+
/(?:help\s+(?:us|the|our)\s+(?:better\s+)?(?:understand|manage|reduce|optimize|plan|improve))\s+([\w\s]{3,30}?)(?:\.|,|$)/i,
|
|
874
|
+
/(?:AI could help.*?)\s+([\w\s]{5,30}?)(?:\.|,|$)/i,
|
|
875
|
+
];
|
|
876
|
+
for (const pat of patterns) {
|
|
877
|
+
const m = query.match(pat);
|
|
878
|
+
if (m?.[1]) {
|
|
879
|
+
const noun = m[1].trim().replace(/^(the|a|an|our|their)\s+/i, '');
|
|
880
|
+
if (noun.length > 3 && noun.length < 40)
|
|
881
|
+
return noun.charAt(0).toUpperCase() + noun.slice(1);
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
// Fallback: extract the most specific noun phrase from the first 200 chars
|
|
885
|
+
const firstPart = query.slice(0, 200).toLowerCase();
|
|
886
|
+
const domainKeywords = firstPart.match(/\b(fleet|packaging|water|emission|procurement|maintenance|inventory|compliance|quality|safety|logistics|delivery|supply chain|workforce|clinical|financial|asset)\s+\w+/i);
|
|
887
|
+
if (domainKeywords?.[0])
|
|
888
|
+
return domainKeywords[0].charAt(0).toUpperCase() + domainKeywords[0].slice(1);
|
|
889
|
+
return 'operational process';
|
|
890
|
+
}
|
|
891
|
+
// ============================================================================
|
|
892
|
+
// ADR-PIPELINE-040: LLM Synthesis for Executive Documents
|
|
893
|
+
// ============================================================================
|
|
894
|
+
const LLM_SYNTHESIS_PROMPT = `You are a senior partner at McKinsey & Company rewriting an enterprise decision document.
|
|
895
|
+
|
|
896
|
+
Rules:
|
|
897
|
+
- Lead with the recommendation and quantified impact (Pyramid Principle)
|
|
898
|
+
- Every financial figure MUST show its derivation (unit × rate × volume = total)
|
|
899
|
+
- Frame "do nothing" as a quantified business risk with competitive and regulatory context
|
|
900
|
+
- Reference specific data from the analysis, never generic benchmarks
|
|
901
|
+
- Ensure timeline consistency across all sections
|
|
902
|
+
- Name specific stakeholder ROLES (VP Operations, Fleet Director, CFO), never "Workforce" or "Team"
|
|
903
|
+
- Include regulatory context relevant to the domain (EU standards, EPA requirements, industry mandates)
|
|
904
|
+
- Include competitive context (what peers/competitors are doing in this space)
|
|
905
|
+
- No sentence fragments, no prompt echo, no template placeholders, no "to be determined" phrases
|
|
906
|
+
- Every section must add insight, not just arrange data
|
|
907
|
+
|
|
908
|
+
Rewrite the document below to be credible in a boardroom at a Fortune 500 company.`;
|
|
909
|
+
/**
|
|
910
|
+
* ADR-PIPELINE-040: Attempt LLM synthesis of an executive document.
|
|
911
|
+
* Takes the template-generated skeleton and rewrites it into consulting-grade prose.
|
|
912
|
+
* Falls back to the skeleton unchanged if LLM is not available.
|
|
913
|
+
*/
|
|
914
|
+
export async function synthesizeExecutiveDocument(skeleton, query, documentType) {
|
|
915
|
+
// Try Claude binary first (works without API key, uses user's subscription)
|
|
916
|
+
try {
|
|
917
|
+
const { execSync } = await import('node:child_process');
|
|
918
|
+
const claudeBin = process.env['AGENTICS_CLAUDE_BIN'] || 'claude';
|
|
919
|
+
const input = `${LLM_SYNTHESIS_PROMPT}\n\nDocument type: ${documentType}\n\nOriginal business context:\n${query.slice(0, 1000)}\n\nDocument to rewrite:\n${skeleton}`;
|
|
920
|
+
const result = execSync(`${claudeBin} --print -`, { input, encoding: 'utf-8', timeout: 60_000, maxBuffer: 500_000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
921
|
+
if (result && result.trim().length > skeleton.length * 0.5) {
|
|
922
|
+
process.stderr.write(`[exec-docs] LLM synthesis succeeded for ${documentType} (${result.length} chars)\n`);
|
|
923
|
+
return result.trim();
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
catch {
|
|
927
|
+
// Claude binary not available — try API
|
|
928
|
+
}
|
|
929
|
+
// Try Anthropic API
|
|
930
|
+
const apiKey = process.env['ANTHROPIC_API_KEY'];
|
|
931
|
+
if (apiKey) {
|
|
932
|
+
try {
|
|
933
|
+
const { execSync } = await import('node:child_process');
|
|
934
|
+
const payload = JSON.stringify({
|
|
935
|
+
model: process.env['AGENTICS_CLAUDE_MODEL'] || 'claude-sonnet-4-20250514',
|
|
936
|
+
max_tokens: 4096,
|
|
937
|
+
messages: [{ role: 'user', content: `${LLM_SYNTHESIS_PROMPT}\n\nDocument type: ${documentType}\n\nBusiness context:\n${query.slice(0, 1000)}\n\nDocument:\n${skeleton}` }],
|
|
938
|
+
});
|
|
939
|
+
const result = execSync(`curl -s -X POST https://api.anthropic.com/v1/messages -H "content-type: application/json" -H "x-api-key: ${apiKey}" -H "anthropic-version: 2023-06-01" -d @-`, { input: payload, encoding: 'utf-8', timeout: 60_000, maxBuffer: 500_000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
940
|
+
const parsed = JSON.parse(result);
|
|
941
|
+
const text = parsed?.content?.[0]?.text;
|
|
942
|
+
if (text && text.length > skeleton.length * 0.5) {
|
|
943
|
+
process.stderr.write(`[exec-docs] API synthesis succeeded for ${documentType} (${text.length} chars)\n`);
|
|
944
|
+
return text;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
catch {
|
|
948
|
+
// API call failed — fall through to skeleton
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
// Tier 3: return skeleton unchanged
|
|
952
|
+
process.stderr.write(`[exec-docs] LLM synthesis unavailable for ${documentType} — using template skeleton\n`);
|
|
953
|
+
return skeleton;
|
|
954
|
+
}
|
|
805
955
|
// ============================================================================
|
|
806
956
|
// Executive Summary Renderer (ADR-PIPELINE-024: Pyramid Principle)
|
|
807
957
|
// ============================================================================
|
|
@@ -927,6 +1077,21 @@ export function renderExecutiveSummary(query, simulationResult, platformResults)
|
|
|
927
1077
|
lines.push(step);
|
|
928
1078
|
}
|
|
929
1079
|
lines.push('');
|
|
1080
|
+
// ADR-PIPELINE-041: Consensus signal disclosure
|
|
1081
|
+
const consensusAgent = platformResults.find(r => r.domain === 'analytics-hub' && r.agent === 'consensus');
|
|
1082
|
+
if (consensusAgent) {
|
|
1083
|
+
const consensusData = extractSignalPayload(consensusAgent.response).data ?? {};
|
|
1084
|
+
const achieved = consensusData['consensusAchieved'] ?? consensusData['consensus_achieved'];
|
|
1085
|
+
const confidence = Number(consensusData['confidence'] ?? consensusData['overallConfidence'] ?? 0);
|
|
1086
|
+
if (achieved === false || confidence < 0.6) {
|
|
1087
|
+
lines.push('## Analysis Confidence Note', '');
|
|
1088
|
+
lines.push(`The multi-agent consensus process achieved **${Math.round(confidence * 100)}% confidence**. ` +
|
|
1089
|
+
`This indicates divergent signals across analytical perspectives. ` +
|
|
1090
|
+
`The recommendation accounts for this uncertainty by proposing a scoped pilot ` +
|
|
1091
|
+
`rather than full commitment, allowing validation before broader investment.`);
|
|
1092
|
+
lines.push('');
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
930
1095
|
// Provenance — ADR-PIPELINE-033: include simulation lineage for decision traceability
|
|
931
1096
|
const sources = platformResults.filter(r => r.status >= 200 && r.status < 300).map(r => `${r.domain}/${r.agent}`);
|
|
932
1097
|
lines.push('---', '');
|