@llm-dev-ops/agentics-cli 2.5.4 → 2.7.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.
Files changed (43) hide show
  1. package/dist/cli/index.js +80 -13
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/agents.d.ts +7 -0
  4. package/dist/commands/agents.d.ts.map +1 -1
  5. package/dist/commands/agents.js +130 -23
  6. package/dist/commands/agents.js.map +1 -1
  7. package/dist/errors/transient.d.ts +67 -0
  8. package/dist/errors/transient.d.ts.map +1 -0
  9. package/dist/errors/transient.js +260 -0
  10. package/dist/errors/transient.js.map +1 -0
  11. package/dist/mcp/agent-event-parser.d.ts +53 -0
  12. package/dist/mcp/agent-event-parser.d.ts.map +1 -0
  13. package/dist/mcp/agent-event-parser.js +159 -0
  14. package/dist/mcp/agent-event-parser.js.map +1 -0
  15. package/dist/mcp/mcp-server.js +34 -15
  16. package/dist/mcp/mcp-server.js.map +1 -1
  17. package/dist/observability/degradations.d.ts +58 -0
  18. package/dist/observability/degradations.d.ts.map +1 -0
  19. package/dist/observability/degradations.js +74 -0
  20. package/dist/observability/degradations.js.map +1 -0
  21. package/dist/pipeline/phase1-verdict.d.ts +55 -0
  22. package/dist/pipeline/phase1-verdict.d.ts.map +1 -0
  23. package/dist/pipeline/phase1-verdict.js +186 -0
  24. package/dist/pipeline/phase1-verdict.js.map +1 -0
  25. package/dist/pipeline/phase2-preflight.d.ts +44 -0
  26. package/dist/pipeline/phase2-preflight.d.ts.map +1 -0
  27. package/dist/pipeline/phase2-preflight.js +120 -0
  28. package/dist/pipeline/phase2-preflight.js.map +1 -0
  29. package/dist/pipeline/swarm-orchestrator.d.ts.map +1 -1
  30. package/dist/pipeline/swarm-orchestrator.js +67 -5
  31. package/dist/pipeline/swarm-orchestrator.js.map +1 -1
  32. package/dist/synthesis/financial-claim-extractor.d.ts +11 -0
  33. package/dist/synthesis/financial-claim-extractor.d.ts.map +1 -1
  34. package/dist/synthesis/financial-claim-extractor.js +24 -0
  35. package/dist/synthesis/financial-claim-extractor.js.map +1 -1
  36. package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
  37. package/dist/synthesis/simulation-artifact-generator.js +28 -3
  38. package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
  39. package/dist/synthesis/simulation-renderers.d.ts +1 -1
  40. package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
  41. package/dist/synthesis/simulation-renderers.js +39 -13
  42. package/dist/synthesis/simulation-renderers.js.map +1 -1
  43. package/package.json +1 -1
@@ -14,6 +14,9 @@ import { createAdapter } from '../adapters/base-adapter.js';
14
14
  import { getAnthropicApiKey } from '../utils/credentials.js';
15
15
  import { executeSimulateCommand } from './simulate.js';
16
16
  import { execFileSync } from 'node:child_process';
17
+ import { isTransientFailure, isTerminalFailure } from '../errors/transient.js';
18
+ import { recordDegradation, drainDegradations } from '../observability/degradations.js';
19
+ import { computePhase1Verdict } from '../pipeline/phase1-verdict.js';
17
20
  // ============================================================================
18
21
  // ADR-066: Copilot agents via claude --print (Claude Max — no API key needed)
19
22
  // ============================================================================
