agentfootprint-lens 0.44.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +133 -0
  2. package/dist/{Lens-B039-UGw.d.cts → Lens-6elRg27j.d.cts} +22 -5
  3. package/dist/{Lens-ey1EgN9i.d.ts → Lens-Bdz-JzVm.d.ts} +22 -5
  4. package/dist/{chunk-OQRL3II6.js → chunk-2UOQEE4U.js} +114 -4
  5. package/dist/chunk-2UOQEE4U.js.map +1 -0
  6. package/dist/{chunk-LRGT7UQL.js → chunk-A4EIPCLY.js} +52 -31
  7. package/dist/chunk-A4EIPCLY.js.map +1 -0
  8. package/dist/{chunk-QNMA25X5.js → chunk-AUGRQKRL.js} +18 -6
  9. package/dist/chunk-AUGRQKRL.js.map +1 -0
  10. package/dist/{chunk-CGR5VFJU.js → chunk-SJEKKUTI.js} +113 -3
  11. package/dist/chunk-SJEKKUTI.js.map +1 -0
  12. package/dist/{chunk-5YSPWSLW.js → chunk-WN2RJKEH.js} +2 -2
  13. package/dist/core.cjs +125 -11
  14. package/dist/core.cjs.map +1 -1
  15. package/dist/core.d.cts +3 -3
  16. package/dist/core.d.ts +3 -3
  17. package/dist/core.js +6 -2
  18. package/dist/index.cjs +302 -46
  19. package/dist/index.cjs.map +1 -1
  20. package/dist/index.d.cts +48 -8
  21. package/dist/index.d.ts +48 -8
  22. package/dist/index.js +9 -5
  23. package/dist/index.js.map +1 -1
  24. package/dist/{scrubAxisFor-Cz214OHj.d.ts → lensStops-DAc3GB8G.d.ts} +157 -1
  25. package/dist/{scrubAxisFor-CNceBlz-.d.cts → lensStops-hQ10t3Lt.d.cts} +157 -1
  26. package/dist/{selectSkillFrameContext-CT_EKu4u.d.cts → selectSkillFrameContext-DJkvrggG.d.cts} +1 -1
  27. package/dist/{selectSkillFrameContext-D0xQV_px.d.ts → selectSkillFrameContext-IPsw4Pbj.d.ts} +1 -1
  28. package/dist/skillgraph.cjs +129 -7
  29. package/dist/skillgraph.cjs.map +1 -1
  30. package/dist/skillgraph.d.cts +2 -2
  31. package/dist/skillgraph.d.ts +2 -2
  32. package/dist/skillgraph.js +2 -2
  33. package/dist/why.cjs +186 -40
  34. package/dist/why.cjs.map +1 -1
  35. package/dist/why.d.cts +5 -4
  36. package/dist/why.d.ts +5 -4
  37. package/dist/why.js +7 -3
  38. package/dist/why.js.map +1 -1
  39. package/package.json +3 -3
  40. package/dist/chunk-CGR5VFJU.js.map +0 -1
  41. package/dist/chunk-LRGT7UQL.js.map +0 -1
  42. package/dist/chunk-OQRL3II6.js.map +0 -1
  43. package/dist/chunk-QNMA25X5.js.map +0 -1
  44. /package/dist/{chunk-5YSPWSLW.js.map → chunk-WN2RJKEH.js.map} +0 -0
package/README.md CHANGED
@@ -808,6 +808,133 @@ const href = to.ok ? `/runs/${runId}?step=${to.step}` : undefined;
808
808
 
809
809
  ---
810
810
 
