agentxchain 2.138.1 → 2.139.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/package.json
CHANGED
|
@@ -68,6 +68,10 @@ import {
|
|
|
68
68
|
} from './verification-replay.js';
|
|
69
69
|
import { executeGateActions } from './gate-actions.js';
|
|
70
70
|
import { detectPendingCheckpoint } from './turn-checkpoint.js';
|
|
71
|
+
import {
|
|
72
|
+
derivePhaseScopeFromIntentMetadata,
|
|
73
|
+
evaluateAcceptanceItemLifecycle,
|
|
74
|
+
} from './intent-phase-scope.js';
|
|
71
75
|
|
|
72
76
|
// ── Constants ────────────────────────────────────────────────────────────────
|
|
73
77
|
|
|
@@ -78,6 +82,7 @@ const STAGING_PATH = '.agentxchain/staging/turn-result.json';
|
|
|
78
82
|
const TALK_PATH = 'TALK.md';
|
|
79
83
|
const ACCEPTANCE_LOCK_PATH = '.agentxchain/locks/accept-turn.lock';
|
|
80
84
|
const ACCEPTANCE_JOURNAL_DIR = '.agentxchain/transactions/accept';
|
|
85
|
+
const INTAKE_INTENTS_DIR = '.agentxchain/intake/intents';
|
|
81
86
|
const STALE_LOCK_TIMEOUT_MS = 30_000;
|
|
82
87
|
const GOVERNED_SCHEMA_VERSION = '1.1';
|
|
83
88
|
|
|
@@ -101,6 +106,69 @@ function buildInitialPhaseGateStatus(config) {
|
|
|
101
106
|
);
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
function listIntakeIntentFiles(root) {
|
|
110
|
+
const intentsDir = join(root, INTAKE_INTENTS_DIR);
|
|
111
|
+
if (!existsSync(intentsDir)) return [];
|
|
112
|
+
|
|
113
|
+
return readdirSync(intentsDir)
|
|
114
|
+
.filter((name) => name.endsWith('.json'))
|
|
115
|
+
.map((name) => join(intentsDir, name));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function retireApprovedPhaseScopedIntents(root, state, config, exitedPhase, now) {
|
|
119
|
+
const retired = [];
|
|
120
|
+
|
|
121
|
+
for (const intentPath of listIntakeIntentFiles(root)) {
|
|
122
|
+
let intent;
|
|
123
|
+
try {
|
|
124
|
+
intent = JSON.parse(readFileSync(intentPath, 'utf8'));
|
|
125
|
+
} catch {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!intent || intent.status !== 'approved') continue;
|
|
130
|
+
if (state?.run_id && intent.approved_run_id && intent.approved_run_id !== state.run_id) continue;
|
|
131
|
+
if (state?.run_id && !intent.approved_run_id && intent.cross_run_durable !== true) continue;
|
|
132
|
+
|
|
133
|
+
const effectivePhaseScope = derivePhaseScopeFromIntentMetadata(intent, config);
|
|
134
|
+
const acceptanceItems = Array.isArray(intent.acceptance_contract) ? intent.acceptance_contract : [];
|
|
135
|
+
const lifecycleStates = acceptanceItems.map((item) => evaluateAcceptanceItemLifecycle(
|
|
136
|
+
item,
|
|
137
|
+
{ phase_scope: effectivePhaseScope },
|
|
138
|
+
state,
|
|
139
|
+
config,
|
|
140
|
+
));
|
|
141
|
+
const retireByPhaseExit = effectivePhaseScope === exitedPhase;
|
|
142
|
+
const retireByGateState = acceptanceItems.length > 0
|
|
143
|
+
&& lifecycleStates.every((entry) => entry.satisfied_by_gate_state || entry.phase_exited);
|
|
144
|
+
|
|
145
|
+
if (!retireByPhaseExit && !retireByGateState) continue;
|
|
146
|
+
|
|
147
|
+
intent.status = 'satisfied';
|
|
148
|
+
intent.phase_scope = effectivePhaseScope || intent.phase_scope || null;
|
|
149
|
+
intent.updated_at = now;
|
|
150
|
+
intent.satisfied_at = now;
|
|
151
|
+
intent.satisfied_reason = retireByPhaseExit
|
|
152
|
+
? `phase ${exitedPhase} exited; ${exitedPhase}-scoped repair no longer required`
|
|
153
|
+
: `acceptance items satisfied by gate state after phase advance from ${exitedPhase}`;
|
|
154
|
+
if (!Array.isArray(intent.history)) {
|
|
155
|
+
intent.history = [];
|
|
156
|
+
}
|
|
157
|
+
intent.history.push({
|
|
158
|
+
from: 'approved',
|
|
159
|
+
to: 'satisfied',
|
|
160
|
+
at: now,
|
|
161
|
+
reason: intent.satisfied_reason,
|
|
162
|
+
exited_phase: exitedPhase,
|
|
163
|
+
entered_phase: state?.phase || null,
|
|
164
|
+
});
|
|
165
|
+
safeWriteJson(intentPath, intent);
|
|
166
|
+
retired.push(intent.intent_id);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return retired;
|
|
170
|
+
}
|
|
171
|
+
|
|
104
172
|
function buildFreshIdleStateForNewRun(state, config) {
|
|
105
173
|
return {
|
|
106
174
|
schema_version: state?.schema_version || GOVERNED_SCHEMA_VERSION,
|
|
@@ -3159,7 +3227,10 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
3159
3227
|
// addresses each acceptance item. Default: strict for p0, lenient for others.
|
|
3160
3228
|
const intakeCtx = currentTurn.intake_context;
|
|
3161
3229
|
if (intakeCtx && Array.isArray(intakeCtx.acceptance_contract) && intakeCtx.acceptance_contract.length > 0) {
|
|
3162
|
-
const intentCoverage = evaluateIntentCoverage(turnResult, intakeCtx
|
|
3230
|
+
const intentCoverage = evaluateIntentCoverage(turnResult, intakeCtx, {
|
|
3231
|
+
state,
|
|
3232
|
+
config,
|
|
3233
|
+
});
|
|
3163
3234
|
const priority = intakeCtx.priority || currentTurn.intake_context?.priority || 'p0';
|
|
3164
3235
|
const intentCoverageMode = config.intent_coverage_mode
|
|
3165
3236
|
|| (priority === 'p0' ? 'strict' : 'lenient');
|
|
@@ -3977,6 +4048,21 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
3977
4048
|
[gateResult.gate_id || 'no_gate']: 'passed',
|
|
3978
4049
|
};
|
|
3979
4050
|
updatedState.queued_phase_transition = null;
|
|
4051
|
+
const retiredIntentIds = retireApprovedPhaseScopedIntents(root, updatedState, config, prevPhase, now);
|
|
4052
|
+
if (retiredIntentIds.length > 0) {
|
|
4053
|
+
emitRunEvent(root, 'intent_retired_by_phase_advance', {
|
|
4054
|
+
run_id: updatedState.run_id,
|
|
4055
|
+
phase: updatedState.phase,
|
|
4056
|
+
status: updatedState.status,
|
|
4057
|
+
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
4058
|
+
payload: {
|
|
4059
|
+
exited_phase: prevPhase,
|
|
4060
|
+
entered_phase: gateResult.next_phase,
|
|
4061
|
+
retired_count: retiredIntentIds.length,
|
|
4062
|
+
retired_intent_ids: retiredIntentIds,
|
|
4063
|
+
},
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
3980
4066
|
emitRunEvent(root, 'phase_entered', {
|
|
3981
4067
|
run_id: updatedState.run_id,
|
|
3982
4068
|
phase: updatedState.phase,
|
|
@@ -4019,6 +4105,21 @@ function _acceptGovernedTurnLocked(root, config, opts) {
|
|
|
4019
4105
|
gate_id: gateResult.gate_id,
|
|
4020
4106
|
timestamp: now,
|
|
4021
4107
|
});
|
|
4108
|
+
const retiredIntentIds = retireApprovedPhaseScopedIntents(root, updatedState, config, prevPhase, now);
|
|
4109
|
+
if (retiredIntentIds.length > 0) {
|
|
4110
|
+
emitRunEvent(root, 'intent_retired_by_phase_advance', {
|
|
4111
|
+
run_id: updatedState.run_id,
|
|
4112
|
+
phase: updatedState.phase,
|
|
4113
|
+
status: updatedState.status,
|
|
4114
|
+
turn: { turn_id: currentTurn.turn_id, role_id: currentTurn.assigned_role },
|
|
4115
|
+
payload: {
|
|
4116
|
+
exited_phase: prevPhase,
|
|
4117
|
+
entered_phase: gateResult.next_phase,
|
|
4118
|
+
retired_count: retiredIntentIds.length,
|
|
4119
|
+
retired_intent_ids: retiredIntentIds,
|
|
4120
|
+
},
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4022
4123
|
emitRunEvent(root, 'phase_entered', {
|
|
4023
4124
|
run_id: updatedState.run_id,
|
|
4024
4125
|
phase: updatedState.phase,
|
|
@@ -5226,7 +5327,7 @@ function deriveNextRecommendedRole(turnResult, state, config) {
|
|
|
5226
5327
|
// intent. Uses a hybrid approach: structural (`intent_response` field) first,
|
|
5227
5328
|
// with semantic fallback scanning `summary`, `decisions`, and `files_changed`.
|
|
5228
5329
|
|
|
5229
|
-
function evaluateIntentCoverage(turnResult, intakeContext) {
|
|
5330
|
+
function evaluateIntentCoverage(turnResult, intakeContext, { state = null, config = null } = {}) {
|
|
5230
5331
|
const acceptanceItems = intakeContext.acceptance_contract || [];
|
|
5231
5332
|
const addressed = [];
|
|
5232
5333
|
const unaddressed = [];
|
|
@@ -5253,6 +5354,14 @@ function evaluateIntentCoverage(turnResult, intakeContext) {
|
|
|
5253
5354
|
|
|
5254
5355
|
for (const item of acceptanceItems) {
|
|
5255
5356
|
const normalizedItem = item.toLowerCase().trim();
|
|
5357
|
+
const lifecycle = state && config
|
|
5358
|
+
? evaluateAcceptanceItemLifecycle(item, intakeContext, state, config)
|
|
5359
|
+
: null;
|
|
5360
|
+
|
|
5361
|
+
if (lifecycle?.phase_exited || lifecycle?.satisfied_by_gate_state) {
|
|
5362
|
+
addressed.push(item);
|
|
5363
|
+
continue;
|
|
5364
|
+
}
|
|
5256
5365
|
|
|
5257
5366
|
// Check 1: Structural — intent_response field with explicit status
|
|
5258
5367
|
const structuralEntry = responseMap.get(normalizedItem);
|
package/src/lib/intake.js
CHANGED
|
@@ -23,6 +23,10 @@ import {
|
|
|
23
23
|
formatLegacyIntentMigrationNotice,
|
|
24
24
|
isPhantomIntent,
|
|
25
25
|
} from './intent-startup-migration.js';
|
|
26
|
+
import {
|
|
27
|
+
derivePhaseScopeFromIntentMetadata,
|
|
28
|
+
getPhaseOrder,
|
|
29
|
+
} from './intent-phase-scope.js';
|
|
26
30
|
|
|
27
31
|
const VALID_SOURCES = ['manual', 'ci_failure', 'git_ref_change', 'schedule', 'vision_scan'];
|
|
28
32
|
const VALID_PRIORITIES = ['p0', 'p1', 'p2', 'p3'];
|
|
@@ -31,8 +35,8 @@ const INTENT_ID_RE = /^intent_\d+_[0-9a-f]{4}$/;
|
|
|
31
35
|
|
|
32
36
|
// V3-S1 through S5 states. `failed` remains read-tolerant for historical/manual
|
|
33
37
|
// intent files, but current first-party intake writers do not transition into it.
|
|
34
|
-
const S1_STATES = new Set(['detected', 'triaged', 'approved', 'planned', 'executing', 'blocked', 'completed', 'failed', 'suppressed', 'rejected']);
|
|
35
|
-
const TERMINAL_STATES = new Set(['suppressed', 'rejected', 'completed', 'failed']);
|
|
38
|
+
const S1_STATES = new Set(['detected', 'triaged', 'approved', 'planned', 'executing', 'blocked', 'completed', 'satisfied', 'failed', 'suppressed', 'rejected']);
|
|
39
|
+
const TERMINAL_STATES = new Set(['suppressed', 'rejected', 'completed', 'satisfied', 'failed']);
|
|
36
40
|
const DISPATCHABLE_STATUSES = new Set(['planned', 'approved']);
|
|
37
41
|
const PRIORITY_RANK = { p0: 0, p1: 1, p2: 2, p3: 3 };
|
|
38
42
|
|
|
@@ -283,7 +287,7 @@ export function validateEventPayload(payload) {
|
|
|
283
287
|
return { valid: errors.length === 0, errors };
|
|
284
288
|
}
|
|
285
289
|
|
|
286
|
-
export function validateTriageFields(fields) {
|
|
290
|
+
export function validateTriageFields(fields, config = null) {
|
|
287
291
|
const errors = [];
|
|
288
292
|
|
|
289
293
|
if (!VALID_PRIORITIES.includes(fields.priority)) {
|
|
@@ -302,6 +306,17 @@ export function validateTriageFields(fields) {
|
|
|
302
306
|
errors.push('acceptance_contract must be a non-empty array');
|
|
303
307
|
}
|
|
304
308
|
|
|
309
|
+
if ('phase_scope' in fields && fields.phase_scope != null) {
|
|
310
|
+
if (typeof fields.phase_scope !== 'string' || !fields.phase_scope.trim()) {
|
|
311
|
+
errors.push('phase_scope must be a non-empty string when provided');
|
|
312
|
+
} else if (config) {
|
|
313
|
+
const validPhases = getPhaseOrder(config);
|
|
314
|
+
if (validPhases.length > 0 && !validPhases.includes(fields.phase_scope.trim())) {
|
|
315
|
+
errors.push(`phase_scope must be one of: ${validPhases.join(', ')}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
305
320
|
return { valid: errors.length === 0, errors };
|
|
306
321
|
}
|
|
307
322
|
|
|
@@ -355,6 +370,7 @@ export function recordEvent(root, payload) {
|
|
|
355
370
|
template: null,
|
|
356
371
|
charter: null,
|
|
357
372
|
acceptance_contract: [],
|
|
373
|
+
phase_scope: null,
|
|
358
374
|
requires_human_start: true,
|
|
359
375
|
target_run: null,
|
|
360
376
|
created_at: now,
|
|
@@ -420,17 +436,25 @@ export function triageIntent(root, intentId, fields) {
|
|
|
420
436
|
return { ok: false, error: `cannot triage from status "${intent.status}" (must be detected)`, exitCode: 1 };
|
|
421
437
|
}
|
|
422
438
|
|
|
423
|
-
const
|
|
439
|
+
const context = loadProjectContext(root);
|
|
440
|
+
const config = context?.config || null;
|
|
441
|
+
const normalizedFields = {
|
|
442
|
+
...fields,
|
|
443
|
+
phase_scope: fields.phase_scope || derivePhaseScopeFromIntentMetadata(fields, config),
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
const validation = validateTriageFields(normalizedFields, config);
|
|
424
447
|
if (!validation.valid) {
|
|
425
448
|
return { ok: false, error: validation.errors.join('; '), exitCode: 1 };
|
|
426
449
|
}
|
|
427
450
|
|
|
428
451
|
const now = nowISO();
|
|
429
452
|
intent.status = 'triaged';
|
|
430
|
-
intent.priority =
|
|
431
|
-
intent.template =
|
|
432
|
-
intent.charter =
|
|
433
|
-
intent.acceptance_contract =
|
|
453
|
+
intent.priority = normalizedFields.priority;
|
|
454
|
+
intent.template = normalizedFields.template;
|
|
455
|
+
intent.charter = normalizedFields.charter;
|
|
456
|
+
intent.acceptance_contract = normalizedFields.acceptance_contract;
|
|
457
|
+
intent.phase_scope = normalizedFields.phase_scope || null;
|
|
434
458
|
intent.updated_at = now;
|
|
435
459
|
intent.history.push({ from: 'detected', to: 'triaged', at: now, reason: 'triage completed' });
|
|
436
460
|
|
|
@@ -946,6 +970,7 @@ export function startIntent(root, intentId, options = {}) {
|
|
|
946
970
|
category: event.category || null,
|
|
947
971
|
charter: intent.charter || null,
|
|
948
972
|
acceptance_contract: Array.isArray(intent.acceptance_contract) ? intent.acceptance_contract : [],
|
|
973
|
+
phase_scope: intent.phase_scope || null,
|
|
949
974
|
};
|
|
950
975
|
|
|
951
976
|
// Load governed project context
|
|
@@ -1782,6 +1807,7 @@ export function injectIntent(root, description, options = {}) {
|
|
|
1782
1807
|
: [description.trim()];
|
|
1783
1808
|
const approver = options.approver || 'human';
|
|
1784
1809
|
const noApprove = options.noApprove === true;
|
|
1810
|
+
const phase_scope = options.phase_scope || null;
|
|
1785
1811
|
|
|
1786
1812
|
// Step 1: Record event
|
|
1787
1813
|
const recordResult = recordEvent(root, {
|
|
@@ -1815,6 +1841,7 @@ export function injectIntent(root, description, options = {}) {
|
|
|
1815
1841
|
template,
|
|
1816
1842
|
charter,
|
|
1817
1843
|
acceptance_contract,
|
|
1844
|
+
phase_scope,
|
|
1818
1845
|
});
|
|
1819
1846
|
|
|
1820
1847
|
if (!triageResult.ok) {
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
function normalizeText(value) {
|
|
2
|
+
return typeof value === 'string' ? value.toLowerCase() : '';
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function getPhaseOrder(config) {
|
|
6
|
+
if (Array.isArray(config?.phases) && config.phases.length > 0) {
|
|
7
|
+
return config.phases
|
|
8
|
+
.map((phase) => phase?.id)
|
|
9
|
+
.filter((phaseId) => typeof phaseId === 'string' && phaseId.trim().length > 0);
|
|
10
|
+
}
|
|
11
|
+
return Object.keys(config?.routing || {});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function buildGateToPhaseMap(config) {
|
|
15
|
+
const mapping = {};
|
|
16
|
+
for (const [phaseId, route] of Object.entries(config?.routing || {})) {
|
|
17
|
+
const gateId = route?.exit_gate;
|
|
18
|
+
if (typeof gateId === 'string' && gateId.trim().length > 0) {
|
|
19
|
+
mapping[gateId] = phaseId;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return mapping;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function extractReferencedGateIds(text, config) {
|
|
26
|
+
const haystack = normalizeText(text);
|
|
27
|
+
if (!haystack) return [];
|
|
28
|
+
|
|
29
|
+
return Object.keys(config?.gates || {}).filter((gateId) => haystack.includes(gateId.toLowerCase()));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function derivePhaseScopeFromGateIds(gateIds, config) {
|
|
33
|
+
const gateToPhase = buildGateToPhaseMap(config);
|
|
34
|
+
const matchedPhases = [...new Set(
|
|
35
|
+
gateIds
|
|
36
|
+
.map((gateId) => gateToPhase[gateId] || null)
|
|
37
|
+
.filter(Boolean),
|
|
38
|
+
)];
|
|
39
|
+
return matchedPhases.length === 1 ? matchedPhases[0] : null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function derivePhaseScopeFromIntentMetadata(intentLike, config) {
|
|
43
|
+
const explicitScope = typeof intentLike?.phase_scope === 'string' && intentLike.phase_scope.trim().length > 0
|
|
44
|
+
? intentLike.phase_scope.trim()
|
|
45
|
+
: null;
|
|
46
|
+
if (explicitScope) return explicitScope;
|
|
47
|
+
if (!config) return null;
|
|
48
|
+
|
|
49
|
+
const texts = [
|
|
50
|
+
intentLike?.charter || '',
|
|
51
|
+
...(Array.isArray(intentLike?.acceptance_contract) ? intentLike.acceptance_contract : []),
|
|
52
|
+
];
|
|
53
|
+
const gateIds = [...new Set(texts.flatMap((text) => extractReferencedGateIds(text, config)))];
|
|
54
|
+
return derivePhaseScopeFromGateIds(gateIds, config);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function deriveAcceptanceItemPhaseScope(item, fallbackPhaseScope, config) {
|
|
58
|
+
if (!config) return fallbackPhaseScope || null;
|
|
59
|
+
const gateIds = extractReferencedGateIds(item, config);
|
|
60
|
+
const derived = derivePhaseScopeFromGateIds(gateIds, config);
|
|
61
|
+
return derived || fallbackPhaseScope || null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function isPhaseExited(currentPhase, scopedPhase, config) {
|
|
65
|
+
if (!currentPhase || !scopedPhase || !config) return false;
|
|
66
|
+
const order = getPhaseOrder(config);
|
|
67
|
+
const currentIndex = order.indexOf(currentPhase);
|
|
68
|
+
const scopeIndex = order.indexOf(scopedPhase);
|
|
69
|
+
if (currentIndex === -1 || scopeIndex === -1) return false;
|
|
70
|
+
return currentIndex > scopeIndex;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isAcceptanceItemSatisfiedByGateState(item, state, config) {
|
|
74
|
+
if (!state || !config) return false;
|
|
75
|
+
const itemText = normalizeText(item);
|
|
76
|
+
if (!itemText) return false;
|
|
77
|
+
|
|
78
|
+
const gateIds = extractReferencedGateIds(item, config);
|
|
79
|
+
if (gateIds.length === 0) return false;
|
|
80
|
+
|
|
81
|
+
const hasGatePassLanguage = /\b(can advance|advance to|gate can advance|gate passes|gate passed|passes|passed)\b/.test(itemText);
|
|
82
|
+
if (!hasGatePassLanguage) return false;
|
|
83
|
+
|
|
84
|
+
return gateIds.some((gateId) => state?.phase_gate_status?.[gateId] === 'passed');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function evaluateAcceptanceItemLifecycle(item, intakeContext, state, config) {
|
|
88
|
+
const fallbackPhaseScope = typeof intakeContext?.phase_scope === 'string' && intakeContext.phase_scope.trim().length > 0
|
|
89
|
+
? intakeContext.phase_scope.trim()
|
|
90
|
+
: null;
|
|
91
|
+
const phaseScope = deriveAcceptanceItemPhaseScope(item, fallbackPhaseScope, config);
|
|
92
|
+
return {
|
|
93
|
+
phase_scope: phaseScope,
|
|
94
|
+
phase_exited: isPhaseExited(state?.phase || null, phaseScope, config),
|
|
95
|
+
satisfied_by_gate_state: isAcceptanceItemSatisfiedByGateState(item, state, config),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
@@ -57,10 +57,15 @@ function describeEvent(eventType, entry) {
|
|
|
57
57
|
case 'dispatch_progress':
|
|
58
58
|
return `${prefix}${eventType}${roleId ? ` [${roleId}]` : ''}`;
|
|
59
59
|
case 'intents_migrated':
|
|
60
|
-
case 'intents_superseded':
|
|
60
|
+
case 'intents_superseded':
|
|
61
|
+
case 'intent_retired_by_phase_advance': {
|
|
61
62
|
const count = Number.isFinite(entry.payload?.archived_count)
|
|
62
63
|
? entry.payload.archived_count
|
|
63
|
-
: (
|
|
64
|
+
: (
|
|
65
|
+
Number.isFinite(entry.payload?.superseded_count)
|
|
66
|
+
? entry.payload.superseded_count
|
|
67
|
+
: (Number.isFinite(entry.payload?.retired_count) ? entry.payload.retired_count : null)
|
|
68
|
+
);
|
|
64
69
|
return `${prefix}${eventType}${count !== null ? ` (${count})` : ''}`;
|
|
65
70
|
}
|
|
66
71
|
case 'run_blocked':
|