@@ -513,6 +516,16 @@ export async function executeAgentsInvokeCommand(domain, agent, payload, options
513
516
  // data we already built in buildDomainPayload().
514
517
  // -------------------------------------------------------------------------
515
518
  const errMsg = err instanceof Error ? err.message : String(err);
519
+ // ADR-PIPELINE-089 §2: every throw-based local fallback path below is a
520
+ // Phase 1 degradation. Record once here (above the per-domain branches)
521
+ // so the Phase 1 verdict sees every backend that failed over to local
522
+ // compute without needing to instrument each return site.
523
+ recordDegradation(correlationId, {
524
+ code: 'local-fallback',
525
+ message: `${domain}/${agent} cloud backend unavailable (${errMsg.slice(0, 120)}) — using local compute`,
526
+ phase: 1,
527
+ source: `agents/${domain}/${agent}`,
528
+ });
516
529
  // costops: ANY costops agent failure gets routed to its dedicated local compute function
517
530
  if (domain === 'costops') {
518
531
  if (options.verbose) {
@@ -619,6 +632,14 @@ export async function executeAgentsInvokeCommand(domain, agent, payload, options
619
632
  if (options.verbose) {
620
633
  console.error(`[local] ${domain}/${agent} using local compute (health-check response)`);
621
634
  }
635
+ // ADR-PIPELINE-089 §2: cloud function returned a generic health page
636
+ // instead of a real agent response — count as a local fallback.
637
+ recordDegradation(correlationId, {
638
+ code: 'local-fallback',
639
+ message: `${domain}/${agent} returned generic health response — using local compute`,
640
+ phase: 1,
641
+ source: `agents/${domain}/${agent}`,
642
+ });
622
643
  return {
623
644
  domain,
624
645
  agent,
@@ -3792,16 +3813,16 @@ export async function executeNaturalLanguageRoute(query, options) {
3792
3813
  }
3793
3814
  catch (err) {
3794
3815
  lastErr = err instanceof Error ? err : new Error(String(err));
3795
- const msg = lastErr.message.toLowerCase();
3796
- const isRetryable = msg.includes('unavailable')
3797
- || msg.includes('timeout')
3798
- || msg.includes('rate exceeded')
3799
- || msg.includes('econnreset')
3800
- || msg.includes('econnrefused')
3801
- || msg.includes('network error')
3802
- || msg.includes('aborterror');
3803
- if (!isRetryable) {
3804
- console.error(` [RUVECTOR] Non-retryable error: ${lastErr.message}`);
3816
+ // ADR-PIPELINE-089 §1: centralized transient/terminal classifier
3817
+ // replaces the narrow substring list. Terminal failures (payload /
3818
+ // auth / canonical 404) short-circuit; transient failures (5xx,
3819
+ // ECONN*, ETIMEDOUT, rate limits, "unreachable") retry.
3820
+ if (isTerminalFailure(err)) {
3821
+ console.error(` [RUVECTOR] Terminal error (not retrying): ${lastErr.message}`);
3822
+ break;
3823
+ }
3824
+ if (!isTransientFailure(err)) {
3825
+ console.error(` [RUVECTOR] Unrecognized error (not retrying): ${lastErr.message}`);
3805
3826
  break;
3806
3827
  }
3807
3828
  console.error(` [RUVECTOR] Attempt ${attempt + 1}/${MAX_RETRIES + 1} failed: ${lastErr.message.slice(0, 150)}`);
@@ -3875,6 +3896,8 @@ export async function executeNaturalLanguageRoute(query, options) {
3875
3896
  }
3876
3897
  }
3877
3898
  // Non-fatal: generate artifacts
3899
+ let artifactFilesGraphRouted = [];
3900
+ let artifactRunDirGraphRouted;
3878
3901
  try {
3879
3902
  const { generateSimulationArtifacts } = await import('../synthesis/simulation-artifact-generator.js');
3880
3903
  const simSettled = allResults[0];
@@ -3895,6 +3918,8 @@ export async function executeNaturalLanguageRoute(query, options) {
3895
3918
  timing: elapsed,
3896
3919
  });
3897
3920
  console.error(`Graph-routed artifacts: ${artifacts.runDir}`);
3921
+ artifactFilesGraphRouted = artifacts.filesWritten;
3922
+ artifactRunDirGraphRouted = artifacts.runDir;
3898
3923
  }
3899
3924
  catch (artifactErr) {
3900
3925
  const artMsg = artifactErr instanceof Error ? artifactErr.message : String(artifactErr);
@@ -3906,6 +3931,43 @@ export async function executeNaturalLanguageRoute(query, options) {
3906
3931
  const routingMeta = graphResult.unresolved.length > 0
3907
3932
  ? { unresolved: graphResult.unresolved.map(u => ({ stage: u.stage, agentName: u.agentName, capability: u.capability })) }
3908
3933
  : { unresolved: [] };
3934
+ // ADR-PIPELINE-089 §2: compute Phase 1 verdict from dispatch
3935
+ // results + drained degradations. The degradation sink is
3936
+ // process-local keyed by correlationId; draining here prevents
3937
+ // leaking state into subsequent runs (tests, daemons).
3938
+ const degradations = drainDegradations(correlationId);
3939
+ const phase1Verdict = computePhase1Verdict(invocations, degradations, artifactFilesGraphRouted);
3940
+ // ADR-PIPELINE-089 §2: patch verdict into manifest.json so
3941
+ // downstream phases (and `agentics status`) can read it without
3942
+ // keeping state in memory. Best-effort; manifest may not exist if
3943
+ // the artifact generator itself threw.
3944
+ if (artifactRunDirGraphRouted) {
3945
+ try {
3946
+ const fsMod = await import('node:fs');
3947
+ const pathMod = await import('node:path');
3948
+ const mPath = pathMod.join(artifactRunDirGraphRouted, 'manifest.json');
3949
+ if (fsMod.existsSync(mPath)) {
3950
+ const m = JSON.parse(fsMod.readFileSync(mPath, 'utf-8'));
3951
+ m['phase1_verdict'] = phase1Verdict.verdict;
3952
+ m['phase1_reason'] = phase1Verdict.reason;
3953
+ m['phase1_degradations'] = phase1Verdict.degradations;
3954
+ m['phase1_stats'] = phase1Verdict.stats;
3955
+ fsMod.writeFileSync(mPath, JSON.stringify(m, null, 2), 'utf-8');
3956
+ }
3957
+ }
3958
+ catch { /* non-fatal */ }
3959
+ }
3960
+ // One-line verdict banner — visible on the terminal and captured by
3961
+ // the MCP tool wrapper for status surfacing.
3962
+ if (phase1Verdict.verdict !== 'healthy') {
3963
+ const label = phase1Verdict.verdict.toUpperCase();
3964
+ console.error(`Phase 1 verdict: ${label} — ${phase1Verdict.reason}`);
3965
+ if (phase1Verdict.degradations.length > 0) {
3966
+ for (const d of phase1Verdict.degradations) {
3967
+ console.error(` · [${d.code}] ${d.message}`);
3968
+ }
3969
+ }
3970
+ }
3909
3971
  return {
3910
3972
  kind: 'multi',
3911
3973
  result: {
@@ -3913,6 +3975,7 @@ export async function executeNaturalLanguageRoute(query, options) {
3913
3975
  intents,
3914
3976
  invocations,
3915
3977
  routing: routingMeta,
3978
+ phase1Verdict,
3916
3979
  timing: elapsed,
3917
3980
  },
3918
3981
  };
@@ -4111,19 +4174,15 @@ export async function executeNaturalLanguageRoute(query, options) {
4111
4174
  }
4112
4175
  catch (err) {
4113
4176
  lastErr = err instanceof Error ? err : new Error(String(err));
4114
- const msg = lastErr.message.toLowerCase();
4115
- // Retry transient errors (rate limits, timeouts, network failures).
4116
- // Contract validation errors are NOT retried — the intent truncation
4117
- // fix ensures payloads always fit within ruvector's 2000-char limit.
4118
- const isRetryable = msg.includes('unavailable')
4119
- || msg.includes('timeout')
4120
- || msg.includes('rate exceeded')
4121
- || msg.includes('econnreset')
4122
- || msg.includes('econnrefused')
4123
- || msg.includes('network error')
4124
- || msg.includes('aborterror');
4125
- if (!isRetryable) {
4126
- console.error(` [RUVECTOR] Non-retryable error: ${lastErr.message}`);
4177
+ // ADR-PIPELINE-089 §1: centralized transient/terminal classifier.
4178
+ // Contract validation / auth errors are terminal; 5xx, ECONN*,
4179
+ // ETIMEDOUT, rate limits, and "unreachable" variants retry.
4180
+ if (isTerminalFailure(err)) {
4181
+ console.error(` [RUVECTOR] Terminal error (not retrying): ${lastErr.message}`);
4182
+ break;
4183
+ }
4184
+ if (!isTransientFailure(err)) {
4185
+ console.error(` [RUVECTOR] Unrecognized error (not retrying): ${lastErr.message}`);
4127
4186
  break;
4128
4187
  }
4129
4188
  console.error(` [RUVECTOR] Attempt ${attempt + 1}/${MAX_RETRIES + 1} failed: ${lastErr.message.slice(0, 150)}`);
@@ -4216,6 +4275,8 @@ export async function executeNaturalLanguageRoute(query, options) {
4216
4275
  }
4217
4276
  }
4218
4277
  // Non-fatal: generate simulation + engineering artifacts
4278
+ let artifactFilesFleet = [];
4279
+ let artifactRunDirFleet;
4219
4280
  try {
4220
4281
  const { generateSimulationArtifacts } = await import('../synthesis/simulation-artifact-generator.js');
4221
4282
  const simResult = allResults[0]?.status === 'fulfilled' ? allResults[0].value : undefined;
@@ -4240,18 +4301,49 @@ export async function executeNaturalLanguageRoute(query, options) {
4240
4301
  timing: elapsed,
4241
4302
  });
4242
4303
  console.error(`Full-fleet artifacts: ${artifacts.runDir}`);
4304
+ artifactFilesFleet = artifacts.filesWritten;
4305
+ artifactRunDirFleet = artifacts.runDir;
4243
4306
  }
4244
4307
  catch (artifactErr) {
4245
4308
  const artMsg = artifactErr instanceof Error ? artifactErr.message : String(artifactErr);
4246
4309
  console.error(`[WARN] Phase 1 artifact generation failed: ${artMsg}`);
4247
4310
  console.error(' Auto-chain Phases 2-6 may fail without Phase 1 manifest.');
4248
4311
  }
4312
+ // ADR-PIPELINE-089 §2: Phase 1 verdict for the full-fleet path.
4313
+ const fleetDegradations = drainDegradations(correlationId);
4314
+ const fleetPhase1Verdict = computePhase1Verdict(invocations, fleetDegradations, artifactFilesFleet);
4315
+ if (artifactRunDirFleet) {
4316
+ try {
4317
+ const fsMod = await import('node:fs');
4318
+ const pathMod = await import('node:path');
4319
+ const mPath = pathMod.join(artifactRunDirFleet, 'manifest.json');
4320
+ if (fsMod.existsSync(mPath)) {
4321
+ const m = JSON.parse(fsMod.readFileSync(mPath, 'utf-8'));
4322
+ m['phase1_verdict'] = fleetPhase1Verdict.verdict;
4323
+ m['phase1_reason'] = fleetPhase1Verdict.reason;
4324
+ m['phase1_degradations'] = fleetPhase1Verdict.degradations;
4325
+ m['phase1_stats'] = fleetPhase1Verdict.stats;
4326
+ fsMod.writeFileSync(mPath, JSON.stringify(m, null, 2), 'utf-8');
4327
+ }
4328
+ }
4329
+ catch { /* non-fatal */ }
4330
+ }
4331
+ if (fleetPhase1Verdict.verdict !== 'healthy') {
4332
+ const label = fleetPhase1Verdict.verdict.toUpperCase();
4333
+ console.error(`Phase 1 verdict: ${label} — ${fleetPhase1Verdict.reason}`);
4334
+ if (fleetPhase1Verdict.degradations.length > 0) {
4335
+ for (const d of fleetPhase1Verdict.degradations) {
4336
+ console.error(` · [${d.code}] ${d.message}`);
4337
+ }
4338
+ }
4339
+ }
4249
4340
  return {
4250
4341
  kind: 'multi',
4251
4342
  result: {
4252
4343
  query,
4253
4344
  intents,
4254
4345
  invocations,
4346
+ phase1Verdict: fleetPhase1Verdict,
4255
4347
  timing: elapsed,
4256
4348
  },
4257
4349
  };
@@ -4278,6 +4370,21 @@ export function formatMultiAgentRouteForDisplay(result) {
4278
4370
  lines.push(`Multi-agent query: "${result.query}"`);
4279
4371
  lines.push(`Agents invoked: ${agentCount} (${succeeded} succeeded${failed > 0 ? `, ${failed} failed` : ''})`);
4280
4372
  lines.push(`Total Timing: ${result.timing}ms`);
4373
+ // ADR-PIPELINE-089 §2: surface the Phase 1 verdict prominently so the
4374
+ // display output (shell or MCP-stdout-relay) shows HEALTHY / DEGRADED /
4375
+ // FAILED with the specific degradation list. Healthy runs stay terse.
4376
+ const v = result.phase1Verdict;
4377
+ if (v) {
4378
+ if (v.verdict === 'healthy') {
4379
+ lines.push(`Phase 1 verdict: HEALTHY — ${v.reason}`);
4380
+ }
4381
+ else {
4382
+ lines.push(`Phase 1 verdict: ${v.verdict.toUpperCase()} — ${v.reason}`);
4383
+ for (const d of v.degradations) {
4384
+ lines.push(` · [${d.code}] ${d.message}`);
4385
+ }
4386
+ }
4387
+ }
4281
4388
  lines.push('');
4282
4389
  // ADR-PIPELINE-087 §4: surface unresolved classifier selections at the top
4283
4390
  // of the display so operators (and MCP clients relaying stdout) see the