811
+ ## Time travel through one port
812
+
813
+ **Nothing to do — this is how the cursor already moves.** Read it if you are
814
+ building a second surface over the same run, or wondering why the ruler behaves
815
+ identically in the Why Lens, the Flow Lens and your own view.
816
+
817
+ ### Why
818
+
819
+ Every consumer of a footprintjs run was writing the same six lines: clamp at
820
+ zero, clamp at the end, resolve an address to a stop, decide what a miss does.
821
+ The lens had them in `<TimeTravel>`, the flow view had its own copy, and a
822
+ dashboard had a third — and they disagreed at exactly the places nobody tests: a
823
+ ▶ at the last stop, a jump to a stage that is not on this ruler, a step stored
824
+ from a longer run.
825
+
826
+ footprintjs 9.17 owns that arithmetic now. `timeTravel(snapshot, { strategy })`
827
+ is the **reader's cursor over a finished trace**: `first` / `last` / `prev` /
828
+ `next` / `jumpTo`, one position, and one law — **a refused move never moves the
829
+ cursor**, it returns the reason and the nearest stop it could name. Since
830
+ 0.46.0 the lens's movement goes through it.
831
+
832
+ **Which stops exist did not change.** The lens still computes its own axis —
833
+ milestones banded by iteration for the Why reading, one stop per executed stage
834
+ for the Flow reading, drill-scoped either way — with the same labels, the same
835
+ order and the same kinds. Those positions ARE the port's stops, through a
836
+ lens-owned `TimeTravelStrategy`. The library decides *where a move lands*; the
837
+ lens decides *what there is to land on*.
838
+
839
+ **Still one cursor.** The port instance is a calculator, not a position: it is
840
+ re-seated on the step the lens owns before every question and remembers nothing
841
+ between them. `<Lens step onStepChange>` is unchanged, and so is everything a
842
+ panel reads.
843
+
844
+ ### The port, headless
845
+
846
+ ```ts
847
+ import { scrubAxisFor, openLensCursor } from 'agentfootprint-lens/core';
848
+
849
+ const positions = scrubAxisFor(recorder, 'group'); // the ruler, as data
850
+ const cursor = openLensCursor(positions);
851
+
852
+ cursor.next(4).step; // 5
853
+ cursor.next(positions.length - 1);
854
+ // { step: <last>, moved: false, reason: 'clamped', clamped: false }
855
+
856
+ cursor.toStep(2, 9999);
857
+ // { step: <last>, moved: true, clamped: true } ← off the axis, said so
858
+
859
+ cursor.toAddress(2, 'call-llm#18');
860
+ // { ok: true, step: 7 }
861
+ cursor.toAddress(2, 'never-ran#4');
862
+ // { ok: false, reason: 'miss', nearest: <the closest stop the library
863
+ // could name — the same stage at another execution index if this axis
864
+ // holds one, otherwise the last stop whose execution index is not past
865
+ // the one you asked for> }
866
+ // the cursor is still at 2 — a miss is offered, never taken
867
+
868
+ cursor.positionAt(7);
869
+ // the LENS position: runtimeGroupId, depth, coActiveGroupIds, milestone —
870
+ // everything the port's `Stop` has no slot for, unchanged and unflattened
871
+ ```
872
+
873
+ `openLensCursor` answers **where**, never **what**: it has no `stateAt`, no
874
+ `changedSince`, no `drill`. Those are folds over the run's commit log, and a
875
+ fold wants the run's snapshot — which movement never reads. Want them over this
876
+ same ruler? Build the library's cursor directly, with the lens's stops:
877
+
878
+ ```ts
879
+ import { timeTravel } from 'footprintjs/trace';
880
+ import { scrubAxisFor, lensStopsStrategy } from 'agentfootprint-lens/core';
881
+
882
+ // A LIVE run. `runner.getLastSnapshot()` IS footprintjs's own snapshot, so it
883
+ // is already a `TimeTravelSource` and the two libraries meet with no cast.
884
+ const snapshot = runner.getLastSnapshot();
885
+ if (snapshot) {
886
+ const positions = scrubAxisFor(recorder, 'group');
887
+ const tt = timeTravel(snapshot, { strategy: lensStopsStrategy(positions) });
888
+ tt.jumpTo(7);
889
+ tt.changedSince(); // the keys this stop's slice of the log wrote
890
+ tt.stateAt().state; // state as of that stop, detached
891
+ }
892
+ ```
893
+
894
+ A **stored** recording needs one narrowing today, and the reason is honest on
895
+ both sides: the lens types `Recording.snapshot.commitLog` as `readonly
896
+ unknown[]` — a recording arrives as parsed JSON, and the lens will not claim
897
+ that what came back off disk is a `CommitBundle` — while `timeTravel` takes
898
+ `TimeTravelSource`, whose `commitLog` is `readonly CommitBundle[]`. So a
899
+ replayed run has to say so at the seam:
900
+
901
+ ```ts
902
+ import type { TimeTravelSource } from 'footprintjs/trace';
903
+
904
+ const stored = recording.snapshot as unknown as TimeTravelSource;
905
+ const tt = timeTravel(stored, { strategy: lensStopsStrategy(positions) });
906
+ ```
907
+
908
+ That cast disappears — with no change here — the day footprintjs widens
909
+ `TimeTravelSource.commitLog` to accept an unvalidated `readonly unknown[]` and
910
+ does the per-bundle narrowing where it folds, which is the only place that can
911
+ honestly do it. It is reported upstream as a port gap. **Movement never needs
912
+ this**: `openLensCursor` reads the stops and no snapshot at all, which is why
913
+ the lens's own cursor is unaffected either way.
914
+
915
+ ### Two axes, and drilling
916
+
917
+ Rebuild the port when the positions change; its identity is that list's, which
918
+ is already memoised per (recording, axis, drill level):
919
+
920
+ ```ts
921
+ const port = useMemo(() => openLensCursor(positions), [positions]);
922
+ ```
923
+
924
+ Drilling rebuilds the axis — the drilled group's own stops, which is a
925
+ different set — so it rebuilds the port. The lens does **not** use the port's
926
+ `drill()` for this: `drill()` opens a subflow's own commit log, and a lens drill
927
+ level is not always a subflow (it is a group, and a subflow whose stages commit
928
+ in their own memory scope is reconstructed from the runtime overlay). Using it
929
+ would change which stops appear at a drill level, which is the one thing this
930
+ change refuses to do.
931
+
932
+ `snapSteps` also stays the lens's. Snaps narrow ◀ ▶ to a *subset* of the axis
933
+ while the cursor may be parked between two of them — a question `jumpTo` does
934
+ not answer, since it takes an exact stop.
935
+
936
+ ---
937
+
811
938
  ## Rendering your own detail pane
