agentfootprint-lens 0.73.0 → 0.75.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/README.md +6 -0
- package/dist/{chunk-GH767WN4.js → chunk-6GTISZYA.js} +3 -3
- package/dist/{chunk-CGZDQEY2.js → chunk-6II3EUOJ.js} +2 -2
- package/dist/{chunk-QHG4HZWH.js → chunk-DZ6B7LD6.js} +5 -5
- package/dist/{chunk-5QKU7VQK.js → chunk-ECAQG3TG.js} +2 -2
- package/dist/{chunk-OYSCNQQ4.js → chunk-FTQQVU7X.js} +6 -6
- package/dist/{chunk-NBBFRC6Z.js → chunk-GCGVODXR.js} +4 -4
- package/dist/{chunk-4XZHSL5T.js → chunk-KCAQVLTA.js} +1 -1
- package/dist/chunk-KCAQVLTA.js.map +1 -0
- package/dist/{chunk-JRPGOX3R.js → chunk-PCOHPEVI.js} +2 -2
- package/dist/{chunk-KBGPQBNO.js → chunk-V4GO5WKN.js} +13 -9
- package/dist/{chunk-KBGPQBNO.js.map → chunk-V4GO5WKN.js.map} +1 -1
- package/dist/{chunk-G5FHO5MN.js → chunk-WMELY5CJ.js} +2 -2
- package/dist/context.cjs.map +1 -1
- package/dist/context.js +4 -4
- package/dist/core.cjs +9 -5
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +14 -5
- package/dist/core.d.ts +14 -5
- package/dist/core.js +4 -4
- package/dist/index.cjs +9 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +10 -10
- package/dist/skillgraph.cjs.map +1 -1
- package/dist/skillgraph.js +5 -5
- package/dist/why.cjs.map +1 -1
- package/dist/why.js +7 -7
- package/package.json +2 -2
- package/dist/chunk-4XZHSL5T.js.map +0 -1
- /package/dist/{chunk-GH767WN4.js.map → chunk-6GTISZYA.js.map} +0 -0
- /package/dist/{chunk-CGZDQEY2.js.map → chunk-6II3EUOJ.js.map} +0 -0
- /package/dist/{chunk-QHG4HZWH.js.map → chunk-DZ6B7LD6.js.map} +0 -0
- /package/dist/{chunk-5QKU7VQK.js.map → chunk-ECAQG3TG.js.map} +0 -0
- /package/dist/{chunk-OYSCNQQ4.js.map → chunk-FTQQVU7X.js.map} +0 -0
- /package/dist/{chunk-NBBFRC6Z.js.map → chunk-GCGVODXR.js.map} +0 -0
- /package/dist/{chunk-JRPGOX3R.js.map → chunk-PCOHPEVI.js.map} +0 -0
- /package/dist/{chunk-G5FHO5MN.js.map → chunk-WMELY5CJ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -1259,6 +1259,12 @@ Headless: `foldTimeRows(ledger, coverage?)`, `answerOfReading(turn, reading)`,
|
|
|
1259
1259
|
`timeStandingOf(assessment, ledger?)`, `timeAskOf(pause)`,
|
|
1260
1260
|
`datasetTimeAxisOf(meta, rows?)`.
|
|
1261
1261
|
|
|
1262
|
+
The dataset-axis helper depends only on AgentFootprint's three time-axis read
|
|
1263
|
+
functions, not its execution runtime. It reads those optional properties at
|
|
1264
|
+
call time: peers before 9.128.0 still show a declared axis as `unjudged`, while
|
|
1265
|
+
newer peers supply the judgment and counts. The browser packaging test checks
|
|
1266
|
+
that importing this helper does not retain the agent or engine execution code.
|
|
1267
|
+
|
|
1262
1268
|
## The Served tab
|
|
1263
1269
|
|
|
1264
1270
|
**At every LLM call, exactly what the model was served — provable from the log.**
|
|
@@ -11,10 +11,10 @@ import {
|
|
|
11
11
|
} from "./chunk-NSTKBU5V.js";
|
|
12
12
|
import {
|
|
13
13
|
T
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-WMELY5CJ.js";
|
|
15
15
|
import {
|
|
16
16
|
foldCursorOf
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-KCAQVLTA.js";
|
|
18
18
|
|
|
19
19
|
// src/react/components/ServedBadge.tsx
|
|
20
20
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -1584,4 +1584,4 @@ export {
|
|
|
1584
1584
|
collapsedTicketOf,
|
|
1585
1585
|
ServedTab
|
|
1586
1586
|
};
|
|
1587
|
-
//# sourceMappingURL=chunk-
|
|
1587
|
+
//# sourceMappingURL=chunk-6GTISZYA.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
humanizeRouteConflict,
|
|
4
4
|
humanizeSkillRejected,
|
|
5
5
|
humanizeTurnRouted
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-PCOHPEVI.js";
|
|
7
7
|
import {
|
|
8
8
|
mountLogsOf
|
|
9
9
|
} from "./chunk-NSTKBU5V.js";
|
|
@@ -2991,4 +2991,4 @@ export {
|
|
|
2991
2991
|
noBookmarkStore,
|
|
2992
2992
|
tagLegend
|
|
2993
2993
|
};
|
|
2994
|
-
//# sourceMappingURL=chunk-
|
|
2994
|
+
//# sourceMappingURL=chunk-6II3EUOJ.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
describeReceived,
|
|
5
5
|
refusalDestinationFor,
|
|
6
6
|
useNarrowRow
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-ECAQG3TG.js";
|
|
8
8
|
import {
|
|
9
9
|
selectSkillBeatAt,
|
|
10
10
|
selectSkillBeats,
|
|
@@ -12,14 +12,14 @@ import {
|
|
|
12
12
|
selectSkillRoute,
|
|
13
13
|
selectSkillTopology,
|
|
14
14
|
stepForRuntimeStageId
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-PCOHPEVI.js";
|
|
16
16
|
import {
|
|
17
17
|
T,
|
|
18
18
|
TimeTravel
|
|
19
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-WMELY5CJ.js";
|
|
20
20
|
import {
|
|
21
21
|
scrubAxisFor
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-KCAQVLTA.js";
|
|
23
23
|
|
|
24
24
|
// src/react/skillGraphFlowLayout.ts
|
|
25
25
|
import dagre from "dagre";
|
|
@@ -1513,4 +1513,4 @@ export {
|
|
|
1513
1513
|
SKILL_GRAPH_READS,
|
|
1514
1514
|
SkillGraphDebugger
|
|
1515
1515
|
};
|
|
1516
|
-
//# sourceMappingURL=chunk-
|
|
1516
|
+
//# sourceMappingURL=chunk-DZ6B7LD6.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
T
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-WMELY5CJ.js";
|
|
4
4
|
|
|
5
5
|
// src/react/narrowLayout.ts
|
|
6
6
|
import { useEffect, useState } from "react";
|
|
@@ -162,4 +162,4 @@ export {
|
|
|
162
162
|
refusalDestinationFor,
|
|
163
163
|
REFUSAL_GO_TO
|
|
164
164
|
};
|
|
165
|
-
//# sourceMappingURL=chunk-
|
|
165
|
+
//# sourceMappingURL=chunk-ECAQG3TG.js.map
|
|
@@ -15,13 +15,13 @@ import {
|
|
|
15
15
|
structureGraphFromRunner,
|
|
16
16
|
tagLegend,
|
|
17
17
|
toSidecar
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-6II3EUOJ.js";
|
|
19
19
|
import {
|
|
20
20
|
useNarrowRow
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-ECAQG3TG.js";
|
|
22
22
|
import {
|
|
23
23
|
ServedTab
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-6GTISZYA.js";
|
|
25
25
|
import {
|
|
26
26
|
snapshotLogKey,
|
|
27
27
|
snapshotOfRunner,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
MODE_PALETTES,
|
|
32
32
|
T,
|
|
33
33
|
TimeTravel
|
|
34
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-WMELY5CJ.js";
|
|
35
35
|
import {
|
|
36
36
|
bandChartGroup,
|
|
37
37
|
bandIndexOf,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
lensCursorFrom,
|
|
44
44
|
resolveNavigation,
|
|
45
45
|
stepBands
|
|
46
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-KCAQVLTA.js";
|
|
47
47
|
import {
|
|
48
48
|
selectAgentInstances
|
|
49
49
|
} from "./chunk-2JAWSPNG.js";
|
|
@@ -5784,4 +5784,4 @@ export {
|
|
|
5784
5784
|
useBookmarkSidecar,
|
|
5785
5785
|
Lens
|
|
5786
5786
|
};
|
|
5787
|
-
//# sourceMappingURL=chunk-
|
|
5787
|
+
//# sourceMappingURL=chunk-FTQQVU7X.js.map
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
ServedTab,
|
|
14
14
|
collapsedTicketOf
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6GTISZYA.js";
|
|
16
16
|
import {
|
|
17
17
|
snapshotLogKey,
|
|
18
18
|
snapshotOfRunner,
|
|
@@ -21,13 +21,13 @@ import {
|
|
|
21
21
|
import {
|
|
22
22
|
T,
|
|
23
23
|
TimeTravel
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-WMELY5CJ.js";
|
|
25
25
|
import {
|
|
26
26
|
foldCommitIdxOf,
|
|
27
27
|
foldCursorOf,
|
|
28
28
|
lensCursorFrom,
|
|
29
29
|
scrubAxisFor
|
|
30
|
-
} from "./chunk-
|
|
30
|
+
} from "./chunk-KCAQVLTA.js";
|
|
31
31
|
|
|
32
32
|
// src/react/components/TimeBand.tsx
|
|
33
33
|
import React, { useMemo } from "react";
|
|
@@ -3476,4 +3476,4 @@ export {
|
|
|
3476
3476
|
CLIP3 as CLIP,
|
|
3477
3477
|
storyMarks
|
|
3478
3478
|
};
|
|
3479
|
-
//# sourceMappingURL=chunk-
|
|
3479
|
+
//# sourceMappingURL=chunk-GCGVODXR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/group/resolveNavigation.ts","../src/core/cursor/foldAt.ts","../src/core/cursor/lensCursor.ts","../src/core/group/groupDisplayName.ts","../src/core/group/Group.ts","../src/core/group/activeChartGroup.ts","../src/core/group/stepBands.ts","../src/core/group/snapSteps.ts","../src/core/group/buildGroups.ts","../src/core/group/groupForRuntimeStageId.ts","../src/core/group/buildCommitSyncMap.ts","../src/core/group/cursorPositionsAtDrill.ts","../src/core/group/scrubAxisFor.ts"],"sourcesContent":["/**\n * `resolveNavigation` — \"take me to this stage\", answered honestly.\n *\n * A host that wants to POINT at evidence (a chat answer citing the step it\n * means, a dashboard row, a deep link) knows one thing: a `runtimeStageId`.\n * The lens's cursor, though, is owned in STEPS on whichever axis the active\n * surface scrubs — and that axis may stop somewhere coarser than the address\n * (a whole iteration rather than the stage inside it), or may not reach it at\n * all. This is the one rule that turns the address into an answer.\n *\n * THE LADDER (the same rungs `stepForRuntimeStageId` has always climbed —\n * this function IS that ladder, with each rung NAMED instead of flattened\n * into a number):\n *\n * 1. **exact** — a stop whose `runtimeStageId` is the one asked for. Several\n * stops can share an id (a group's start and end); the FIRST is returned,\n * because a mover means \"take me there\", not \"take me to the end of\n * there\". → `{ ok: true, match: 'exact' }`\n * 2. **enclosing** — the stop whose subflow CONTAINS the address. An address\n * inside a subflow (`sf-x/inner#7`) is held by that subflow's own stop\n * (`sf-x#5`). Landing there is a true landing: the cursor really is\n * standing on the evidence, at the granularity this axis has. The stop's\n * own id comes back in `runtimeStageId`, so the caller always knows where\n * it actually went. → `{ ok: true, match: 'enclosing' }`\n * 3. **nearest-previous — OFFERED, NEVER TAKEN.** When no stop holds the\n * address, the stop with the largest executionIndex ≤ the address's is\n * handed back as DATA on a MISS (`{ ok: false, nearest }`). Standing just\n * before something is a true answer, but it is not the answer that was\n * asked for — so the caller decides whether to take it. A jump that\n * silently lands somewhere else is the failure this shape exists to\n * prevent.\n * 4. **nothing** — no stop is at or before the address (it pre-dates the\n * first stop, the axis is empty, or the string is not an address at all).\n * → `{ ok: false }` with no `nearest`. Do not move.\n *\n * Pure, never throws, and never guesses FORWARD: a cursor that lands past its\n * target has answered a question nobody asked.\n *\n * @example Headless — a server-rendered link that needs a step, no React.\n * ```ts\n * import { scrubAxisFor, resolveNavigation } from 'agentfootprint-lens/core';\n *\n * const positions = scrubAxisFor(recorder, 'step');\n * const to = resolveNavigation(positions, 'llm#7');\n * const href = to.ok ? `/run/${runId}?step=${to.step}` : undefined;\n * ```\n *\n * @example The miss, offered rather than taken.\n * ```ts\n * const to = resolveNavigation(positions, 'sf-tools/search#42');\n * if (!to.ok && to.nearest) {\n * // Render it as a choice, not a jump:\n * // \"That step isn't on this ruler. Go to the one before it (Iteration 3)?\"\n * }\n * ```\n */\n\nimport type { CursorPosition } from './cursorPositionsAtDrill.js';\n\n/** How the address was matched to the stop the cursor lands on. */\nexport type NavigationMatch =\n /** The stop IS the address. */\n | 'exact'\n /** The stop's subflow CONTAINS the address — a true landing on a coarser\n * axis. Read `runtimeStageId` for where the cursor actually stands. */\n | 'enclosing';\n\n/** Why an address could not be moved to. Branch on this, not on `message`. */\nexport type NavigationMiss =\n /** No address was given (empty string). */\n | 'no-id'\n /** The axis has no stops yet — nothing to move to. */\n | 'empty-axis'\n /** The address is real but no stop holds it. `nearest` may offer the stop\n * standing just before it. */\n | 'not-on-axis'\n /** Every stop on the axis comes AFTER the address. There is nothing at or\n * before it, so there is no honest place to stand. */\n | 'before-first-stop';\n\n/** The cursor moved (or, headlessly, can move) — this is where it lands. */\nexport interface NavigationHit {\n readonly ok: true;\n /** The step on the axis that was passed in. */\n readonly step: number;\n /** The address of the stop landed on — the one asked for when `match` is\n * `'exact'`, the ENCLOSING stop's own address when it is `'enclosing'`. */\n readonly runtimeStageId: string;\n /** Which rung of the ladder answered. */\n readonly match: NavigationMatch;\n /** The stop's human label, as the step strip and timeline spell it. */\n readonly label: string;\n}\n\n/** The cursor did NOT move, and here is what is true instead. */\nexport interface NavigationRefusal {\n readonly ok: false;\n /** The machine-checkable reason. */\n readonly reason: NavigationMiss;\n /** The same reason as one plain sentence, safe to show a person. */\n readonly message: string;\n /**\n * The nearest stop BEFORE the address, when there is one — an OFFER, not a\n * move. Take it by navigating to `nearest.runtimeStageId` (an exact hit, by\n * construction), or ignore it.\n */\n readonly nearest?: {\n readonly runtimeStageId: string;\n readonly step: number;\n readonly label: string;\n };\n}\n\nexport type NavigationResult = NavigationHit | NavigationRefusal;\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 axis.\n *\n * @param positions the scrub axis to resolve against — the ACTIVE one. From\n * `scrubAxisFor(recorder, granularity)` outside React, or\n * `useCursorPositions(recorder, drillPath)` inside it.\n * @param runtimeStageId the address to move to.\n */\nexport function resolveNavigation(\n positions: readonly CursorPosition[],\n runtimeStageId: string,\n): NavigationResult {\n if (typeof runtimeStageId !== 'string' || runtimeStageId === '') {\n return {\n ok: false,\n reason: 'no-id',\n message:\n 'No address was given. navigateTo needs a runtimeStageId — footprintjs\\'s ' +\n 'own address for a stage, like \"llm#3\" or \"sf-tools/search#7\".',\n };\n }\n if (positions.length === 0) {\n return {\n ok: false,\n reason: 'empty-axis',\n message:\n 'This ruler has no stops yet, so there is nowhere to move. A run that has ' +\n 'not committed a stage has no positions to scrub.',\n };\n }\n\n // Rung 1 — exact.\n const exact = positions.findIndex((p) => p.runtimeStageId === runtimeStageId);\n if (exact >= 0) {\n return {\n ok: true,\n step: exact,\n runtimeStageId,\n match: 'exact',\n label: positions[exact]?.label ?? '',\n };\n }\n\n const target = execIndex(runtimeStageId);\n if (target === undefined) {\n return {\n ok: false,\n reason: 'not-on-axis',\n message:\n `\"${runtimeStageId}\" is not a stop on this ruler, and it carries no ` +\n '#executionIndex, so no nearby stop can be worked out from it either. ' +\n 'A runtimeStageId looks like \"llm#3\".',\n };\n }\n\n // Rung 2 — enclosing. The address's scopes, innermost first: `a/b/c#7` is\n // held by `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 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) {\n return {\n ok: true,\n step: best,\n runtimeStageId: positions[best]?.runtimeStageId ?? '',\n match: 'enclosing',\n label: positions[best]?.label ?? '',\n };\n }\n }\n\n // Rung 3 — nearest-previous. Computed, then OFFERED on a miss.\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 stops can share an executionIndex (a group's start and\n // 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 if (prev >= 0) {\n const at = positions[prev]!;\n return {\n ok: false,\n reason: 'not-on-axis',\n message:\n `\"${runtimeStageId}\" is not a stop on this ruler. The nearest stop before ` +\n `it is \"${at.runtimeStageId}\" (step ${prev}${at.label ? `, ${at.label}` : ''}) — ` +\n 'offered, not taken: moving there is your call.',\n nearest: { runtimeStageId: at.runtimeStageId, step: prev, label: at.label },\n };\n }\n\n // Rung 4 — nothing at or before it.\n return {\n ok: false,\n reason: 'before-first-stop',\n message:\n `\"${runtimeStageId}\" comes before this ruler's first stop, so there is nothing ` +\n 'at or before it to stand on. The cursor did not move.',\n };\n}\n","/**\n * foldAt — the ONE owner of \"which commit a stop's fold runs THROUGH\".\n *\n * A position's `commitIdx` is where it ANCHORS on the log (what `jumpTo`, the\n * address and `stepForCommitIdx` read). For every stop but one, that is also\n * the last commit the record held there: a stage's stop stands on its own\n * commit. The exception is the run's opening bookend, \"Run · start\" (the\n * root group's `group-start`, depth 0): the root group OPENS at the first\n * commit, so the bookend shares that index with the first stage's stop — but\n * it stands BEFORE that commit happened. Folding it inclusively shows the\n * first stage's writes at \"Run · start\", and on a RESUMED leg that is the\n * paused call's stand-in: the answer bound, the call dispatched — every stop\n * of the leg then looked the same from the first (found in the 2026-10-01\n * demo video). footprintjs's own axis says the same thing: its `'start'` stop\n * is `commitIdx: -1`, the fold base (`splitAxis` · `axis.start.commitIdx`).\n *\n * So every fold of the record at a stop — `contextAt`, the Served tab, the\n * Time view a host mounts — asks here: `-1` (the base: the run's\n * `initialState`, which on a resumed leg is the state at the pause) for the\n * opening bookend, the position's own `commitIdx` otherwise. The reading a\n * cursor hands out carries it (`LensCursorReading.foldCommitIdx`), and\n * {@link foldCursorOf} turns a reading into the cursor a fold takes.\n *\n * Pure; no React.\n */\nimport type { CursorPosition } from '../group/cursorPositionsAtDrill.js';\nimport type { ServedCursor } from '../served/types.js';\n\n/**\n * The last commit the record held at `position`: `-1` at the run's opening\n * bookend (\"Run · start\", or a `user-in` bookend — depth 0), its `commitIdx`\n * at every other stop.\n *\n * @example\n * ```ts\n * const positions = scrubAxisFor(recorder, 'group');\n * foldCommitIdxOf(positions[0]!); // -1 — Run · start folds the base\n * foldCommitIdxOf(positions[1]!); // positions[1].commitIdx\n * ```\n */\nexport function foldCommitIdxOf(position: Pick<CursorPosition, 'kind' | 'depth' | 'commitIdx'>): number {\n const opening = position.kind === 'group-start' || position.kind === 'user-in';\n return opening && position.depth === 0 ? -1 : position.commitIdx;\n}\n\n/**\n * The cursor a fold takes (`contextAt`, `servedRowAt`) for a cursor reading:\n * its address, folded through `foldCommitIdx`. A reading built before the\n * field existed (a host's own) carries none, and folds its `commitIdx`.\n *\n * @example\n * ```ts\n * const at = shared.forAxis('group').at;\n * const ledger = contextAt(snapshot, foldCursorOf(at)).keys.find((k) => k.path === 'findingsLedger')?.value;\n * ```\n */\nexport function foldCursorOf(at: {\n readonly runtimeStageId: string;\n readonly commitIdx: number;\n readonly foldCommitIdx?: number;\n}): ServedCursor {\n return { runtimeStageId: at.runtimeStageId, commitIdx: at.foldCommitIdx ?? at.commitIdx };\n}\n","/**\n * `LensCursor` — the ONE cursor, in the ONE vocabulary every view is handed.\n *\n * THE LAW THIS FILE EXISTS FOR:\n *\n * **A stage id is an ADDRESS, not a POSITION.** It says WHICH stage, never\n * WHERE on an axis. Only an axis can answer that, and it may honestly answer\n * \"not here\".\n *\n * Before this shape there were three vocabularies for the same cursor, and a\n * fourth was invented by every new view:\n *\n * | view | handed | reads |\n * |---|---|---|\n * | skill graph | `cursorRuntimeStageId` + `onJumpTo` | the EVENT record |\n * | a consumer's data graph | `{ step, total, stepOf(id), onStep }` | the COMMIT record |\n * | Served tab / graph | `cursorRuntimeStageId` + `commitIdx` | the COMMIT record |\n *\n * This is their superset, and it is deliberately only three things:\n *\n * - `at` — a READING. Where the one cursor stands, in every unit the lens\n * knows. Not a position anyone here owns: it is derived from the axis and\n * the step on every build, so a view that renders it cannot drift from the\n * cursor, and a view that STORED it would be storing a stale copy.\n * - `resolve(runtimeStageId)` — an ADDRESS, answered honestly by the axis.\n * A hit with its step, or a refusal carrying `reason`, `message` and an\n * optional `nearest`. This is the rung-named ladder (`resolveNavigation`),\n * handed to the view rather than re-derived inside it.\n * - `moveTo(step)` — the ONE funnel. The same one the step strip, the ◀ ▶\n * buttons, the arrow keys, a chart click and the live auto-advance use.\n *\n * WHAT IS DELIBERATELY ABSENT: a position of its own. There is no `setStep`\n * here, no internal state, no second axis. A view that wants to move calls\n * `moveTo` and waits to be told where the cursor went — which is the correct\n * behaviour for a controlled view whose owner ignored it.\n *\n * THE THREE CASES A CONTRACT MUST NAME, because each is a real run:\n *\n * 1. **The axis does not stop there.** A filtered or tag axis over an\n * untagged stage. `resolve` refuses `'not-on-axis'` and offers the\n * nearest earlier stop. The element is drawn UNPLACED, with the\n * refusal's own message — never hidden. (Omit never deny.)\n * 2. **The id belongs to an inner log.** A subflow's stages commit into\n * their own isolated log. The ladder's `'enclosing'` rung answers with\n * the MOUNT: `{ ok: true, match: 'enclosing' }`, whose `runtimeStageId`\n * is the mount's, not the one asked for. A view that must distinguish\n * \"landed on it\" from \"landed on the thing that contains it\" branches on\n * `match` — and offers a drill to go deeper.\n * 3. **The event has no stage at all.** Run start, run end. There is no id\n * to resolve; `resolve('')` refuses `'no-id'` and the view says so\n * rather than inventing an address.\n *\n * Pure, frozen, no React. `react/useLensCursor.ts` returns one of these beside\n * what it always returned; `<Lens>` hands it to every view it mounts.\n *\n * @example Placing an element by ADDRESS, and drawing the unplaced case.\n * ```tsx\n * function Element({ cursor, runtimeStageId }: { cursor: LensCursor; runtimeStageId: string }) {\n * const to = cursor.resolve(runtimeStageId);\n * if (!to.ok) {\n * // Drawn, not hidden — and the sentence is the LIBRARY's, not this view's.\n * return <li data-unplaced=\"true\" title={to.message}>{runtimeStageId}</li>;\n * }\n * return (\n * <li\n * data-step={to.step}\n * data-enclosing={to.match === 'enclosing' ? 'true' : undefined}\n * onClick={() => cursor.moveTo(to.step)}\n * >\n * {to.label}\n * </li>\n * );\n * }\n * ```\n */\n\nimport type { CursorPosition } from '../group/cursorPositionsAtDrill.js';\nimport { resolveNavigation, type NavigationResult } from '../group/resolveNavigation.js';\nimport { foldCommitIdxOf } from './foldAt.js';\nimport type { SourcePosition } from './sourcePrefix.js';\n\n/**\n * Where the ONE cursor stands, in every unit the lens knows.\n *\n * This is the READING half of `LensCursorAt` (`react/useLensCursor.ts`),\n * which adds the `clamped` flag a MOVE REPORT needs and this does not: a\n * reading is a fact about now, not a report about a move.\n */\nexport interface LensCursorReading {\n /** Present only while the owner holds an exact source-prefix target; never a stage address. */\n readonly sourcePosition?: SourcePosition;\n /** The cursor, on the lens's step axis. */\n readonly step: number;\n /** How many positions the axis has right now. Valid steps are\n * `0 … totalSteps - 1`. GROWS during a live run. */\n readonly totalSteps: number;\n /** The cursor's address in footprintjs's address space\n * (`[subflowPath/]stageId#executionIndex`). `''` when the axis is empty. */\n readonly runtimeStageId: string;\n /** The commit-log index this position anchors to. `-1` when unknown. */\n readonly commitIdx: number;\n /**\n * The last commit the record HELD at this stop — what a fold at the stop\n * runs through (`foldAt.ts` · `foldCommitIdxOf`): `-1` at \"Run · start\",\n * which shares its anchor with the first stage's commit but stands before\n * it; `commitIdx` everywhere else. Hand it to a fold with `foldCursorOf(at)`.\n * Optional only so a reading a host built itself still type-checks; every\n * reading the lens builds carries it.\n */\n readonly foldCommitIdx?: number;\n /** The position's human label, as the step strip and the timeline spell it\n * (\"Iteration 2\", \"Context 3\", \"Run · start\"). */\n readonly label: string;\n /** What kind of stop this is. Absent when the axis is empty. */\n readonly kind?: string;\n}\n\n/**\n * The ONE cursor, as every view receives it: a reading plus two functions.\n *\n * It holds NO position of its own — `at` is derived, `resolve` is a pure query\n * over the axis, and `moveTo` is the owner's funnel. Rebuild it when the axis\n * or the step changes (`lensCursorFrom`); never store it.\n */\nexport interface LensCursor {\n /** Where the cursor stands. Derived, never owned. */\n readonly at: LensCursorReading;\n /** How many positions the axis has — the same number as `at.totalSteps`,\n * named the way a view that only counts asks for it. */\n readonly total: number;\n /**\n * Resolve an ADDRESS against this axis, honestly.\n *\n * A hit carries the `step` to move to and `match` (`'exact'` — the stop IS\n * the address; `'enclosing'` — the stop CONTAINS it, and `runtimeStageId` is\n * then the containing stop's). A refusal carries a machine-checkable\n * `reason`, a `message` safe to print, and sometimes a `nearest` OFFER.\n *\n * The offer is never taken here: taking it is another call.\n */\n resolve(runtimeStageId: string): NavigationResult;\n /**\n * Move the ONE cursor to a step — the same funnel every built-in mover goes\n * through. Out-of-range asks are the funnel's business, not this object's.\n */\n moveTo(step: number): void;\n}\n\n/** The reading of an EMPTY axis — no stop, so no address and no label. */\nconst EMPTY_READING: LensCursorReading = Object.freeze({\n step: 0,\n totalSteps: 0,\n runtimeStageId: '',\n commitIdx: -1,\n foldCommitIdx: -1,\n label: '',\n});\n\n/**\n * Build the one cursor over one axis.\n *\n * @param positions the ACTIVE scrub axis — `useCursorPositions(recorder,\n * drillPath, undefined, axis)` in React, or `scrubAxisFor(recorder,\n * granularity)` outside it. The SAME list the lens draws its ruler from;\n * handing a different one would be a second axis wearing the first's name.\n * @param step where the cursor's owner says it stands.\n * @param moveTo the owner's funnel.\n *\n * @example Headless — no React, no mount.\n * ```ts\n * import { scrubAxisFor, lensCursorFrom } from 'agentfootprint-lens/core';\n *\n * const positions = scrubAxisFor(recorder, 'step');\n * const cursor = lensCursorFrom(positions, 0, (n) => { myStep = n; });\n * const to = cursor.resolve('llm#7');\n * const href = to.ok ? `/run/${runId}?step=${to.step}` : undefined;\n * ```\n */\nexport function lensCursorFrom(\n positions: readonly CursorPosition[],\n step: number,\n moveTo: (step: number) => void,\n): LensCursor {\n const here = positions[step];\n const at: LensCursorReading =\n here === undefined\n ? Object.freeze({ ...EMPTY_READING, step, totalSteps: positions.length })\n : Object.freeze({\n step,\n totalSteps: positions.length,\n runtimeStageId: here.runtimeStageId,\n commitIdx: here.commitIdx,\n foldCommitIdx: foldCommitIdxOf(here),\n label: here.label,\n ...(here.kind !== undefined ? { kind: here.kind } : {}),\n });\n return Object.freeze({\n at,\n total: positions.length,\n resolve: (runtimeStageId: string): NavigationResult =>\n resolveNavigation(positions, runtimeStageId),\n moveTo,\n });\n}\n","/**\n * groupDisplayName — THE one spelling of a group's name.\n *\n * Pure function. Layer 1 / Tier B / Lens v0.1.\n *\n * A group appears in three places at once: the WHAT HAPPENED boundary rail,\n * the grouped ruler's stop label, and (since 0.36) the chip on the chart's\n * drawn group boundary. Before this function each of those picked its own\n * fallback chain, so the same subflow could be \"Committee\" on the rail and\n * `sf-committee#0` on the chart — two names for one place, which reads as two\n * places.\n *\n * The chain, outermost preference first:\n *\n * 1. `subflowName` — what the author called the subflow.\n * 2. `compositionName` — what the author called the composition\n * (Parallel / Sequence / Loop / Conditional).\n * 3. `primitiveKind` — the kind of thing it is (\"Agent\", \"LLMCall\") when\n * the author named nothing.\n * 4. `'Run'` — for the synthetic run root, which has no name to\n * inherit and is never a `sf-…` id to a reader.\n * 5. `runtimeStageId` — the honest last resort: the address itself. Never\n * a guess, never an empty chip.\n *\n * Anything that names a group calls THIS. A second `??` chain over the same\n * fields anywhere else in the codebase is the bug this file exists to prevent.\n */\n\nimport type { BoundaryRangeLabel } from 'agentfootprint/observe';\n\n/** The label fields a name can come from — structurally typed so a caller can\n * pass a `BoundaryRangeLabel`, a `Group`, or any row carrying the same fields\n * without a cast. */\nexport interface GroupNameSource {\n readonly subflowName?: string | undefined;\n readonly compositionName?: string | undefined;\n readonly primitiveKind?: string | undefined;\n readonly runtimeStageId: string;\n /** `'run.entry'` marks the synthetic root. Optional so a `Group` (which\n * carries `isRoot` instead) can be passed through `groupDisplayNameOf`. */\n readonly type?: string | undefined;\n}\n\n/** The display name for one boundary label. See the chain above. */\nexport function groupDisplayName(label: GroupNameSource): string {\n const named = label.subflowName ?? label.compositionName ?? label.primitiveKind;\n if (named !== undefined && named !== '') return named;\n if (label.type === 'run.entry') return 'Run';\n return label.runtimeStageId;\n}\n\n/** Same chain for a wire `BoundaryRangeLabel` — the shape the boundary rail\n * and the commentary selectors hand around. Exists so callers holding the\n * agentfootprint type don't have to widen it themselves. */\nexport function groupDisplayNameForLabel(label: BoundaryRangeLabel): string {\n return groupDisplayName(label as unknown as GroupNameSource);\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 * snapSteps — the stops a transport's ◀ ▶ is allowed to LAND on, as pure\n * queries over the ONE step axis.\n *\n * ── The problem, from a real consumer ───────────────────────────────────────\n * A hosted view shares the host's axis. That is the one-cursor law working\n * correctly — but the host's axis is the run's, not the view's. A four-tool\n * turn puts 73 stops on it, and a view that only changes at eight of them\n * spends 65 presses of ◀ ▶ showing the reader the same picture twice. The\n * scrubbing is not wrong; it is DEAD AIR.\n *\n * The fix that must NOT be taken is a second slider with its own numbers: two\n * transports over one run is the drift class this package keeps refusing\n * (`lens_v0_1_one_cursor_architecture`). So the axis stays the host's, the\n * numbers stay the host's, and the only thing that changes is WHICH of those\n * numbers the step buttons stop at.\n *\n * ── What a snap list IS ─────────────────────────────────────────────────────\n * An ascending list of positions on the axis the transport already receives.\n * It is not an axis, not a projection, and carries no addresses: every entry\n * is a step the host could already have moved to. Nothing here is stored, and\n * nothing downstream may store an index into it — a stored snap index would be\n * a second cursor, the exact thing the locked v0.1 architecture bans.\n *\n * ── The two laws, and why they are STRICT ───────────────────────────────────\n * `nextSnapStep` takes the least stop strictly GREATER than the cursor;\n * `prevSnapStep` the greatest stop strictly LESS. Strictness is what makes a\n * cursor parked BETWEEN two stops behave: ◀ from step 5 with stops\n * `[0, 4, 9]` lands on 4 — back onto the stop you are standing past — while ▶\n * lands on 9. A non-strict `prev` would skip 4 and jump to 0, stranding the\n * reader before the stop the readout just told them they were near.\n *\n * Both are min/max scans, so they answer correctly whatever order the list\n * arrives in; `snapPositionOf`'s INDEX is the one answer that assumes the\n * documented ascending order (it is a display position — \"stop 2 of 5\").\n *\n * Never throw, never invent, and never move a cursor on their own: they answer\n * `undefined` for \"there is no such stop\" and let the caller decide that a\n * button is disabled.\n */\n\n/**\n * Where a step stands relative to a list of snap stops.\n *\n * The two fields answer two different questions, and a readout that collapses\n * them lies: `index` is which stop the cursor is AT OR PAST, `exact` is\n * whether it is actually ON it.\n */\nexport interface SnapPosition {\n /**\n * Index in the snap list of the stop AT-OR-BEFORE the step — `-1` when the\n * step falls before the first stop (or the list is empty).\n */\n readonly index: number;\n /**\n * `true` when the step IS that stop; `false` when it stands BETWEEN that\n * stop and the next (or past the last one). A cursor between stops is a real\n * position, not a stop, and a readout must say so rather than rounding it\n * down silently.\n */\n readonly exact: boolean;\n}\n\n/**\n * Resolve a step onto a snap list: the stop at-or-before it, and whether the\n * step is that stop or merely past it.\n *\n * @param snapSteps the permitted stops, ascending.\n * @param step the cursor's position on the same axis.\n *\n * @example\n * ```ts\n * snapPositionOf([0, 4, 9], 4); // → { index: 1, exact: true } — on stop 2\n * snapPositionOf([0, 4, 9], 5); // → { index: 1, exact: false } — between 2 and 3\n * snapPositionOf([4, 9], 0); // → { index: -1, exact: false } — before stop 1\n * ```\n */\nexport function snapPositionOf(snapSteps: readonly number[], step: number): SnapPosition {\n let index = -1;\n let best = -Infinity;\n for (let i = 0; i < snapSteps.length; i += 1) {\n const s = snapSteps[i];\n if (s === undefined || s > step) continue;\n if (s >= best) {\n best = s;\n index = i;\n }\n }\n return { index, exact: index >= 0 && best === step };\n}\n\n/**\n * The next stop after `step` — the least entry strictly greater than it.\n *\n * `undefined` means \"no stop ahead\", which is what disables a ▶ button. There\n * is deliberately no wrap: a transport that wrapped would make the end of a\n * run indistinguishable from its start.\n *\n * @example\n * ```ts\n * nextSnapStep([0, 4, 9], 5); // → 9\n * nextSnapStep([0, 4, 9], 9); // → undefined\n * ```\n */\nexport function nextSnapStep(snapSteps: readonly number[], step: number): number | undefined {\n let best: number | undefined;\n for (const s of snapSteps) {\n if (s > step && (best === undefined || s < best)) best = s;\n }\n return best;\n}\n\n/**\n * The previous stop before `step` — the greatest entry strictly less than it.\n *\n * Strictly less is what makes a between-position walk back onto the stop it is\n * standing past: from 5 with stops `[0, 4, 9]` this answers 4, not 0.\n *\n * @example\n * ```ts\n * prevSnapStep([0, 4, 9], 5); // → 4 — the stop you are standing past\n * prevSnapStep([0, 4, 9], 4); // → 0 — already on a stop, so the one before it\n * prevSnapStep([0, 4, 9], 0); // → undefined\n * ```\n */\nexport function prevSnapStep(snapSteps: readonly number[], step: number): number | undefined {\n let best: number | undefined;\n for (const s of snapSteps) {\n if (s < step && (best === undefined || s > best)) best = s;\n }\n return best;\n}\n","/**\n * buildGroups — derive the ordered list of `Group`s from a `CommitRangeIndex`\n * of `BoundaryRangeLabel`s.\n *\n * Pure function. Layer 1 / Tier B / Lens v0.1.\n *\n * Maps the agentfootprint wire type (`BoundaryRangeLabel`) into the\n * Lens-facing `Group` developer alias. Adds:\n *\n * - `runtimeGroupId` — alias of `label.runtimeStageId` (the subflow's\n * execution id; same address space as commit\n * runtimeStageIds).\n * - `parentGroupId` — derived by walking the enclosing chain at the\n * group's open-commit.\n * - `opensAtCommitIdx` — the range's `startIdx`.\n * - `closesAtCommitIdx` — the range's `endIdx` (undefined while in-flight).\n *\n * Output ordering: groups appear in `opensAtCommitIdx` ascending order\n * (chronological). When two groups open at the same commit (Parallel's\n * fork emits the parent + children at the same idx), the OUTER one\n * appears first (depth ascending tie-break) — matching how `enclosing()`\n * orders outer→inner.\n *\n * Performance\n * ───────────\n * O(n²) worst case for `parentGroupId` resolution (one `enclosing()`\n * call per range). Acceptable up to a few hundred groups per run;\n * beyond that, a single-pass stack walk would be faster but isn't\n * needed for Lens v0.1's expected scale.\n */\n\nimport type { BoundaryQueries } from './BoundaryQueries.js';\nimport type { Group } from './Group.js';\nimport { groupDisplayNameForLabel } from './groupDisplayName.js';\n\n/** Exact equality of two subflow-path arrays (the chart-nesting chain). */\nfunction samePath(a: readonly string[], b: readonly string[]): boolean {\n return a.length === b.length && a.every((s, i) => s === b[i]);\n}\n\nexport function buildGroups(\n boundaryIndex: BoundaryQueries,\n): readonly Group[] {\n // CommitRangeIndex doesn't expose a direct \"all ranges\" accessor, but\n // `overlapping(0, MAX)` returns every range overlapping that slice —\n // which is all of them. Returns them in outer→inner order already.\n const all = boundaryIndex.overlapping(0, Number.MAX_SAFE_INTEGER);\n if (all.length === 0) return [];\n\n // Dedupe by runtimeStageId. BoundaryRecorder can register a range\n // twice when it observes events from both the FlowRecorder channel\n // (`runner.attach`) AND the typed-event channel (`runner.on('*')`).\n // Keep the FIRST opened range — it has the correct startIdx + the\n // first-closed endIdx (if both got closed).\n const seen = new Set<string>();\n\n const result: Group[] = [];\n for (let i = 0; i < all.length; i++) {\n const entry = all[i]!;\n const label = entry.label;\n if (seen.has(label.runtimeStageId)) continue;\n seen.add(label.runtimeStageId);\n\n // Parent resolution.\n //\n // subflow.entry: the AUTHORITATIVE parent is the enclosing EXECUTION whose\n // `subflowPath` equals this node's parent path (its own subflowPath minus\n // the last segment). `subflowPath` encodes the true chart nesting, so this:\n // • disambiguates parallel SIBLINGS — their overlapping commit ranges\n // would otherwise let \"innermost enclosing\" pick a sibling as parent\n // (e.g. legal → ethics); a sibling has a DIFFERENT path so it's never\n // chosen; and\n // • fixes the old composition-preference FLATTENING (deep nesting\n // branch → llm-call → slots no longer collapses its parent onto the\n // outermost composition — each node parents onto its immediate\n // enclosing subflow).\n // The enclosing scan picks the right EXECUTION instance (loops run a\n // subflow N times; commit containment selects the wrapping instance).\n //\n // composition.start: parent = innermost enclosing range other than self\n // (compositions don't carry a self-segment in subflowPath the same way).\n //\n // run.entry: synthetic root, no parent.\n let parentGroupId: string | undefined;\n if (label.type === 'subflow.entry') {\n const enclosing = boundaryIndex.enclosing(entry.startIdx); // outer→inner\n const parentPath = label.subflowPath.slice(0, -1);\n for (let j = enclosing.length - 1; j >= 0; j--) {\n const cand = enclosing[j]!.label;\n if (cand.runtimeStageId === label.runtimeStageId) continue; // skip self\n if (samePath(cand.subflowPath, parentPath)) {\n parentGroupId = cand.runtimeStageId;\n break;\n }\n }\n // Fallback (malformed/missing path): innermost enclosing non-self.\n if (parentGroupId === undefined) {\n for (let j = enclosing.length - 1; j >= 0; j--) {\n const cand = enclosing[j]!.label;\n if (cand.runtimeStageId === label.runtimeStageId) continue;\n parentGroupId = cand.runtimeStageId;\n break;\n }\n }\n } else if (label.type === 'composition.start') {\n const enclosing = boundaryIndex.enclosing(entry.startIdx);\n for (let j = enclosing.length - 1; j >= 0; j--) {\n const cand = enclosing[j]!.label;\n if (cand.runtimeStageId === label.runtimeStageId) continue;\n parentGroupId = cand.runtimeStageId;\n break;\n }\n }\n\n const isRoot = label.type === 'run.entry';\n // ONE naming source. `groupDisplayName` is the same chain the WHAT HAPPENED\n // boundary rail and the chart's group-boundary chip read, so a group cannot\n // be called two things in one view. See groupDisplayName.ts.\n const name = groupDisplayNameForLabel(label);\n const compositionKind = label.type === 'composition.start'\n ? label.compositionKind\n : undefined;\n\n result.push({\n runtimeGroupId: label.runtimeStageId,\n name,\n parentGroupId,\n subflowPath: label.subflowPath,\n depth: label.depth,\n opensAtCommitIdx: entry.startIdx,\n closesAtCommitIdx: entry.endIdx,\n isRoot,\n ...(compositionKind !== undefined ? { compositionKind } : {}),\n ...(label.slotKind !== undefined ? { slotKind: label.slotKind } : {}),\n ...(label.primitiveKind !== undefined ? { primitiveKind: label.primitiveKind } : {}),\n });\n }\n\n return result;\n}\n","/**\n * groupForRuntimeStageId — find the innermost Group that contains a\n * given runtimeStageId.\n *\n * Pure function. Layer 1 / Tier C / Lens v0.1.\n *\n * Used by Lens whenever a panel has a runtimeStageId cursor and needs\n * to know which subflow box to highlight (the chart) or which\n * breadcrumb segment to bold.\n *\n * Algorithm\n * ─────────\n * `rid` has the format `[subflowPath/]stageId#executionIndex`. The\n * stage is `stageId#executionIndex`; the subflow path is everything\n * BEFORE that, slash-separated. Containment is a `subflowPath` prefix\n * match — the deepest matching `Group.subflowPath` wins.\n *\n * Important: we match on the `subflowPath` field (an array of names)\n * NOT on the `runtimeGroupId` string. That's because:\n * - `Group.runtimeGroupId` = `'sf-x#3'` or `'parent/sf-x#3'` (an id, `#`-terminated)\n * - Commit `runtimeStageId` = `'parent/sf-x/call-llm#5'` (a path, `/`-segmented)\n *\n * A naive `startsWith` collapses these mismatched separators. Walking\n * the structural `subflowPath` array is unambiguous.\n *\n * Edge cases\n * ──────────\n * - `rid === ''` → undefined\n * - groups empty → undefined\n * - rid has no path (`'call#1'`) → returns root group when present, else undefined\n * - rid IS a group's own id (`'sf-x#3'`) → returns that group (innermost = itself)\n */\n\nimport type { Group } from './Group.js';\n\n/** Extract the subflow path of a runtimeStageId. Drops the leaf segment\n * (the `stageId#executionIndex` after the last `/`). */\nfunction pathOf(rid: string): string[] {\n if (rid.length === 0) return [];\n const lastSlash = rid.lastIndexOf('/');\n if (lastSlash < 0) return [];\n return rid.slice(0, lastSlash).split('/');\n}\n\n/** Group's effective subflow path: drop the synthetic '__root__' segment\n * the engine prepends. Real subflows under root have path\n * `['__root__', 'sf-x']` → effective `['sf-x']`. */\nfunction effectivePath(group: Group): string[] {\n return group.subflowPath[0] === '__root__'\n ? group.subflowPath.slice(1)\n : [...group.subflowPath];\n}\n\n/** True when `prefix` is a prefix (or equal) of `path`. */\nfunction isPathPrefix(prefix: readonly string[], path: readonly string[]): boolean {\n if (prefix.length > path.length) return false;\n for (let i = 0; i < prefix.length; i++) {\n if (prefix[i] !== path[i]) return false;\n }\n return true;\n}\n\n/** Same identity (the rid points AT this group). Used for the\n * \"rid IS the group's own runtimeGroupId\" early-return. */\nfunction ridMatchesGroup(rid: string, group: Group): boolean {\n return group.runtimeGroupId === rid;\n}\n\nexport function groupForRuntimeStageId(\n groups: readonly Group[],\n rid: string,\n): Group | undefined {\n if (rid.length === 0 || groups.length === 0) return undefined;\n\n // Fast path: exact identity. Catches `groupForRuntimeStageId(groups, group.runtimeGroupId)`.\n for (const g of groups) {\n if (ridMatchesGroup(rid, g)) return g;\n }\n\n const path = pathOf(rid);\n\n // No path part → it's a top-level stage. Match the root group if present.\n if (path.length === 0) {\n return groups.find((g) => g.isRoot);\n }\n\n // Pick the deepest group whose effective subflowPath is a prefix of\n // `path`. Walk by depth descending so first match wins.\n const sorted = groups.slice().sort((a, b) => b.depth - a.depth);\n for (const g of sorted) {\n if (g.isRoot) continue; // root matches any path; only return it when nothing else does\n const ep = effectivePath(g);\n if (ep.length === 0) continue;\n if (isPathPrefix(ep, path)) return g;\n }\n // Fall back to the root group.\n return groups.find((g) => g.isRoot);\n}\n","/**\n * buildCommitSyncMap — the single-source-of-truth denormalization that\n * lets Lens drive ALL panels (slider, chart highlight, commentary,\n * breadcrumb) from one cursor.\n *\n * Pure function. Layer 1 / Tier B / Lens v0.1.\n *\n * For every commit in the run, produces one `CommitSyncEntry`\n * carrying:\n *\n * - `runtimeStageId` — THE cursor of record. Same key Trace uses.\n * - `commitIdx` — numeric form for HTML slider min/max.\n * - `runtimeGroupId` — innermost enclosing subflow's runtimeStageId\n * (for chart-box highlight).\n * - `subflowPath` — full subflow chain for breadcrumb sync.\n * - `depth` — subflow stack depth at this commit.\n * - `label` — human-readable hover label.\n *\n * The returned array is in commit order: `entry.commitIdx === i` at\n * index `i`. The slider's value is just an integer in `[0, length)`.\n *\n * One cursor in, all panels out\n * ─────────────────────────────\n * ```\n * const syncMap = buildCommitSyncMap(recorder);\n * const cursor = syncMap[commitIdx]; // one cursor row\n * cursor.runtimeStageId → Trace + slider value + commentary cutoff\n * cursor.runtimeGroupId → Lens chart box highlight\n * cursor.subflowPath → Breadcrumb\n * cursor.label → Tooltip\n * ```\n *\n * No new ID concept. The two key columns (`runtimeStageId` and\n * `runtimeGroupId`) are both runtimeStageIds in form — they just\n * point at different stages in the same subflow stack.\n *\n * Performance\n * ───────────\n * O(commitCount) for the linear pass + O(commitCount × groupCount) for\n * the per-commit `groupForRuntimeStageId` lookup. For Lens-scale\n * runs (<10K commits, <100 groups), this is comfortably under 50ms.\n * If we ever exceed that scale, we can switch to a single-pass\n * subflow-stack walk over the commit log (groups in opens-at order\n * form a stack-pushable sequence).\n */\n\nimport type { LensRecorder } from '../LensRecorder.js';\nimport type { Group } from './Group.js';\nimport { buildGroups } from './buildGroups.js';\nimport { groupForRuntimeStageId } from './groupForRuntimeStageId.js';\n\nexport interface CommitSyncEntry {\n /** Universal slider cursor — same key Trace uses. */\n readonly runtimeStageId: string;\n /** Numeric index into the commit log; equal to this entry's array index. */\n readonly commitIdx: number;\n /** Innermost enclosing subflow's runtimeStageId. Chart-box highlight key.\n * When no group encloses (no Run group yet) → empty string. */\n readonly runtimeGroupId: string;\n /** Full subflow chain at this commit (for breadcrumb sync). */\n readonly subflowPath: readonly string[];\n /** Subflow stack depth at this commit (0 = top-level / under Run). */\n readonly depth: number;\n /** Human-readable hover label (\"ethics · call-llm\"). */\n readonly label: string;\n /** Scope keys this commit CHANGED (footprintjs commits are change-only). Empty\n * for a no-op commit. Drives the \"light only the slots that changed\" highlight. */\n readonly overwriteKeys: readonly string[];\n}\n\n/** Build the per-commit sync map from a populated `LensRecorder`.\n * Reads `recorder.snapshot.commitLog` for the commit list and\n * `recorder.boundary.boundaryIndex` for the group structure. */\nexport function buildCommitSyncMap(\n recorder: LensRecorder,\n): readonly CommitSyncEntry[] {\n // The COMMIT LOG comes from the executor's snapshot (reached through\n // the currently-observed runner), NOT from LensSnapshotRecorder. See\n // `LensRecorder.getCommitLog()`. The boundary index — same boundary\n // recorder — gives us the group-containment info.\n const commitLog = recorder.getCommitLog();\n if (commitLog.length === 0) return [];\n\n // Build groups once; cheap (O(group_count²) worst case).\n const groups = buildGroups(recorder.boundary.boundaryIndex);\n\n const result: CommitSyncEntry[] = [];\n for (let i = 0; i < commitLog.length; i++) {\n const c = commitLog[i]!;\n const rid: string = c.runtimeStageId ?? '';\n const group = groupForRuntimeStageId(groups, rid);\n\n result.push({\n runtimeStageId: rid,\n commitIdx: i,\n runtimeGroupId: group?.runtimeGroupId ?? '',\n subflowPath: group?.subflowPath ?? [],\n depth: group?.depth ?? 0,\n label: labelFor(rid, group, c.stageId),\n overwriteKeys: c.overwriteKeys ?? [],\n });\n }\n return result;\n}\n\nfunction labelFor(\n rid: string,\n group: Group | undefined,\n stageId: string | undefined,\n): string {\n // \"ethics · call-llm\" — group's display name + the bare stageId.\n const leaf = stageId ?? lastSegmentOf(rid);\n if (group && !group.isRoot) return `${group.name} · ${leaf}`;\n return leaf;\n}\n\nfunction lastSegmentOf(rid: string): string {\n if (rid.length === 0) return '';\n const lastSlash = rid.lastIndexOf('/');\n const segment = lastSlash < 0 ? rid : rid.slice(lastSlash + 1);\n const hashIdx = segment.lastIndexOf('#');\n return hashIdx < 0 ? segment : segment.slice(0, hashIdx);\n}\n","/**\n * cursorPositionsAtDrill — compute the slider's valid cursor positions\n * for the current drill level.\n *\n * Pure function. Layer 1 / Tier B / Lens v0.1.\n *\n * The COMPOUND time axis rule (locked architecture):\n *\n * The slider does NOT iterate every commit. Its positions equal the\n * chart's visible nodes at the current drill level. One slider stop\n * per chart box.\n *\n * drill depth 0 → top-level groups (each composition = ONE stop)\n * drill depth N → direct sub-groups of the drilled group\n * leaf (no sub-groups) → commits enclosed by the drilled group\n *\n * Cursor type is still `runtimeStageId`. Only the SUBSET of valid\n * positions changes with drill depth. ONE cursor concept; the\n * position set scales by drill.\n *\n * Inputs\n * ──────\n * `groups` — all groups in the run (from `buildGroups`)\n * `commits` — the full commit log (for leaf-group commit detail)\n * `drillPath` — the chain of `runtimeGroupId`s the user has drilled\n * into. Empty means \"top-level Run.\" The LAST element\n * is the currently-drilled group; everything BEFORE it\n * is the path used by the breadcrumb.\n */\n\nimport type { Group } from './Group.js';\nimport type { CommitSyncEntry } from './buildCommitSyncMap.js';\n\n/** Synthetic runtimeStageIds for the lens-only \"User · sends\" / \"User\n * · receives\" slider bookends. These are NOT footprintjs ids — they\n * exist solely to give lens distinct cursor states for \"before the\n * chart ran\" and \"after the chart returned\". The runtime overlay\n * branches on these to gate visible state (empty vs full). */\nexport const LENS_USER_IN_RUNTIME_ID = '__lens_user_in__#0';\nexport const LENS_USER_OUT_RUNTIME_ID = '__lens_user_out__#0';\n\n/**\n * Domain-declared milestone classifier (e.g. agentfootprint's `milestoneFor`).\n * Given a stage/group id, returns the milestone it anchors — or `null` when the\n * id is NOT a scrub-worthy step (its commits fold into the surrounding\n * milestone's collection). Injected so this module stays domain-agnostic.\n */\nexport type MilestoneClassifier = (id: string) => { readonly kind: string; readonly label: string } | null;\n\nexport interface CursorPosition {\n /** Slider value — a runtimeStageId in footprintjs's address space,\n * OR one of the lens-synthetic ids above for user-in/user-out. */\n readonly runtimeStageId: string;\n /** Same as runtimeStageId when this position IS a group's start/end. */\n readonly runtimeGroupId: string;\n /** Human-readable label (\"Committee · forks\", \"merged\", \"Run · start\"). */\n readonly label: string;\n /** Discriminates slider tick rendering. `user-in` and `user-out` are\n * lens-synthesized bookends at top-level drill. `parallel` is a single stop\n * that represents a parallel fork — see `coActiveGroupIds`. */\n readonly kind: 'group-start' | 'group-end' | 'commit' | 'user-in' | 'user-out' | 'parallel';\n /** Depth in the outline (for indentation / breadcrumb sync). */\n readonly depth: number;\n /** Commit index this position anchors to (for footprintjs jumpTo). */\n readonly commitIdx: number;\n /**\n * When this stop represents a PARALLEL fork (the context slots, or parallel\n * branches), the runtimeGroupIds of ALL concurrent branches that ran — so the\n * chart can highlight them SIMULTANEOUSLY at this one stop. `undefined` for\n * ordinary single-node stops (old behaviour byte-identical). The canonical\n * `runtimeStageId` above is still ONE id (the earliest-opening branch — the\n * fork anchor); this is auxiliary chart-highlight data only, never a second\n * cursor. The one-cursor invariant holds: panels (commentary/details/trace)\n * stay on `runtimeStageId`; only the CHART lights the whole set.\n */\n readonly coActiveGroupIds?: readonly string[];\n /**\n * The DOMAIN's classification of this stop, verbatim from the milestone\n * classifier (`'iteration'`, `'llm-turn'`, `'tool-call'`, …; the collapsed\n * slot run carries `'context'`). `undefined` on structural stops and the\n * root bookends. Preserved — rather than flattened into `kind` — because the\n * grouped ruler bands the axis by it (`stepBands`): an `'iteration'` stop is\n * where a new group of steps begins.\n */\n readonly milestone?: string;\n}\n\nfunction rootGroup(groups: readonly Group[]): Group | undefined {\n return groups.find((g) => g.isRoot);\n}\n\n/** Resolve \"the currently-drilled-into group\" from a drillPath. Empty\n * drillPath means \"Run is the current group\" — we return the root. */\nfunction currentGroup(groups: readonly Group[], drillPath: readonly string[]): Group | undefined {\n if (drillPath.length === 0) return rootGroup(groups);\n const innermost = drillPath[drillPath.length - 1]!;\n return groups.find((g) => g.runtimeGroupId === innermost);\n}\n\nfunction directChildren(\n groups: readonly Group[],\n parent: Group,\n): readonly Group[] {\n return groups\n .filter((g) => g.parentGroupId === parent.runtimeGroupId)\n .sort((a, b) => a.opensAtCommitIdx - b.opensAtCommitIdx);\n}\n\n/** Should this group earn a separate `composition.end` slider position?\n * v0.1 product rule (see locked memory):\n * - Parallel → YES (the \"merge\" is a distinct moment)\n * - Loop → YES (the \"exit\" is a distinct moment)\n * - Sequence / Conditional → NO (next step IS the natural next position)\n * - Plain subflows / Agents / LLMCalls → NO (single-position) */\nfunction emitsEndPosition(group: Group): boolean {\n return group.compositionKind === 'Parallel' || group.compositionKind === 'Loop';\n}\n\n/** Slot subflows (sf-system-prompt / sf-messages / sf-tools) are hidden\n * at top level — they become visible only when the user drills into\n * the containing Agent or LLMCall. */\nfunction isHiddenAtTopLevel(group: Group): boolean {\n return group.slotKind !== undefined;\n}\n\n/** The CONCURRENT branches of a `Parallel` composition (its direct member groups\n * that ran at the same time). Branches are the composition's `directChildren`\n * (buildGroups parents a composition's members to the composition group); a\n * trailing merge that opens only after the branches close overlaps no sibling\n * and is excluded (it gets its own `merged` end stop). These ids (after `#`\n * strip) match the rendered branch node ids, so the chart lights them together\n * at the composition's stop. */\nfunction parallelBranchIds(composition: Group, groups: readonly Group[]): string[] {\n const members = directChildren(groups, composition);\n const closeOf = (g: Group): number => g.closesAtCommitIdx ?? Number.MAX_SAFE_INTEGER;\n return members\n .filter((k) =>\n members.some(\n (o) => o !== k && o.opensAtCommitIdx <= closeOf(k) && k.opensAtCommitIdx <= closeOf(o),\n ),\n )\n .map((g) => g.runtimeGroupId);\n}\n\n/**\n * Structural cursor stops — the path used when no domain MILESTONES apply (plain\n * control-flow charts: Sequence / Conditional / Loop / Parallel, and non-agent\n * subflow trees). Milestone charts (agents) never reach here.\n *\n * Rule: ONE stop per VISIBLE step at this drill level. COMPOSITIONS are\n * TRANSPARENT — the cursor descends through them so the user scrubs the actual\n * steps, not just the wrapper box (the old behaviour emitted a single stop for\n * the whole composition, so a top-level Sequence/Conditional/Loop got only one\n * scrub stop and its steps never lit). Per composition kind:\n * - Sequence → composition-start, then one stop per step (in order).\n * - Conditional → composition-start, then the chosen branch (only it ran, so\n * only it is a member group).\n * - Loop → composition-start, one stop per body iteration, then `exit`.\n * - Parallel → ONE stop lighting all concurrent branches at once\n * (`coActiveGroupIds`) — they ran simultaneously, so NOT\n * one-per-branch — plus a `merged` end.\n * Plain (non-composition) subflows are LEAF stops; descent recurses through\n * nested compositions but stops at plain subflows (drill in to see their slots).\n *\n * Members are found by PARENT pointer (`directChildren`), NOT commit containment:\n * buildGroups parents a composition's members to the composition group, so the\n * ROOT (parent: undefined) and same-level SIBLINGS can never be mistaken for\n * members. A commit-range scan WOULD misfire — a top-level composition shares the\n * root's open commit (0) and, while in-flight, its open-ended close — capturing\n * the root (and later siblings) as phantom member stops.\n *\n * Each stop's `runtimeStageId` is a real group id whose `#`-stripped form matches\n * the rendered chart node id, so the runtime overlay lights the right node(s).\n */\nfunction structuralPositions(current: Group, groups: readonly Group[]): CursorPosition[] {\n const out: CursorPosition[] = [];\n emitMembers(directChildren(groups, current), groups, out, new Set());\n return out;\n}\n\n/** Emit a sibling cohort of member groups, ordinal-suffixing repeated PRIMARY\n * (first-stop) labels SCOPED to this cohort — so \"body 1 / 2 / 3\" counts within\n * ONE loop and a second loop's bodies restart at \"body 1\". Each member's own\n * sub-stops live in their own buffer so the ordinal keys on the member's headline\n * label, never its internals. Hidden (slot) members are skipped. */\nfunction emitMembers(\n members: readonly Group[],\n groups: readonly Group[],\n out: CursorPosition[],\n visited: ReadonlySet<string>,\n): void {\n const buffers: CursorPosition[][] = [];\n for (const m of members) {\n if (isHiddenAtTopLevel(m)) continue;\n const buf: CursorPosition[] = [];\n appendGroupStops(m, groups, buf, visited);\n if (buf.length > 0) buffers.push(buf);\n }\n const totals = new Map<string, number>();\n for (const buf of buffers) totals.set(buf[0]!.label, (totals.get(buf[0]!.label) ?? 0) + 1);\n const seen = new Map<string, number>();\n for (const buf of buffers) {\n const label = buf[0]!.label;\n if ((totals.get(label) ?? 0) > 1) {\n const n = (seen.get(label) ?? 0) + 1;\n seen.set(label, n);\n buf[0] = { ...buf[0]!, label: `${label} ${n}` };\n }\n out.push(...buf);\n }\n}\n\n/** Emit the cursor stop(s) for one group, descending through compositions.\n * `visited` carries the runtimeGroupIds on the current descent path; a group\n * reached again (only possible via a malformed parentGroupId cycle A↔B) is\n * emitted as a single leaf stop instead of recursing forever. */\nfunction appendGroupStops(\n g: Group,\n groups: readonly Group[],\n out: CursorPosition[],\n visited: ReadonlySet<string>,\n): void {\n const startStop = (label: string): CursorPosition => ({\n runtimeStageId: g.runtimeGroupId,\n runtimeGroupId: g.runtimeGroupId,\n label,\n kind: 'group-start',\n depth: g.depth,\n commitIdx: g.opensAtCommitIdx,\n });\n const endStop = (label: string): CursorPosition => ({\n runtimeStageId: g.runtimeGroupId,\n runtimeGroupId: g.runtimeGroupId,\n label,\n kind: 'group-end',\n depth: g.depth,\n commitIdx: g.closesAtCommitIdx!,\n });\n\n if (g.compositionKind === 'Parallel') {\n // Concurrent fork: ONE stop lighting every branch at once.\n const branchIds = parallelBranchIds(g, groups);\n out.push({ ...startStop(g.name), ...(branchIds.length >= 2 ? { coActiveGroupIds: branchIds } : {}) });\n if (g.closesAtCommitIdx !== undefined) out.push(endStop(`${g.name} · merged`));\n return;\n }\n\n if (g.compositionKind === 'Sequence' || g.compositionKind === 'Conditional' || g.compositionKind === 'Loop') {\n out.push(startStop(g.name));\n // Cycle guard: only descend if this composition isn't already on the path.\n if (!visited.has(g.runtimeGroupId)) {\n const nextVisited = new Set(visited);\n nextVisited.add(g.runtimeGroupId);\n emitMembers(directChildren(groups, g), groups, out, nextVisited);\n }\n if (g.compositionKind === 'Loop' && g.closesAtCommitIdx !== undefined) out.push(endStop(`${g.name} · exit`));\n return;\n }\n\n // Plain (non-composition) subflow — a leaf stop; do NOT descend (drill in for\n // its slots). This is a step/branch/iteration member, or a bare subflow.\n out.push(startStop(g.name));\n}\n\n/**\n * Domain-declared MILESTONE stops at the current drill level. A milestone is a\n * stage the domain marks as scrub-worthy (the boundary of a COLLECTION of\n * commits). Two sources, merged in commit order:\n *\n * - child GROUPS directly under `current` whose id classifies (e.g. the\n * injection-engine subflow → an `iteration` boundary), and\n * - direct COMMITS in `current` (not inside a deeper child group) whose stage\n * classifies (e.g. `call-llm` → llm-turn, `tool-calls` → tool-call).\n *\n * Each stop is a real commit's `runtimeStageId`, so the one-cursor invariant\n * holds. Repeated kinds get an ordinal (\"LLM turn 1\", \"LLM turn 2\").\n */\nfunction milestonePositions(\n current: Group,\n groups: readonly Group[],\n commits: readonly CommitSyncEntry[],\n classify: MilestoneClassifier,\n): CursorPosition[] {\n interface Raw {\n runtimeStageId: string;\n runtimeGroupId: string;\n commitIdx: number;\n depth: number;\n kind: string;\n label: string;\n /** Set on a collapsed parallel (\"Context\") stop — the concurrent branches. */\n coActiveGroupIds?: string[];\n }\n const raw: Raw[] = [];\n\n // Source 1 — child groups that classify (e.g. the per-iteration loop entry).\n for (const child of directChildren(groups, current)) {\n const m = classify(child.runtimeGroupId);\n if (!m) continue;\n raw.push({\n runtimeStageId: child.runtimeGroupId,\n runtimeGroupId: child.runtimeGroupId,\n commitIdx: child.opensAtCommitIdx,\n depth: child.depth,\n kind: m.kind,\n label: m.label,\n });\n }\n\n // Source 2 — commits directly in `current` (innermost enclosing group IS\n // current, so NOT inside a deeper child subflow) whose stage classifies.\n for (const c of commits) {\n if (c.runtimeGroupId !== current.runtimeGroupId) continue;\n // Skip `current`'s OWN boundary commit(s): a subflow's mount commit shares\n // its runtimeGroupId but represents the boundary, NOT an internal milestone.\n // Without this, drilling INTO a classified subflow (e.g. the Injection\n // Engine — classified as `iteration`) surfaces its OWN boundary as bogus\n // \"Iteration\" stops, masking the subflow's internal stages (the overlay\n // fallback below never runs because this path looks non-empty).\n if (stripExecIndex(c.runtimeStageId) === stripExecIndex(current.runtimeGroupId)) continue;\n const m = classify(c.runtimeStageId);\n if (!m) continue;\n raw.push({\n runtimeStageId: c.runtimeStageId,\n runtimeGroupId: current.runtimeGroupId,\n commitIdx: c.commitIdx,\n depth: current.depth + 1,\n kind: m.kind,\n label: m.label,\n });\n }\n\n raw.sort((a, b) => a.commitIdx - b.commitIdx);\n\n // Collapse a maximal CONSECUTIVE run of 'slot' stops into ONE 'parallel'\n // \"Context\" stop — the slots run concurrently, so the chart should light them\n // all at once rather than stepping through them one-by-one. A run of ONE slot\n // (classic mode, where only Messages re-ran) stays an individual stop, so\n // \"which slot got updated\" still reads. The collapsed stop carries\n // `coActiveGroupIds` = every concurrent branch's group id; its canonical\n // cursor is the earliest-opening branch (one-cursor invariant intact).\n const collapsed: Raw[] = [];\n for (let i = 0; i < raw.length; ) {\n if (raw[i]!.kind === 'slot') {\n let j = i;\n while (j < raw.length && raw[j]!.kind === 'slot') j++;\n const run = raw.slice(i, j);\n if (run.length >= 2) {\n // Light ONLY the slots whose contribution actually CHANGED this turn.\n // A slot's change surfaces in the parent context-mount commit's overwrite,\n // keyed by its injection key (the slot's own subflow commit is empty — it\n // bubbles via outputMapper). footprintjs commits are change-only, so an\n // unchanged slot's key is simply absent.\n const changedKeys = changedSlotKeys(run, groups, commits);\n const matched = run.filter((r) => {\n const k = slotInjectionKey(r.runtimeGroupId);\n return k !== null && changedKeys.has(k);\n });\n // Safety: if we couldn't attribute ANY change (no commit-key data, or a\n // non-agentfootprint consumer), fall back to lighting ALL slots — never\n // light nothing at a Context stop.\n const changed = matched.length > 0 ? matched : run;\n // Cursor anchors on the first CHANGED slot so the cursor's own group is a\n // lit one (the highlight ORs the cursor group into the active set).\n const anchor = changed[0]!;\n collapsed.push({\n runtimeStageId: anchor.runtimeStageId,\n runtimeGroupId: anchor.runtimeGroupId,\n commitIdx: anchor.commitIdx,\n depth: anchor.depth,\n kind: 'parallel',\n label: 'Context',\n coActiveGroupIds: changed.map((r) => r.runtimeGroupId),\n });\n } else {\n collapsed.push(run[0]!); // single slot → keep individual (\"Messages\")\n }\n i = j;\n } else {\n collapsed.push(raw[i]!);\n i++;\n }\n }\n\n // Ordinal repeated LABELS so the slider reads \"Iteration 1 / 2 / 3\",\n // \"Context 1 / 2 / 3\", \"Messages 1 / 2 / 3\" independently (per-label).\n const totals = new Map<string, number>();\n for (const r of collapsed) totals.set(r.label, (totals.get(r.label) ?? 0) + 1);\n const seen = new Map<string, number>();\n return collapsed.map((r) => {\n const n = (seen.get(r.label) ?? 0) + 1;\n seen.set(r.label, n);\n const label = (totals.get(r.label) ?? 0) > 1 ? `${r.label} ${n}` : r.label;\n return {\n runtimeStageId: r.runtimeStageId,\n runtimeGroupId: r.runtimeGroupId,\n label,\n kind: (r.kind === 'parallel' ? 'parallel' : 'commit') as CursorPosition['kind'],\n depth: r.depth,\n commitIdx: r.commitIdx,\n // The domain's own word for the stop, kept for the grouped ruler's\n // banding. The collapsed slot run is 'context' (its label's word).\n milestone: r.kind === 'parallel' ? 'context' : r.kind,\n ...(r.coActiveGroupIds && r.coActiveGroupIds.length > 0\n ? { coActiveGroupIds: r.coActiveGroupIds }\n : {}),\n };\n });\n}\n\n/**\n * agentfootprint slot subflow id → its injection scope key. The lens IS the\n * agentfootprint consumer, so this mapping is intentional + local. Returns null\n * for a non-slot group.\n */\nfunction slotInjectionKey(runtimeGroupId: string): string | null {\n if (runtimeGroupId.includes('system-prompt')) return 'systemPromptInjections';\n if (runtimeGroupId.includes('messages')) return 'messagesInjections';\n if (runtimeGroupId.includes('tools')) return 'toolsInjections';\n return null;\n}\n\n/**\n * Which slot injection keys CHANGED across a slot run's commit span — i.e. which\n * slots' contributions actually mutated this turn. A slot's change lands in the\n * parent context-mount commit's (change-only) overwrite keys, which fire as the\n * slot subflow exits — so we scan the commits spanning the run's open→close\n * (plus a small margin for the exit/outputMapper commit).\n */\nfunction changedSlotKeys(\n run: readonly { readonly runtimeGroupId: string; readonly commitIdx: number }[],\n groups: readonly Group[],\n commits: readonly CommitSyncEntry[],\n): ReadonlySet<string> {\n const SLOT_KEYS = new Set(['systemPromptInjections', 'messagesInjections', 'toolsInjections']);\n let start = Infinity;\n let end = -Infinity;\n for (const r of run) {\n const g = groups.find((gg) => gg.runtimeGroupId === r.runtimeGroupId);\n start = Math.min(start, r.commitIdx, g?.opensAtCommitIdx ?? r.commitIdx);\n end = Math.max(end, g?.closesAtCommitIdx ?? r.commitIdx);\n }\n // The context-mount (outputMapper) commit carrying each slot's injection key\n // fires around the slot boundary — sometimes just BEFORE the slot's open,\n // sometimes among the slots. Widen the window both sides to catch them.\n // Only context-mount commits carry SLOT_KEYS, so a generous window can't\n // pick up unrelated commits; iterations are far enough apart not to bleed.\n const MARGIN = 4;\n start -= MARGIN;\n end += MARGIN;\n const changed = new Set<string>();\n for (const c of commits) {\n if (c.commitIdx < start || c.commitIdx > end) continue;\n for (const k of c.overwriteKeys) if (SLOT_KEYS.has(k)) changed.add(k);\n }\n return changed;\n}\n\n/**\n * Minimal shape of a runtime-overlay execution-order entry the drilled-cursor\n * logic needs. Mirrors explain-ui's `TraceRuntimeOverlay.executionOrder[i]`,\n * kept local so this core module doesn't depend on the renderer's types.\n */\nexport interface ExecOrderEntry {\n /** Full runtimeStageId WITH `#executionIndex` (e.g. `sf-injection-engine/gather#2`). */\n readonly runtimeStageId: string;\n /** Local stage id (e.g. `gather`). */\n readonly stageId?: string;\n /** Human display name (e.g. `Gather`). */\n readonly stageName?: string;\n /** ms since run start (mirrors eui's `RuntimeExecutionStep.timestampMs`) —\n * used as a timestamp fallback for timeline moments. */\n readonly timestampMs?: number;\n}\n\n/** Strip the trailing `#executionIndex`, mirroring eui's overlay id match. */\nfunction stripExecIndex(id: string): string {\n const i = id.lastIndexOf('#');\n return i >= 0 ? id.slice(0, i) : id;\n}\n\n/**\n * Cursor stops for a drilled subflow whose internal stages are NEITHER child\n * groups NOR parent-log commits — e.g. the agent's Injection Engine, whose\n * Gather/Evaluate/Route/Delta run in the subflow's OWN memory scope, so their\n * commits never reach the parent commit log (verified: footprintjs records a\n * subflow as ONE mount-boundary commit). They DO appear in the runtime overlay's\n * `executionOrder` (the engine fires stage events globally, path-prefixed), so we\n * derive the drill's scrub stops from there — the one source that actually has\n * them. This is why a drilled subflow's internals now light as you scrub, where\n * the commit-log-only path could never reach them.\n *\n * Scope = THIS drilled iteration only. From the drilled subflow's boundary entry,\n * walk `executionOrder` FORWARD collecting the subflow's DIRECT internal stages\n * (prefix-match, no deeper `/`), stopping at the first entry that LEAVES the\n * subflow. A looping subflow's later iterations are separated by the parent's own\n * stages, so the walk naturally ends at this iteration's boundary. Nested\n * subflows surface as a single (drillable) boundary stop — their own internals\n * are walked over here, revealed by drilling again.\n *\n * Each stop's `runtimeStageId` is the EXACT overlay id (WITH `#index`) so\n * `LensFlow`'s `executionOrder.findIndex` resolves a scrubIndex and `TracedFlow`\n * lights the matching node as the cursor reaches it.\n */\nfunction subflowInternalPositions(\n current: Group,\n executionOrder: readonly ExecOrderEntry[],\n): CursorPosition[] {\n const prefix = `${stripExecIndex(current.runtimeGroupId)}/`;\n const boundaryIdx = executionOrder.findIndex(\n (e) => e.runtimeStageId === current.runtimeGroupId,\n );\n if (boundaryIdx < 0) return [];\n\n const raw: { runtimeStageId: string; label: string }[] = [];\n for (let i = boundaryIdx + 1; i < executionOrder.length; i++) {\n const e = executionOrder[i]!;\n const stripped = stripExecIndex(e.runtimeStageId);\n if (!stripped.startsWith(prefix)) break; // left the subflow → iteration done\n const rest = stripped.slice(prefix.length);\n if (rest.includes('/')) continue; // grandchild (inside a nested subflow) — skip\n // Label from the stage's display name. The engine prefixes a subflow stage's\n // name with its path (`sf-injection-engine/Gather`); strip ONLY that KNOWN\n // prefix — not any `/`, which would mangle a legit name like `Read/Write` or\n // `I/O` — and fall back to the already-prefix-free local id.\n const name = e.stageName ?? e.stageId ?? rest;\n const label = name.startsWith(prefix) ? name.slice(prefix.length) : name;\n raw.push({ runtimeStageId: e.runtimeStageId, label });\n }\n\n // Ordinal repeated labels (a nested loop could re-run an internal stage).\n const totals = new Map<string, number>();\n for (const r of raw) totals.set(r.label, (totals.get(r.label) ?? 0) + 1);\n const seen = new Map<string, number>();\n return raw.map((r) => {\n const n = (seen.get(r.label) ?? 0) + 1;\n seen.set(r.label, n);\n const label = (totals.get(r.label) ?? 0) > 1 ? `${r.label} ${n}` : r.label;\n return {\n runtimeStageId: r.runtimeStageId,\n runtimeGroupId: r.runtimeStageId,\n label,\n kind: 'commit' as const,\n depth: current.depth + 1,\n // No per-internal commit exists (subflow-scoped); anchor the data view to\n // the subflow's open commit so the details panel shows the subflow's state.\n commitIdx: current.opensAtCommitIdx,\n };\n });\n}\n\n/**\n * commitAxisPositions — the COMMIT axis: one scrub stop per EXECUTED STAGE,\n * straight off the recording's commit log. This is the Flow Lens's axis\n * (granularity `'step'`).\n *\n * Why it exists: the milestone axis above stops only at the moments the domain\n * classifies (iteration / context / LLM turn / route / tool call). That is the\n * right altitude for the GROUPED reading — but the per-step reading promises\n * \"every step\", and a ruler built from milestones skips the stages between\n * them (a 37-stage run got 17 stops; stage 23 was unreachable between stops 9\n * and 10). Here every executed stage IS a stop, so the ruler's count and the\n * inspector's \"stage N of M\" count the same unit and nothing is skippable.\n *\n * THE UNIT IS THE STAGE, NOT THE RAW COMMIT ROW: a boundary stage can commit\n * more than once under ONE runtimeStageId (a subflow mounts with an entry\n * commit and closes with an exit commit — footprintjs's law is one stage, one\n * CommitBundle; the doubles are the boundary's own bundles). Two stops with\n * one address would be the same place twice, so a stage's stops collapse to\n * ONE, anchored at its FIRST commit — the moment it started, which is\n * execution order (runtimeStageIds are stamped before the stage runs).\n *\n * The one-cursor law holds unchanged: each stop's `runtimeStageId` is the\n * stage's own, `commitIdx` is its first commit's index, and nothing here\n * stores state.\n *\n * Drilled scope: stops are the stages INSIDE the drilled group (membership by\n * the enclosing-group chain, never a bare index range — parallel siblings\n * interleave commits). A drilled subflow whose internals commit in its OWN\n * memory scope (only the mount boundary reaches the parent log) falls back to\n * the runtime overlay's executionOrder — the same source the milestone axis\n * uses for those internals (`subflowInternalPositions`).\n */\nexport function commitAxisPositions(\n groups: readonly Group[],\n commits: readonly CommitSyncEntry[],\n drillPath: readonly string[] = [],\n executionOrder?: readonly ExecOrderEntry[],\n): readonly CursorPosition[] {\n if (commits.length === 0) return [];\n\n const toStop = (c: CommitSyncEntry): CursorPosition => ({\n runtimeStageId: c.runtimeStageId,\n runtimeGroupId: c.runtimeGroupId !== '' ? c.runtimeGroupId : c.runtimeStageId,\n label: c.label,\n kind: 'commit',\n depth: c.depth,\n commitIdx: c.commitIdx,\n });\n\n // One stop per STAGE: collapse a stage's repeated commits (boundary\n // entry/exit bundles share the runtimeStageId) onto the first — execution\n // order, and the anchor every consumer reads state \"as of the start\" from.\n const oneStopPerStage = (list: readonly CommitSyncEntry[]): CursorPosition[] => {\n const seen = new Set<string>();\n const stops: CursorPosition[] = [];\n for (const c of list) {\n if (seen.has(c.runtimeStageId)) continue;\n seen.add(c.runtimeStageId);\n stops.push(toStop(c));\n }\n return ordinalizeLabels(stops);\n };\n\n // Top level: the whole log, one stop per executed stage, in execution order.\n if (drillPath.length === 0) return oneStopPerStage(commits);\n\n const current = currentGroup(groups, drillPath);\n if (!current) return [];\n\n // Membership by enclosing-group CHAIN (commit's innermost group is the\n // drilled group, or a descendant of it) — never a bare [opens..closes]\n // index range, which would capture interleaved parallel-sibling commits.\n const byId = new Map(groups.map((g) => [g.runtimeGroupId, g] as const));\n const inScope = commits.filter((c) => {\n let gid: string | undefined = c.runtimeGroupId;\n while (gid !== undefined && gid !== '') {\n if (gid === current.runtimeGroupId) return true;\n gid = byId.get(gid)?.parentGroupId;\n }\n return false;\n });\n\n // A drilled subflow whose stops are ONLY its own boundary commit(s) keeps\n // its internal commits in its own memory scope — the overlay's\n // executionOrder is the one place those stage executions appear.\n const stripped = stripExecIndex(current.runtimeGroupId);\n const hasInternals = inScope.some((c) => stripExecIndex(c.runtimeStageId) !== stripped);\n if (!hasInternals && executionOrder !== undefined && executionOrder.length > 0) {\n const overlayStops = subflowInternalPositions(current, executionOrder);\n if (overlayStops.length > 0) return overlayStops;\n }\n\n return oneStopPerStage(inScope);\n}\n\n/** Ordinal repeated labels (\"call-llm\" ×3 → \"call-llm 1/2/3\") so a list of\n * stops stays readable — same per-label rule the milestone axis uses. */\nfunction ordinalizeLabels(positions: readonly CursorPosition[]): CursorPosition[] {\n const totals = new Map<string, number>();\n for (const p of positions) totals.set(p.label, (totals.get(p.label) ?? 0) + 1);\n const seen = new Map<string, number>();\n return positions.map((p) => {\n const n = (seen.get(p.label) ?? 0) + 1;\n seen.set(p.label, n);\n return (totals.get(p.label) ?? 0) > 1 ? { ...p, label: `${p.label} ${n}` } : p;\n });\n}\n\nexport function cursorPositionsAtDrill(\n groups: readonly Group[],\n commits: readonly CommitSyncEntry[],\n drillPath: readonly string[],\n milestoneFor?: MilestoneClassifier,\n executionOrder?: readonly ExecOrderEntry[],\n): readonly CursorPosition[] {\n if (groups.length === 0) return [];\n const current = currentGroup(groups, drillPath);\n if (!current) return [];\n\n const positions: CursorPosition[] = [];\n\n // 1) Always emit the outer group's start (Run / Agent / LLMCall) so\n // the user has an anchor for \"I'm at the beginning of this view.\"\n positions.push({\n runtimeStageId: current.runtimeGroupId,\n runtimeGroupId: current.runtimeGroupId,\n label: current.isRoot ? 'Run · start' : `${current.name} · start`,\n kind: 'group-start',\n depth: current.depth,\n commitIdx: current.opensAtCommitIdx,\n });\n\n // 2) Middle positions. PREFER domain-declared MILESTONES (stage-by-stage\n // scrub stops: iteration → llm-turn → tool-call → decision) when a\n // classifier is supplied AND it actually classifies something at this\n // level. Otherwise FALL BACK to the structural walk (one stop per visible\n // step, descending transparently through compositions) — which keeps\n // multi-agent supervisor levels + plain control-flow charts scrubbing.\n const middle = milestoneFor\n ? milestonePositions(current, groups, commits, milestoneFor)\n : [];\n if (middle.length > 0) {\n positions.push(...middle);\n } else {\n const structural = structuralPositions(current, groups);\n if (structural.length > 0) {\n positions.push(...structural);\n // NOTE (exclusive-source assumption): structural and overlay-internal stops\n // are mutually exclusive here. A MIXED subflow — plain stages PLUS a nested\n // sub-subflow — would take this branch (the nested child group is structural)\n // and its plain stages would get no scrub stops. No shipping subflow has that\n // shape today (the Injection Engine is 4 plain stages → structural empty →\n // overlay branch below). When a mixed shape ships, make these additive:\n // merge structural child-group stops with overlay stops for plain stages not\n // covered by a child group, sorted by executionOrder position.\n } else if (drillPath.length > 0 && executionOrder && executionOrder.length > 0) {\n // Drilled into a subflow whose internals are NEITHER child groups NOR\n // parent-log commits (their commits live in the subflow's own memory\n // scope) — e.g. the Injection Engine's Gather/Evaluate/Route/Delta. The\n // milestone + structural paths above find nothing for it. Derive the\n // scrub stops from the runtime overlay's executionOrder, which is the one\n // place those stage executions DO appear, so the internals light on scrub.\n positions.push(...subflowInternalPositions(current, executionOrder));\n }\n }\n\n // 3) Emit the outer group's end position ONLY when the group has\n // actually closed — i.e., the chart has returned and root.exit\n // has fired. Previously this emitted unconditionally and fell\n // back to `opensAtCommitIdx` when `closesAtCommitIdx` was undefined,\n // which meant the slider showed `Run · end` from the moment the\n // chart started running — before any of the commits between start\n // and end had actually fired. Confusing UX: the user could scrub\n // to the \"end\" position while the LLM was still in flight.\n //\n // Now we gate on `closesAtCommitIdx !== undefined`. Mid-run the\n // slider shows positions for commits that have actually happened;\n // once the run completes the end position appears. The slider grows\n // progressively, matching the user's mental model of \"this is what\n // has happened so far.\"\n if ((current.isRoot || emitsEndPosition(current)) && current.closesAtCommitIdx !== undefined) {\n positions.push({\n runtimeStageId: current.runtimeGroupId,\n runtimeGroupId: current.runtimeGroupId,\n label: current.isRoot ? 'Run · end' : `${current.name} · end`,\n kind: 'group-end',\n depth: current.depth,\n commitIdx: current.closesAtCommitIdx,\n });\n }\n\n // The previous version of this function prepended/appended synthetic\n // `User · sends` / `User · receives` cursor positions to bookend the\n // top-level Run view. They were a workaround for atomic LLMCall having\n // only a single commit (run.entry/run.exit collapsed onto the root\n // group's runtimeGroupId, making the slider's start/end indistinguishable\n // for the runtime overlay).\n //\n // That workaround is no longer needed: LLMCall now wraps its\n // invocation in a real `sf-llm-call` subflow, giving atomic LLMCall\n // three real footprintjs commit positions at top level — root.start,\n // sf-llm-call#1, root.end — which the runtime overlay can discriminate\n // by runtimeStageId without any lens-side synthesis.\n //\n // The constants `LENS_USER_IN_RUNTIME_ID` / `LENS_USER_OUT_RUNTIME_ID`\n // and the `user-in` / `user-out` kind values stay exported on the\n // interface for now; nothing currently produces them, but the kinds\n // remain reserved for future affordances (e.g., chat-mode where\n // pause+resume around a Client stage could legitimately surface\n // user-facing scrub points).\n\n return positions;\n}\n","/**\n * `scrubAxisFor` — the scrub axis for a recording, as a PURE function.\n *\n * The same positions the mounted `<Lens>` scrubs at that granularity,\n * computable OUTSIDE React. `useCursorPositions` is the same computation with\n * a memo and a subscription around it; this is the one a non-React host calls.\n *\n * TWO AXES, ONE CURSOR (the 0.39.0 reading):\n *\n * `'step'` → the COMMIT axis: one stop per executed stage, straight off the\n * commit log. Nothing is skippable and the ruler's count is the\n * commit log's count by construction.\n * `'group'` → the MILESTONE axis: one stop per agent-meaningful moment\n * (iteration → context → LLM turn → route → tool call), as the\n * domain's `milestoneFor` classifies them.\n *\n * The cursor type is still `runtimeStageId` either way. Only the SET of valid\n * positions differs.\n *\n * Lives in `/core` (zero React) because the hosts that need it most have no\n * component mounted: a server-rendered dashboard link, a CLI, a chat answer\n * resolving the step it means with `resolveNavigation`.\n *\n * @example\n * ```ts\n * import { scrubAxisFor, resolveNavigation } from 'agentfootprint-lens/core';\n *\n * const positions = scrubAxisFor(recorder, 'step');\n * const to = resolveNavigation(positions, 'llm#7'); // → { ok: true, step: 7, … }\n * ```\n */\n\nimport { milestoneFor } from 'agentfootprint';\nimport type { LensRecorder } from '../LensRecorder.js';\nimport { buildGroups } from './buildGroups.js';\nimport { buildCommitSyncMap } from './buildCommitSyncMap.js';\nimport { commitAxisPositions, cursorPositionsAtDrill, type CursorPosition } from './cursorPositionsAtDrill.js';\n\n/** Which projection of the run the scrub axis stops at. */\nexport type ScrubAxis = 'commit' | 'milestone';\n\n/**\n * Build the axis a `<Lens>` at this granularity would scrub.\n *\n * @param recorder the recording (live `lensRecorder()` or a replayed one).\n * @param granularity `'step'` = the commit axis, `'group'` = the milestone axis\n * — the same two values `<Lens granularity>` takes.\n * @param drillPath the drill level to build the axis at (default: top level).\n * @returns the positions, or `[]` when the recording has no commits yet (or on\n * any read error — same posture as the hook).\n */\nexport function scrubAxisFor(\n recorder: LensRecorder,\n granularity: 'step' | 'group',\n drillPath: readonly string[] = [],\n): readonly CursorPosition[] {\n try {\n const groups = buildGroups(recorder.boundary.boundaryIndex);\n const commits = buildCommitSyncMap(recorder);\n const overlay = recorder.runtime.getOverlay();\n if (granularity === 'step') {\n const commitAxis = commitAxisPositions(groups, commits, drillPath, overlay.executionOrder);\n if (commitAxis.length > 0) return commitAxis;\n // A recording with NO commit log (a Trace replay carries only the\n // boundary log) still deserves an axis — the milestone/structural stops\n // are the finest truth it has. Never quieter than before 0.39.0.\n }\n return cursorPositionsAtDrill(groups, commits, drillPath, milestoneFor, overlay.executionOrder);\n } catch {\n return [];\n }\n}\n"],"mappings":"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","names":["milestoneFor"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveNavigation
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-KCAQVLTA.js";
|
|
4
4
|
|
|
5
5
|
// src/core/group/stepForRuntimeStageId.ts
|
|
6
6
|
function stepForRuntimeStageId(positions, runtimeStageId) {
|
|
@@ -961,4 +961,4 @@ export {
|
|
|
961
961
|
selectSkillTopology,
|
|
962
962
|
selectSkillFrameContext
|
|
963
963
|
};
|
|
964
|
-
//# sourceMappingURL=chunk-
|
|
964
|
+
//# sourceMappingURL=chunk-PCOHPEVI.js.map
|
|
@@ -2,14 +2,14 @@ import {
|
|
|
2
2
|
formatBytes,
|
|
3
3
|
stepForCommitIdx,
|
|
4
4
|
structureGraphFromRunner
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6II3EUOJ.js";
|
|
6
6
|
import {
|
|
7
7
|
stepForRuntimeStageId
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PCOHPEVI.js";
|
|
9
9
|
import {
|
|
10
10
|
foldCommitIdxOf,
|
|
11
11
|
lensCursorFrom
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-KCAQVLTA.js";
|
|
13
13
|
|
|
14
14
|
// src/core/cursor/sharedCursor.ts
|
|
15
15
|
function addressOf(position, drillPath) {
|
|
@@ -1212,7 +1212,11 @@ function isConsentDecision(value) {
|
|
|
1212
1212
|
// src/core/time/timeAxis.ts
|
|
1213
1213
|
import * as agentfootprint from "agentfootprint";
|
|
1214
1214
|
function door() {
|
|
1215
|
-
return
|
|
1215
|
+
return {
|
|
1216
|
+
readTimeAxis: agentfootprint.readTimeAxis,
|
|
1217
|
+
describeTimeAxis: agentfootprint.describeTimeAxis,
|
|
1218
|
+
normaliseInstants: agentfootprint.normaliseInstants
|
|
1219
|
+
};
|
|
1216
1220
|
}
|
|
1217
1221
|
function valuesOf(rows, axis) {
|
|
1218
1222
|
const normalise = door().normaliseInstants;
|
|
@@ -1232,12 +1236,12 @@ function valuesOf(rows, axis) {
|
|
|
1232
1236
|
function datasetTimeAxisOf(meta, rows) {
|
|
1233
1237
|
const raw = meta !== null && typeof meta === "object" ? meta.timeAxis : void 0;
|
|
1234
1238
|
if (raw === void 0) return { status: "absent" };
|
|
1235
|
-
const { readTimeAxis, describeTimeAxis } = door();
|
|
1236
|
-
if (typeof
|
|
1237
|
-
const reading =
|
|
1239
|
+
const { readTimeAxis: readTimeAxis2, describeTimeAxis: describeTimeAxis2 } = door();
|
|
1240
|
+
if (typeof readTimeAxis2 !== "function") return { status: "unjudged", raw };
|
|
1241
|
+
const reading = readTimeAxis2(meta);
|
|
1238
1242
|
if (reading.status === "absent") return { status: "absent" };
|
|
1239
1243
|
if (reading.status === "malformed") return { status: "malformed", issues: reading.issues };
|
|
1240
|
-
const summary = typeof
|
|
1244
|
+
const summary = typeof describeTimeAxis2 === "function" ? describeTimeAxis2(reading.axis) : void 0;
|
|
1241
1245
|
const values = rows !== void 0 ? valuesOf(rows, reading.axis) : void 0;
|
|
1242
1246
|
return {
|
|
1243
1247
|
status: "declared",
|
|
@@ -1291,4 +1295,4 @@ export {
|
|
|
1291
1295
|
isConsentDecision,
|
|
1292
1296
|
datasetTimeAxisOf
|
|
1293
1297
|
};
|
|
1294
|
-
//# sourceMappingURL=chunk-
|
|
1298
|
+
//# sourceMappingURL=chunk-V4GO5WKN.js.map
|