@llm-dev-ops/agentics-cli 2.5.4 → 2.6.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/cli/index.js +80 -13
- package/dist/cli/index.js.map +1 -1
- package/dist/commands/agents.d.ts +7 -0
- package/dist/commands/agents.d.ts.map +1 -1
- package/dist/commands/agents.js +130 -23
- package/dist/commands/agents.js.map +1 -1
- package/dist/errors/transient.d.ts +67 -0
- package/dist/errors/transient.d.ts.map +1 -0
- package/dist/errors/transient.js +260 -0
- package/dist/errors/transient.js.map +1 -0
- package/dist/observability/degradations.d.ts +58 -0
- package/dist/observability/degradations.d.ts.map +1 -0
- package/dist/observability/degradations.js +74 -0
- package/dist/observability/degradations.js.map +1 -0
- package/dist/pipeline/phase1-verdict.d.ts +55 -0
- package/dist/pipeline/phase1-verdict.d.ts.map +1 -0
- package/dist/pipeline/phase1-verdict.js +186 -0
- package/dist/pipeline/phase1-verdict.js.map +1 -0
- package/dist/pipeline/phase2-preflight.d.ts +44 -0
- package/dist/pipeline/phase2-preflight.d.ts.map +1 -0
- package/dist/pipeline/phase2-preflight.js +120 -0
- package/dist/pipeline/phase2-preflight.js.map +1 -0
- package/dist/pipeline/swarm-orchestrator.d.ts.map +1 -1
- package/dist/pipeline/swarm-orchestrator.js +67 -5
- package/dist/pipeline/swarm-orchestrator.js.map +1 -1
- package/dist/synthesis/financial-claim-extractor.d.ts +11 -0
- package/dist/synthesis/financial-claim-extractor.d.ts.map +1 -1
- package/dist/synthesis/financial-claim-extractor.js +24 -0
- package/dist/synthesis/financial-claim-extractor.js.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.d.ts.map +1 -1
- package/dist/synthesis/simulation-artifact-generator.js +28 -3
- package/dist/synthesis/simulation-artifact-generator.js.map +1 -1
- package/dist/synthesis/simulation-renderers.d.ts +1 -1
- package/dist/synthesis/simulation-renderers.d.ts.map +1 -1
- package/dist/synthesis/simulation-renderers.js +39 -13
- package/dist/synthesis/simulation-renderers.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-PIPELINE-089 §2 — Phase 1 Health Gate.
|
|
3
|
+
*
|
|
4
|
+
* Classifies the outcome of `executeNaturalLanguageRoute` (Phase 1) into
|
|
5
|
+
* one of three verdicts:
|
|
6
|
+
*
|
|
7
|
+
* - 'failed' — auto-chain must be skipped; user sees the specific cause.
|
|
8
|
+
* - 'degraded' — auto-chain proceeds, but we warn loudly about what is
|
|
9
|
+
* missing so the user can judge the output.
|
|
10
|
+
* - 'healthy' — all inputs intact; auto-chain proceeds silently.
|
|
11
|
+
*
|
|
12
|
+
* Inputs:
|
|
13
|
+
* - invocations: the per-agent dispatch results (from RouteResult)
|
|
14
|
+
* - degradations: the list drained from the degradation sink (tag
|
|
15
|
+
* survival, consensus, sector-classifier, local
|
|
16
|
+
* fallbacks, etc.)
|
|
17
|
+
* - artifactFiles: the list of files the artifact generator wrote
|
|
18
|
+
*
|
|
19
|
+
* The verdict is computed structurally — no stderr parsing, no global
|
|
20
|
+
* state. Same inputs always produce the same verdict.
|
|
21
|
+
*/
|
|
22
|
+
import { classifyFailure } from '../errors/transient.js';
|
|
23
|
+
// ============================================================================
|
|
24
|
+
// Classifier
|
|
25
|
+
// ============================================================================
|
|
26
|
+
const DEGRADED_MAX_AGENT_ERROR_RATIO = 0.5; // >50% agent errors → failed
|
|
27
|
+
const HEALTHY_REQUIRES_ZERO_DEGRADATIONS = true;
|
|
28
|
+
/**
|
|
29
|
+
* Compute the Phase 1 verdict from structural dispatch results plus the
|
|
30
|
+
* drained degradation list.
|
|
31
|
+
*
|
|
32
|
+
* @param invocations Per-agent invocation results (as `multi.result.invocations`)
|
|
33
|
+
* @param degradations Collected degradation entries for this trace
|
|
34
|
+
* @param artifactFiles Files the artifact generator produced on disk
|
|
35
|
+
*/
|
|
36
|
+
export function computePhase1Verdict(invocations, degradations, artifactFiles) {
|
|
37
|
+
const simInvocation = invocations.find(i => i.intent.domain === 'simulator' && i.intent.agent === 'enterprise');
|
|
38
|
+
const simStatus = classifySimulator(simInvocation);
|
|
39
|
+
const agentInvocations = invocations.filter(i => !(i.intent.domain === 'simulator' && i.intent.agent === 'enterprise'));
|
|
40
|
+
const erroredAgents = agentInvocations.filter(i => isErrorResult(i.result)).length;
|
|
41
|
+
const totalAgents = agentInvocations.length;
|
|
42
|
+
const succeededAgents = totalAgents - erroredAgents;
|
|
43
|
+
const agentErrorRatio = totalAgents === 0 ? 0 : erroredAgents / totalAgents;
|
|
44
|
+
const degSnapshot = degradations.slice();
|
|
45
|
+
const stats = {
|
|
46
|
+
totalAgents,
|
|
47
|
+
succeededAgents,
|
|
48
|
+
erroredAgents,
|
|
49
|
+
artifactCount: artifactFiles.length,
|
|
50
|
+
simulatorStatus: simStatus,
|
|
51
|
+
};
|
|
52
|
+
// ── failed cases ────────────────────────────────────────────────────────
|
|
53
|
+
if (simStatus === 'failed-terminal' || simStatus === 'failed-unknown') {
|
|
54
|
+
return {
|
|
55
|
+
verdict: 'failed',
|
|
56
|
+
degradations: [
|
|
57
|
+
...degSnapshot,
|
|
58
|
+
{
|
|
59
|
+
code: 'simulator-terminal',
|
|
60
|
+
message: extractErrorMessage(simInvocation?.result) ??
|
|
61
|
+
'Enterprise simulation failed with a non-retryable error',
|
|
62
|
+
phase: 1,
|
|
63
|
+
source: 'simulator/enterprise',
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
reason: `Enterprise simulation failed terminally: ${extractErrorMessage(simInvocation?.result) ?? 'unknown error'}`,
|
|
67
|
+
stats,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
if (simStatus === 'failed-transient') {
|
|
71
|
+
// Retry loop already gave up. Treat as failed for gating purposes but
|
|
72
|
+
// tag the degradation distinctly so the user knows a re-run is likely
|
|
73
|
+
// to succeed.
|
|
74
|
+
return {
|
|
75
|
+
verdict: 'failed',
|
|
76
|
+
degradations: [
|
|
77
|
+
...degSnapshot,
|
|
78
|
+
{
|
|
79
|
+
code: 'simulator-transient',
|
|
80
|
+
message: extractErrorMessage(simInvocation?.result) ??
|
|
81
|
+
'Enterprise simulation exhausted retries on transient errors',
|
|
82
|
+
phase: 1,
|
|
83
|
+
source: 'simulator/enterprise',
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
reason: `Enterprise simulation exhausted retries: ${extractErrorMessage(simInvocation?.result) ?? 'transient errors'} — re-run recommended`,
|
|
87
|
+
stats,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
if (artifactFiles.length === 0) {
|
|
91
|
+
return {
|
|
92
|
+
verdict: 'failed',
|
|
93
|
+
degradations: [
|
|
94
|
+
...degSnapshot,
|
|
95
|
+
{
|
|
96
|
+
code: 'artifact-missing',
|
|
97
|
+
message: 'Phase 1 artifact generator produced zero files',
|
|
98
|
+
phase: 1,
|
|
99
|
+
source: 'artifact-generator',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
reason: 'Phase 1 produced no artifacts on disk',
|
|
103
|
+
stats,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
if (agentErrorRatio > DEGRADED_MAX_AGENT_ERROR_RATIO) {
|
|
107
|
+
return {
|
|
108
|
+
verdict: 'failed',
|
|
109
|
+
degradations: degSnapshot,
|
|
110
|
+
reason: `${erroredAgents} of ${totalAgents} graph-routed agents errored (>${Math.round(DEGRADED_MAX_AGENT_ERROR_RATIO * 100)}% threshold)`,
|
|
111
|
+
stats,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
// ── degraded cases ──────────────────────────────────────────────────────
|
|
115
|
+
if (erroredAgents > 0 || degSnapshot.length > 0) {
|
|
116
|
+
return {
|
|
117
|
+
verdict: 'degraded',
|
|
118
|
+
degradations: degSnapshot,
|
|
119
|
+
reason: buildDegradedReason(erroredAgents, totalAgents, degSnapshot),
|
|
120
|
+
stats,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// ── healthy ────────────────────────────────────────────────────────────
|
|
124
|
+
if (HEALTHY_REQUIRES_ZERO_DEGRADATIONS && degSnapshot.length > 0) {
|
|
125
|
+
// Defensive: theoretically unreachable given the branch above, but keep
|
|
126
|
+
// the invariant visible.
|
|
127
|
+
return {
|
|
128
|
+
verdict: 'degraded',
|
|
129
|
+
degradations: degSnapshot,
|
|
130
|
+
reason: buildDegradedReason(0, totalAgents, degSnapshot),
|
|
131
|
+
stats,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
verdict: 'healthy',
|
|
136
|
+
degradations: [],
|
|
137
|
+
reason: `${succeededAgents}/${totalAgents} agents succeeded, ${artifactFiles.length} artifacts written`,
|
|
138
|
+
stats,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Helpers
|
|
143
|
+
// ============================================================================
|
|
144
|
+
function classifySimulator(inv) {
|
|
145
|
+
if (!inv)
|
|
146
|
+
return 'not-invoked';
|
|
147
|
+
if (!isErrorResult(inv.result))
|
|
148
|
+
return 'succeeded';
|
|
149
|
+
const msg = extractErrorMessage(inv.result) ?? '';
|
|
150
|
+
const kind = classifyFailure(msg);
|
|
151
|
+
if (kind === 'terminal')
|
|
152
|
+
return 'failed-terminal';
|
|
153
|
+
if (kind === 'transient')
|
|
154
|
+
return 'failed-transient';
|
|
155
|
+
return 'failed-unknown';
|
|
156
|
+
}
|
|
157
|
+
function isErrorResult(r) {
|
|
158
|
+
return typeof r === 'object' && r !== null && 'error' in r;
|
|
159
|
+
}
|
|
160
|
+
function extractErrorMessage(r) {
|
|
161
|
+
if (typeof r !== 'object' || r === null)
|
|
162
|
+
return undefined;
|
|
163
|
+
const err = r.error;
|
|
164
|
+
if (typeof err === 'string')
|
|
165
|
+
return err;
|
|
166
|
+
if (typeof err === 'object' && err !== null && 'message' in err &&
|
|
167
|
+
typeof err.message === 'string') {
|
|
168
|
+
return err.message;
|
|
169
|
+
}
|
|
170
|
+
return undefined;
|
|
171
|
+
}
|
|
172
|
+
function buildDegradedReason(erroredAgents, totalAgents, degradations) {
|
|
173
|
+
const parts = [];
|
|
174
|
+
if (erroredAgents > 0) {
|
|
175
|
+
parts.push(`${erroredAgents} of ${totalAgents} agents errored`);
|
|
176
|
+
}
|
|
177
|
+
const codeCounts = new Map();
|
|
178
|
+
for (const d of degradations) {
|
|
179
|
+
codeCounts.set(d.code, (codeCounts.get(d.code) ?? 0) + 1);
|
|
180
|
+
}
|
|
181
|
+
for (const [code, count] of codeCounts) {
|
|
182
|
+
parts.push(count > 1 ? `${code} × ${count}` : code);
|
|
183
|
+
}
|
|
184
|
+
return parts.length > 0 ? parts.join('; ') : 'one or more degradations recorded';
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=phase1-verdict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase1-verdict.js","sourceRoot":"","sources":["../../src/pipeline/phase1-verdict.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-PIPELINE-089 §5 — Phase 2 preflight.
|
|
3
|
+
*
|
|
4
|
+
* Before Phase 2 dispatches its ~7 platform agents in parallel, probe
|
|
5
|
+
* each backend's health endpoint with a short timeout. Cloud Run services
|
|
6
|
+
* that are already failing health checks get marked `skipped-unhealthy`
|
|
7
|
+
* instead of being called, which saves 60-120s per dead backend — the
|
|
8
|
+
* dominant wall-time sink in the observed 2026-04-22 reproduction.
|
|
9
|
+
*
|
|
10
|
+
* If a majority (≥ PREFLIGHT_ABORT_THRESHOLD) of backends fail, the
|
|
11
|
+
* caller should skip Phase 2 entirely and set status.json's
|
|
12
|
+
* phase2_preflight.verdict = 'abort'. The user sees a clean "skipped:
|
|
13
|
+
* N of M backends unhealthy" line from `agentics status` instead of
|
|
14
|
+
* watching a mystery 10-minute "in progress" pause.
|
|
15
|
+
*/
|
|
16
|
+
export interface PreflightAgentRef {
|
|
17
|
+
/** Logical domain/agent pair as used by the invoker. */
|
|
18
|
+
domain: string;
|
|
19
|
+
agent: string;
|
|
20
|
+
/** Service endpoint key (from src/config/endpoints.ts). */
|
|
21
|
+
service: string;
|
|
22
|
+
}
|
|
23
|
+
export interface PreflightResult {
|
|
24
|
+
probed: number;
|
|
25
|
+
healthy: string[];
|
|
26
|
+
unhealthy: string[];
|
|
27
|
+
skipped: string[];
|
|
28
|
+
durationMs: number;
|
|
29
|
+
verdict: 'healthy' | 'partial' | 'abort';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Runs a parallel health probe for each unique service backing the
|
|
33
|
+
* supplied agents. Returns which services responded, which timed out, and
|
|
34
|
+
* which agents should be skipped.
|
|
35
|
+
*
|
|
36
|
+
* Probes are cached per-process for 60 seconds keyed by service URL to
|
|
37
|
+
* avoid amplification when multiple phases call the same backend during
|
|
38
|
+
* one pipeline run. Callers can pass a traceId to scope the cache;
|
|
39
|
+
* omitting it uses a global bucket (fine for a single CLI invocation).
|
|
40
|
+
*/
|
|
41
|
+
export declare function runPhase2Preflight(agents: readonly PreflightAgentRef[], _traceId?: string): Promise<PreflightResult>;
|
|
42
|
+
/** Test helper — clear the probe cache. */
|
|
43
|
+
export declare function __resetPreflightCacheForTests(): void;
|
|
44
|
+
//# sourceMappingURL=phase2-preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase2-preflight.d.ts","sourceRoot":"","sources":["../../src/pipeline/phase2-preflight.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,MAAM,WAAW,iBAAiB;IAChC,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;CAC1C;AAOD;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,SAAS,iBAAiB,EAAE,EACpC,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,CAAC,CAoC1B;AAuDD,2CAA2C;AAC3C,wBAAgB,6BAA6B,IAAI,IAAI,CAEpD"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ADR-PIPELINE-089 §5 — Phase 2 preflight.
|
|
3
|
+
*
|
|
4
|
+
* Before Phase 2 dispatches its ~7 platform agents in parallel, probe
|
|
5
|
+
* each backend's health endpoint with a short timeout. Cloud Run services
|
|
6
|
+
* that are already failing health checks get marked `skipped-unhealthy`
|
|
7
|
+
* instead of being called, which saves 60-120s per dead backend — the
|
|
8
|
+
* dominant wall-time sink in the observed 2026-04-22 reproduction.
|
|
9
|
+
*
|
|
10
|
+
* If a majority (≥ PREFLIGHT_ABORT_THRESHOLD) of backends fail, the
|
|
11
|
+
* caller should skip Phase 2 entirely and set status.json's
|
|
12
|
+
* phase2_preflight.verdict = 'abort'. The user sees a clean "skipped:
|
|
13
|
+
* N of M backends unhealthy" line from `agentics status` instead of
|
|
14
|
+
* watching a mystery 10-minute "in progress" pause.
|
|
15
|
+
*/
|
|
16
|
+
import { loadEndpointConfig, DEFAULT_ENDPOINTS } from '../config/endpoints.js';
|
|
17
|
+
/** Tunable — the ADR calls for 3s; overridable via env for cold starts. */
|
|
18
|
+
const PROBE_TIMEOUT_MS = Number(process.env['AGENTICS_PREFLIGHT_TIMEOUT_MS'] ?? 3000);
|
|
19
|
+
/** ≥ this fraction of backends unhealthy → abort Phase 2. 4/7 ≈ 0.57. */
|
|
20
|
+
const PREFLIGHT_ABORT_THRESHOLD = 4 / 7;
|
|
21
|
+
/**
|
|
22
|
+
* Runs a parallel health probe for each unique service backing the
|
|
23
|
+
* supplied agents. Returns which services responded, which timed out, and
|
|
24
|
+
* which agents should be skipped.
|
|
25
|
+
*
|
|
26
|
+
* Probes are cached per-process for 60 seconds keyed by service URL to
|
|
27
|
+
* avoid amplification when multiple phases call the same backend during
|
|
28
|
+
* one pipeline run. Callers can pass a traceId to scope the cache;
|
|
29
|
+
* omitting it uses a global bucket (fine for a single CLI invocation).
|
|
30
|
+
*/
|
|
31
|
+
export async function runPhase2Preflight(agents, _traceId) {
|
|
32
|
+
const start = Date.now();
|
|
33
|
+
const uniqueServices = Array.from(new Set(agents.map(a => a.service)));
|
|
34
|
+
const healthyServices = [];
|
|
35
|
+
const unhealthyServices = [];
|
|
36
|
+
await Promise.all(uniqueServices.map(async (service) => {
|
|
37
|
+
const ok = await probeService(service);
|
|
38
|
+
if (ok)
|
|
39
|
+
healthyServices.push(service);
|
|
40
|
+
else
|
|
41
|
+
unhealthyServices.push(service);
|
|
42
|
+
}));
|
|
43
|
+
const unhealthySet = new Set(unhealthyServices);
|
|
44
|
+
const skipped = agents
|
|
45
|
+
.filter(a => unhealthySet.has(a.service))
|
|
46
|
+
.map(a => `${a.domain}/${a.agent}`);
|
|
47
|
+
const totalServices = uniqueServices.length;
|
|
48
|
+
const unhealthyRatio = totalServices === 0 ? 0 : unhealthyServices.length / totalServices;
|
|
49
|
+
let verdict;
|
|
50
|
+
if (unhealthyServices.length === 0)
|
|
51
|
+
verdict = 'healthy';
|
|
52
|
+
else if (unhealthyRatio >= PREFLIGHT_ABORT_THRESHOLD)
|
|
53
|
+
verdict = 'abort';
|
|
54
|
+
else
|
|
55
|
+
verdict = 'partial';
|
|
56
|
+
return {
|
|
57
|
+
probed: uniqueServices.length,
|
|
58
|
+
healthy: healthyServices,
|
|
59
|
+
unhealthy: unhealthyServices,
|
|
60
|
+
skipped,
|
|
61
|
+
durationMs: Date.now() - start,
|
|
62
|
+
verdict,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
// ============================================================================
|
|
66
|
+
// Probe
|
|
67
|
+
// ============================================================================
|
|
68
|
+
const probeCache = new Map();
|
|
69
|
+
const PROBE_CACHE_TTL_MS = 60_000;
|
|
70
|
+
async function probeService(service) {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const cached = probeCache.get(service);
|
|
73
|
+
if (cached && cached.expires > now)
|
|
74
|
+
return cached.ok;
|
|
75
|
+
let baseUrl;
|
|
76
|
+
try {
|
|
77
|
+
// Service name is runtime-derived (`${domain}-agents`). Validate it
|
|
78
|
+
// against the registered endpoint keys so we can't silently probe a
|
|
79
|
+
// typo — unknown names fall through to the unhealthy path.
|
|
80
|
+
if (!(service in DEFAULT_ENDPOINTS)) {
|
|
81
|
+
probeCache.set(service, { ok: false, expires: now + PROBE_CACHE_TTL_MS });
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
const cfg = loadEndpointConfig(service);
|
|
85
|
+
baseUrl = cfg.baseUrl;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
probeCache.set(service, { ok: false, expires: now + PROBE_CACHE_TTL_MS });
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
const url = baseUrl.replace(/\/$/, '') + '/v1/health';
|
|
92
|
+
const controller = new AbortController();
|
|
93
|
+
const timer = setTimeout(() => controller.abort(), PROBE_TIMEOUT_MS);
|
|
94
|
+
try {
|
|
95
|
+
// Any response that isn't a network/timeout failure counts as "alive."
|
|
96
|
+
// A 404 from /v1/health means the service is up but doesn't implement
|
|
97
|
+
// the endpoint — which is fine for our purposes (ruvvector-service
|
|
98
|
+
// actually returns a structured 404 here).
|
|
99
|
+
const resp = await fetch(url, {
|
|
100
|
+
method: 'GET',
|
|
101
|
+
signal: controller.signal,
|
|
102
|
+
headers: { accept: 'application/json' },
|
|
103
|
+
});
|
|
104
|
+
const ok = resp.status < 500; // 5xx = backend is failing
|
|
105
|
+
probeCache.set(service, { ok, expires: now + PROBE_CACHE_TTL_MS });
|
|
106
|
+
return ok;
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
probeCache.set(service, { ok: false, expires: now + PROBE_CACHE_TTL_MS });
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Test helper — clear the probe cache. */
|
|
117
|
+
export function __resetPreflightCacheForTests() {
|
|
118
|
+
probeCache.clear();
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=phase2-preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase2-preflight.js","sourceRoot":"","sources":["../../src/pipeline/phase2-preflight.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"swarm-orchestrator.d.ts","sourceRoot":"","sources":["../../src/pipeline/swarm-orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAeH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,mBAAmB,GAAG,MAAM,CAAC;IACjE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,QAAQ,EAAE,aAAa,GAAG,UAAU,CAAC;IAC9C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,iEAAiE;AACjE,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,CAAC;IACjD,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,uBAAuB,EAAE,MAAM,CAAC;IACzC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CA0JzD,CAAC;AAg3CF;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,gBAAgB,EAC7B,MAAM,EAAE,MAAM,GACb,mBAAmB,EAAE,CAsDvB;AAMD;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAqP7D;AA8DD;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAmCpD;AASD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,OAAO,CAU/C;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,IAAI,IAAI,CAI7C;AAmMD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CA+BtD;AAED;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,gBAAgB,EAC7B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,aAAa,SAAK,GACjB,OAAO,CAAC,qBAAqB,EAAE,CAAC,
|
|
1
|
+
{"version":3,"file":"swarm-orchestrator.d.ts","sourceRoot":"","sources":["../../src/pipeline/swarm-orchestrator.ts"],"names":[],"mappings":"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"}
|
|
@@ -2166,11 +2166,42 @@ export async function dispatchPhaseAgents(phaseConfig, traceId, runDir, scenario
|
|
|
2166
2166
|
allServices.push(svc);
|
|
2167
2167
|
}
|
|
2168
2168
|
}
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2169
|
+
// ── 3a. ADR-PIPELINE-089 §5: Phase 2 preflight ──
|
|
2170
|
+
// Before dispatching, probe each unique backend's /v1/health with a
|
|
2171
|
+
// short timeout. Skip agents whose backend is failing so we don't burn
|
|
2172
|
+
// 60-120s per dead Cloud Run service. If a majority fail, abort Phase 2
|
|
2173
|
+
// entirely — downstream artifacts built on no-op responses are worse
|
|
2174
|
+
// than no artifacts at all. Write the preflight result into status.json
|
|
2175
|
+
// so `agentics status` can surface it.
|
|
2176
|
+
let dispatchServices = allServices;
|
|
2177
|
+
if (phaseConfig.phase === 2 && allServices.length > 0) {
|
|
2178
|
+
const { runPhase2Preflight } = await import('./phase2-preflight.js');
|
|
2179
|
+
const preflightRefs = allServices.map(s => ({
|
|
2180
|
+
domain: s.domain,
|
|
2181
|
+
agent: s.agent,
|
|
2182
|
+
service: `${s.domain}-agents`,
|
|
2183
|
+
}));
|
|
2184
|
+
const preflight = await runPhase2Preflight(preflightRefs, traceId);
|
|
2185
|
+
console.error(` [PREFLIGHT] ${preflight.healthy.length}/${preflight.probed} backends healthy ` +
|
|
2186
|
+
`in ${preflight.durationMs}ms (verdict: ${preflight.verdict})`);
|
|
2187
|
+
if (preflight.unhealthy.length > 0) {
|
|
2188
|
+
console.error(` [PREFLIGHT] Unhealthy: ${preflight.unhealthy.join(', ')}`);
|
|
2189
|
+
console.error(` [PREFLIGHT] Skipping ${preflight.skipped.length} agents bound to unhealthy backends`);
|
|
2190
|
+
}
|
|
2191
|
+
// Persist preflight outcome to status.json for `agentics status` consumers.
|
|
2192
|
+
await writePhase2PreflightToStatus(traceId, preflight);
|
|
2193
|
+
if (preflight.verdict === 'abort') {
|
|
2194
|
+
console.error(' [PREFLIGHT] ≥4/7 backends unhealthy — aborting Phase 2 dispatch cleanly');
|
|
2195
|
+
return agenticsResults; // empty — caller handles missing content
|
|
2196
|
+
}
|
|
2197
|
+
const skippedSet = new Set(preflight.skipped);
|
|
2198
|
+
dispatchServices = allServices.filter(s => !skippedSet.has(`${s.domain}/${s.agent}`));
|
|
2199
|
+
}
|
|
2200
|
+
if (dispatchServices.length > 0) {
|
|
2201
|
+
const serviceNames = dispatchServices.map(s => `${s.domain}/${s.agent}`);
|
|
2202
|
+
console.error(` [AGENTICS] Dispatching ${dispatchServices.length} platform agents: ${serviceNames.join(', ')}`);
|
|
2172
2203
|
const prompt = buildPhasePrompt(phaseConfig, scenarioQuery, runDir);
|
|
2173
|
-
const results = await dispatchAgenticsServices(
|
|
2204
|
+
const results = await dispatchAgenticsServices(dispatchServices, prompt, traceId);
|
|
2174
2205
|
for (const r of results) {
|
|
2175
2206
|
agenticsResults.push(r);
|
|
2176
2207
|
const resp = r.response;
|
|
@@ -2190,7 +2221,7 @@ export async function dispatchPhaseAgents(phaseConfig, traceId, runDir, scenario
|
|
|
2190
2221
|
return resp && resp['_fallback'] === true;
|
|
2191
2222
|
}).length;
|
|
2192
2223
|
const cloudCount = results.length - fallbackCount;
|
|
2193
|
-
console.error(` [AGENTICS] ${results.length}/${
|
|
2224
|
+
console.error(` [AGENTICS] ${results.length}/${dispatchServices.length} agents responded for Phase ${phaseConfig.phase} (${cloudCount} cloud, ${fallbackCount} local fallback)`);
|
|
2194
2225
|
if (results.length === 0) {
|
|
2195
2226
|
console.error(` [AGENTICS] WARNING: No agents responded — costops/analytics output will be missing`);
|
|
2196
2227
|
}
|
|
@@ -2200,6 +2231,37 @@ export async function dispatchPhaseAgents(phaseConfig, traceId, runDir, scenario
|
|
|
2200
2231
|
}
|
|
2201
2232
|
return agenticsResults;
|
|
2202
2233
|
}
|
|
2234
|
+
/**
|
|
2235
|
+
* ADR-PIPELINE-089 §6: patch status.json with the Phase 2 preflight
|
|
2236
|
+
* outcome so `agentics status` and the `agentics-status` MCP tool can
|
|
2237
|
+
* surface "skipped 5 unhealthy backends" instead of "Phase 2 running for
|
|
2238
|
+
* 7 minutes." Best-effort — status.json may not exist for shell-mode
|
|
2239
|
+
* runs, and that is the expected code path (no MCP fast-return detach).
|
|
2240
|
+
*/
|
|
2241
|
+
async function writePhase2PreflightToStatus(traceId, preflight) {
|
|
2242
|
+
try {
|
|
2243
|
+
const fsMod = await import('node:fs');
|
|
2244
|
+
const pathMod = await import('node:path');
|
|
2245
|
+
const os = await import('node:os');
|
|
2246
|
+
const statusPath = pathMod.join(os.homedir(), '.agentics', 'runs', traceId, 'status.json');
|
|
2247
|
+
if (!fsMod.existsSync(statusPath))
|
|
2248
|
+
return;
|
|
2249
|
+
const current = JSON.parse(fsMod.readFileSync(statusPath, 'utf-8'));
|
|
2250
|
+
current['phase2_preflight'] = {
|
|
2251
|
+
probed: preflight.probed,
|
|
2252
|
+
healthy: preflight.healthy,
|
|
2253
|
+
unhealthy: preflight.unhealthy,
|
|
2254
|
+
skipped: preflight.skipped,
|
|
2255
|
+
duration_ms: preflight.durationMs,
|
|
2256
|
+
verdict: preflight.verdict,
|
|
2257
|
+
probed_at: new Date().toISOString(),
|
|
2258
|
+
};
|
|
2259
|
+
const tmp = statusPath + '.tmp';
|
|
2260
|
+
fsMod.writeFileSync(tmp, JSON.stringify(current, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
2261
|
+
fsMod.renameSync(tmp, statusPath);
|
|
2262
|
+
}
|
|
2263
|
+
catch { /* non-fatal */ }
|
|
2264
|
+
}
|
|
2203
2265
|
/**
|
|
2204
2266
|
* Store phase artifacts in swarm memory for cross-phase coordination.
|
|
2205
2267
|
*/
|