812
939
 
813
940
  `slots.detail` replaces the CONTENT of the shipped right column. The column
@@ -1112,6 +1239,12 @@ so instead of drawing an empty graph.
1112
1239
  / CLI view on the same primitives — see `ChangeNotifier`'s JSDoc for adapter
1113
1240
  snippets.
1114
1241
 
1242
+ The cursor is headless too: `scrubAxisFor` builds the ruler, `openLensCursor`
1243
+ moves along it through footprintjs's `timeTravel()` port, `resolveNavigation`
1244
+ turns an address into a step with every rung named, and `stepForCommitIdx`
1245
+ carries a position between the two axes. See
1246
+ [Time travel through one port](#time-travel-through-one-port).
1247
+
1115
1248
  ---
1116
1249
 
1117
1250
  ## Why this design
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
4
4
  import { CommentaryTemplates } from 'agentfootprint';
5
5
  import React__default, { Ref } from 'react';
6
6
  import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.cjs';
7
- import { C as ChartGroupHighlight, H as Humanizer } from './scrubAxisFor-CNceBlz-.cjs';
7
+ import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-hQ10t3Lt.cjs';
8
8
  import { NodeTypes } from '@xyflow/react';
9
9
  import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
10
10
 
@@ -153,6 +153,7 @@ declare const LensFlow: React__default.FC<LensFlowProps>;
153
153
  * Omit `controlledStep` and nothing changes: the hook keeps today's internal
154
154
  * state and today's auto-advance, byte for byte.
155
155
  */
156
+
156
157
  /**
157
158
  * Where the cursor landed, in all three units the lens knows — handed to
158
159
  * `onStepChange` alongside the step so a host never has to reverse-engineer
@@ -178,9 +179,13 @@ interface LensCursorAt {
178
179
  /** What kind of stop this is. Absent when the axis is empty. */
179
180
  readonly kind?: string;
180
181
  /**
181
- * `true` when this call is Lens CORRECTING an out-of-range `step` you
182
- * passed (or one an internal jump produced), not a move someone made.
183
- * Store the corrected value and the two cursors agree again.
182
+ * `true` when this call is Lens CORRECTING a step that is not a position on
183
+ * the axis it is now read against, not a move someone made. That is an
184
+ * out-of-range `step` you passed, one an internal jump produced, OR — when
185
+ * you passed no `step` at all — the lens's own remembered step after the
186
+ * axis shrank under it. Uncontrolled snaps are reported too, so this flag
187
+ * means the same thing in both modes. Store the corrected value and the two
188
+ * cursors agree again.
184
189
  */
185
190
  readonly clamped: boolean;
186
191
  }
