agentfootprint-lens 0.20.0 → 0.23.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-N3YJKWK5.js → chunk-ZJKD43L7.js} +146 -13
- package/dist/chunk-ZJKD43L7.js.map +1 -0
- package/dist/core.cjs +148 -12
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +6 -2327
- package/dist/core.d.ts +6 -2327
- package/dist/core.js +7 -1
- package/dist/index-BH_ctbLK.d.cts +2504 -0
- package/dist/index-BH_ctbLK.d.ts +2504 -0
- package/dist/index.cjs +1053 -315
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +254 -7
- package/dist/index.d.ts +254 -7
- package/dist/index.js +880 -276
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/chunk-N3YJKWK5.js.map +0 -1
|
@@ -306,6 +306,7 @@ function relTime(runStartMs) {
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// src/core/LensRecorder.ts
|
|
309
|
+
var DEFAULT_MAX_EVENTS = 5e4;
|
|
309
310
|
var KNOWN_EVENT_TYPES = new Set(ALL_EVENT_TYPES);
|
|
310
311
|
var LensRecorder = class {
|
|
311
312
|
constructor(rootLabel = "Run", options = {}) {
|
|
@@ -387,7 +388,18 @@ var LensRecorder = class {
|
|
|
387
388
|
/** Unknown types already warned about — warn ONCE per type, not per
|
|
388
389
|
* event, so a chatty unknown emitter can't flood the console. */
|
|
389
390
|
this.warnedUnknownTypes = /* @__PURE__ */ new Set();
|
|
391
|
+
/** Entries evicted by the cap so far. Surfaced via `getDiagnostics()`. */
|
|
392
|
+
this.droppedEventCount = 0;
|
|
393
|
+
/** Eviction already warned about — warn ONCE per run, not per batch. */
|
|
394
|
+
this.warnedEviction = false;
|
|
390
395
|
this.debug = options.debug;
|
|
396
|
+
const cap = options.maxEvents ?? DEFAULT_MAX_EVENTS;
|
|
397
|
+
if (cap !== Number.POSITIVE_INFINITY && (!Number.isInteger(cap) || cap < 1)) {
|
|
398
|
+
throw new RangeError(
|
|
399
|
+
`LensRecorder: maxEvents must be a positive integer or Infinity, got ${cap}`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
this.maxEvents = cap;
|
|
391
403
|
this.root = {
|
|
392
404
|
id: "run-root",
|
|
393
405
|
kind: "run",
|
|
@@ -417,6 +429,8 @@ var LensRecorder = class {
|
|
|
417
429
|
this.unknownEventTypes.clear();
|
|
418
430
|
this.bracketMismatchCount = 0;
|
|
419
431
|
this.warnedUnknownTypes.clear();
|
|
432
|
+
this.droppedEventCount = 0;
|
|
433
|
+
this.warnedEviction = false;
|
|
420
434
|
this.liveState.clear();
|
|
421
435
|
this.boundary.clear();
|
|
422
436
|
this.runtime.reset();
|
|
@@ -436,14 +450,18 @@ var LensRecorder = class {
|
|
|
436
450
|
* `composition.exit`, ...) whose kind didn't match the top of the
|
|
437
451
|
* build stack (malformed ordering). The close is skipped; the
|
|
438
452
|
* tree stays partially structured rather than crashing.
|
|
453
|
+
* - `droppedEvents` — entries evicted by the `maxEvents` FIFO cap
|
|
454
|
+
* (U3). When non-zero, log-derived views cover only the retained
|
|
455
|
+
* tail of the run.
|
|
439
456
|
*
|
|
440
|
-
*
|
|
441
|
-
* Returns a fresh snapshot object on every call.
|
|
457
|
+
* All are `{}` / `0` on a well-formed run that stayed under the cap.
|
|
458
|
+
* Reset by `clear()`. Returns a fresh snapshot object on every call.
|
|
442
459
|
*/
|
|
443
460
|
getDiagnostics() {
|
|
444
461
|
return {
|
|
445
462
|
unknownEventTypes: Object.fromEntries(this.unknownEventTypes),
|
|
446
|
-
bracketMismatches: this.bracketMismatchCount
|
|
463
|
+
bracketMismatches: this.bracketMismatchCount,
|
|
464
|
+
droppedEvents: this.droppedEventCount
|
|
447
465
|
};
|
|
448
466
|
}
|
|
449
467
|
/** Whether diagnostic warnings go to the console: explicit option
|
|
@@ -604,8 +622,52 @@ var LensRecorder = class {
|
|
|
604
622
|
this.top().events.push(entry);
|
|
605
623
|
this.noteUnknownType(event.type);
|
|
606
624
|
this.dispatch(event, runOffsetMs, entry);
|
|
625
|
+
this.enforceCap();
|
|
607
626
|
this.bumpVersion();
|
|
608
627
|
}
|
|
628
|
+
/**
|
|
629
|
+
* U3 — enforce the `maxEvents` FIFO cap. When the store exceeds the
|
|
630
|
+
* cap, evict the oldest entries down to ~90% of the cap in ONE batch
|
|
631
|
+
* (amortized O(1) per event: one O(retained) rebuild per ~10%-of-cap
|
|
632
|
+
* pushes), then prune the SAME evicted entries from every run-tree
|
|
633
|
+
* node's `events` list — entry objects are shared references, so
|
|
634
|
+
* skipping the tree would hide the memory, not release it.
|
|
635
|
+
*
|
|
636
|
+
* `SequenceStore` is append-only by design (no removal API), so the
|
|
637
|
+
* batch rebuild (clear + re-push retained) is the supported eviction
|
|
638
|
+
* path; the per-step key + range indices rebuild correctly during
|
|
639
|
+
* re-push.
|
|
640
|
+
*/
|
|
641
|
+
enforceCap() {
|
|
642
|
+
if (this.store.size <= this.maxEvents) return;
|
|
643
|
+
const all = this.store.getAll();
|
|
644
|
+
const evictBatch = Math.max(1, Math.floor(this.maxEvents / 10));
|
|
645
|
+
const retainCount = Math.max(1, this.maxEvents - evictBatch);
|
|
646
|
+
const dropCount = all.length - retainCount;
|
|
647
|
+
const retained = all.slice(dropCount);
|
|
648
|
+
this.store.clear();
|
|
649
|
+
for (const e of retained) this.store.push(e);
|
|
650
|
+
this.droppedEventCount += dropCount;
|
|
651
|
+
this.pruneNodeEvents(this.root, retained[0].seq);
|
|
652
|
+
if (this.debugEnabled() && !this.warnedEviction) {
|
|
653
|
+
this.warnedEviction = true;
|
|
654
|
+
console.warn(
|
|
655
|
+
`[lens] LensRecorder: maxEvents cap (${this.maxEvents}) reached \u2014 evicting oldest events (FIFO, ~10% per batch). Log-derived views now cover only the retained tail; evicted total in getDiagnostics().droppedEvents. Raise the cap via lensRecorder('Run', { maxEvents }) or pass Infinity to disable. (Warned once.)`
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
/** Drop entries with `seq < minSeq` from a node's `events` list (and
|
|
660
|
+
* its descendants'). Per-node lists are seq-ordered, so this is a
|
|
661
|
+
* prefix splice — in place, preserving the node object identity the
|
|
662
|
+
* build stack may still hold. */
|
|
663
|
+
pruneNodeEvents(node, minSeq) {
|
|
664
|
+
if (node.events.length > 0 && node.events[0].seq < minSeq) {
|
|
665
|
+
let keepFrom = 0;
|
|
666
|
+
while (keepFrom < node.events.length && node.events[keepFrom].seq < minSeq) keepFrom++;
|
|
667
|
+
node.events.splice(0, keepFrom);
|
|
668
|
+
}
|
|
669
|
+
for (const child of node.children) this.pruneNodeEvents(child, minSeq);
|
|
670
|
+
}
|
|
609
671
|
/** Notify all subscribers + bump version. Delegated to ChangeNotifier. */
|
|
610
672
|
bumpVersion() {
|
|
611
673
|
this.notifier.notify();
|
|
@@ -904,7 +966,13 @@ var LensRecorder = class {
|
|
|
904
966
|
}
|
|
905
967
|
/** Summary stats — computed lazily via `store.aggregate()`.
|
|
906
968
|
* Single-pass fold; types derived from the AgentfootprintEvent
|
|
907
|
-
* discriminated union.
|
|
969
|
+
* discriminated union.
|
|
970
|
+
*
|
|
971
|
+
* U3 caveat: once the `maxEvents` cap has evicted entries
|
|
972
|
+
* (`getDiagnostics().droppedEvents > 0`), the folded counts/tokens
|
|
973
|
+
* reflect only RETAINED events. `startedAt` / `durationMs` stay
|
|
974
|
+
* anchored to the true first event of the run (tracked outside the
|
|
975
|
+
* store), so the time axis never shifts. */
|
|
908
976
|
selectSummary() {
|
|
909
977
|
const init = {
|
|
910
978
|
llmCallCount: 0,
|
|
@@ -944,7 +1012,7 @@ var LensRecorder = class {
|
|
|
944
1012
|
}
|
|
945
1013
|
}, init);
|
|
946
1014
|
const entries = this.store.getAll();
|
|
947
|
-
const startedAt = entries[0]?.wallClockMs ?? 0;
|
|
1015
|
+
const startedAt = this.runStartMs ?? entries[0]?.wallClockMs ?? 0;
|
|
948
1016
|
const endedAt = entries[entries.length - 1]?.wallClockMs;
|
|
949
1017
|
return {
|
|
950
1018
|
startedAt,
|
|
@@ -1658,6 +1726,47 @@ function selectCommentaryRanges(boundary) {
|
|
|
1658
1726
|
}));
|
|
1659
1727
|
}
|
|
1660
1728
|
|
|
1729
|
+
// src/core/selectors/selectToolChoiceCall.ts
|
|
1730
|
+
function execIndex(runtimeStageId) {
|
|
1731
|
+
const hash = runtimeStageId.lastIndexOf("#");
|
|
1732
|
+
if (hash < 0) return void 0;
|
|
1733
|
+
const n = Number(runtimeStageId.slice(hash + 1));
|
|
1734
|
+
return Number.isInteger(n) && n >= 0 ? n : void 0;
|
|
1735
|
+
}
|
|
1736
|
+
function selectToolChoiceCall(calls, cursorRuntimeStageId, cursorKind) {
|
|
1737
|
+
if (calls.length === 0) return void 0;
|
|
1738
|
+
const last = calls[calls.length - 1];
|
|
1739
|
+
if (cursorKind === "user-in") return void 0;
|
|
1740
|
+
if (cursorKind === "user-out") return last;
|
|
1741
|
+
if (!cursorRuntimeStageId) return last;
|
|
1742
|
+
const base = cursorRuntimeStageId.split("#")[0];
|
|
1743
|
+
if (base === "__root__") {
|
|
1744
|
+
return cursorKind === "group-start" ? void 0 : last;
|
|
1745
|
+
}
|
|
1746
|
+
const exact = calls.find((c) => c.runtimeStageId === cursorRuntimeStageId);
|
|
1747
|
+
if (exact) return exact;
|
|
1748
|
+
const cursorIdx = execIndex(cursorRuntimeStageId);
|
|
1749
|
+
if (cursorIdx === void 0) return void 0;
|
|
1750
|
+
const prefix = `${base}/`;
|
|
1751
|
+
let within;
|
|
1752
|
+
let withinIdx = Infinity;
|
|
1753
|
+
let prev;
|
|
1754
|
+
let prevIdx = -1;
|
|
1755
|
+
for (const c of calls) {
|
|
1756
|
+
const idx = execIndex(c.runtimeStageId);
|
|
1757
|
+
if (idx === void 0) continue;
|
|
1758
|
+
if (c.runtimeStageId.startsWith(prefix) && idx > cursorIdx && idx < withinIdx) {
|
|
1759
|
+
within = c;
|
|
1760
|
+
withinIdx = idx;
|
|
1761
|
+
}
|
|
1762
|
+
if (idx <= cursorIdx && idx > prevIdx) {
|
|
1763
|
+
prev = c;
|
|
1764
|
+
prevIdx = idx;
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1767
|
+
return within ?? prev;
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1661
1770
|
// src/core/translate/helpers/makeNodeId.ts
|
|
1662
1771
|
function makeRootNodeId(kind, id) {
|
|
1663
1772
|
return `${kind.toLowerCase()}:${id}`;
|
|
@@ -2000,7 +2109,8 @@ import { walkSubflowSpec, splitStageId } from "footprintjs/trace";
|
|
|
2000
2109
|
import { createTraceStructureRecorder } from "footprint-explainable-ui/flowchart";
|
|
2001
2110
|
import { stageRole } from "agentfootprint";
|
|
2002
2111
|
function emphasisForRole(role) {
|
|
2003
|
-
if (role === "hero-slot" || role === "hero-llm" || role === "hero-action")
|
|
2112
|
+
if (role === "hero-slot" || role === "hero-llm" || role === "hero-action")
|
|
2113
|
+
return "hero";
|
|
2004
2114
|
if (role === "plumbing") return "muted";
|
|
2005
2115
|
return void 0;
|
|
2006
2116
|
}
|
|
@@ -2026,9 +2136,12 @@ function slotKindForLocalId(localId) {
|
|
|
2026
2136
|
return void 0;
|
|
2027
2137
|
}
|
|
2028
2138
|
function structureGraphFromRunner(runner) {
|
|
2139
|
+
return structureGraphFromSpec(runner.getSpec().buildTimeStructure);
|
|
2140
|
+
}
|
|
2141
|
+
function structureGraphFromSpec(buildTimeStructure) {
|
|
2029
2142
|
const trace = createTraceStructureRecorder();
|
|
2030
2143
|
const recorder = trace.recorder;
|
|
2031
|
-
const spec =
|
|
2144
|
+
const spec = buildTimeStructure;
|
|
2032
2145
|
const subflowSpecs = [];
|
|
2033
2146
|
for (const item of walkSubflowSpec(spec, "", { recurse: false })) {
|
|
2034
2147
|
switch (item.kind) {
|
|
@@ -2076,8 +2189,14 @@ function structureGraphFromRunner(runner) {
|
|
|
2076
2189
|
const internal = expandSubflowInternals(subflowSpecs);
|
|
2077
2190
|
const seenNodes = new Set(baseGraph.nodes.map((n) => n.id));
|
|
2078
2191
|
const seenEdges = new Set(baseGraph.edges.map((e) => e.id));
|
|
2079
|
-
const nodes = [
|
|
2080
|
-
|
|
2192
|
+
const nodes = [
|
|
2193
|
+
...baseGraph.nodes,
|
|
2194
|
+
...internal.nodes.filter((n) => !seenNodes.has(n.id))
|
|
2195
|
+
];
|
|
2196
|
+
const edges = [
|
|
2197
|
+
...baseGraph.edges,
|
|
2198
|
+
...internal.edges.filter((e) => !seenEdges.has(e.id))
|
|
2199
|
+
];
|
|
2081
2200
|
for (const node of nodes) {
|
|
2082
2201
|
const role = stageRole(node.id);
|
|
2083
2202
|
const data = node.data;
|
|
@@ -2111,7 +2230,9 @@ function expandSubflowInternals(subflows) {
|
|
|
2111
2230
|
stageId: item.stageId,
|
|
2112
2231
|
name: item.name,
|
|
2113
2232
|
type: item.type,
|
|
2114
|
-
...item.isPausable !== void 0 && {
|
|
2233
|
+
...item.isPausable !== void 0 && {
|
|
2234
|
+
isPausable: item.isPausable
|
|
2235
|
+
},
|
|
2115
2236
|
spec: item.spec
|
|
2116
2237
|
});
|
|
2117
2238
|
break;
|
|
@@ -2143,10 +2264,19 @@ function expandSubflowInternals(subflows) {
|
|
|
2143
2264
|
const prefix = path.endsWith("/") ? path : `${path}/`;
|
|
2144
2265
|
const q = (id) => id.startsWith(prefix) ? id : `${prefix}${id}`;
|
|
2145
2266
|
for (const n of sub.nodes) {
|
|
2146
|
-
nodes.push({
|
|
2267
|
+
nodes.push({
|
|
2268
|
+
...n,
|
|
2269
|
+
id: q(n.id),
|
|
2270
|
+
data: { ...n.data, subflowOf: subflowId }
|
|
2271
|
+
});
|
|
2147
2272
|
}
|
|
2148
2273
|
for (const e of sub.edges) {
|
|
2149
|
-
edges.push({
|
|
2274
|
+
edges.push({
|
|
2275
|
+
...e,
|
|
2276
|
+
id: `${q(e.source)}->${q(e.target)}`,
|
|
2277
|
+
source: q(e.source),
|
|
2278
|
+
target: q(e.target)
|
|
2279
|
+
});
|
|
2150
2280
|
}
|
|
2151
2281
|
}
|
|
2152
2282
|
return { nodes, edges };
|
|
@@ -2348,6 +2478,7 @@ export {
|
|
|
2348
2478
|
ChangeNotifier,
|
|
2349
2479
|
lensSnapshotRecorder,
|
|
2350
2480
|
LensSnapshotRecorder,
|
|
2481
|
+
DEFAULT_MAX_EVENTS,
|
|
2351
2482
|
LensRecorder,
|
|
2352
2483
|
lensRecorder,
|
|
2353
2484
|
buildStepGraphFromSnapshot,
|
|
@@ -2368,6 +2499,7 @@ export {
|
|
|
2368
2499
|
selectContextEngineeringInjections,
|
|
2369
2500
|
selectCommentaryAt,
|
|
2370
2501
|
selectCommentaryRanges,
|
|
2502
|
+
selectToolChoiceCall,
|
|
2371
2503
|
makeRootNodeId,
|
|
2372
2504
|
makeChildNodeId,
|
|
2373
2505
|
translateAgent,
|
|
@@ -2381,8 +2513,9 @@ export {
|
|
|
2381
2513
|
translateSequence,
|
|
2382
2514
|
lensGroupTranslator,
|
|
2383
2515
|
structureGraphFromRunner,
|
|
2516
|
+
structureGraphFromSpec,
|
|
2384
2517
|
toReactFlow,
|
|
2385
2518
|
defaultSize,
|
|
2386
2519
|
layoutLensGraph
|
|
2387
2520
|
};
|
|
2388
|
-
//# sourceMappingURL=chunk-
|
|
2521
|
+
//# sourceMappingURL=chunk-ZJKD43L7.js.map
|