agentfootprint-lens 0.40.0 → 0.41.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/{chunk-L2Z6HTJI.js → chunk-36MMC2GY.js} +4 -4
- package/dist/{chunk-EBQCTQQU.js → chunk-5E2N7IP7.js} +2 -2
- package/dist/{chunk-EYCTYBSV.js → chunk-6RXMOK2E.js} +81 -11
- package/dist/chunk-6RXMOK2E.js.map +1 -0
- package/dist/{chunk-P4F3RGXR.js → chunk-HBVKOHFU.js} +2 -2
- package/dist/{chunk-S7FLEXJ3.js → chunk-OZUS4IAS.js} +61 -12
- package/dist/chunk-OZUS4IAS.js.map +1 -0
- package/dist/{chunk-CHFRN2WV.js → chunk-XO5VNJYM.js} +2 -2
- package/dist/core.cjs +80 -10
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +1 -1
- package/dist/core.d.ts +1 -1
- package/dist/core.js +3 -3
- package/dist/index.cjs +138 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -14
- package/dist/index.d.ts +24 -14
- package/dist/index.js +6 -6
- package/dist/{selectSkillFrameContext-Hjjf75t9.d.ts → selectSkillFrameContext-AX9VtcCf.d.ts} +97 -21
- package/dist/{selectSkillFrameContext-DdObTJz3.d.cts → selectSkillFrameContext-C19Sqv3W.d.cts} +97 -21
- package/dist/skillgraph.cjs +138 -19
- package/dist/skillgraph.cjs.map +1 -1
- package/dist/skillgraph.d.cts +2 -2
- package/dist/skillgraph.d.ts +2 -2
- package/dist/skillgraph.js +3 -3
- package/dist/why.js +4 -4
- package/package.json +2 -2
- package/dist/chunk-EYCTYBSV.js.map +0 -1
- package/dist/chunk-S7FLEXJ3.js.map +0 -1
- /package/dist/{chunk-L2Z6HTJI.js.map → chunk-36MMC2GY.js.map} +0 -0
- /package/dist/{chunk-EBQCTQQU.js.map → chunk-5E2N7IP7.js.map} +0 -0
- /package/dist/{chunk-P4F3RGXR.js.map → chunk-HBVKOHFU.js.map} +0 -0
- /package/dist/{chunk-CHFRN2WV.js.map → chunk-XO5VNJYM.js.map} +0 -0
|
@@ -9,18 +9,18 @@ import {
|
|
|
9
9
|
selectHops,
|
|
10
10
|
selectToolChoiceCall,
|
|
11
11
|
structureGraphFromRunner
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-XO5VNJYM.js";
|
|
13
13
|
import {
|
|
14
14
|
MODE_PALETTES,
|
|
15
15
|
T,
|
|
16
16
|
TimeTravel,
|
|
17
17
|
useNarrowRow
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-5E2N7IP7.js";
|
|
19
19
|
import {
|
|
20
20
|
bandChartGroup,
|
|
21
21
|
bandIndexOf,
|
|
22
22
|
stepBands
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-6RXMOK2E.js";
|
|
24
24
|
import {
|
|
25
25
|
selectAgentInstances
|
|
26
26
|
} from "./chunk-2JAWSPNG.js";
|
|
@@ -4156,4 +4156,4 @@ export {
|
|
|
4156
4156
|
WhereFrom,
|
|
4157
4157
|
Lens
|
|
4158
4158
|
};
|
|
4159
|
-
//# sourceMappingURL=chunk-
|
|
4159
|
+
//# sourceMappingURL=chunk-36MMC2GY.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bandIndexOf
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-6RXMOK2E.js";
|
|
4
4
|
|
|
5
5
|
// src/react/theme/tokens.ts
|
|
6
6
|
var RAW_DEFAULTS = {
|
|
@@ -533,4 +533,4 @@ export {
|
|
|
533
533
|
refusalDestinationFor,
|
|
534
534
|
REFUSAL_GO_TO
|
|
535
535
|
};
|
|
536
|
-
//# sourceMappingURL=chunk-
|
|
536
|
+
//# sourceMappingURL=chunk-5E2N7IP7.js.map
|
|
@@ -21,6 +21,7 @@ var LLM_START = "agentfootprint.stream.llm_start";
|
|
|
21
21
|
var TOOL_START = "agentfootprint.stream.tool_start";
|
|
22
22
|
var TOOL_END = "agentfootprint.stream.tool_end";
|
|
23
23
|
var REJECTED = "agentfootprint.skill.rejected";
|
|
24
|
+
var GRAPH_DECLARED = "agentfootprint.skill.graph_declared";
|
|
24
25
|
var ROUTE_CONFLICT = "agentfootprint.skill.route_conflict";
|
|
25
26
|
var REROUTE_SUPERSEDED = "agentfootprint.skill.reroute_superseded";
|
|
26
27
|
var TURN_ROUTED = "agentfootprint.skill.turn_routed";
|
|
@@ -67,6 +68,8 @@ function selectSkillRoute(args) {
|
|
|
67
68
|
const pendingRefusals = [];
|
|
68
69
|
const openCalls = /* @__PURE__ */ new Map();
|
|
69
70
|
let hasRouting = false;
|
|
71
|
+
let declaredComplete = false;
|
|
72
|
+
const entryIds = [];
|
|
70
73
|
let turnIndex = 0;
|
|
71
74
|
let iteration = 0;
|
|
72
75
|
const keyOf = (t, i) => `${t}:${i}`;
|
|
@@ -116,7 +119,9 @@ function selectSkillRoute(args) {
|
|
|
116
119
|
const id = str(c?.id);
|
|
117
120
|
if (id === void 0) continue;
|
|
118
121
|
hasRouting = true;
|
|
119
|
-
|
|
122
|
+
const known = catalog.get(id) ?? {};
|
|
123
|
+
if (known.description === void 0) known.description = str(c?.description);
|
|
124
|
+
catalog.set(id, known);
|
|
120
125
|
}
|
|
121
126
|
for (const row of Array.isArray(p.routing) ? p.routing : []) {
|
|
122
127
|
const r = obj(row);
|
|
@@ -143,6 +148,7 @@ function selectSkillRoute(args) {
|
|
|
143
148
|
draft.by = str(move.by);
|
|
144
149
|
draft.witness = witnessOf(move.witness);
|
|
145
150
|
if (Array.isArray(move.offered)) draft.offered = strList(move.offered);
|
|
151
|
+
if (Array.isArray(move.reachable)) draft.reachable = strList(move.reachable);
|
|
146
152
|
if (typeof move.declinedOffer === "boolean") draft.declinedOffer = move.declinedOffer;
|
|
147
153
|
if (draft.to !== void 0) visited.add(draft.to);
|
|
148
154
|
if (draft.from !== void 0) visited.add(draft.from);
|
|
@@ -163,6 +169,43 @@ function selectSkillRoute(args) {
|
|
|
163
169
|
}
|
|
164
170
|
}
|
|
165
171
|
draft.toolsAsSent = tools;
|
|
172
|
+
const promptText = str(p.systemPromptText);
|
|
173
|
+
if (promptText !== void 0) draft.systemPromptText = promptText;
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case GRAPH_DECLARED: {
|
|
177
|
+
hasRouting = true;
|
|
178
|
+
declaredComplete = true;
|
|
179
|
+
for (const row of Array.isArray(p.nodes) ? p.nodes : []) {
|
|
180
|
+
const n = obj(row);
|
|
181
|
+
const id = str(n?.id);
|
|
182
|
+
if (id === void 0) continue;
|
|
183
|
+
const existing = catalog.get(id) ?? {};
|
|
184
|
+
if (existing.description === void 0) existing.description = str(n?.description);
|
|
185
|
+
if (existing.kind === void 0) existing.kind = str(n?.kind);
|
|
186
|
+
if (existing.label === void 0) existing.label = str(n?.label);
|
|
187
|
+
catalog.set(id, existing);
|
|
188
|
+
}
|
|
189
|
+
for (const row of Array.isArray(p.edges) ? p.edges : []) {
|
|
190
|
+
const e = obj(row);
|
|
191
|
+
const to = str(e?.to);
|
|
192
|
+
if (to === void 0) continue;
|
|
193
|
+
if (e?.from === null) {
|
|
194
|
+
if (!entryIds.includes(to)) entryIds.push(to);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const from = str(e?.from);
|
|
198
|
+
if (from === void 0) continue;
|
|
199
|
+
const edgeKey = `${from}->${to}`;
|
|
200
|
+
if (!declared.has(edgeKey)) {
|
|
201
|
+
declared.set(edgeKey, {
|
|
202
|
+
from,
|
|
203
|
+
to,
|
|
204
|
+
...some("label", str(e?.label)),
|
|
205
|
+
...some("triggerKind", str(e?.kind))
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}
|
|
166
209
|
break;
|
|
167
210
|
}
|
|
168
211
|
case INJECTED: {
|
|
@@ -366,12 +409,16 @@ function selectSkillRoute(args) {
|
|
|
366
409
|
superseded: d.superseded,
|
|
367
410
|
toolsAsSent: d.toolsAsSent,
|
|
368
411
|
...d.readSkillDescription !== void 0 ? { readSkillDescription: d.readSkillDescription } : {},
|
|
412
|
+
...d.reachable !== void 0 ? { reachable: d.reachable } : {},
|
|
413
|
+
...d.systemPromptText !== void 0 ? { systemPromptText: d.systemPromptText } : {},
|
|
369
414
|
skillInjections: d.skillInjections,
|
|
370
415
|
...d.evidence !== void 0 ? { evidence: d.evidence } : {}
|
|
371
416
|
}));
|
|
372
|
-
const nodes = [...catalog.entries()].map(([id,
|
|
417
|
+
const nodes = [...catalog.entries()].map(([id, row]) => ({
|
|
373
418
|
id,
|
|
374
|
-
...description !== void 0 ? { description } : {},
|
|
419
|
+
...row.description !== void 0 ? { description: row.description } : {},
|
|
420
|
+
...row.kind !== void 0 ? { kind: row.kind } : {},
|
|
421
|
+
...row.label !== void 0 ? { label: row.label } : {},
|
|
375
422
|
visited: visited.has(id)
|
|
376
423
|
}));
|
|
377
424
|
for (const id of visited) {
|
|
@@ -406,6 +453,8 @@ function selectSkillRoute(args) {
|
|
|
406
453
|
hops,
|
|
407
454
|
observedEdges: [...edges.values()].map((r) => r.edge),
|
|
408
455
|
declaredEdges: [...declared.values()],
|
|
456
|
+
declaredComplete,
|
|
457
|
+
...entryIds.length > 0 ? { entryIds } : {},
|
|
409
458
|
turns
|
|
410
459
|
};
|
|
411
460
|
}
|
|
@@ -667,7 +716,7 @@ function selectSkillBeats({ route }) {
|
|
|
667
716
|
const askedFrom = hop.from ?? hop.to;
|
|
668
717
|
const declared = askedFrom !== void 0 ? declaredFrom.get(askedFrom) : void 0;
|
|
669
718
|
const allowed = hop.refusals.find((r) => r.allowed.length > 0)?.allowed;
|
|
670
|
-
const reachable = allowed !== void 0 ? { ids: allowed, source: "refusal" } : declared !== void 0 && declared.length > 0 ? { ids: declared, source: "declared-edges" } : void 0;
|
|
719
|
+
const reachable = hop.reachable !== void 0 ? { ids: hop.reachable, source: "cursor-move" } : allowed !== void 0 ? { ids: allowed, source: "refusal" } : declared !== void 0 && declared.length > 0 ? { ids: declared, source: "declared-edges" } : void 0;
|
|
671
720
|
return {
|
|
672
721
|
index,
|
|
673
722
|
turnIndex: hop.turnIndex,
|
|
@@ -776,6 +825,8 @@ function selectSkillTopology({
|
|
|
776
825
|
}
|
|
777
826
|
}
|
|
778
827
|
const activeId = beat !== void 0 && beat.moved && beat.hop.from !== void 0 && beat.hop.to !== void 0 ? `${beat.hop.from}->${beat.hop.to}` : void 0;
|
|
828
|
+
const entryIds = route.entryIds ?? [];
|
|
829
|
+
const entrySet = new Set(entryIds);
|
|
779
830
|
const nodes = /* @__PURE__ */ new Map();
|
|
780
831
|
const state = (id) => {
|
|
781
832
|
if (id === current) return "current";
|
|
@@ -784,29 +835,43 @@ function selectSkillTopology({
|
|
|
784
835
|
if (visitedSoFar.has(id)) return "visited";
|
|
785
836
|
return "idle";
|
|
786
837
|
};
|
|
787
|
-
const addNode = (id,
|
|
838
|
+
const addNode = (id, detail, visitedInRun) => {
|
|
788
839
|
const existing = nodes.get(id);
|
|
789
840
|
if (existing !== void 0) {
|
|
790
|
-
if (existing.description === void 0 && description !== void 0) {
|
|
791
|
-
nodes.set(id, { ...existing, description });
|
|
841
|
+
if (existing.description === void 0 && detail?.description !== void 0) {
|
|
842
|
+
nodes.set(id, { ...existing, description: detail.description });
|
|
792
843
|
}
|
|
793
844
|
return;
|
|
794
845
|
}
|
|
795
846
|
nodes.set(id, {
|
|
796
847
|
id,
|
|
797
|
-
...description !== void 0 ? { description } : {},
|
|
848
|
+
...detail?.description !== void 0 ? { description: detail.description } : {},
|
|
849
|
+
...detail?.kind !== void 0 ? { kind: detail.kind } : {},
|
|
850
|
+
...detail?.label !== void 0 ? { label: detail.label } : {},
|
|
851
|
+
isEntry: entrySet.has(id),
|
|
798
852
|
state: state(id),
|
|
799
853
|
pickedByModel: beat?.modelPickedId === id,
|
|
800
854
|
refusedHere: refused.has(id),
|
|
801
855
|
visitedInRun: visitedInRun ?? visitedSoFar.has(id)
|
|
802
856
|
});
|
|
803
857
|
};
|
|
804
|
-
for (const n of route.nodes)
|
|
858
|
+
for (const n of route.nodes) {
|
|
859
|
+
addNode(
|
|
860
|
+
n.id,
|
|
861
|
+
{
|
|
862
|
+
...n.description !== void 0 ? { description: n.description } : {},
|
|
863
|
+
...n.kind !== void 0 ? { kind: n.kind } : {},
|
|
864
|
+
...n.label !== void 0 ? { label: n.label } : {}
|
|
865
|
+
},
|
|
866
|
+
n.visited
|
|
867
|
+
);
|
|
868
|
+
}
|
|
805
869
|
for (const row of edges.values()) {
|
|
806
870
|
addNode(row.from);
|
|
807
871
|
addNode(row.to);
|
|
808
872
|
}
|
|
809
873
|
for (const id of refused) addNode(id);
|
|
874
|
+
const declaredSource = route.declaredComplete === true ? "recording-declared" : declaredEdges !== void 0 && declaredEdges.length > 0 ? "graph" : route.declaredEdges.length > 0 ? "recording" : "none";
|
|
810
875
|
return {
|
|
811
876
|
nodes: [...nodes.values()],
|
|
812
877
|
edges: [...edges.entries()].map(([id, row]) => ({
|
|
@@ -821,7 +886,12 @@ function selectSkillTopology({
|
|
|
821
886
|
takenAt: row.takenAt,
|
|
822
887
|
active: id === activeId
|
|
823
888
|
})),
|
|
824
|
-
|
|
889
|
+
// The run's own declared map outranks a passed-in graph for the LABEL —
|
|
890
|
+
// both are complete, but the recording's copy is from the run that
|
|
891
|
+
// actually executed, so it is the truer provenance to state.
|
|
892
|
+
declaredSource,
|
|
893
|
+
declaredComplete: declaredSource === "recording-declared" || declaredSource === "graph",
|
|
894
|
+
entryIds
|
|
825
895
|
};
|
|
826
896
|
}
|
|
827
897
|
|
|
@@ -1050,4 +1120,4 @@ export {
|
|
|
1050
1120
|
bandChartGroup,
|
|
1051
1121
|
stepForRuntimeStageId
|
|
1052
1122
|
};
|
|
1053
|
-
//# sourceMappingURL=chunk-
|
|
1123
|
+
//# sourceMappingURL=chunk-6RXMOK2E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/selectors/selectContextEngineeringInjections.ts","../src/core/selectors/selectSkillRoute.ts","../src/core/humanizeRouting.ts","../src/core/selectors/selectSkillBeats.ts","../src/core/selectors/selectSkillTopology.ts","../src/core/selectors/selectSkillFrameContext.ts","../src/core/group/Group.ts","../src/core/group/activeChartGroup.ts","../src/core/group/stepBands.ts","../src/core/group/stepForRuntimeStageId.ts"],"sourcesContent":["/**\n * `selectContextEngineeringInjections` — filter injections to the\n * ENGINEERED set (exclude baseline user + tool-result).\n *\n * Pattern: pure function over a step's injections. Applies the stable\n * `BASELINE_SOURCES` filter: `user` and `tool-result` are the\n * standard LLM-API flow and don't constitute \"context\n * engineering.\" Everything else (`rag`, `skill`, `memory`,\n * `instruction`, `grounding`, consumer-custom) is engineered.\n * Role: The teaching surface for Lens's Context Engineering bin.\n * Matches the filter that `agent.enable.contextEngineering()`\n * will apply once that API lands — consumers get identical\n * results whether they derive client-side via this selector\n * or subscribe to the enable handle.\n *\n * Why the filter lives here (selector) AND in agentfootprint (enable\n * handle) when that lands:\n * - Today's Lens consumes `StepNode.injections[]` directly; the\n * selector is the only place to drop baseline.\n * - Tomorrow, `enable.contextEngineering({ onInjection })` gives\n * non-Lens consumers a pre-filtered stream — they don't need the\n * selector at all.\n * - The filter rule is the SAME (stable set of baseline sources),\n * so the two paths produce identical output. Documenting once\n * here keeps them in sync.\n */\n\nimport type { ContextInjection } from 'agentfootprint/observe';\n\n/**\n * Sources that represent baseline LLM-API flow — NOT context engineering:\n *\n * - `user` → the user's current-turn message OR prior user-turn\n * history replay (standard conversation flow)\n * - `tool-result` → tool return for the current call OR prior-turn\n * tool-result history replay\n * - `assistant` → prior-turn assistant output replayed as history\n * (standard conversation continuity)\n * - `base` → static system prompt configured at build time\n * via `.system('...')` — NOT engineered\n * - `registry` → tool registry configured at build time via\n * `.tool(...)` — the static tool list\n *\n * What's left — the ENGINEERED sources (chips in the Lens bin):\n * `rag` · `skill` · `memory` · `instruction` · `grounding` +\n * consumer-custom sources\n *\n * Contract for library extensions: if you re-inject content with\n * engineered intent (memory strategy, RAG retriever, skill activator,\n * instruction system, grounding rule), you MUST set your own source\n * at the injection site — don't let role-based inference drop you\n * into the baseline bucket.\n */\nexport const BASELINE_SOURCES: ReadonlySet<string> = new Set([\n 'user',\n 'tool-result',\n 'assistant',\n 'base',\n 'registry',\n]);\n\n/**\n * True when this injection represents engineered context (not baseline\n * API flow). Reads the immutable `source` field — no derivation from\n * role or slot.\n */\nexport function isContextEngineering(inj: ContextInjection): boolean {\n return !BASELINE_SOURCES.has(inj.source);\n}\n\n/**\n * Filter a step's injections to the engineered subset. Returns a new\n * readonly array; empty if the step had only baseline injections.\n *\n * Use: Lens's ContextBinNode gets this array; empty → \"No engineered\n * context yet\" empty state. Full → the 5-axis teaching chips.\n */\nexport function selectContextEngineeringInjections(\n injections: readonly ContextInjection[] | undefined,\n): readonly ContextInjection[] {\n if (!injections || injections.length === 0) return [];\n return injections.filter(isContextEngineering);\n}\n","/**\n * `selectSkillRoute` — fold a recording's event log into the ROUTING RECORD:\n * where the skill-graph cursor stood on every iteration, what moved it, what\n * the model was refused, and what it was looking at when it asked.\n *\n * Pattern: pure function over the lens event log (`recorder.selectEventLog()`),\n * same shape as `selectHops` — no framework, no caching, no state.\n * Role: the STRUCTURED half of routing. Until now every routing fact in this\n * package went through `defaultHumanizer` and came out a `string`;\n * a panel that wants to draw the graph, group by skill, or answer\n * \"why was I refused\" had to re-parse prose. This is the same facts,\n * still typed.\n *\n * THE TWO VOCABULARIES (do not conflate them — they answer different questions):\n *\n * • `SkillCursorCause` — `context.evaluated.cursorMove.by`. \"What moved the\n * cursor on THIS iteration.\" Nine values, `'stay'` and `'route'` among them.\n * • `SkillTurnVerdict` — `skill.turn_routed.by`. \"Which tier decided where the\n * TURN started.\" Six values, `'menu'` among them — and `'menu'` is not a\n * cursor cause at all (a menu is an offer; the cursor has not moved yet).\n *\n * They are kept on different TYPES at different levels (`SkillHop.by` vs\n * `SkillTurnStart.by`) rather than in one field with a merged union, because a\n * merged union is exactly the mistake a reader cannot see themselves making.\n *\n * THE ONE-ITERATION LAG (measured on a real run, not assumed):\n *\n * iter 1 the model calls `read_skill('audit-log')` → the gate refuses;\n * `skill.rejected { iteration: 1 }`\n * iter 2 the Evaluate stage resolves the cursor and finds no pending pick →\n * `context.evaluated { iteration: 2, cursorMove: { by: 'stay' } }`\n *\n * The refusal and its CONSEQUENCE are stamped with different iterations, so a\n * naive same-iteration join shows a refusal beside the cursor state that\n * preceded it. Each refusal therefore carries `cursorAfter` — the next\n * iteration's cursor row — which is what makes \"the pick was refused AND the\n * cursor did not move\" one fact instead of two rows a reader has to pair up.\n * `undefined` when the run ended on the refusing iteration: no next row exists,\n * and inventing one would be a claim about a resolution that never ran.\n *\n * ERA NOTE (the `humanizeRouting` precedent, same reasoning): several of these\n * events postdate the agentfootprint version this package compiles against\n * (`agent.evidence_checked` shipped in 9.35.0, after the 9.30.0 devDependency),\n * so they are matched by raw type STRING and read through structural mirrors.\n * A recording from an older era simply carries fewer fields, and the house law\n * holds throughout: report ONLY what the event carries — an absent field is\n * absent, never guessed.\n */\n\nimport type { EventLogEntry } from '../types.js';\n\n// ─── The two vocabularies ────────────────────────────────────────────────\n\n/**\n * How the skill-graph cursor moved on one iteration — agentfootprint's\n * `CursorMoveCause`, all nine of it (`skillGraph.ts`). The library's own\n * `ContextEvaluatedPayload` docstring lists only seven; it is stale (missing\n * `'tool-proposal'` and `'decider'`) and the union is the source of truth.\n *\n * The `(string & {})` arm keeps a future era's tenth value readable instead of\n * un-assignable, while editors still autocomplete the nine that exist.\n */\nexport type SkillCursorCause =\n | 'entry' // a start rule matched (cold start)\n | 'route' // an author-declared edge fired\n | 'model-pick' // the model's accepted `read_skill` pick moved it\n | 'tool-proposal' // an accepted `propose-transition` tool effect moved it\n | 'intent' // the turn-start scorer routed the turn\n | 'continuity' // the inherited conversation cursor held\n | 'decider' // a configured tier-3 decider resolved an outstanding menu\n | 'stay' // nothing fired; the cursor is sticky and stayed put\n | 'none' // no cursor at all\n | (string & {});\n\n/**\n * Which TIER decided where a turn started — agentfootprint's\n * `SkillTurnRoutedPayload.by`. NOT the cursor vocabulary: `'menu'` lives only\n * here (an offer is not a move), and `'route'` / `'model-pick'` / `'stay'` live\n * only in {@link SkillCursorCause}.\n */\nexport type SkillTurnVerdict =\n | 'entry'\n | 'intent'\n | 'continuity'\n | 'menu'\n | 'decider'\n | 'none'\n | (string & {});\n\n// ─── The folded shapes ───────────────────────────────────────────────────\n\n/** What the message said that routed a hop (`match:` rules only). */\nexport interface SkillRouteWitness {\n readonly text: string;\n readonly keyword?: string;\n}\n\n/** A skill the run knew about — the catalog the model was offered. */\nexport interface SkillRouteNode {\n readonly id: string;\n /** The catalog description, verbatim — the same text the model read. */\n readonly description?: string;\n /** The drawn kind (`'skill'` box / `'predicate'` diamond), when the\n * recording carried the declared map (`skill.graph_declared`, 9.50.0). */\n readonly kind?: string;\n /** The drawn caption, when the declared map named one (predicate nodes). */\n readonly label?: string;\n /** The cursor stood here on at least one iteration. */\n readonly visited: boolean;\n}\n\n/** One (turn, iteration) address — how a hop is referred to from elsewhere. */\nexport interface SkillHopRef {\n readonly turnIndex: number;\n readonly iteration: number;\n}\n\n/** A hop the cursor was OBSERVED to take, aggregated over the run. */\nexport interface SkillObservedEdge {\n /** The cursor before the hop. `undefined` = cold start (there was none). */\n readonly from?: string;\n readonly to: string;\n readonly by: SkillCursorCause;\n /** Every (turn, iteration) this hop was taken on, in order. */\n readonly takenAt: readonly SkillHopRef[];\n /** The author's caption, when `routing[]` named this edge. */\n readonly label?: string;\n /** The compiled trigger kind of the declared edge, when there was one. */\n readonly triggerKind?: string;\n}\n\n/** An edge the AUTHOR declared. Reported by `routing[]` provenance (an edge\n * is named once it FIRES — a lower bound), or verbatim by the run's\n * `skill.graph_declared` map (9.50.0 — the WHOLE declaration; see\n * {@link SkillRoute.declaredComplete}). `triggerKind` carries `routing[]`'s\n * compiled trigger kind or the declared map's edge `kind` — one vocabulary\n * (`'entry' | 'predicate' | 'on-tool-return' | 'on-tool-status' | 'model'`). */\nexport interface SkillDeclaredEdge {\n readonly from: string;\n readonly to: string;\n readonly label?: string;\n readonly triggerKind?: string;\n}\n\n/** The cursor row that FOLLOWED a refusal — the proof it did or did not move. */\nexport interface SkillCursorAfter {\n readonly iteration: number;\n readonly from?: string;\n readonly to?: string;\n readonly by?: SkillCursorCause;\n /** `false` is the refusal's whole point: the pick changed nothing. */\n readonly moved: boolean;\n}\n\n/** A `read_skill` pick the gate refused. */\nexport interface SkillRefusal {\n readonly requestedId: string;\n readonly currentSkillId?: string;\n /** The REACHABILITY set named in the re-prompt (not the offered menu). */\n readonly allowed: readonly string[];\n /** `'guard'` / `'rails'` when a POSTURE refused, absent for reachability. */\n readonly posture?: string;\n readonly turnIndex: number;\n readonly iteration: number;\n /** The provider's tool-call id of the refused call, when it was paired. */\n readonly toolCallId?: string;\n /**\n * The refusal sentence the MODEL read — the refused `read_skill` call's own\n * tool result, paired by tool-call id. Absent when the recording carried no\n * `stream.tool_end` for it (a truncated log, or a refusal raised outside a\n * tool call).\n */\n readonly refusalText?: string;\n /** Where the cursor stood on the next iteration — see the lag note above. */\n readonly cursorAfter?: SkillCursorAfter;\n}\n\n/** Two tool results of one batch matched edges to different targets. */\nexport interface SkillRouteConflict {\n readonly fromSkillId?: string;\n readonly winner?: { readonly toolName?: string; readonly target?: string; readonly toolCallId?: string };\n readonly losers: readonly {\n readonly toolName?: string;\n readonly target?: string;\n readonly toolCallId?: string;\n }[];\n /** `'tool-proposal'` when proposals conflicted rather than declared edges. */\n readonly source?: string;\n}\n\n/** An accepted pick a declared edge outranked. */\nexport interface SkillSuperseded {\n readonly volunteeredId: string;\n readonly wonId?: string;\n readonly fromSkillId?: string;\n readonly source?: string;\n}\n\n/** One tool as it was SENT to the model on this iteration. */\nexport interface SkillToolAsSent {\n readonly name: string;\n /** Verbatim, as the model saw it. */\n readonly description?: string;\n}\n\n/** Something a SKILL put into the prompt on this iteration. */\nexport interface SkillInjectionSeen {\n readonly slot: string;\n /** The skill that injected it. */\n readonly skillId?: string;\n readonly summary: string;\n /** The full text, when the recording captured it (`rawContent`). */\n readonly text?: string;\n}\n\n/** The evidence gate's verdict on an iteration's answer (agentfootprint 9.35.0+). */\nexport interface SkillEvidenceCheck {\n readonly posture?: string;\n readonly candidates?: number;\n readonly unsupported: readonly { readonly value: string; readonly shape?: string }[];\n readonly action?: 'grounded' | 'flagged' | 'revision-asked' | 'refused' | (string & {});\n readonly afterRevision?: boolean;\n /** The evidence index hit its ceiling — the verdict judged a partial corpus. */\n readonly evidenceTruncated?: boolean;\n}\n\n/** The turn-start verdict — one per turn on a cascade graph. */\nexport interface SkillTurnStart {\n readonly turnIndex: number;\n readonly by: SkillTurnVerdict;\n readonly from?: string;\n readonly to?: string;\n readonly scorer?: string;\n readonly scores: readonly {\n readonly id: string;\n readonly score: number;\n readonly relevance: number;\n }[];\n readonly runnerUp?: { readonly id: string; readonly gap: number };\n readonly decisive?: boolean;\n readonly witness?: SkillRouteWitness;\n readonly offered?: readonly string[];\n readonly stayOffered?: boolean;\n readonly policy?: {\n readonly nearTieMargin?: number;\n readonly menuSize?: number;\n readonly floor?: number;\n };\n readonly window?: number;\n readonly droppedResume?: { readonly id: string; readonly reason?: string };\n readonly decider?: { readonly provider?: string; readonly model?: string; readonly picked?: string };\n}\n\n/**\n * One iteration of the run, from the routing point of view. There is a hop for\n * every iteration the log mentions — including iterations whose evaluation\n * carried no `cursorMove` (`by` is then `undefined`), because dropping those\n * would drop the refusals and tool menus stamped on them.\n */\nexport interface SkillHop {\n readonly turnIndex: number;\n readonly iteration: number;\n /** The evaluate stage this iteration's cursor resolved at, when known —\n * the lens's ONE cursor, so a view can jump straight to it. */\n readonly runtimeStageId?: string;\n readonly from?: string;\n readonly to?: string;\n readonly by?: SkillCursorCause;\n /** Did the cursor actually change? Computed once so no view re-derives it. */\n readonly moved: boolean;\n readonly witness?: SkillRouteWitness;\n /** The turn-start menu this pick resolved, when the move closed one. */\n readonly offered?: readonly string[];\n /** The accepted pick was reachable but NOT on the offered menu. */\n readonly declinedOffer?: boolean;\n readonly activeIds: readonly string[];\n /** Entries whose own rule matched but which the cursor law kept off the wire. */\n readonly supersededIds: readonly string[];\n readonly refusals: readonly SkillRefusal[];\n readonly conflicts: readonly SkillRouteConflict[];\n readonly superseded: readonly SkillSuperseded[];\n /** The tool catalog as sent on this iteration — what the model chose from. */\n readonly toolsAsSent: readonly SkillToolAsSent[];\n /** `read_skill`'s description, verbatim: the menu that told the model what\n * was reachable. The highest-value field here — it is the model's own view\n * of the graph, and it is free on `stream.llm_start`. */\n readonly readSkillDescription?: string;\n /**\n * The REACHABLE set from the cursor this iteration's move landed on —\n * `cursorMove.reachable` (9.50.0), the same set the gate builds the\n * `read_skill` menu and its refusals from, as DATA. `[]` is a fact (a dead\n * end: nothing was admissible); ABSENT means the recording's era predates\n * the field and the set was honestly not on the record.\n */\n readonly reachable?: readonly string[];\n /**\n * The ASSEMBLED system prompt, byte-for-byte as sent to the provider\n * (`llm_start.systemPromptText`, 9.50.0). Present ONLY when the producer\n * opted in (`recordSystemPrompt: true` — off by default, for privacy);\n * absent, the string is honestly not in the recording.\n */\n readonly systemPromptText?: string;\n /** What the active skills put into the prompt on this iteration. */\n readonly skillInjections: readonly SkillInjectionSeen[];\n readonly evidence?: SkillEvidenceCheck;\n}\n\n/** The whole routing record for a run. */\nexport interface SkillRoute {\n /**\n * Did this run route at all? `true` when the log carried ANY routing fact —\n * a skill catalog, a cursor move, a turn verdict, a refusal, a conflict, a\n * superseded pick. `false` on an agent with no skills, so a view says \"this\n * run had no skill graph\" instead of drawing an empty one. Note that\n * `context.evaluated` fires on EVERY agent run (the injection engine\n * evaluates whether or not skills exist), so its mere presence proves\n * nothing and is deliberately not the signal.\n */\n readonly hasRouting: boolean;\n readonly nodes: readonly SkillRouteNode[];\n readonly hops: readonly SkillHop[];\n readonly observedEdges: readonly SkillObservedEdge[];\n readonly declaredEdges: readonly SkillDeclaredEdge[];\n /**\n * `true` when the run's `agentfootprint.skill.graph_declared` event (9.50.0)\n * supplied the declared set — the author's WHOLE map, verbatim from the\n * built graph. `false` (or absent, on a route folded elsewhere) means\n * `declaredEdges` is the `routing[]` LOWER BOUND — an edge is named only\n * once it fires — and a view must say so.\n */\n readonly declaredComplete?: boolean;\n /**\n * Entry skills — targets of the declared map's synthetic-START edges\n * (`from: null` in `graph_declared`). Where a turn CAN begin, as the author\n * drew it. Absent when the recording carried no declared map (or it had no\n * entry edge), never guessed from observed cold starts.\n */\n readonly entryIds?: readonly string[];\n readonly turns: readonly SkillTurnStart[];\n}\n\nexport interface SelectSkillRouteArgs {\n /** The lens event log, in order (`recorder.selectEventLog()`). */\n readonly log: readonly EventLogEntry[];\n}\n\n// ─── Event types read here (raw strings — see the era note) ──────────────\n\nconst EVALUATED = 'agentfootprint.context.evaluated';\nconst INJECTED = 'agentfootprint.context.injected';\nconst LLM_START = 'agentfootprint.stream.llm_start';\nconst TOOL_START = 'agentfootprint.stream.tool_start';\nconst TOOL_END = 'agentfootprint.stream.tool_end';\nconst REJECTED = 'agentfootprint.skill.rejected';\nconst GRAPH_DECLARED = 'agentfootprint.skill.graph_declared';\nconst ROUTE_CONFLICT = 'agentfootprint.skill.route_conflict';\nconst REROUTE_SUPERSEDED = 'agentfootprint.skill.reroute_superseded';\nconst TURN_ROUTED = 'agentfootprint.skill.turn_routed';\nconst TURN_START = 'agentfootprint.agent.turn_start';\nconst ITERATION_START = 'agentfootprint.agent.iteration_start';\nconst EVIDENCE_CHECKED = 'agentfootprint.agent.evidence_checked';\n\n/** The gate's own tool — the one whose description carries the menu. */\nconst READ_SKILL = 'read_skill';\n\n// ─── Small readers (payloads are data, never trusted shapes) ─────────────\n\ntype Payload = Record<string, unknown>;\n\nfunction payloadOf(entry: EventLogEntry): Payload {\n const p = (entry.event as { payload?: unknown }).payload;\n return p !== null && typeof p === 'object' ? (p as Payload) : {};\n}\n\nfunction str(v: unknown): string | undefined {\n return typeof v === 'string' ? v : undefined;\n}\n\nfunction num(v: unknown): number | undefined {\n return typeof v === 'number' && Number.isFinite(v) ? v : undefined;\n}\n\nfunction strList(v: unknown): readonly string[] {\n return Array.isArray(v) ? v.filter((x): x is string => typeof x === 'string') : [];\n}\n\nfunction obj(v: unknown): Payload | undefined {\n return v !== null && typeof v === 'object' && !Array.isArray(v) ? (v as Payload) : undefined;\n}\n\n/**\n * `{ key: value }` when the value is there, `{}` when it is not — the\n * \"absent ⇒ omitted\" law as one expression, so every optional field on every\n * folded shape obeys it the same way instead of each spread re-deriving it.\n */\nfunction some<K extends string, V>(key: K, value: V | undefined): { [P in K]?: V } {\n return value === undefined ? {} : ({ [key]: value } as { [P in K]?: V });\n}\n\nfunction witnessOf(v: unknown): SkillRouteWitness | undefined {\n const w = obj(v);\n const text = str(w?.text);\n if (text === undefined) return undefined;\n const keyword = str(w?.keyword);\n return keyword === undefined ? { text } : { text, keyword };\n}\n\n// ─── The fold ────────────────────────────────────────────────────────────\n\n/** A hop under construction — the readonly `SkillHop` is built from it at the end. */\ninterface HopDraft {\n turnIndex: number;\n iteration: number;\n runtimeStageId?: string;\n from?: string;\n to?: string;\n by?: SkillCursorCause;\n witness?: SkillRouteWitness;\n offered?: readonly string[];\n declinedOffer?: boolean;\n activeIds: readonly string[];\n supersededIds: readonly string[];\n refusals: SkillRefusal[];\n conflicts: SkillRouteConflict[];\n superseded: SkillSuperseded[];\n toolsAsSent: readonly SkillToolAsSent[];\n readSkillDescription?: string;\n reachable?: readonly string[];\n systemPromptText?: string;\n skillInjections: SkillInjectionSeen[];\n evidence?: SkillEvidenceCheck;\n}\n\n/**\n * Did the cursor change on this iteration? A cold start (no `from`, a `to`) IS\n * a move — the cursor went from nowhere to somewhere; `by: 'stay'` never is,\n * and `by: 'none'` has no `to` to move to.\n */\nfunction didMove(d: Pick<HopDraft, 'by' | 'from' | 'to'>): boolean {\n return d.by !== undefined && d.to !== undefined && d.to !== d.from;\n}\n\n/**\n * A refusal that has been recorded but whose SENTENCE has not arrived yet —\n * the gate fires `skill.rejected` between the refused call's `tool_start` and\n * its `tool_end`, so the text the model read is always one event behind.\n */\ninterface PendingRefusal {\n /** Where the refusal was filed, so the result can be written back to it. */\n draftKey: string;\n index: number;\n requestedId: string;\n /** Still waiting for the tool result that carries the refusal sentence. */\n awaitingResult: boolean;\n}\n\n/**\n * Fold the log into the routing record.\n *\n * Never throws: a payload that is not the shape this reads contributes nothing\n * rather than aborting the fold — a debugger that dies on one malformed event\n * is worse than one that shows the rest of the run.\n *\n * @example\n * ```ts\n * const route = selectSkillRoute({ log: recorder.selectEventLog() });\n * const refused = route.hops.flatMap((h) => h.refusals);\n * // \"audit-log was refused on iteration 1, and the cursor stayed at triage\"\n * refused[0].requestedId; // 'audit-log'\n * refused[0].allowed; // ['volume-lookup']\n * refused[0].cursorAfter?.by; // 'stay'\n * refused[0].cursorAfter?.moved; // false\n * ```\n */\nexport function selectSkillRoute(args: SelectSkillRouteArgs): SkillRoute {\n const { log } = args;\n\n interface CatalogRow {\n description?: string;\n kind?: string;\n label?: string;\n }\n const drafts = new Map<string, HopDraft>();\n const catalog = new Map<string, CatalogRow>();\n const visited = new Set<string>();\n const declared = new Map<string, SkillDeclaredEdge>();\n const turns: SkillTurnStart[] = [];\n const pendingRefusals: PendingRefusal[] = [];\n /** Open tool calls, by the provider's call id — `tool_end` carries no name. */\n const openCalls = new Map<string, { name: string; requestedId?: string }>();\n\n let hasRouting = false;\n let declaredComplete = false;\n const entryIds: string[] = [];\n // The running address. Iterations restart at 1 on every turn, so a hop key\n // without the turn would collide across turns of one conversation.\n let turnIndex = 0;\n let iteration = 0;\n\n const keyOf = (t: number, i: number): string => `${t}:${i}`;\n\n const draftAt = (t: number, i: number): HopDraft => {\n const key = keyOf(t, i);\n let draft = drafts.get(key);\n if (draft === undefined) {\n draft = {\n turnIndex: t,\n iteration: i,\n activeIds: [],\n supersededIds: [],\n refusals: [],\n conflicts: [],\n superseded: [],\n toolsAsSent: [],\n skillInjections: [],\n };\n drafts.set(key, draft);\n }\n return draft;\n };\n\n for (const entry of log) {\n const type = str((entry.event as { type?: unknown }).type) ?? '';\n const p = payloadOf(entry);\n\n // Keep the running address current BEFORE the event is folded: every\n // iteration-stamped payload updates it, and the un-stamped events\n // (tool_start / tool_end) inherit whatever the last stamp said — which is\n // exactly where they ran.\n if (type === TURN_START) {\n turnIndex = num(p.turnIndex) ?? turnIndex;\n iteration = 0;\n continue;\n }\n if (type === ITERATION_START) {\n turnIndex = num(p.turnIndex) ?? turnIndex;\n iteration = num(p.iterIndex) ?? iteration;\n draftAt(turnIndex, iteration);\n continue;\n }\n const stamped = num(p.iteration);\n if (stamped !== undefined) iteration = stamped;\n\n switch (type) {\n case EVALUATED: {\n const draft = draftAt(turnIndex, iteration);\n // The evaluate stage IS where the cursor resolved, so its stage id is\n // the one a view should move the lens cursor to for this hop.\n draft.runtimeStageId = entry.runtimeStageId ?? draft.runtimeStageId;\n draft.activeIds = strList(p.activeIds);\n draft.supersededIds = strList(p.supersededIds);\n\n for (const row of Array.isArray(p.skillCatalog) ? p.skillCatalog : []) {\n const c = obj(row);\n const id = str(c?.id);\n if (id === undefined) continue;\n hasRouting = true;\n const known = catalog.get(id) ?? {};\n if (known.description === undefined) known.description = str(c?.description);\n catalog.set(id, known);\n }\n\n for (const row of Array.isArray(p.routing) ? p.routing : []) {\n const r = obj(row);\n const to = str(r?.injectionId);\n const from = str(r?.from);\n // Only a `route` entry names an edge; `entry` / `tree` / `model`\n // provenance says how a skill is REACHABLE, not that an edge exists.\n if (to === undefined || from === undefined) continue;\n const edgeKey = `${from}->${to}`;\n if (!declared.has(edgeKey)) {\n const label = str(r?.label);\n const triggerKind = str(r?.triggerKind);\n declared.set(edgeKey, {\n from,\n to,\n ...(label !== undefined ? { label } : {}),\n ...(triggerKind !== undefined ? { triggerKind } : {}),\n });\n }\n }\n\n const move = obj(p.cursorMove);\n if (move !== undefined) {\n hasRouting = true;\n draft.from = str(move.from);\n draft.to = str(move.to);\n draft.by = str(move.by);\n draft.witness = witnessOf(move.witness);\n if (Array.isArray(move.offered)) draft.offered = strList(move.offered);\n // `[]` is kept: an empty reachable set is a dead end, a FACT — only\n // a missing field (an older era) leaves the hop's set absent.\n if (Array.isArray(move.reachable)) draft.reachable = strList(move.reachable);\n if (typeof move.declinedOffer === 'boolean') draft.declinedOffer = move.declinedOffer;\n if (draft.to !== undefined) visited.add(draft.to);\n if (draft.from !== undefined) visited.add(draft.from);\n }\n break;\n }\n\n case LLM_START: {\n const draft = draftAt(turnIndex, iteration);\n const tools: SkillToolAsSent[] = [];\n for (const row of Array.isArray(p.tools) ? p.tools : []) {\n const t = obj(row);\n const name = str(t?.name);\n if (name === undefined) continue;\n const description = str(t?.description);\n tools.push(description === undefined ? { name } : { name, description });\n // The gate rewrites this description every iteration with the\n // reachable set — it is the model's own view of the graph, and the\n // only place a reader can see the menu the model was actually given.\n if (name === READ_SKILL && description !== undefined) {\n draft.readSkillDescription = description;\n }\n }\n draft.toolsAsSent = tools;\n // The assembled prompt rides llm_start only when the producer opted\n // in (`recordSystemPrompt: true`) — absent is the privacy default.\n const promptText = str(p.systemPromptText);\n if (promptText !== undefined) draft.systemPromptText = promptText;\n break;\n }\n\n case GRAPH_DECLARED: {\n // The author's map, verbatim from the built graph (9.50.0) — fired\n // once per run, right after the run manifest. Everything it names is\n // COMPLETE (the whole declaration), unlike `routing[]`'s fired-only\n // lower bound; `declaredComplete` records which one this route has.\n hasRouting = true;\n declaredComplete = true;\n for (const row of Array.isArray(p.nodes) ? p.nodes : []) {\n const n = obj(row);\n const id = str(n?.id);\n if (id === undefined) continue;\n const existing = catalog.get(id) ?? {};\n if (existing.description === undefined) existing.description = str(n?.description);\n if (existing.kind === undefined) existing.kind = str(n?.kind);\n if (existing.label === undefined) existing.label = str(n?.label);\n catalog.set(id, existing);\n }\n for (const row of Array.isArray(p.edges) ? p.edges : []) {\n const e = obj(row);\n const to = str(e?.to);\n if (to === undefined) continue;\n // `from: null` is the synthetic START — an entry fact, not an edge\n // between two skills. Kept apart so a canvas can mark entries\n // without drawing a node that does not exist.\n if (e?.from === null) {\n if (!entryIds.includes(to)) entryIds.push(to);\n continue;\n }\n const from = str(e?.from);\n if (from === undefined) continue;\n const edgeKey = `${from}->${to}`;\n if (!declared.has(edgeKey)) {\n declared.set(edgeKey, {\n from,\n to,\n ...some('label', str(e?.label)),\n ...some('triggerKind', str(e?.kind)),\n });\n }\n }\n break;\n }\n\n case INJECTED: {\n // Skill-sourced injections only: this is the ROUTING record, and a\n // user message or the base prompt is not a routing fact.\n if (str(p.source) !== 'skill') break;\n const draft = draftAt(turnIndex, iteration);\n const slot = str(p.slot) ?? 'unknown';\n const skillId = str(p.sourceId);\n const text = str(p.rawContent);\n draft.skillInjections.push({\n slot,\n ...(skillId !== undefined ? { skillId } : {}),\n summary: str(p.contentSummary) ?? '',\n ...(text !== undefined ? { text } : {}),\n });\n break;\n }\n\n case TOOL_START: {\n const id = str(p.toolCallId);\n const name = str(p.toolName);\n if (id === undefined || name === undefined) break;\n const requestedId = str(obj(p.args)?.id);\n openCalls.set(id, { name, ...(requestedId !== undefined ? { requestedId } : {}) });\n break;\n }\n\n case TOOL_END: {\n const id = str(p.toolCallId);\n if (id === undefined) break;\n const call = openCalls.get(id);\n openCalls.delete(id);\n if (call === undefined || call.name !== READ_SKILL) break;\n const result = str(p.result);\n if (result === undefined) break;\n // Pair the refusal with the sentence the model read back. The gate\n // fires `skill.rejected` BETWEEN this call's start and end, so the\n // still-unanswered refusal for this same requested id is this call's.\n for (let i = pendingRefusals.length - 1; i >= 0; i--) {\n const pending = pendingRefusals[i]!;\n if (!pending.awaitingResult) continue;\n if (call.requestedId !== undefined && pending.requestedId !== call.requestedId) continue;\n const draft = drafts.get(pending.draftKey);\n const refusal = draft?.refusals[pending.index];\n if (refusal === undefined) continue;\n draft!.refusals[pending.index] = { ...refusal, toolCallId: id, refusalText: result };\n pending.awaitingResult = false;\n break;\n }\n break;\n }\n\n case REJECTED: {\n hasRouting = true;\n const requestedId = str(p.requestedId);\n if (requestedId === undefined) break;\n const draft = draftAt(turnIndex, iteration);\n const currentSkillId = str(p.currentSkillId);\n const posture = str(p.posture);\n draft.refusals.push({\n requestedId,\n ...(currentSkillId !== undefined ? { currentSkillId } : {}),\n allowed: strList(p.allowed),\n ...(posture !== undefined ? { posture } : {}),\n turnIndex,\n iteration,\n });\n pendingRefusals.push({\n draftKey: keyOf(turnIndex, iteration),\n index: draft.refusals.length - 1,\n requestedId,\n awaitingResult: true,\n });\n break;\n }\n\n case ROUTE_CONFLICT: {\n hasRouting = true;\n const draft = draftAt(turnIndex, iteration);\n const outcome = (row: Payload): SkillRouteConflict['losers'][number] => ({\n ...some('toolName', str(row.toolName)),\n ...some('target', str(row.target)),\n ...some('toolCallId', str(row.toolCallId)),\n });\n const winner = obj(p.winner);\n draft.conflicts.push({\n ...some('fromSkillId', str(p.fromSkillId)),\n ...some('winner', winner !== undefined ? outcome(winner) : undefined),\n losers: (Array.isArray(p.losers) ? p.losers : [])\n .map((row) => obj(row))\n .filter((l): l is Payload => l !== undefined)\n .map(outcome),\n ...some('source', str(p.source)),\n });\n break;\n }\n\n case REROUTE_SUPERSEDED: {\n hasRouting = true;\n const volunteeredId = str(p.volunteeredId);\n if (volunteeredId === undefined) break;\n const draft = draftAt(turnIndex, iteration);\n draft.superseded.push({\n volunteeredId,\n ...some('wonId', str(p.wonId)),\n ...some('fromSkillId', str(p.fromSkillId)),\n ...some('source', str(p.source)),\n });\n break;\n }\n\n case TURN_ROUTED: {\n hasRouting = true;\n const by = str(p.by);\n if (by === undefined) break;\n const scores = (Array.isArray(p.scores) ? p.scores : [])\n .map((row) => obj(row))\n .filter((s): s is Payload => s !== undefined)\n .map((s) => ({\n id: str(s.id) ?? '',\n score: num(s.score) ?? 0,\n relevance: num(s.relevance) ?? 0,\n }));\n const runnerUp = obj(p.runnerUp);\n const runnerUpId = str(runnerUp?.id);\n const policy = obj(p.policy);\n const dropped = obj(p.droppedResume);\n const droppedId = str(dropped?.id);\n const decider = obj(p.decider);\n turns.push({\n turnIndex,\n by,\n ...some('from', str(p.from)),\n ...some('to', str(p.to)),\n ...some('scorer', str(p.scorer)),\n scores,\n ...some(\n 'runnerUp',\n runnerUpId !== undefined\n ? { id: runnerUpId, gap: num(runnerUp?.gap) ?? 0 }\n : undefined,\n ),\n ...some('decisive', typeof p.decisive === 'boolean' ? p.decisive : undefined),\n ...some('witness', witnessOf(p.witness)),\n ...some('offered', Array.isArray(p.offered) ? strList(p.offered) : undefined),\n ...some('stayOffered', typeof p.stayOffered === 'boolean' ? p.stayOffered : undefined),\n ...some(\n 'policy',\n policy !== undefined\n ? {\n ...some('nearTieMargin', num(policy.nearTieMargin)),\n ...some('menuSize', num(policy.menuSize)),\n ...some('floor', num(policy.floor)),\n }\n : undefined,\n ),\n ...some('window', num(p.window)),\n ...some(\n 'droppedResume',\n droppedId !== undefined\n ? { id: droppedId, ...some('reason', str(dropped?.reason)) }\n : undefined,\n ),\n ...some(\n 'decider',\n decider !== undefined\n ? {\n ...some('provider', str(decider.provider)),\n ...some('model', str(decider.model)),\n ...some('picked', str(decider.picked)),\n }\n : undefined,\n ),\n });\n break;\n }\n\n case EVIDENCE_CHECKED: {\n const draft = draftAt(turnIndex, iteration);\n const unsupported = (Array.isArray(p.unsupported) ? p.unsupported : [])\n .map((row) => obj(row))\n .filter((u): u is Payload => u !== undefined)\n .map((u) => ({ value: str(u.value) ?? '', ...some('shape', str(u.shape)) }));\n draft.evidence = {\n ...some('posture', str(p.posture)),\n ...some('candidates', num(p.candidates)),\n unsupported,\n ...some('action', str(p.action)),\n ...some('afterRevision', typeof p.afterRevision === 'boolean' ? p.afterRevision : undefined),\n ...some(\n 'evidenceTruncated',\n typeof p.evidenceTruncated === 'boolean' ? p.evidenceTruncated : undefined,\n ),\n };\n break;\n }\n\n default:\n break;\n }\n }\n\n // ── Link each refusal to the cursor row that answered it ───────────────\n // The lag is the law (see the header): the resolution of an iteration's pick\n // is reported by the NEXT iteration's evaluate. No next row ⇒ no claim.\n for (const draft of drafts.values()) {\n if (draft.refusals.length === 0) continue;\n const next = drafts.get(keyOf(draft.turnIndex, draft.iteration + 1));\n if (next === undefined || next.by === undefined) continue;\n const cursorAfter: SkillCursorAfter = {\n iteration: next.iteration,\n ...(next.from !== undefined ? { from: next.from } : {}),\n ...(next.to !== undefined ? { to: next.to } : {}),\n by: next.by,\n moved: didMove(next),\n };\n draft.refusals = draft.refusals.map((refusal) => ({ ...refusal, cursorAfter }));\n }\n\n // ── Freeze the drafts, in the order the run produced them ──────────────\n const hops: SkillHop[] = [...drafts.values()].map((d) => ({\n turnIndex: d.turnIndex,\n iteration: d.iteration,\n ...(d.runtimeStageId !== undefined ? { runtimeStageId: d.runtimeStageId } : {}),\n ...(d.from !== undefined ? { from: d.from } : {}),\n ...(d.to !== undefined ? { to: d.to } : {}),\n ...(d.by !== undefined ? { by: d.by } : {}),\n moved: didMove(d),\n ...(d.witness !== undefined ? { witness: d.witness } : {}),\n ...(d.offered !== undefined ? { offered: d.offered } : {}),\n ...(d.declinedOffer !== undefined ? { declinedOffer: d.declinedOffer } : {}),\n activeIds: d.activeIds,\n supersededIds: d.supersededIds,\n refusals: d.refusals,\n conflicts: d.conflicts,\n superseded: d.superseded,\n toolsAsSent: d.toolsAsSent,\n ...(d.readSkillDescription !== undefined\n ? { readSkillDescription: d.readSkillDescription }\n : {}),\n ...(d.reachable !== undefined ? { reachable: d.reachable } : {}),\n ...(d.systemPromptText !== undefined ? { systemPromptText: d.systemPromptText } : {}),\n skillInjections: d.skillInjections,\n ...(d.evidence !== undefined ? { evidence: d.evidence } : {}),\n }));\n\n // ── The graph: catalog nodes, the hops the cursor was seen to take ─────\n const nodes: SkillRouteNode[] = [...catalog.entries()].map(([id, row]) => ({\n id,\n ...(row.description !== undefined ? { description: row.description } : {}),\n ...(row.kind !== undefined ? { kind: row.kind } : {}),\n ...(row.label !== undefined ? { label: row.label } : {}),\n visited: visited.has(id),\n }));\n // A cursor position the catalog never listed is still a node of the graph\n // the run walked — report it rather than silently dropping the hop's target.\n for (const id of visited) {\n if (!catalog.has(id)) nodes.push({ id, visited: true });\n }\n\n const edges = new Map<string, { edge: SkillObservedEdge; takenAt: SkillHopRef[] }>();\n for (const hop of hops) {\n if (!hop.moved || hop.to === undefined || hop.by === undefined) continue;\n const key = `${hop.from ?? ''}->${hop.to}#${hop.by}`;\n const declaredEdge = hop.from !== undefined ? declared.get(`${hop.from}->${hop.to}`) : undefined;\n let row = edges.get(key);\n if (row === undefined) {\n const takenAt: SkillHopRef[] = [];\n row = {\n edge: {\n ...(hop.from !== undefined ? { from: hop.from } : {}),\n to: hop.to,\n by: hop.by,\n takenAt,\n ...(declaredEdge?.label !== undefined ? { label: declaredEdge.label } : {}),\n ...(declaredEdge?.triggerKind !== undefined\n ? { triggerKind: declaredEdge.triggerKind }\n : {}),\n },\n takenAt,\n };\n edges.set(key, row);\n }\n row.takenAt.push({ turnIndex: hop.turnIndex, iteration: hop.iteration });\n }\n\n return {\n hasRouting,\n nodes,\n hops,\n observedEdges: [...edges.values()].map((r) => r.edge),\n declaredEdges: [...declared.values()],\n declaredComplete,\n ...(entryIds.length > 0 ? { entryIds } : {}),\n turns,\n };\n}\n","/**\n * humanizeRouting — natural-language lines for the skill-graph ROUTING\n * events (agentfootprint 9.16.0 / 9.17.0):\n *\n * • `agentfootprint.skill.turn_routed` — the turn-start routing verdict\n * • `agentfootprint.skill.route_conflict`— edge-vs-edge inside one tool batch\n * • `agentfootprint.skill.rejected` — now with a `posture` refusal reason\n * • `context.evaluated`'s `cursorMove` — the model-pick decorations\n * (`offered` / `declinedOffer`)\n *\n * Pattern: pure sentence builders, composed by `defaultHumanizer`.\n * Role: plain-language commentary for a NON-developer reader.\n *\n * Era note (the budget_pressure precedent, applied at the TYPE level):\n * `turn_routed` / `route_conflict` postdate the agentfootprint version this\n * package compiles against, so its `AgentfootprintEvent` union does not name\n * them yet. They are matched by raw type STRING and their payloads are read\n * through the local structural interfaces below — mirrors of the shipped\n * shapes (agentfootprint src/events/payloads.ts), every field optional-safe.\n * A recording from an older era simply carries fewer fields, and the\n * house law applies: render ONLY what the event carries — an absent field\n * means its clause is OMITTED, never guessed.\n *\n * Vocabulary note — TWO vocabularies, and they are not the same list:\n *\n * • `turn_routed.by` (which TIER decided where the turn started) ships as\n * `'entry' | 'intent' | 'continuity' | 'menu' | 'decider' | 'none'`.\n * `'entry'` is the recorded vocabulary for rule-won starts, `'menu'` for an\n * in-band menu, `'decider'` for a tier-3 resolver (agentfootprint 9.19.0).\n * Design-era aliases (`'rule'`, `'model-pick'`) are tolerated as synonyms\n * so a recording from an intermediate build still renders.\n * • `cursorMove.by` (what moved the CURSOR this iteration) ships as\n * `'entry' | 'route' | 'model-pick' | 'tool-proposal' | 'intent' |\n * 'continuity' | 'decider' | 'stay' | 'none'` — nine causes, the union\n * `CursorMoveCause` in agentfootprint's `skillGraph.ts`. The library's own\n * `ContextEvaluatedPayload` docstring lists only seven (it predates\n * `'tool-proposal'` and `'decider'`); the union is the source of truth.\n * `'menu'` is NOT one of them — an offer is not a move.\n *\n * Anything outside either list falls back to an honest raw line.\n */\n\n// ─── Structural payload mirrors (events read, never constructed here) ───\n\n/** Mirror of `SkillTurnRoutedPayload` (agentfootprint 9.17.0). */\nexport interface TurnRoutedLike {\n readonly by: string;\n readonly from?: string;\n readonly to?: string;\n readonly scorer?: string;\n readonly window?: number;\n readonly scores?: ReadonlyArray<{\n readonly id: string;\n readonly score: number;\n readonly relevance: number;\n }>;\n readonly runnerUp?: { readonly id: string; readonly gap: number };\n readonly decisive?: boolean;\n readonly offered?: readonly string[];\n readonly stayOffered?: boolean;\n /** `reason` is optional because a RECORDING's is: the fold that reads these\n * events (`selectSkillRoute`) omits a field the payload did not carry, and\n * no sentence here reads it — only the id is spoken. Requiring it would\n * make a real recording un-narratable over a word nobody says. */\n readonly droppedResume?: { readonly id: string; readonly reason?: string };\n readonly policy?: {\n readonly nearTieMargin?: number;\n readonly menuSize?: number;\n readonly floor?: number;\n };\n}\n\n/** Mirror of `SkillRouteConflictPayload` (agentfootprint 9.16.0). */\nexport interface RouteConflictLike {\n readonly iteration?: number;\n readonly fromSkillId?: string;\n readonly winner?: {\n readonly toolCallId?: string;\n readonly toolName?: string;\n readonly target?: string;\n };\n readonly losers?: ReadonlyArray<{\n readonly toolCallId?: string;\n readonly toolName?: string;\n readonly target?: string;\n }>;\n}\n\n/** Mirror of `SkillRejectedPayload` + the 9.17.0 `posture` widening. */\nexport interface SkillRejectedLike {\n readonly requestedId: string;\n readonly currentSkillId?: string;\n /** The REACHABILITY set (hops + open skills) — NOT the offered menu. A\n * guard refusal fires only AFTER reachability passed, so under\n * posture='guard' this list always contains the refused `requestedId`\n * itself; the menu the model was shown is not on the event at all. */\n readonly allowed?: readonly string[];\n readonly iteration?: number;\n readonly posture?: string; // 'guard' | 'rails' when a posture refused\n}\n\n/**\n * Mirror of `ContextEvaluatedPayload.cursorMove` — the 9.17.0 decorations\n * (`offered` / `declinedOffer`) and the 9.28.0 `witness` included.\n *\n * `by` is typed as the NINE shipped causes plus a `(string & {})` arm, so\n * editors autocomplete the real vocabulary while a future era's tenth value\n * still reads (and renders through the honest raw fallback).\n */\nexport interface CursorMoveLike {\n readonly from?: string;\n readonly to?: string;\n readonly by:\n | 'entry'\n | 'route'\n | 'model-pick'\n | 'tool-proposal'\n | 'intent'\n | 'continuity'\n | 'decider'\n | 'stay'\n | 'none'\n | (string & {});\n readonly offered?: readonly string[];\n readonly declinedOffer?: boolean;\n /** WHAT the message said that routed this hop — `match:` rules only. */\n readonly witness?: { readonly text: string; readonly keyword?: string };\n}\n\n// ─── Small shared formatters ─────────────────────────────────────────────\n\n/** Raw scorer score → short human number (\"0.87\"). */\nfunction fmtScore(n: number): string {\n return String(Number(n.toFixed(2)));\n}\n\n/** Softmax relevance share → whole percent (\"44%\"). */\nfunction fmtShare(n: number): string {\n return `${Math.round(n * 100)}%`;\n}\n\nfunction quoteList(ids: readonly string[]): string {\n return ids.join(', ');\n}\n\n/** \"billing\" → `\"billing\"`, undefined → undefined. */\nfunction q(id: string | undefined): string | undefined {\n return id === undefined ? undefined : `\"${id}\"`;\n}\n\n/** The movement clause shared by several verdicts:\n * from+to (different) → Moved from A to B; same / no from → started/stayed. */\nfunction movement(p: { from?: string; to?: string }): string {\n const { from, to } = p;\n if (to !== undefined && from !== undefined) {\n return from === to ? `Stayed in ${q(to)}` : `Moved from ${q(from)} to ${q(to)}`;\n }\n if (to !== undefined) return `Turn started in ${q(to)}`;\n if (from !== undefined) return `Stayed in ${q(from)}`;\n return 'The turn was routed';\n}\n\n/** The droppedResume clause — appended to any verdict it decorates. */\nfunction droppedResumeNote(p: TurnRoutedLike): string {\n const d = p.droppedResume;\n if (!d) return '';\n return ` The conversation's saved place (${q(d.id)}) no longer exists in this deployment.`;\n}\n\n// ─── agentfootprint.skill.turn_routed ────────────────────────────────────\n\nexport function humanizeTurnRouted(p: TurnRoutedLike): string {\n switch (p.by) {\n case 'entry':\n case 'rule': // design-era alias for 'entry'\n return `${movement(p)} — a rule matched (tier 1).${droppedResumeNote(p)}`;\n\n case 'intent': {\n // Tier-2 scorer decided. Winner score = scores[0] (ranked best-first);\n // the runner-up's own score comes from its scores[] row.\n const winner = p.scores?.[0];\n const ruRow =\n p.runnerUp && p.scores ? p.scores.find((s) => s.id === p.runnerUp!.id) : undefined;\n let clause = ' — intent';\n if (winner) {\n clause += ` scored ${fmtScore(winner.score)}`;\n if (ruRow) clause += ` vs ${fmtScore(ruRow.score)} for runner-up ${q(ruRow.id)}`;\n else if (p.runnerUp)\n clause += `, ${fmtScore(p.runnerUp.gap)} ahead of ${q(p.runnerUp.id)}`;\n } else {\n clause += ' matched';\n }\n const scorer = p.scorer ? ` (${p.scorer} scorer)` : '';\n return `${movement(p)}${clause}${scorer}.${droppedResumeNote(p)}`;\n }\n\n case 'continuity': {\n // Near-tie stays carry the losing numbers; sticky stays carry none.\n const nearTie = p.decisive === false && p.scores && p.scores.length >= 2;\n if (nearTie) {\n const [a, b] = p.scores!;\n const where = p.to ?? p.from;\n const tail = where !== undefined ? `; continuing in ${q(where)}` : '';\n return (\n `Stayed — near-tie between ${q(a.id)} (${fmtShare(a.relevance)}) and ` +\n `${q(b.id)} (${fmtShare(b.relevance)})${tail}.${droppedResumeNote(p)}`\n );\n }\n const where = p.to ?? p.from;\n const head = where !== undefined ? `Stayed in ${q(where)}` : 'Stayed';\n return `${head} — treated as a continuation of the conversation.${droppedResumeNote(p)}`;\n }\n\n case 'menu':\n case 'model-pick': {\n // A menu went in-band; the turn's start awaits the model's pick\n // (the pick itself lands as a cursorMove `model-pick`).\n let line = 'No single skill won — the model was offered a menu';\n if (p.offered && p.offered.length > 0)\n line += ` of ${p.offered.length}: ${quoteList(p.offered)}`;\n if (p.stayOffered) line += `; 'stay' was on the menu`;\n return `${line}.${droppedResumeNote(p)}`;\n }\n\n case 'none': {\n // Nothing the loop may act on: a dropped resume, or a rails-mode\n // menu whose offer is recorded but not actionable by the model.\n if (p.droppedResume) {\n let line =\n `The conversation's saved place (${q(p.droppedResume.id)}) no longer exists ` +\n `in this deployment — the turn started fresh.`;\n if (p.offered && p.offered.length > 0)\n line += ` Options on the record: ${quoteList(p.offered)}.`;\n return line;\n }\n if (p.offered && p.offered.length > 0) {\n // WHY the cascade ended in a menu is said only when the event carries\n // the evidence (house law): `decisive: false` with a floor-clearing\n // top score is a near-tie — intent DID match, more than once, and\n // `offered` is the tied set; a top score at/below the recorded floor\n // is honestly unmatched — `offered` is the full menu, so \"close\n // candidates\" would be a lie there. No numbers → posture-only prose.\n const top = p.scores?.[0]?.score;\n const floor = p.policy?.floor;\n const cleared =\n top !== undefined && Number.isFinite(top) && (floor === undefined || top > floor);\n if (p.decisive === false && (p.scores?.length ?? 0) >= 2 && cleared) {\n return (\n `This message matched ${quoteList(p.offered)} too closely to call, and rails ` +\n `mode does not let the model break the tie — the turn ran on the base prompt.`\n );\n }\n if (top !== undefined && !cleared) {\n return (\n `No rule or intent matched this message strongly enough, and rails mode does ` +\n `not let the model route — the turn ran on the base prompt. The full menu ` +\n `stayed on the record: ${quoteList(p.offered)}.`\n );\n }\n return (\n `Routing ended in a menu, but rails mode does not let the model route — ` +\n `the turn ran on the base prompt. On the record: ${quoteList(p.offered)}.`\n );\n }\n return 'Routing decided nothing this turn — the turn ran on the base prompt.';\n }\n\n default:\n // Unknown verdict vocabulary (a future era): render honestly, raw.\n return `Turn routing: ${p.by}${p.to !== undefined ? ` → ${q(p.to)}` : ''}.`;\n }\n}\n\n// ─── agentfootprint.skill.route_conflict ─────────────────────────────────\n\nconst COUNT_WORDS = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five'] as const;\n\nexport function humanizeRouteConflict(p: RouteConflictLike): string {\n const winner = p.winner;\n const losers = p.losers ?? [];\n const winnerClause = winner\n ? `${winner.toolName ?? 'a tool'} → ${q(winner.target) ?? 'its target'} won (first in the batch)`\n : 'the first match in the batch won';\n\n if (losers.length === 0) {\n return `Tool results conflicted on the next skill — ${winnerClause}.`;\n }\n\n const count = losers.length + 1;\n const countWord = count < COUNT_WORDS.length ? COUNT_WORDS[count] : String(count);\n const loserClause = losers\n .map((l) => `${l.toolName ?? 'a tool'} → ${q(l.target) ?? 'another skill'}`)\n .join(', ');\n const verb = losers.length === 1 ? 'was' : 'were';\n return (\n `${countWord} tool results wanted different next skills — ${winnerClause}; ` +\n `${loserClause} ${verb} suppressed and ${losers.length === 1 ? 'is' : 'are'} on the record.`\n );\n}\n\n// ─── agentfootprint.skill.rejected (with 9.17.0 posture) ─────────────────\n\nexport function humanizeSkillRejected(p: SkillRejectedLike): string {\n const pick = `The model tried to switch to ${q(p.requestedId)}`;\n if (p.posture === 'guard') {\n // `allowed` is the reachability set, which ALWAYS contains the refused\n // pick under guard — presenting it as \"the menu\" would put the refused\n // pick on the very menu it was refused for. The actual offered menu is\n // not on the event, so the sentence labels the set as what it is.\n let line = `${pick}, but that pick was off the menu it was given — refused (guard).`;\n if (p.allowed && p.allowed.length > 0) line += ` Reachable: ${quoteList(p.allowed)}.`;\n return line;\n }\n if (p.posture === 'rails') {\n return `${pick}, but rails mode does not let the model route — refused.`;\n }\n // Today's reachability refusal, unchanged semantics.\n const at = p.currentSkillId !== undefined ? q(p.currentSkillId) : 'the start';\n let line = `${pick}, but it is not reachable from ${at} — refused.`;\n if (p.allowed && p.allowed.length > 0) line += ` Reachable: ${quoteList(p.allowed)}.`;\n return line;\n}\n\n// ─── context.evaluated cursorMove — the model-pick decorations ───────────\n\n/**\n * Renders the model-pick line, or `null` to keep the event silent.\n *\n * `context.evaluated` fires every iteration and stays hidden from commentary\n * (low signal); the ONE exception is the iteration a model pick lands WITH\n * its 9.17.0 decorations (`offered` / `declinedOffer`) — the assist-posture\n * divergence the record exists to explain. Undecorated model picks (older\n * recordings) stay silent, exactly as they rendered before.\n */\nexport function humanizeCursorMovePick(move: CursorMoveLike | undefined): string | null {\n if (!move || move.by !== 'model-pick') return null;\n if (move.offered === undefined && move.declinedOffer === undefined) return null;\n\n let line =\n move.to !== undefined ? `The model chose ${q(move.to)}` : 'The model made a pick';\n // A declined offer means the pick was NOT on this menu — and the emitter\n // always stamps `offered` alongside `declinedOffer` (they are one resolved\n // menu object), so the menu clause must stay silent here or the sentence\n // claims the pick came \"from\" a menu it then says the pick was not on.\n if (move.offered !== undefined && move.declinedOffer !== true) {\n line += ` from a ${move.offered.length}-item menu`;\n const rest = move.to !== undefined ? move.offered.filter((id) => id !== move.to) : move.offered;\n if (rest.length > 0) line += ` (${quoteList(rest)} were offered too)`;\n }\n if (move.from !== undefined && move.from !== move.to) {\n line += `, moving from ${q(move.from)}`;\n }\n line += '.';\n if (move.declinedOffer === true) {\n line += ' Its pick was not on the offered menu — the divergence is on the record.';\n }\n return line;\n}\n\n// ─── context.evaluated cursorMove — all NINE causes ──────────────────────\n\n/**\n * Where the cursor ended up, in plain words. `Moved from A to B` when it\n * changed, `Started in B` on a cold start, `Stayed in A` when it did not.\n */\nfunction cursorMovement(move: CursorMoveLike): string {\n const { from, to } = move;\n if (to !== undefined && from !== undefined) {\n return from === to ? `Stayed in ${q(to)}` : `Moved from ${q(from)} to ${q(to)}`;\n }\n if (to !== undefined) return `Started in ${q(to)}`;\n if (from !== undefined) return `Stayed in ${q(from)}`;\n return 'The skill in play was settled';\n}\n\n/**\n * The evidence clause for a rule that matched on the message TEXT — the\n * `witness` agentfootprint 9.28.0 added. Quoted, never paraphrased: the whole\n * value of a witness is that it is the user's own words.\n */\nfunction witnessNote(move: CursorMoveLike): string {\n const w = move.witness;\n if (!w || typeof w.text !== 'string' || w.text === '') return '';\n return w.keyword !== undefined\n ? ` The message said \"${w.text}\" — the word ${q(w.keyword)} is what matched.`\n : ` The message said \"${w.text}\".`;\n}\n\n/**\n * Renders the cursor hop for ANY of the nine causes, or `null` to stay silent.\n *\n * Coverage note (why this exists): `humanizeCursorMovePick` renders exactly one\n * cause, `'model-pick'`, and only when the 9.17.0 menu decorations are on it —\n * so on a skill-graph run the other eight causes rendered NOTHING at all, and\n * a reader watching a routed conversation saw no routing. This is that gap\n * closed. `'model-pick'` is deliberately delegated to the existing function\n * rather than rewritten: its sentence (and its silence on an undecorated\n * pick, which is what an older recording carries) is unchanged, byte for byte.\n *\n * The audience is a non-developer, so the vocabulary is deliberately plain:\n * \"a rule the author wrote\", \"a separate chooser\", \"nothing moved\".\n */\nexport function humanizeCursorMove(move: CursorMoveLike | undefined): string | null {\n if (!move || typeof move.by !== 'string') return null;\n\n switch (move.by) {\n case 'model-pick':\n // Unchanged — see the note above.\n return humanizeCursorMovePick(move);\n\n case 'entry':\n return `${cursorMovement(move)} — a rule the author wrote matched this message.${witnessNote(move)}`;\n\n case 'route':\n return `${cursorMovement(move)} — the author drew this path, and the run took it automatically.`;\n\n case 'tool-proposal':\n return `${cursorMovement(move)} — a tool asked for this move, and it was allowed.`;\n\n case 'intent': {\n const fit = move.to !== undefined ? ` ${q(move.to)} fit best` : ' one skill fit best';\n return `${cursorMovement(move)} — this message was compared against the skills, and${fit}.`;\n }\n\n case 'continuity':\n return `${cursorMovement(move)} — the conversation was already there, so it carried on.`;\n\n case 'decider':\n return `${cursorMovement(move)} — a separate chooser was asked to settle it, and this is what it picked.`;\n\n case 'stay': {\n // The sticky-stay clause: the resolver found nothing that could move the\n // cursor. This is the sentence a refused `read_skill` pick ends up\n // explaining, so it says plainly that nothing happened.\n const where = move.to ?? move.from;\n return where !== undefined\n ? `Nothing moved — it stayed in ${q(where)}.`\n : 'Nothing moved — the skill in play stayed as it was.';\n }\n\n case 'none':\n return 'No skill was in play — nothing routed this turn.';\n\n default:\n // A future era's cause: say what the record says, invent nothing.\n return `The skill in play was settled by \"${move.by}\"${\n move.to !== undefined ? ` → ${q(move.to)}` : ''\n }.`;\n }\n}\n","/**\n * `selectSkillBeats` — the routing record as a TIME axis, and the resolver that\n * puts the Lens's ONE cursor on it.\n *\n * Pattern: pure functions over `selectSkillRoute`'s output — no framework, no\n * state, no caching. `selectSkillRoute` answers \"what happened\";\n * this answers \"what is true AT this point\", which is the only extra\n * question a scrubbing view asks.\n * Role: the headless half of the SkillGraph debugger. A beat is one hop\n * plus the facts that ACCUMULATE (where the cursor stands, what has\n * been visited) and the SENTENCE the library already writes for it.\n *\n * THE ONE-CURSOR LAW (this package's founding rule, `lens_v0_1_one_cursor`):\n * a beat is NOT a second cursor. It carries the hop's `runtimeStageId` — the\n * lens's own address — and {@link selectSkillBeatAt} RESOLVES the one cursor\n * onto the beat list. Nothing here stores a position; a view that renders\n * beats must derive the active one from the cursor on every render, and move\n * the cursor (never a local index) when the reader clicks a beat.\n *\n * WHERE THE SENTENCES COME FROM (and why none are written here):\n * `headline` and `notes` are `humanizeCursorMove` / `humanizeSkillRejected` /\n * `humanizeRouteConflict` / `humanizeTurnRouted` — the SAME builders\n * `defaultHumanizer` composes for the Commentary panel. A product-facing rail\n * shows the library's sentences revealed in cursor order; it does not get its\n * own prose, because two prose systems drift and only one of them is tested.\n * The two exceptions are spelled out at {@link fallbackHeadline} — both are\n * formatters over the hop's own fields, for the two cases the humanizer\n * answers `null` to.\n *\n * WHAT IS DELIBERATELY NOT DERIVED — the reachable set:\n * The gate rewrites `read_skill`'s description every iteration with the\n * reachable set, so the words are on the record — as PROSE. Parsing them back\n * into ids would re-create the exact mistake `selectSkillRoute` exists to\n * remove (a view re-parsing sentences), and would break on the first era that\n * rewords the menu. So `reachable` is filled only from a TYPED list, and it\n * names which one it used ({@link SkillReachableSet.source}), in this\n * priority order:\n *\n * `'cursor-move'` `cursorMove.reachable` (9.50.0) — the gate's own\n * admissible set from the landed cursor, on EVERY move.\n * Exact, and `[]` is kept: an empty set is a dead end,\n * a fact, never rendered as absence.\n * `'refusal'` `skill.rejected.allowed` — the gate's own reachability\n * set. Exact, but present only on iterations where the\n * model was refused.\n * `'declared-edges'` the edges `context.evaluated`'s `routing[]` named. A\n * LOWER BOUND: `routing[]` names an edge only once it\n * fires, so a graph's unfired edges are missing.\n *\n * The last two are fallbacks for recordings older than `cursorMove.reachable`;\n * `undefined` (no typed list at all) is the honest fourth answer — absent,\n * never empty.\n */\n\nimport {\n humanizeCursorMove,\n humanizeRouteConflict,\n humanizeSkillRejected,\n humanizeTurnRouted,\n} from '../humanizeRouting.js';\nimport type {\n SkillCursorCause,\n SkillHop,\n SkillRoute,\n SkillTurnStart,\n} from './selectSkillRoute.js';\n\n// ─── Shapes ──────────────────────────────────────────────────────────────\n\n/** A reachable set, and the typed field it was read from — never prose. */\nexport interface SkillReachableSet {\n readonly ids: readonly string[];\n /** Which typed field supplied it, in the priority order the header states:\n * `'cursor-move'` is exact and present on every 9.50.0+ move (`ids: []` is\n * a dead end — a fact); the other two are older-era fallbacks. */\n readonly source: 'cursor-move' | 'refusal' | 'declared-edges';\n}\n\n/**\n * One stop on the routing axis: an iteration's hop, plus what is TRUE there.\n *\n * Everything accumulating (`cursorSkillId`, `visited`) is computed once, in\n * run order, so no view re-folds the run to answer \"where am I\".\n */\nexport interface SkillBeat {\n /** Position in the beat list. A LIST INDEX, not a cursor — see the header. */\n readonly index: number;\n readonly turnIndex: number;\n readonly iteration: number;\n /** The evaluate stage this iteration resolved at — the lens's ONE cursor\n * address for this beat. Absent when the recording carried no stage id. */\n readonly runtimeStageId?: string;\n /** The fold row this beat projects, verbatim — every fact, un-summarized. */\n readonly hop: SkillHop;\n /** Where the cursor stands AFTER this beat. Carried forward across\n * iterations that recorded no `cursorMove`, so a beat always knows the\n * skill in play; `undefined` only before the first one. */\n readonly cursorSkillId?: string;\n readonly cause?: SkillCursorCause;\n readonly moved: boolean;\n /** The skill the MODEL picked on this beat (`by: 'model-pick'`). */\n readonly modelPickedId?: string;\n /** Skills the gate refused on this beat, in the order they were asked for. */\n readonly refusedIds: readonly string[];\n /** Every skill the cursor has stood in up to AND INCLUDING this beat. */\n readonly visited: readonly string[];\n /** A typed reachable set, when one is on the record. See the header. */\n readonly reachable?: SkillReachableSet;\n /** The stop's name, as a strip spells it (\"Iteration 3\"). */\n readonly label: string;\n /** The library's sentence for this hop — `humanizeCursorMove`. */\n readonly headline: string;\n /** The library's sentences for what ELSE the beat carried: the turn-start\n * verdict, refusals, route conflicts, superseded picks. In that order. */\n readonly notes: readonly string[];\n}\n\nexport interface SelectSkillBeatsArgs {\n readonly route: SkillRoute;\n}\n\n// ─── The projection ──────────────────────────────────────────────────────\n\n/**\n * The two cases `humanizeCursorMove` answers `null` to, and what a routing\n * view says instead. Both are formatters over the hop's OWN fields — never a\n * story, and never a sentence a different cause could have produced.\n *\n * NO CAUSE the iteration carried no `cursorMove` at all. The beat still\n * exists (refusals and menus are stamped on such iterations),\n * so it states the ABSENCE — a fact about the record.\n * UNDECORATED a `'model-pick'` without the 9.17.0 menu decorations. The\n * MODEL PICK humanizer stays deliberately silent there because an\n * undecorated pick is low signal in a COMMENTARY stream; in a\n * routing view the pick is the whole point, so the movement\n * clause is rendered — the same clause, minus the menu it does\n * not have.\n */\nfunction fallbackHeadline(hop: SkillHop): string {\n if (hop.by === undefined) {\n return hop.refusals.length > 0\n ? 'This iteration recorded no cursor resolution — only the refusal below.'\n : 'This iteration recorded no cursor resolution.';\n }\n if (hop.by === 'model-pick') {\n if (hop.to === undefined) return 'The model made a pick.';\n const from = hop.from !== undefined && hop.from !== hop.to ? `, moving from \"${hop.from}\"` : '';\n return `The model chose \"${hop.to}\"${from}.`;\n }\n const to = hop.to !== undefined ? ` → \"${hop.to}\"` : '';\n return `The skill in play was settled by \"${hop.by}\"${to}.`;\n}\n\n/** The library's line for a superseded pick. Kept beside the other three\n * humanizers even though `humanizeRouting` ships none: the fields are the\n * whole sentence, so there is nothing to interpret. */\nfunction supersededLine(s: {\n readonly volunteeredId: string;\n readonly wonId?: string;\n readonly fromSkillId?: string;\n}): string {\n const from = s.fromSkillId !== undefined ? ` from \"${s.fromSkillId}\"` : '';\n return s.wonId !== undefined\n ? `The model volunteered \"${s.volunteeredId}\", but the author's edge${from} to \"${s.wonId}\" outranked it.`\n : `The model volunteered \"${s.volunteeredId}\", but an author-declared edge${from} outranked it.`;\n}\n\n/** Turn-start verdicts, indexed by the turn they opened. */\nfunction turnsByIndex(turns: readonly SkillTurnStart[]): Map<number, SkillTurnStart[]> {\n const byTurn = new Map<number, SkillTurnStart[]>();\n for (const t of turns) {\n const rows = byTurn.get(t.turnIndex);\n if (rows === undefined) byTurn.set(t.turnIndex, [t]);\n else rows.push(t);\n }\n return byTurn;\n}\n\n/**\n * Project the routing record onto the time axis: one beat per hop, in run\n * order, each carrying what accumulated up to it.\n *\n * Never throws, and never invents: a route with no hops projects to no beats\n * (which is what a run with no skill graph must look like).\n *\n * @example\n * ```ts\n * const route = selectSkillRoute({ log: recorder.selectEventLog() });\n * const beats = selectSkillBeats({ route });\n * const here = selectSkillBeatAt(beats, cursorRuntimeStageId);\n * here?.headline; // 'Moved from \"support\" to \"billing\" — …'\n * here?.visited; // ['support', 'billing']\n * ```\n */\nexport function selectSkillBeats({ route }: SelectSkillBeatsArgs): readonly SkillBeat[] {\n const byTurn = turnsByIndex(route.turns);\n const seenTurn = new Set<number>();\n const visited: string[] = [];\n const seenSkill = new Set<string>();\n // Declared targets per source skill — the `'declared-edges'` lower bound.\n const declaredFrom = new Map<string, string[]>();\n for (const e of route.declaredEdges) {\n const rows = declaredFrom.get(e.from);\n if (rows === undefined) declaredFrom.set(e.from, [e.to]);\n else if (!rows.includes(e.to)) rows.push(e.to);\n }\n\n let cursor: string | undefined;\n\n return route.hops.map((hop, index) => {\n // `from` first: on a cold start there is none, and on every later hop it\n // is where the cursor already stood — so the visited list stays in the\n // order the run walked it.\n for (const id of [hop.from, hop.to]) {\n if (id === undefined || seenSkill.has(id)) continue;\n seenSkill.add(id);\n visited.push(id);\n }\n if (hop.to !== undefined) cursor = hop.to;\n else if (hop.from !== undefined) cursor = hop.from;\n\n const notes: string[] = [];\n // The turn-start verdict opens the first beat of its turn.\n if (!seenTurn.has(hop.turnIndex)) {\n seenTurn.add(hop.turnIndex);\n for (const t of byTurn.get(hop.turnIndex) ?? []) notes.push(humanizeTurnRouted(t));\n }\n for (const r of hop.refusals) {\n notes.push(\n humanizeSkillRejected({\n requestedId: r.requestedId,\n ...(r.currentSkillId !== undefined ? { currentSkillId: r.currentSkillId } : {}),\n allowed: r.allowed,\n ...(r.posture !== undefined ? { posture: r.posture } : {}),\n }),\n );\n }\n for (const c of hop.conflicts) notes.push(humanizeRouteConflict(c));\n for (const s of hop.superseded) notes.push(supersededLine(s));\n\n // No cause ⇒ no move to narrate. `humanizeCursorMove` is not asked to\n // invent one (an empty `by` would fall through to its unknown-era arm and\n // print a sentence about a cause that does not exist).\n const headline =\n (hop.by === undefined\n ? null\n : humanizeCursorMove({\n ...(hop.from !== undefined ? { from: hop.from } : {}),\n ...(hop.to !== undefined ? { to: hop.to } : {}),\n by: hop.by,\n ...(hop.offered !== undefined ? { offered: hop.offered } : {}),\n ...(hop.declinedOffer !== undefined ? { declinedOffer: hop.declinedOffer } : {}),\n ...(hop.witness !== undefined ? { witness: hop.witness } : {}),\n })) ?? fallbackHeadline(hop);\n\n // Reachability: the move's own typed set first (`cursorMove.reachable`,\n // exact and present on every 9.50.0+ move — `[]` KEPT, a dead end is a\n // fact); else the gate's list when a refusal put one on the record; else\n // the declared edges leaving the skill the cursor stood in BEFORE the\n // hop (which is the position the reachability question was asked from).\n const askedFrom = hop.from ?? hop.to;\n const declared = askedFrom !== undefined ? declaredFrom.get(askedFrom) : undefined;\n const allowed = hop.refusals.find((r) => r.allowed.length > 0)?.allowed;\n const reachable: SkillReachableSet | undefined =\n hop.reachable !== undefined\n ? { ids: hop.reachable, source: 'cursor-move' }\n : allowed !== undefined\n ? { ids: allowed, source: 'refusal' }\n : declared !== undefined && declared.length > 0\n ? { ids: declared, source: 'declared-edges' }\n : undefined;\n\n return {\n index,\n turnIndex: hop.turnIndex,\n iteration: hop.iteration,\n ...(hop.runtimeStageId !== undefined ? { runtimeStageId: hop.runtimeStageId } : {}),\n hop,\n ...(cursor !== undefined ? { cursorSkillId: cursor } : {}),\n ...(hop.by !== undefined ? { cause: hop.by } : {}),\n moved: hop.moved,\n ...(hop.by === 'model-pick' && hop.to !== undefined ? { modelPickedId: hop.to } : {}),\n refusedIds: hop.refusals.map((r) => r.requestedId),\n visited: [...visited],\n ...(reachable !== undefined ? { reachable } : {}),\n label: `Iteration ${hop.iteration}`,\n headline,\n notes,\n } satisfies SkillBeat;\n });\n}\n\n// ─── The cursor resolver ─────────────────────────────────────────────────\n\n/** Parse the `#N` executionIndex suffix off a runtimeStageId. */\nfunction execIndex(runtimeStageId: string): number | undefined {\n const hash = runtimeStageId.lastIndexOf('#');\n if (hash < 0) return undefined;\n const n = Number(runtimeStageId.slice(hash + 1));\n return Number.isInteger(n) && n >= 0 ? n : undefined;\n}\n\n/**\n * Resolve the ONE cursor to the beat in effect there — the same rule\n * `selectToolChoiceCall` resolves a tool-choice call with, for the same\n * reason: the skill view SCRUBS the lens's cursor, it does not keep its own.\n *\n * 1. cursor AT an evaluate stage (exact `runtimeStageId`) → that beat;\n * 2. cursor INSIDE the evaluate stage's subflow (the cursor is the subflow\n * root, a beat ran under it) → the FIRST such beat after the cursor;\n * 3. otherwise → the nearest-PREVIOUS beat (largest executionIndex ≤ the\n * cursor's) — \"the routing state you are standing in\".\n *\n * Bookends: `__root__` at a run-start position is \"nothing has routed yet\"\n * (`undefined`); at a run-end position it is the whole run (the last beat).\n * An empty cursor (a live run before its first commit) also resolves to the\n * last beat, so a live view follows the run.\n *\n * Monotone with the cursor, and never throws.\n */\nexport function selectSkillBeatAt(\n beats: readonly SkillBeat[],\n cursorRuntimeStageId: string,\n cursorKind?: string,\n): SkillBeat | undefined {\n if (beats.length === 0) return undefined;\n const last = beats[beats.length - 1];\n\n if (cursorKind === 'user-in') return undefined;\n if (cursorKind === 'user-out') return last;\n if (!cursorRuntimeStageId) return last; // live edge, no positions yet\n\n const base = cursorRuntimeStageId.split('#')[0] ?? '';\n if (base === '__root__') return cursorKind === 'group-start' ? undefined : last;\n\n const exact = beats.find((b) => b.runtimeStageId === cursorRuntimeStageId);\n if (exact !== undefined) return exact;\n\n const cursorIdx = execIndex(cursorRuntimeStageId);\n if (cursorIdx === undefined) return undefined; // unparsable synthetic id\n\n const prefix = `${base}/`;\n let within: SkillBeat | undefined;\n let withinIdx = Infinity;\n let prev: SkillBeat | undefined;\n let prevIdx = -1;\n for (const b of beats) {\n const id = b.runtimeStageId;\n if (id === undefined) continue;\n const idx = execIndex(id);\n if (idx === undefined) continue;\n if (id.startsWith(prefix) && idx > cursorIdx && idx < withinIdx) {\n within = b;\n withinIdx = idx;\n }\n if (idx <= cursorIdx && idx > prevIdx) {\n prev = b;\n prevIdx = idx;\n }\n }\n return within ?? prev;\n}\n","/**\n * `selectSkillTopology` — the skill graph as it stands AT one beat: which\n * nodes the run touched, which edges are the author's and which the run was\n * observed to take, and what state each node is in right now.\n *\n * Pattern: pure function over `selectSkillRoute`'s record + one {@link SkillBeat}.\n * Space, where `selectSkillBeats` is time. No framework, no state.\n * Role: the headless half of the debugger's topology canvas. The canvas\n * places these nodes and paints these states; it decides nothing.\n *\n * DECLARED vs OBSERVED — the distinction the whole canvas rests on:\n *\n * DECLARED an edge the AUTHOR drew. On the record it comes from one of\n * two places, and they differ in COMPLETENESS:\n * `skill.graph_declared` (9.50.0) is the author's WHOLE map,\n * verbatim from the built graph — complete; `routing[]`\n * provenance names an edge only once it FIRES — a LOWER BOUND.\n * A consumer holding the built graph can still pass it in\n * through {@link SelectSkillTopologyArgs.declaredEdges}; without\n * either complete source, the view must say the topology is\n * partial rather than imply the author drew only what fired.\n * {@link SkillTopology.declaredSource} (and its one-boolean\n * digest {@link SkillTopology.declaredComplete}) is that flag,\n * so no caller has to remember the rule.\n * OBSERVED a hop the CURSOR was seen to take, with the cause that moved it.\n * An observed edge with no declared twin is the interesting case:\n * the model routed somewhere the recording never saw declared.\n *\n * They are one edge row with two booleans rather than two lists, because a\n * view that draws them as two lists draws the common case — an author's edge\n * that fired — twice.\n */\n\nimport type { SkillBeat } from './selectSkillBeats.js';\nimport type { SkillCursorCause, SkillHopRef, SkillRoute } from './selectSkillRoute.js';\n\n/**\n * What a node is, at the beat being shown. One value, in precedence order:\n *\n * `'current'` the cursor stands here.\n * `'refused'` the model asked to go here on this beat and the gate said no.\n * `'reachable'` a TYPED reachable set named it (see `SkillReachableSet`).\n * `'visited'` the cursor stood here earlier in the run.\n * `'idle'` the catalog listed it; the run has not been there.\n *\n * `'model-picked'` is deliberately NOT in this list: a picked node is also the\n * current one, and a state machine that has to choose between the two loses a\n * fact. It rides {@link SkillTopologyNode.pickedByModel} instead.\n */\nexport type SkillNodeState = 'current' | 'refused' | 'reachable' | 'visited' | 'idle';\n\nexport interface SkillTopologyNode {\n readonly id: string;\n /** The catalog description, verbatim — the text the model read. */\n readonly description?: string;\n /** The drawn kind (`'skill'` / `'predicate'`), when the declared map named it. */\n readonly kind?: string;\n /** The drawn caption, when the declared map named one. */\n readonly label?: string;\n /** A synthetic-START edge points here — a turn CAN begin at this skill, as\n * the author drew it. Only ever `true` when the recording carried the\n * declared map; never guessed from an observed cold start. */\n readonly isEntry: boolean;\n readonly state: SkillNodeState;\n /** The cursor moved here on this beat because the MODEL picked it. */\n readonly pickedByModel: boolean;\n /** The gate refused a move here on this beat. */\n readonly refusedHere: boolean;\n /** The cursor stood here at some point in the WHOLE run (not just so far) —\n * `route.nodes[].visited`, kept so a view can dim never-entered skills. */\n readonly visitedInRun: boolean;\n}\n\nexport interface SkillTopologyEdge {\n /** `from->to` — stable across beats, so a canvas can key on it. */\n readonly id: string;\n readonly from: string;\n readonly to: string;\n /** The author drew this edge (as far as the record shows — see the header). */\n readonly declared: boolean;\n /** The cursor was observed to take it. */\n readonly observed: boolean;\n readonly label?: string;\n readonly triggerKind?: string;\n /** What moved the cursor along it, when it was observed. */\n readonly by?: SkillCursorCause;\n /** Every (turn, iteration) it was taken on. Empty for a never-taken edge. */\n readonly takenAt: readonly SkillHopRef[];\n /** THIS beat is the hop along this edge. */\n readonly active: boolean;\n}\n\nexport interface SkillTopology {\n readonly nodes: readonly SkillTopologyNode[];\n readonly edges: readonly SkillTopologyEdge[];\n /**\n * Where the declared edges came from — and, with it, whether they are the\n * WHOLE map or only what fired:\n * `'recording-declared'` the run's own `skill.graph_declared` event\n * (9.50.0) — the author's complete declaration,\n * carried by the recording itself.\n * `'recording'` the log's `routing[]` only — a LOWER BOUND (an\n * edge is named once it fires), and a view must\n * say so.\n * `'graph'` the caller passed the built graph's edges —\n * complete.\n * `'none'` the recording named no declared edge and none\n * was passed.\n */\n readonly declaredSource: 'recording-declared' | 'recording' | 'graph' | 'none';\n /**\n * The one-boolean digest of `declaredSource` a caveat line branches on:\n * `true` for `'recording-declared'` and `'graph'` (the whole map is drawn —\n * no \"the author may have drawn more\" warning), `false` for `'recording'`\n * and `'none'` (a lower bound — the warning stays).\n */\n readonly declaredComplete: boolean;\n /** Entry skills — the declared map's synthetic-START targets. Empty when\n * the recording carried no declared map. */\n readonly entryIds: readonly string[];\n}\n\n/** A declared edge as a caller supplies it (`graph.edges`, `from !== null`). */\nexport interface DeclaredEdgeInput {\n readonly from: string;\n readonly to: string;\n readonly label?: string;\n readonly triggerKind?: string;\n}\n\nexport interface SelectSkillTopologyArgs {\n readonly route: SkillRoute;\n /** The beat being shown. `undefined` = before the run's first routing stop:\n * every node is `'idle'` and no edge is active, which is the truth then. */\n readonly beat?: SkillBeat;\n /**\n * The author's edges, from the BUILT graph — what a recording cannot carry.\n * A consumer holding `skillGraph().build()` passes\n * `graph.edges.filter((e) => e.from !== null)`.\n */\n readonly declaredEdges?: readonly DeclaredEdgeInput[];\n}\n\n/**\n * Fold the record + one beat into the drawable graph.\n *\n * @example\n * ```ts\n * const topo = selectSkillTopology({ route, beat });\n * topo.nodes.find((n) => n.state === 'current')?.id; // 'billing'\n * topo.edges.filter((e) => e.observed && !e.declared); // the model's own hops\n * topo.declaredSource; // 'recording' → partial\n * ```\n */\nexport function selectSkillTopology({\n route,\n beat,\n declaredEdges,\n}: SelectSkillTopologyArgs): SkillTopology {\n const visitedSoFar = new Set(beat?.visited ?? []);\n const reachable = new Set(beat?.reachable?.ids ?? []);\n const refused = new Set(beat?.refusedIds ?? []);\n const current = beat?.cursorSkillId;\n\n const edges = new Map<string, {\n from: string;\n to: string;\n declared: boolean;\n observed: boolean;\n label?: string;\n triggerKind?: string;\n by?: SkillCursorCause;\n takenAt: readonly SkillHopRef[];\n }>();\n\n const put = (from: string, to: string): NonNullable<ReturnType<typeof edges.get>> => {\n const id = `${from}->${to}`;\n let row = edges.get(id);\n if (row === undefined) {\n row = { from, to, declared: false, observed: false, takenAt: [] };\n edges.set(id, row);\n }\n return row;\n };\n\n // Declared first, so an author's caption survives the observed merge.\n const declaredRows = declaredEdges ?? route.declaredEdges;\n for (const e of declaredRows) {\n const row = put(e.from, e.to);\n row.declared = true;\n if (e.label !== undefined) row.label = e.label;\n if (e.triggerKind !== undefined) row.triggerKind = e.triggerKind;\n }\n // Anything the RECORDING declared is true regardless of what was passed in:\n // a caller's list can be stale, but an edge that fired demonstrably exists.\n if (declaredEdges !== undefined) {\n for (const e of route.declaredEdges) {\n const row = put(e.from, e.to);\n row.declared = true;\n if (row.label === undefined && e.label !== undefined) row.label = e.label;\n if (row.triggerKind === undefined && e.triggerKind !== undefined) {\n row.triggerKind = e.triggerKind;\n }\n }\n }\n\n for (const e of route.observedEdges) {\n // A cold start has no `from` — it is an entry, not an edge between nodes.\n if (e.from === undefined) continue;\n const row = put(e.from, e.to);\n row.observed = true;\n row.by = e.by;\n row.takenAt = e.takenAt;\n if (row.label === undefined && e.label !== undefined) row.label = e.label;\n if (row.triggerKind === undefined && e.triggerKind !== undefined) {\n row.triggerKind = e.triggerKind;\n }\n }\n\n const activeId =\n beat !== undefined && beat.moved && beat.hop.from !== undefined && beat.hop.to !== undefined\n ? `${beat.hop.from}->${beat.hop.to}`\n : undefined;\n\n // Every endpoint is a node, even one the catalog never listed — the run\n // walked it, so hiding it would hide a hop.\n const entryIds = route.entryIds ?? [];\n const entrySet = new Set(entryIds);\n const nodes = new Map<string, SkillTopologyNode>();\n const state = (id: string): SkillNodeState => {\n if (id === current) return 'current';\n if (refused.has(id)) return 'refused';\n if (reachable.has(id)) return 'reachable';\n if (visitedSoFar.has(id)) return 'visited';\n return 'idle';\n };\n const addNode = (\n id: string,\n detail?: { description?: string; kind?: string; label?: string },\n visitedInRun?: boolean,\n ): void => {\n const existing = nodes.get(id);\n if (existing !== undefined) {\n if (existing.description === undefined && detail?.description !== undefined) {\n nodes.set(id, { ...existing, description: detail.description });\n }\n return;\n }\n nodes.set(id, {\n id,\n ...(detail?.description !== undefined ? { description: detail.description } : {}),\n ...(detail?.kind !== undefined ? { kind: detail.kind } : {}),\n ...(detail?.label !== undefined ? { label: detail.label } : {}),\n isEntry: entrySet.has(id),\n state: state(id),\n pickedByModel: beat?.modelPickedId === id,\n refusedHere: refused.has(id),\n visitedInRun: visitedInRun ?? visitedSoFar.has(id),\n });\n };\n\n for (const n of route.nodes) {\n addNode(\n n.id,\n {\n ...(n.description !== undefined ? { description: n.description } : {}),\n ...(n.kind !== undefined ? { kind: n.kind } : {}),\n ...(n.label !== undefined ? { label: n.label } : {}),\n },\n n.visited,\n );\n }\n for (const row of edges.values()) {\n addNode(row.from);\n addNode(row.to);\n }\n for (const id of refused) addNode(id);\n\n const declaredSource: SkillTopology['declaredSource'] =\n route.declaredComplete === true\n ? 'recording-declared'\n : declaredEdges !== undefined && declaredEdges.length > 0\n ? 'graph'\n : route.declaredEdges.length > 0\n ? 'recording'\n : 'none';\n\n return {\n nodes: [...nodes.values()],\n edges: [...edges.entries()].map(([id, row]) => ({\n id,\n from: row.from,\n to: row.to,\n declared: row.declared,\n observed: row.observed,\n ...(row.label !== undefined ? { label: row.label } : {}),\n ...(row.triggerKind !== undefined ? { triggerKind: row.triggerKind } : {}),\n ...(row.by !== undefined ? { by: row.by } : {}),\n takenAt: row.takenAt,\n active: id === activeId,\n })),\n // The run's own declared map outranks a passed-in graph for the LABEL —\n // both are complete, but the recording's copy is from the run that\n // actually executed, so it is the truer provenance to state.\n declaredSource,\n declaredComplete: declaredSource === 'recording-declared' || declaredSource === 'graph',\n entryIds,\n };\n}\n","/**\n * `selectSkillFrameContext` — what reached the model on ONE routing beat.\n *\n * Pattern: pure function over the StepGraph + a {@link SkillBeat}. No\n * framework, no state.\n * Role: the developer lens's right panel asks a question the routing fold\n * alone cannot answer. `SkillHop.skillInjections` carries what the\n * SKILLS put in the prompt; the prompt also carried RAG passages,\n * memory, instructions — everything\n * {@link selectContextEngineeringInjections} calls engineered. Those\n * live on the StepGraph's LLM-call step, not on the routing events.\n *\n * THE PAIRING (why it is index arithmetic and not a join):\n * a beat is stamped at the Evaluate stage that resolved the cursor; the call\n * it prepared is the NEXT LLM step. footprintjs's `executionIndex` is globally\n * monotonic within a run, so \"the first LLM step after this beat's evaluate,\n * and before the next beat's\" identifies it exactly, with no id convention to\n * break. A beat with no LLM step after it (the run ended at the evaluate)\n * pairs with nothing, and says so — `stepRuntimeStageId` absent, `injections`\n * empty, {@link SkillFrameContext.paired} `false`. Absent is not empty here:\n * `paired: false` means \"no call was recorded for this beat\", while `paired:\n * true` with no engineered injections means \"the call carried none\".\n */\n\nimport type { ContextInjection, StepGraph, StepNode } from 'agentfootprint/observe';\nimport { selectContextEngineeringInjections } from './selectContextEngineeringInjections.js';\nimport type { SkillBeat } from './selectSkillBeats.js';\n\n/** What the model was handed on one beat, as the recording carried it. */\nexport interface SkillFrameContext {\n /** An LLM call was found for this beat. `false` ⇒ nothing to report, not\n * \"the call was empty\". */\n readonly paired: boolean;\n /** The paired call's address, so a view can jump the ONE cursor to it. */\n readonly stepRuntimeStageId?: string;\n /** The ENGINEERED injections on that call — baseline user / tool-result /\n * assistant / base prompt / static tool registry filtered out. */\n readonly injections: readonly ContextInjection[];\n /** How many injections the call carried in total, engineered or not. Lets a\n * panel say \"4 of 11 were engineered\" instead of implying 4 was all. */\n readonly totalInjections: number;\n}\n\nexport interface SelectSkillFrameContextArgs {\n /** The recording's step graph (`recorder.getStepGraph()`). */\n readonly graph: Pick<StepGraph, 'nodes'>;\n /** The beat being shown. */\n readonly beat: SkillBeat;\n /** The beat AFTER it, when there is one — the upper bound of the window. */\n readonly nextBeat?: SkillBeat;\n}\n\n/** Parse the `#N` executionIndex suffix off a runtimeStageId. */\nfunction execIndex(runtimeStageId: string | undefined): number | undefined {\n if (runtimeStageId === undefined) return undefined;\n const hash = runtimeStageId.lastIndexOf('#');\n if (hash < 0) return undefined;\n const n = Number(runtimeStageId.slice(hash + 1));\n return Number.isInteger(n) && n >= 0 ? n : undefined;\n}\n\n/** An LLM-facing step — the kinds whose node carries the call's injections. */\nfunction isLLMStep(node: StepNode): boolean {\n return node.kind === 'user->llm' || node.kind === 'tool->llm';\n}\n\n/**\n * Resolve the beat to the call it prepared, and report that call's engineered\n * context. Never throws; a graph with no steps pairs with nothing.\n *\n * @example\n * ```ts\n * const ctx = selectSkillFrameContext({ graph: recorder.getStepGraph(), beat, nextBeat });\n * ctx.paired; // true\n * ctx.injections.map((i) => i.source); // ['skill', 'rag']\n * `${ctx.injections.length} of ${ctx.totalInjections} engineered`;\n * ```\n */\nexport function selectSkillFrameContext({\n graph,\n beat,\n nextBeat,\n}: SelectSkillFrameContextArgs): SkillFrameContext {\n const from = execIndex(beat.runtimeStageId);\n const to = execIndex(nextBeat?.runtimeStageId);\n\n let best: StepNode | undefined;\n let bestIdx = Infinity;\n for (const node of graph.nodes ?? []) {\n if (!isLLMStep(node)) continue;\n const idx = execIndex(node.runtimeStageId);\n if (idx === undefined) continue;\n if (from !== undefined && idx <= from) continue;\n if (to !== undefined && idx >= to) continue;\n if (idx < bestIdx) {\n best = node;\n bestIdx = idx;\n }\n }\n\n if (best === undefined) return { paired: false, injections: [], totalInjections: 0 };\n const all = best.injections ?? [];\n return {\n paired: true,\n ...(best.runtimeStageId !== undefined ? { stepRuntimeStageId: best.runtimeStageId } : {}),\n injections: selectContextEngineeringInjections(all),\n totalInjections: all.length,\n };\n}\n","/**\n * Group — a subflow execution. The conceptual container the Lens chart\n * highlights at any cursor position.\n *\n * Layer 1 / Lens v0.1.\n *\n * Naming convention (locked — see\n * `memory/lens_v0_1_one_cursor_architecture.md`):\n *\n * - `runtimeStageId` = the leaf (one stage execution that wrote one\n * commit). THE cursor of the slider.\n * - `runtimeGroupId` = the group (a subflow execution). Sibling key\n * used for chart-box highlight and breadcrumb.\n *\n * Both are runtimeStageIds in form (string `[subflowPath/]stageId#executionIndex`).\n * `runtimeGroupId` is derived from `runtimeStageId` via the boundary index\n * — same address space, two views of the same subflow stack.\n *\n * A Group is a developer-friendly alias for what footprintjs internally\n * exposes as a `BoundaryRangeLabel`. Same data, easier to talk about.\n *\n * Why the alias\n * ─────────────\n * `BoundaryRangeLabel` is the wire type from agentfootprint. `Group` is\n * the Lens-facing API name developers see — short, domain-neutral.\n * Reducing the surface to the fields Lens actually needs makes\n * testing trivial (we don't need to invent `ts` / `slotKind` etc.\n * in synthetic test inputs).\n */\n\nexport interface Group {\n /** Identity = the subflow root's runtimeStageId. Same format and\n * address space as a commit's runtimeStageId — both are\n * `[subflowPath/]stageId#executionIndex`. */\n readonly runtimeGroupId: string;\n\n /** Display name (the subflow's `subflowName` or the synthetic run-root label). */\n readonly name: string;\n\n /** Parent group's runtimeGroupId, if nested. `undefined` for the\n * top-level Run group (no parent). */\n readonly parentGroupId: string | undefined;\n\n /** Subflow path the engine assigned (e.g. `['__root__', 'sf-Committee', 'sf-ethics']`). */\n readonly subflowPath: readonly string[];\n\n /** 0 = top-level run, 1 = first subflow level, etc. */\n readonly depth: number;\n\n /** Commit index at which this group opened. Use with the `commitLog`\n * array to find the first commit in the group. */\n readonly opensAtCommitIdx: number;\n\n /** Commit index at which this group closed. `undefined` while the\n * group is still in-flight (subflow hasn't exited yet — useful for\n * live-mode rendering). */\n readonly closesAtCommitIdx: number | undefined;\n\n /** True when the group represents the synthetic Run root (`run.entry`),\n * false for real subflow boundaries. Lens uses this to skip the root\n * in breadcrumbs (since the root is implicit). */\n readonly isRoot: boolean;\n\n /** Composition primitive — set when the boundary range came from a\n * `composition.start` event (Parallel / Sequence / Loop / Conditional).\n * Undefined for plain subflows. v0.1 special-cases `'Parallel'` for\n * inline-pills rendering + start/end-as-positions. */\n readonly compositionKind?: 'Parallel' | 'Sequence' | 'Loop' | 'Conditional';\n\n /** Slot kind — set on Agent/LLMCall internal context-engineering\n * subflows (sf-system-prompt / sf-messages / sf-tools). Slider\n * filters these out at top level; they become visible when the\n * containing Agent or LLMCall is drilled into. */\n readonly slotKind?: string;\n\n /** Primitive kind from the subflow root description ('Agent' / 'LLMCall'\n * / 'Sequence' / etc.). Used to identify drillable leaves (Agent,\n * LLMCall) vs. compositions. */\n readonly primitiveKind?: string;\n}\n\n/** Check whether `commitIdx` falls inside this group's open/close range. */\nexport function groupContainsCommit(group: Group, commitIdx: number): boolean {\n if (commitIdx < group.opensAtCommitIdx) return false;\n if (group.closesAtCommitIdx === undefined) return true; // open-ended\n return commitIdx <= group.closesAtCommitIdx;\n}\n","/**\n * activeChartGroup — which chart nodes belong to the group the cursor is in.\n *\n * Pure function. Layer 1 / Tier B / Lens v0.1.\n *\n * ── Why this exists ─────────────────────────────────────────────────────────\n * On the GROUPED ruler (🔍 Why Lens) one slider stop is one boundary, so the\n * cursor's position IS a group — not a stage. The chart has to say that: the\n * group's members light as ONE unit and everything else recedes. To draw that,\n * a renderer needs the members as CHART NODE IDS, and nothing upstream hands it\n * that list.\n *\n * It is derivable from the recording alone — no new fetch, no new recorder:\n *\n * the boundary index → the group's commit range (`buildGroups`)\n * the commit log → the runtimeStageId per commit in that range\n * `chartNodeIdOf` → the chart id for each (strip `#executionIndex`)\n *\n * The last step is the rule every other id-matching site in the ecosystem uses\n * (Lens's `coActiveStageIds`, the be-server's chart-click jump): a chart node id\n * is a runtimeStageId minus its `#executionIndex`. The subflow path stays — it\n * is part of the id.\n *\n * ── What the group's box contains ───────────────────────────────────────────\n * Every commit inside `[opensAtCommitIdx … closesAtCommitIdx]`, PLUS the group's\n * own node (its mount). Nested child groups fall inside that range, so their\n * commits are members too — a place contains its rooms. At a drill level where\n * a subflow renders as ONE card, the mount is the only member on screen, and the\n * boundary is drawn around that card; that is why the mount is included rather\n * than assumed present.\n *\n * A member id that is not on the chart right now (a subflow's internals while\n * the chart shows the collapsed card) simply has no node to light. The renderer\n * skips it; nothing is invented.\n */\n\nimport type { Group } from './Group.js';\nimport { groupContainsCommit } from './Group.js';\n\n/**\n * The chart id for a runtimeStageId: everything before `#executionIndex`.\n *\n * `sf-committee/legal/call-llm#4` → `sf-committee/legal/call-llm`. The subflow\n * path is part of the chart id (that is how `structureGraphFromRunner` names\n * the nodes it materialises for a subflow's internals), so only the execution\n * index is dropped. Last-`#` based, like every runtimeStageId parser in the\n * engine.\n */\nexport function chartNodeIdOf(runtimeStageId: string): string {\n const hash = runtimeStageId.lastIndexOf('#');\n return hash < 0 ? runtimeStageId : runtimeStageId.slice(0, hash);\n}\n\n/** The group at the cursor, resolved to chart ids — what a grouped-ruler chart\n * needs to light one group as a unit. */\nexport interface ChartGroupHighlight {\n /** The group's identity (its subflow root's runtimeStageId). */\n readonly runtimeGroupId: string;\n /** The group's name — `groupDisplayName`, the same spelling the WHAT\n * HAPPENED boundary rail uses. Goes on the boundary's chip. */\n readonly name: string;\n /** Chart node ids belonging to this group. Never empty: the group's own\n * mount is always a member. */\n readonly memberNodeIds: ReadonlySet<string>;\n /** The commit range the membership was read from (inclusive). `closesAt` is\n * undefined while the group is still in flight. */\n readonly opensAtCommitIdx: number;\n readonly closesAtCommitIdx: number | undefined;\n /** Depth in the boundary tree — 0 is the run root. */\n readonly depth: number;\n}\n\n/** One commit as this function reads it — the only field it needs. Structural,\n * so a `CommitBundle`, a `CommitSyncEntry`, or the be-server's `CommitStop`\n * all fit without a cast. */\nexport interface CommitWithStage {\n readonly runtimeStageId?: string | undefined;\n}\n\nexport interface ActiveChartGroupArgs {\n /** Every group in the run (from `buildGroups`). */\n readonly groups: readonly Group[];\n /** The run's commit log, in commit order (index === commitIdx). */\n readonly commits: readonly CommitWithStage[];\n /** THE cursor, as a commit index. */\n readonly commitIdx: number;\n /**\n * Let the synthetic Run root be the active group. Default `false`: a boundary\n * drawn around the WHOLE chart states nothing, so at a commit no subflow\n * encloses, the honest answer is \"no group here\" and the chart renders\n * unchanged.\n */\n readonly includeRoot?: boolean;\n}\n\n/**\n * The innermost group enclosing `commitIdx`, with its members resolved to chart\n * node ids. `undefined` when no group encloses the cursor (or only the run root\n * does and `includeRoot` is off) — the caller renders the chart as it always did.\n */\nexport function activeChartGroup(args: ActiveChartGroupArgs): ChartGroupHighlight | undefined {\n const { groups, commits, commitIdx, includeRoot = false } = args;\n if (!Number.isFinite(commitIdx) || commitIdx < 0) return undefined;\n\n // Innermost enclosing = deepest; ties (a composition and its first member\n // opening on the same commit) break to the one that opened LAST, which is the\n // inner one. Same order `CommitRangeIndex.enclosing()` returns outer→inner.\n let best: Group | undefined;\n for (const group of groups) {\n if (group.isRoot && !includeRoot) continue;\n if (!groupContainsCommit(group, commitIdx)) continue;\n if (best === undefined) {\n best = group;\n continue;\n }\n if (group.depth > best.depth) best = group;\n else if (group.depth === best.depth && group.opensAtCommitIdx >= best.opensAtCommitIdx) best = group;\n }\n if (best === undefined) return undefined;\n\n const memberNodeIds = new Set<string>();\n // The group's own mount is always a member — at a collapsed drill level it is\n // the only node on screen that IS the group.\n memberNodeIds.add(chartNodeIdOf(best.runtimeGroupId));\n\n const from = Math.max(0, best.opensAtCommitIdx);\n const to = Math.min(commits.length - 1, best.closesAtCommitIdx ?? commits.length - 1);\n for (let i = from; i <= to; i++) {\n const rid = commits[i]?.runtimeStageId;\n if (rid === undefined || rid === '') continue;\n memberNodeIds.add(chartNodeIdOf(rid));\n }\n\n return {\n runtimeGroupId: best.runtimeGroupId,\n name: best.name,\n memberNodeIds,\n opensAtCommitIdx: best.opensAtCommitIdx,\n closesAtCommitIdx: best.closesAtCommitIdx,\n depth: best.depth,\n };\n}\n","/**\n * stepBands — the grouped ruler's BANDS, as a pure projection of the step axis.\n *\n * ── The one-cursor law, restated for this file ──────────────────────────────\n * The only cursor in the system is the step on the lens's scrub axis (whose\n * address is a runtimeStageId — see `cursorPositionsAtDrill`). A band is NOT a\n * second axis and NOT a stored position: it is a contiguous RANGE of steps,\n * derived from the positions array alone. Which band is \"active\" is derived\n * from which range contains the cursor; clicking a band moves the ONE cursor\n * to the band's first step. Nothing here holds state, and nothing downstream\n * may store a band index — a stored band index would be a beat counter, the\n * exact thing the locked v0.1 architecture bans.\n *\n * ── What a band IS ──────────────────────────────────────────────────────────\n * The grouped reading of an agent run is its LOOP PASSES. The step axis\n * already knows where they begin: the domain's milestone classifier marks the\n * per-iteration boundary stop as `'iteration'` (`CursorPosition.milestone`),\n * and everything after it — context, LLM turn, routing, tool call — belongs to\n * that pass until the next one begins. So:\n *\n * • a depth-0 `group-start` / `group-end` stop (Run · start / Run · end) is\n * a band of its own — the bookends;\n * • an `'iteration'` milestone OPENS a band, labelled with that stop's own\n * label (\"Iteration 2\" — the spelling the step strip already used);\n * • every other stop extends the band it follows.\n *\n * ── Honesty fallback ────────────────────────────────────────────────────────\n * An axis with NO iteration milestones (a plain Sequence chart, a drilled\n * subflow's internals) has no grouped structure to read — so every step is its\n * own band, and the grouped ruler degenerates to the per-step one. That is a\n * true statement about the chart, not a rendering choice: inventing bands for\n * a run that has none would be fabricating structure.\n *\n * Invariant (pinned in tests): the bands tile the axis exactly — every step is\n * in exactly one band, in order, with no gaps.\n */\n\nimport type { CursorPosition } from './cursorPositionsAtDrill.js';\nimport type { CommitWithStage, ChartGroupHighlight } from './activeChartGroup.js';\nimport { chartNodeIdOf } from './activeChartGroup.js';\n\nexport interface StepBand {\n /** The band's name, as the strip shows it — the opening stop's own label. */\n readonly label: string;\n /** First step (inclusive) of the range this band covers. */\n readonly firstStep: number;\n /** Last step (inclusive). `firstStep === lastStep` for a one-stop band. */\n readonly lastStep: number;\n /** What opened the band — the root bookends against a real group of steps. */\n readonly kind: 'run-start' | 'run-end' | 'group';\n}\n\n/** Is this stop a depth-0 run bookend? */\nfunction isRootBookend(p: CursorPosition): boolean {\n return p.depth === 0 && (p.kind === 'group-start' || p.kind === 'group-end');\n}\n\nexport function stepBands(positions: readonly CursorPosition[]): readonly StepBand[] {\n if (positions.length === 0) return [];\n\n // Honesty fallback: no iteration milestones → no grouped structure → one\n // band per step (the grouped ruler degenerates to the per-step one).\n const hasIterations = positions.some((p) => p.milestone === 'iteration');\n if (!hasIterations) {\n return positions.map((p, i) => ({\n label: p.label,\n firstStep: i,\n lastStep: i,\n kind: bookendKind(p) ?? 'group',\n }));\n }\n\n const bands: StepBand[] = [];\n let open: { label: string; firstStep: number; kind: StepBand['kind'] } | undefined;\n\n const close = (lastStep: number): void => {\n if (open === undefined) return;\n bands.push({ label: open.label, firstStep: open.firstStep, lastStep, kind: open.kind });\n open = undefined;\n };\n\n positions.forEach((p, i) => {\n const bookend = bookendKind(p);\n if (bookend !== undefined) {\n close(i - 1);\n bands.push({ label: p.label, firstStep: i, lastStep: i, kind: bookend });\n return;\n }\n if (p.milestone === 'iteration' || open === undefined) {\n close(i - 1);\n open = { label: p.label, firstStep: i, kind: 'group' };\n return;\n }\n // extends the open band\n });\n close(positions.length - 1);\n\n return bands;\n}\n\nfunction bookendKind(p: CursorPosition): 'run-start' | 'run-end' | undefined {\n if (!isRootBookend(p)) return undefined;\n return p.kind === 'group-start' ? 'run-start' : 'run-end';\n}\n\n/** The band containing `step` — derived, never stored. `-1` when out of range. */\nexport function bandIndexOf(bands: readonly StepBand[], step: number): number {\n return bands.findIndex((b) => step >= b.firstStep && step <= b.lastStep);\n}\n\n/**\n * The band the cursor stands in, resolved to CHART NODE IDS — the grouped\n * ruler's chart highlight, shaped exactly like `activeChartGroup`'s answer so\n * the same boundary renderer draws it.\n *\n * Membership is the band's COMMIT SPAN: from the band's first stop's commit to\n * just before the NEXT band's first commit (the last band runs to the end of\n * the log). That span is what the group of steps actually did — including the\n * commits between the strip's stops — so the whole pass lights as one place.\n *\n * The bookend bands return `undefined`: a boundary drawn around the whole\n * chart states nothing (the same judgement `useChartGroup`'s `includeRoot`\n * default makes).\n */\nexport function bandChartGroup(args: {\n readonly bands: readonly StepBand[];\n readonly bandIndex: number;\n readonly positions: readonly CursorPosition[];\n /** The run's commit log, in commit order (index === commitIdx). */\n readonly commits: readonly CommitWithStage[];\n}): ChartGroupHighlight | undefined {\n const { bands, bandIndex, positions, commits } = args;\n const band = bands[bandIndex];\n if (band === undefined || band.kind !== 'group') return undefined;\n\n const first = positions[band.firstStep];\n if (first === undefined) return undefined;\n const from = Math.max(0, first.commitIdx);\n\n // Up to (not including) the next band's first commit; the last band runs out\n // the log. A next band that is a bookend anchors the end the same way.\n const next = bands[bandIndex + 1];\n const nextFirst = next !== undefined ? positions[next.firstStep] : undefined;\n const to = Math.min(\n commits.length - 1,\n nextFirst !== undefined ? nextFirst.commitIdx - 1 : commits.length - 1,\n );\n\n const memberNodeIds = new Set<string>();\n for (let i = from; i <= to; i++) {\n const rid = commits[i]?.runtimeStageId;\n if (rid === undefined || rid === '') continue;\n memberNodeIds.add(chartNodeIdOf(rid));\n }\n if (memberNodeIds.size === 0) return undefined;\n\n return {\n runtimeGroupId: first.runtimeGroupId,\n name: band.label,\n memberNodeIds,\n opensAtCommitIdx: from,\n closesAtCommitIdx: to,\n depth: 1,\n };\n}\n","/**\n * `stepForRuntimeStageId` — the inverse of the cursor's `describe`: an address\n * back to the position that HOLDS it.\n *\n * The lens's cursor is owned in STEPS (`<Lens step onStepChange>`) and reported\n * in three units, `runtimeStageId` among them. A view that wants to MOVE the\n * cursor, though, only ever knows an address — a slice frame's stage, a\n * routing beat's evaluate stage, a chart node — and the step axis at the\n * current drill level may not contain it. Every such view was re-deriving the\n * same rule; this is it, once.\n *\n * THE RULE (positions are a COARSER axis than addresses, never a wrong one):\n *\n * 1. exact — a position whose `runtimeStageId` is the one asked for. Several\n * positions can share an id (a group's start and end); the FIRST is\n * returned, because a mover means \"take me there\", not \"take me to the\n * end of there\".\n * 2. enclosing — the position whose subflow CONTAINS the address. An\n * address inside a subflow (`sf-x/inner#7`) is held by that subflow's own\n * position (`sf-x#5`): the nearest one whose executionIndex is ≤ the\n * address's. This is what makes \"jump to iteration 3's evaluate stage\"\n * work on an axis whose stops are whole iterations.\n * 3. nearest-previous — the position with the largest executionIndex ≤ the\n * address's. Standing just before something is a true answer; standing\n * after it is not.\n * 4. `-1` — no position is at or before the address (it is before the run's\n * first stop, or the axis is empty). A caller must not move.\n *\n * Deliberately never throws and never guesses forward: a jump that silently\n * lands past its target is worse than one that does not happen.\n */\n\nimport type { CursorPosition } from './cursorPositionsAtDrill.js';\n\n/** Parse the `#N` executionIndex suffix off a runtimeStageId. */\nfunction execIndex(runtimeStageId: string): number | undefined {\n const hash = runtimeStageId.lastIndexOf('#');\n if (hash < 0) return undefined;\n const n = Number(runtimeStageId.slice(hash + 1));\n return Number.isInteger(n) && n >= 0 ? n : undefined;\n}\n\n/**\n * Resolve an address to a step on the given position list.\n *\n * @param positions the cursor axis at the CURRENT drill level\n * (`useCursorPositions(recorder, drillPath)`).\n * @param runtimeStageId the address to move to.\n * @returns the step index, or `-1` when no position holds it.\n *\n * @example\n * ```ts\n * // The axis stops at whole iterations; the beat is a stage inside one.\n * stepForRuntimeStageId(positions, 'sf-injection-engine/evaluate#3'); // → the\n * // step of `sf-injection-engine#1`, the iteration that contains it.\n * ```\n */\nexport function stepForRuntimeStageId(\n positions: readonly CursorPosition[],\n runtimeStageId: string,\n): number {\n if (positions.length === 0 || runtimeStageId === '') return -1;\n\n const exact = positions.findIndex((p) => p.runtimeStageId === runtimeStageId);\n if (exact >= 0) return exact;\n\n const target = execIndex(runtimeStageId);\n if (target === undefined) return -1;\n\n // The enclosing scopes of the address, innermost first: `a/b/c#7` is held by\n // `a/b`, then by `a`.\n const scopes: string[] = [];\n const base = runtimeStageId.split('#')[0] ?? '';\n for (let cut = base.lastIndexOf('/'); cut > 0; cut = base.lastIndexOf('/', cut - 1)) {\n scopes.push(base.slice(0, cut));\n }\n\n for (const scope of scopes) {\n let best = -1;\n let bestIdx = -1;\n for (let i = 0; i < positions.length; i += 1) {\n const id = positions[i]?.runtimeStageId ?? '';\n if ((id.split('#')[0] ?? '') !== scope) continue;\n const idx = execIndex(id);\n if (idx === undefined || idx > target || idx <= bestIdx) continue;\n best = i;\n bestIdx = idx;\n }\n if (best >= 0) return best;\n }\n\n let prev = -1;\n let prevIdx = -1;\n for (let i = 0; i < positions.length; i += 1) {\n const idx = execIndex(positions[i]?.runtimeStageId ?? '');\n // `<=` not `<`: two positions can share an executionIndex (a group's start\n // and its end), and the FIRST of them is the one a mover means.\n if (idx === undefined || idx > target || idx <= prevIdx) continue;\n prev = i;\n prevIdx = idx;\n }\n return prev;\n}\n"],"mappings":"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","names":["where","line","execIndex","execIndex"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBytes,
|
|
3
3
|
structureGraphFromRunner
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-XO5VNJYM.js";
|
|
5
5
|
|
|
6
6
|
// src/core/buildStepGraphFromSnapshot.ts
|
|
7
7
|
function buildStepGraphFromSnapshot(snapshot) {
|
|
@@ -1189,4 +1189,4 @@ export {
|
|
|
1189
1189
|
decisionSentence,
|
|
1190
1190
|
isConsentDecision
|
|
1191
1191
|
};
|
|
1192
|
-
//# sourceMappingURL=chunk-
|
|
1192
|
+
//# sourceMappingURL=chunk-HBVKOHFU.js.map
|