@@ -201,6 +206,18 @@ interface UseLensCursorArgs {
201
206
  readonly maxStep: number;
202
207
  /** Resolve a step to its address. Called only when a move is reported. */
203
208
  readonly describe: (step: number) => LensCursorPlace;
209
+ /**
210
+ * The MOVEMENT port — footprintjs 9.17's reader cursor over the Lens's own
211
+ * stops (`openLensCursor(positions)`). When present, every move this funnel
212
+ * makes is decided by the library: where a step lands, what happens at the
213
+ * ends of the axis, and what an out-of-range ask does. Omit it and the
214
+ * funnel keeps its own arithmetic, unchanged — which is what the hook's own
215
+ * tests drive, so the two readings can be compared.
216
+ *
217
+ * It is NOT a second cursor: the port is re-seated on the step this hook
218
+ * owns before every question and remembers nothing between them.
219
+ */
220
+ readonly port?: LensCursorPort | undefined;
204
221
  }
205
222
  interface UseLensCursorResult {
206
223
  /** The cursor to render. */
@@ -215,7 +232,7 @@ interface UseLensCursorResult {
215
232
  * are not positions, so they clamp too — and the clamp is always reported.
216
233
  */
217
234
  declare function clampStep(n: number, maxStep: number): number;
218
- declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, }: UseLensCursorArgs): UseLensCursorResult;
235
+ declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, port, }: UseLensCursorArgs): UseLensCursorResult;
219
236
 
220
237
  /**
221
238
  * useLensNavigator — move the ONE cursor to a stage by its address.
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
4
4
  import { CommentaryTemplates } from 'agentfootprint';
5
5
  import React__default, { Ref } from 'react';
6
6
  import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.js';
7
- import { C as ChartGroupHighlight, H as Humanizer } from './scrubAxisFor-Cz214OHj.js';
7
+ import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-DAc3GB8G.js';
8
8
  import { NodeTypes } from '@xyflow/react';
9
9
  import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
10
10
 
@@ -153,6 +153,7 @@ declare const LensFlow: React__default.FC<LensFlowProps>;
153
153
  * Omit `controlledStep` and nothing changes: the hook keeps today's internal
154
154
  * state and today's auto-advance, byte for byte.
155
155
  */
156
+
156
157
  /**
157
158
  * Where the cursor landed, in all three units the lens knows — handed to
158
159
  * `onStepChange` alongside the step so a host never has to reverse-engineer
@@ -178,9 +179,13 @@ interface LensCursorAt {
178
179
  /** What kind of stop this is. Absent when the axis is empty. */
179
180
  readonly kind?: string;
180
181
  /**
181
- * `true` when this call is Lens CORRECTING an out-of-range `step` you
182
- * passed (or one an internal jump produced), not a move someone made.
183
- * Store the corrected value and the two cursors agree again.
182
+ * `true` when this call is Lens CORRECTING a step that is not a position on
183
+ * the axis it is now read against, not a move someone made. That is an
184
+ * out-of-range `step` you passed, one an internal jump produced, OR — when
185
+ * you passed no `step` at all — the lens's own remembered step after the
186
+ * axis shrank under it. Uncontrolled snaps are reported too, so this flag
187
+ * means the same thing in both modes. Store the corrected value and the two
188
+ * cursors agree again.
184
189
  */
185
190
  readonly clamped: boolean;
186
191
  }
@@ -201,6 +206,18 @@ interface UseLensCursorArgs {
201
206
  readonly maxStep: number;
202
207
  /** Resolve a step to its address. Called only when a move is reported. */
203
208
  readonly describe: (step: number) => LensCursorPlace;
209
+ /**
210
+ * The MOVEMENT port — footprintjs 9.17's reader cursor over the Lens's own
211
+ * stops (`openLensCursor(positions)`). When present, every move this funnel
212
+ * makes is decided by the library: where a step lands, what happens at the
213
+ * ends of the axis, and what an out-of-range ask does. Omit it and the
214
+ * funnel keeps its own arithmetic, unchanged — which is what the hook's own
215
+ * tests drive, so the two readings can be compared.
216
+ *
217
+ * It is NOT a second cursor: the port is re-seated on the step this hook
218
+ * owns before every question and remembers nothing between them.
219
+ */
220
+ readonly port?: LensCursorPort | undefined;
204
221
  }
205
222
  interface UseLensCursorResult {
206
223
  /** The cursor to render. */
@@ -215,7 +232,7 @@ interface UseLensCursorResult {
215
232
  * are not positions, so they clamp too — and the clamp is always reported.
216
233
  */
217
234
  declare function clampStep(n: number, maxStep: number): number;
218
- declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, }: UseLensCursorArgs): UseLensCursorResult;
235
+ declare function useLensCursor({ controlledStep, onStepChange, maxStep, describe, port, }: UseLensCursorArgs): UseLensCursorResult;
219
236
 
220
237
  /**
221
238
  * useLensNavigator — move the ONE cursor to a stage by its address.
@@ -6,7 +6,7 @@ import {
6
6
  describeReceived,
7
7
  refusalDestinationFor,
8
8
  useNarrowRow
9
- } from "./chunk-QNMA25X5.js";
9
+ } from "./chunk-AUGRQKRL.js";
10
10
  import {
11
11
  selectSkillBeatAt,
12
12
  selectSkillBeats,
@@ -608,7 +608,7 @@ var CAUSE_LABEL = {
608
608
  stay: "nothing moved it",
609
609
  none: "no skill was in play"
610
610
  };
611
- function RouteDecisionCard({ beat, lens }) {
611
+ function RouteDecisionCard({ beat, turnStart, lens }) {
612
612
  return /* @__PURE__ */ jsx4(
613
613
  "aside",
614
614
  {
@@ -736,6 +736,7 @@ function RouteDecisionCard({ beat, lens }) {
736
736
  children: beat.notes.map((n, i) => /* @__PURE__ */ jsx4("li", { children: n }, i))
737
737
  }
738
738
  ),
739
+ turnStart !== void 0 && (turnStart.by === "intent" || turnStart.by === "continuity") && /* @__PURE__ */ jsx4(ScoreTable, { start: turnStart }),
739
740
  lens === "developer" && beat.runtimeStageId !== void 0 && /* @__PURE__ */ jsx4("div", { style: { marginTop: 8, fontFamily: T.fontMono, fontSize: 10, color: T.textMuted }, children: beat.runtimeStageId })
740
741
  ] })
741
742
  }
@@ -761,6 +762,104 @@ function Evidence({
761
762
  children
762
763
  ] });
763
764
  }
765
+ function ScoreTable({ start }) {
766
+ if (start.scores.length === 0) return null;
767
+ const floor = start.policy?.floor;
768
+ const margin = start.policy?.nearTieMargin;
769
+ return /* @__PURE__ */ jsxs4("div", { style: { marginTop: 8 }, children: [
770
+ /* @__PURE__ */ jsxs4(
771
+ "div",
772
+ {
773
+ style: {
774
+ fontSize: 10,
775
+ letterSpacing: "0.08em",
776
+ textTransform: "uppercase",
777
+ color: T.textMuted,
778
+ marginBottom: 4
779
+ },
780
+ children: [
781
+ "Scored",
782
+ start.scorer ? ` by ${start.scorer}` : "",
783
+ floor !== void 0 ? ` \xB7 floor ${floor}` : "",
784
+ start.runnerUp !== void 0 && margin !== void 0 ? ` \xB7 gap ${fmt(start.runnerUp.gap)} of ${margin} needed` : ""
785
+ ]
786
+ }
787
+ ),
788
+ /* @__PURE__ */ jsxs4(
789
+ "table",
790
+ {
791
+ style: { borderCollapse: "collapse", width: "100%", fontSize: 12 },
792
+ children: [
793
+ /* @__PURE__ */ jsx4(
794
+ "caption",
795
+ {
796
+ style: {
797
+ captionSide: "top",
798
+ textAlign: "left",
799
+ fontSize: 11,
800
+ color: T.textMuted,
801
+ paddingBottom: 3
802
+ },
803
+ children: "Every candidate this turn was scored against, best first."
804
+ }
805
+ ),
806
+ /* @__PURE__ */ jsx4("tbody", { children: start.scores.map((row) => {
807
+ const belowFloor = floor !== void 0 && row.score <= floor;
808
+ const won = row.id === start.to;
809
+ return /* @__PURE__ */ jsxs4("tr", { children: [
810
+ /* @__PURE__ */ jsxs4(
811
+ "td",
812
+ {
813
+ style: {
814
+ padding: "2px 6px 2px 0",
815
+ color: belowFloor ? T.textMuted : T.textPrimary,
816
+ fontWeight: won ? 700 : 400,
817
+ fontFamily: T.fontMono
818
+ },
819
+ children: [
820
+ won ? "\u2192 " : " ",
821
+ row.id
822
+ ]
823
+ }
824
+ ),
825
+ /* @__PURE__ */ jsx4(
826
+ "td",
827
+ {
828
+ style: {
829
+ padding: "2px 6px",
830
+ textAlign: "right",
831
+ fontFamily: T.fontMono,
832
+ color: belowFloor ? T.textMuted : T.textPrimary
833
+ },
834
+ children: fmt(row.score)
835
+ }
836
+ ),
837
+ /* @__PURE__ */ jsxs4(
838
+ "td",
839
+ {
840
+ style: {
841
+ padding: "2px 6px",
842
+ textAlign: "right",
843
+ fontFamily: T.fontMono,
844
+ color: T.textMuted
845
+ },
846
+ children: [
847
+ Math.round(row.relevance * 100),
848
+ "%"
849
+ ]
850
+ }
851
+ ),
852
+ /* @__PURE__ */ jsx4("td", { style: { padding: "2px 0 2px 6px", color: T.textMuted, fontSize: 11 }, children: belowFloor ? "below floor" : won ? "" : "beaten" })
853
+ ] }, row.id);
854
+ }) })
855
+ ]
856
+ }
857
+ )
858
+ ] });
859
+ }
860
+ function fmt(n) {
861
+ return String(Math.round(n * 1e3) / 1e3);
862
+ }
764
863
 
765
864
  // src/react/skillgraph/skillTopologyPositions.ts
766
865
  function skillTopologyPositions(nodeIds, edges) {
@@ -1123,6 +1222,10 @@ function SkillGraphDebugger({
1123
1222
  [beats, cursorRuntimeStageId, cursorKind]
1124
1223
  );
1125
1224
  const activeIndex = activeBeat?.index;
1225
+ const activeTurnStart = useMemo2(
1226
+ () => activeBeat === void 0 || route === void 0 ? void 0 : route.turns.find((t) => t.turnIndex === activeBeat.turnIndex),
1227
+ [route, activeBeat]
1228
+ );
1126
1229
  const topology = useMemo2(
1127
1230
  () => route !== void 0 ? selectSkillTopology({
1128
1231
  route,
@@ -1264,7 +1367,14 @@ function SkillGraphDebugger({
1264
1367
  onPickSkill: jumpToSkill
1265
1368
  }
1266
1369
  ) }),
1267
- /* @__PURE__ */ jsx6(RouteDecisionCard, { ...activeBeat !== void 0 ? { beat: activeBeat } : {}, lens })
1370
+ /* @__PURE__ */ jsx6(
1371
+ RouteDecisionCard,
1372
+ {
1373
+ ...activeBeat !== void 0 ? { beat: activeBeat } : {},
1374
+ ...activeTurnStart !== void 0 ? { turnStart: activeTurnStart } : {},
1375
+ lens
1376
+ }
1377
+ )
1268
1378
  ]
1269
1379
  }
1270
1380
  ),
@@ -1382,4 +1492,4 @@ export {
1382
1492
  SKILL_GRAPH_READS,
1383
1493
  SkillGraphDebugger
1384
1494
  };
1385
- //# sourceMappingURL=chunk-OQRL3II6.js.map
1495
+ //# sourceMappingURL=chunk-2UOQEE4U.js.map