@unerr-ai/unerr 0.2.0 → 0.2.2
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/cli.js +37236 -35793
- package/package.json +6 -1
- package/dist/behaviors/agent-llm-bridge.js +0 -166
- package/dist/behaviors/architecture-guard.js +0 -256
- package/dist/behaviors/auto-doc.js +0 -247
- package/dist/behaviors/cascade-guard.js +0 -289
- package/dist/behaviors/change-narrative.js +0 -270
- package/dist/behaviors/convention-drift.js +0 -290
- package/dist/behaviors/framework.js +0 -235
- package/dist/behaviors/guard-formatter.js +0 -44
- package/dist/behaviors/incomplete-work.js +0 -270
- package/dist/behaviors/loop-breaker.js +0 -300
- package/dist/behaviors/session-continuity.js +0 -208
- package/dist/commands/branches.js +0 -97
- package/dist/commands/check-commit.js +0 -225
- package/dist/commands/compress-output.js +0 -64
- package/dist/commands/config-verify.js +0 -243
- package/dist/commands/daemon.js +0 -905
- package/dist/commands/dashboard.js +0 -52
- package/dist/commands/debug.js +0 -200
- package/dist/commands/enrich.js +0 -184
- package/dist/commands/exec.js +0 -233
- package/dist/commands/gain.js +0 -156
- package/dist/commands/hook.js +0 -88
- package/dist/commands/index.js +0 -88
- package/dist/commands/init.js +0 -74
- package/dist/commands/install.js +0 -505
- package/dist/commands/learn.js +0 -116
- package/dist/commands/manifest.js +0 -193
- package/dist/commands/rewind.js +0 -103
- package/dist/commands/serve.js +0 -19
- package/dist/commands/setup-wizard.js +0 -414
- package/dist/commands/skills.js +0 -64
- package/dist/commands/stats.js +0 -20
- package/dist/commands/status.js +0 -654
- package/dist/commands/timeline.js +0 -139
- package/dist/commands/uninstall.js +0 -230
- package/dist/components/App.js +0 -109
- package/dist/components/Banner.js +0 -12
- package/dist/components/ConfirmPrompt.js +0 -25
- package/dist/components/DriftSummary.js +0 -23
- package/dist/components/GradeBadge.js +0 -15
- package/dist/components/HealthCard.js +0 -18
- package/dist/components/InkSpinner.js +0 -22
- package/dist/components/InputBox.js +0 -17
- package/dist/components/KeyValue.js +0 -13
- package/dist/components/MessageList.js +0 -14
- package/dist/components/ProgressBar.js +0 -26
- package/dist/components/Section.js +0 -16
- package/dist/components/SessionSummaryCard.js +0 -73
- package/dist/components/StartupDisplay.js +0 -24
- package/dist/components/StatusDashboard.js +0 -57
- package/dist/components/StatusLine.js +0 -8
- package/dist/components/StepLine.js +0 -22
- package/dist/components/Theme.js +0 -20
- package/dist/components/ToolProgress.js +0 -8
- package/dist/components/ViolationList.js +0 -21
- package/dist/components/render.js +0 -13
- package/dist/config/agent-registry.js +0 -237
- package/dist/config/claude-settings-hooks.js +0 -304
- package/dist/config/hook-installer.js +0 -65
- package/dist/config/instruction-writer.js +0 -388
- package/dist/config/mcp-config-writer.js +0 -266
- package/dist/config/settings.js +0 -174
- package/dist/config/tool-detector.js +0 -42
- package/dist/config/value-surfacing.js +0 -119
- package/dist/core/context-assembly.js +0 -108
- package/dist/core/conversation.js +0 -33
- package/dist/core/local-chat-provider.js +0 -475
- package/dist/core/provider-factory.js +0 -55
- package/dist/core/providers.js +0 -90
- package/dist/core/query-engine.js +0 -174
- package/dist/daemon/api.js +0 -312
- package/dist/daemon/autostart.js +0 -119
- package/dist/daemon/bootstrap.js +0 -39
- package/dist/daemon/client.js +0 -164
- package/dist/daemon/detect-ci.js +0 -81
- package/dist/daemon/platform-linux.js +0 -146
- package/dist/daemon/platform-macos.js +0 -134
- package/dist/daemon/platform-windows.js +0 -116
- package/dist/daemon/process-manager.js +0 -299
- package/dist/daemon/protocol.js +0 -23
- package/dist/daemon/registry.js +0 -270
- package/dist/daemon/settings-schema.js +0 -72
- package/dist/daemon/system-health.js +0 -134
- package/dist/daemon/version-checker.js +0 -262
- package/dist/daemon/warm-start.js +0 -223
- package/dist/entrypoints/cli.js +0 -1043
- package/dist/entrypoints/daemon.js +0 -380
- package/dist/entrypoints/repl.js +0 -147
- package/dist/hooks/adapters/claude-code.js +0 -90
- package/dist/hooks/adapters/cline.js +0 -100
- package/dist/hooks/adapters/cursor.js +0 -98
- package/dist/hooks/hook-dedup.js +0 -79
- package/dist/hooks/hook-runner.js +0 -113
- package/dist/hooks/navigation-hooks.js +0 -175
- package/dist/hooks/prompt-hooks.js +0 -63
- package/dist/hooks/shell-hooks.js +0 -47
- package/dist/ignore.js +0 -111
- package/dist/intelligence/approach-suggester.js +0 -61
- package/dist/intelligence/ast-extractor.js +0 -2615
- package/dist/intelligence/ast-worker.js +0 -34
- package/dist/intelligence/background-indexer.js +0 -121
- package/dist/intelligence/blast-radius.js +0 -200
- package/dist/intelligence/community-detection.js +0 -691
- package/dist/intelligence/community-detector.js +0 -184
- package/dist/intelligence/computation-scheduler.js +0 -75
- package/dist/intelligence/confidence-propagation.js +0 -47
- package/dist/intelligence/convention-detector.js +0 -242
- package/dist/intelligence/convention-learner.js +0 -205
- package/dist/intelligence/convention-matcher.js +0 -205
- package/dist/intelligence/cozo-schema.js +0 -376
- package/dist/intelligence/decision-point-detector.js +0 -90
- package/dist/intelligence/deep-dive-tools.js +0 -586
- package/dist/intelligence/durability-scorer.js +0 -84
- package/dist/intelligence/exploration-cost.js +0 -204
- package/dist/intelligence/exploration-pattern-tracker.js +0 -61
- package/dist/intelligence/fact-generator.js +0 -322
- package/dist/intelligence/facts-schema.js +0 -90
- package/dist/intelligence/file-intelligence.js +0 -59
- package/dist/intelligence/graph-holder.js +0 -220
- package/dist/intelligence/graph-temporal-joiner.js +0 -238
- package/dist/intelligence/health-grade.js +0 -423
- package/dist/intelligence/health-grader.js +0 -200
- package/dist/intelligence/health-map-data.js +0 -259
- package/dist/intelligence/import-symbols.js +0 -136
- package/dist/intelligence/incremental-indexer.js +0 -658
- package/dist/intelligence/indexer/centrality.js +0 -62
- package/dist/intelligence/indexer/cfg-context.js +0 -95
- package/dist/intelligence/indexer/confidence.js +0 -34
- package/dist/intelligence/indexer/cross-file-resolver.js +0 -104
- package/dist/intelligence/indexer/edge-repair.js +0 -89
- package/dist/intelligence/indexer/entity-key.js +0 -17
- package/dist/intelligence/indexer/export-map.js +0 -132
- package/dist/intelligence/indexer/git-cochange.js +0 -128
- package/dist/intelligence/indexer/graph-patch.js +0 -147
- package/dist/intelligence/indexer/incremental.js +0 -78
- package/dist/intelligence/indexer/ingest.js +0 -160
- package/dist/intelligence/indexer/language-detect.js +0 -226
- package/dist/intelligence/indexer/metadata.js +0 -63
- package/dist/intelligence/indexer/mutation-tracker.js +0 -79
- package/dist/intelligence/indexer/orchestrator.js +0 -155
- package/dist/intelligence/indexer/plugin-interface.js +0 -31
- package/dist/intelligence/indexer/plugins/csharp.js +0 -440
- package/dist/intelligence/indexer/plugins/go.js +0 -335
- package/dist/intelligence/indexer/plugins/java.js +0 -370
- package/dist/intelligence/indexer/plugins/python.js +0 -358
- package/dist/intelligence/indexer/plugins/regex-fallback.js +0 -82
- package/dist/intelligence/indexer/plugins/ruby.js +0 -290
- package/dist/intelligence/indexer/plugins/rust.js +0 -484
- package/dist/intelligence/indexer/plugins/tier2-generic.js +0 -310
- package/dist/intelligence/indexer/plugins/typescript.js +0 -456
- package/dist/intelligence/indexer/resource-monitor.js +0 -93
- package/dist/intelligence/indexer/scip/decoder.js +0 -253
- package/dist/intelligence/indexer/scip/detector.js +0 -232
- package/dist/intelligence/indexer/scip/downloader.js +0 -427
- package/dist/intelligence/indexer/scip/fallback.js +0 -34
- package/dist/intelligence/indexer/scip/merger.js +0 -109
- package/dist/intelligence/indexer/scip/orchestrator.js +0 -433
- package/dist/intelligence/indexer/scip/runner.js +0 -98
- package/dist/intelligence/indexer/snapshot.js +0 -66
- package/dist/intelligence/indexer/test-detector.js +0 -196
- package/dist/intelligence/indexer/watch-integration.js +0 -61
- package/dist/intelligence/indexer/worker.js +0 -85
- package/dist/intelligence/local-convention-detector.js +0 -437
- package/dist/intelligence/local-embeddings.js +0 -190
- package/dist/intelligence/local-graph.js +0 -1946
- package/dist/intelligence/local-indexer.js +0 -1575
- package/dist/intelligence/local-llm.js +0 -163
- package/dist/intelligence/local-rule-generator.js +0 -154
- package/dist/intelligence/local-snapshot.js +0 -213
- package/dist/intelligence/negative-knowledge.js +0 -103
- package/dist/intelligence/persistent-db.js +0 -85
- package/dist/intelligence/query-router.js +0 -2556
- package/dist/intelligence/risk-classifier.js +0 -116
- package/dist/intelligence/rule-evaluator.js +0 -380
- package/dist/intelligence/rule-generator.js +0 -49
- package/dist/intelligence/search-index.js +0 -173
- package/dist/intelligence/semantic/docstring-extractor.js +0 -67
- package/dist/intelligence/semantic/embedding-store.js +0 -52
- package/dist/intelligence/semantic/enrichment-orchestrator.js +0 -48
- package/dist/intelligence/semantic/git-message-miner.js +0 -114
- package/dist/intelligence/semantic/identifier-tokenizer.js +0 -51
- package/dist/intelligence/semantic/node2vec-embeddings.js +0 -71
- package/dist/intelligence/semantic/node2vec-walks.js +0 -103
- package/dist/intelligence/semantic/path-domain-inference.js +0 -112
- package/dist/intelligence/semantic/similarity-engine.js +0 -60
- package/dist/intelligence/semantic/tfidf-vectors.js +0 -88
- package/dist/intelligence/session-brief-builder.js +0 -159
- package/dist/intelligence/session-context.js +0 -221
- package/dist/intelligence/session-health-monitor.js +0 -211
- package/dist/intelligence/session-narrative.js +0 -197
- package/dist/intelligence/session-pattern-analyzer.js +0 -218
- package/dist/intelligence/signal-scorer.js +0 -390
- package/dist/intelligence/signal-show-store.js +0 -182
- package/dist/intelligence/smart-truncate.js +0 -158
- package/dist/intelligence/subgraph-cache.js +0 -88
- package/dist/intelligence/temporal-facts.js +0 -494
- package/dist/intelligence/token-estimator.js +0 -100
- package/dist/intelligence/tool-injector.js +0 -87
- package/dist/intelligence/tree-sitter-loader.js +0 -71
- package/dist/intelligence/worker-pool.js +0 -116
- package/dist/proxy/arg-validator.js +0 -79
- package/dist/proxy/auto-bootstrap.js +0 -167
- package/dist/proxy/bridge.js +0 -147
- package/dist/proxy/budget-enforcer.js +0 -70
- package/dist/proxy/compression-quality-monitor.js +0 -160
- package/dist/proxy/compression-stats.js +0 -51
- package/dist/proxy/context-rot-detector.js +0 -137
- package/dist/proxy/drift-detector.js +0 -139
- package/dist/proxy/efficiency-tracker.js +0 -79
- package/dist/proxy/fact-ranking.js +0 -154
- package/dist/proxy/format-encoder.js +0 -266
- package/dist/proxy/http-transport.js +0 -90
- package/dist/proxy/lifecycle-actor.js +0 -55
- package/dist/proxy/lifecycle-machine.js +0 -187
- package/dist/proxy/log-tailer.js +0 -265
- package/dist/proxy/model-pricing.js +0 -98
- package/dist/proxy/network-firewall.js +0 -141
- package/dist/proxy/nudge-state.js +0 -93
- package/dist/proxy/output-compressor.js +0 -185
- package/dist/proxy/pid-lock.js +0 -291
- package/dist/proxy/proxy-context.js +0 -11
- package/dist/proxy/proxy.js +0 -2633
- package/dist/proxy/response-enrichment.js +0 -32
- package/dist/proxy/response-envelope.js +0 -313
- package/dist/proxy/session-dedup.js +0 -82
- package/dist/proxy/session-legend.js +0 -30
- package/dist/proxy/session-persistence.js +0 -210
- package/dist/proxy/session-resume.js +0 -94
- package/dist/proxy/session-stats.js +0 -513
- package/dist/proxy/shell-classifier.js +0 -1346
- package/dist/proxy/shell-compression-log.js +0 -93
- package/dist/proxy/shell-compressor.js +0 -390
- package/dist/proxy/shell-graph-boost.js +0 -202
- package/dist/proxy/shell-monitor-map.js +0 -18
- package/dist/proxy/shell-stats.js +0 -54
- package/dist/proxy/shell-strategies/cloud.js +0 -215
- package/dist/proxy/shell-strategies/diff.js +0 -159
- package/dist/proxy/shell-strategies/error-diagnostic.js +0 -796
- package/dist/proxy/shell-strategies/filter-dsl.js +0 -358
- package/dist/proxy/shell-strategies/git-status.js +0 -177
- package/dist/proxy/shell-strategies/key-value.js +0 -193
- package/dist/proxy/shell-strategies/log-text.js +0 -154
- package/dist/proxy/shell-strategies/omni.js +0 -188
- package/dist/proxy/shell-strategies/progress.js +0 -55
- package/dist/proxy/shell-strategies/redact.js +0 -76
- package/dist/proxy/shell-strategies/structured.js +0 -241
- package/dist/proxy/shell-strategies/tabular.js +0 -243
- package/dist/proxy/shell-strategies/test-results-types.js +0 -13
- package/dist/proxy/shell-strategies/test-results.js +0 -784
- package/dist/proxy/shell-strategies/tree-paths.js +0 -144
- package/dist/proxy/shell-strategies/yaml.js +0 -182
- package/dist/proxy/shell-tee.js +0 -111
- package/dist/proxy/signal-dedup.js +0 -171
- package/dist/proxy/startup-renderer.js +0 -158
- package/dist/proxy/task-token-display.js +0 -38
- package/dist/proxy/token-counter.js +0 -61
- package/dist/proxy/tool-clusters.js +0 -273
- package/dist/proxy/tool-definitions.js +0 -525
- package/dist/proxy/transport-mux.js +0 -229
- package/dist/proxy/wire-cap.js +0 -268
- package/dist/rules/developer.mozilla.org.json +0 -9
- package/dist/rules/github.com.json +0 -21
- package/dist/schemas/api/skills.js +0 -19
- package/dist/schemas/common/errors.js +0 -7
- package/dist/schemas/common/headers.js +0 -5
- package/dist/schemas/entities/edge.js +0 -25
- package/dist/schemas/entities/entity.js +0 -22
- package/dist/schemas/entities/rule.js +0 -18
- package/dist/schemas/index.js +0 -14
- package/dist/server/event-bus.js +0 -59
- package/dist/server/http.js +0 -156
- package/dist/server/middleware.js +0 -70
- package/dist/server/routes/drift.js +0 -97
- package/dist/server/routes/intelligence.js +0 -1217
- package/dist/server/routes/reasoning-quality.js +0 -444
- package/dist/server/routes/session.js +0 -86
- package/dist/server/routes/stream.js +0 -120
- package/dist/server/routes/system.js +0 -73
- package/dist/server/routes/temporal.js +0 -170
- package/dist/server/routes/timeline.js +0 -232
- package/dist/server/routes/token-flow.js +0 -403
- package/dist/skills/effectiveness-tracker.js +0 -93
- package/dist/skills/local-pack.js +0 -380
- package/dist/skills/resolver.js +0 -495
- package/dist/state-detector.js +0 -83
- package/dist/timeline/intent-detector.js +0 -263
- package/dist/timeline/loop-miner.js +0 -140
- package/dist/timeline/open-threads.js +0 -49
- package/dist/timeline/signal-reinforcer.js +0 -62
- package/dist/timeline/timeline-bootstrap.js +0 -151
- package/dist/timeline/timeline-store.js +0 -618
- package/dist/tools/coding/bash.js +0 -49
- package/dist/tools/coding/file-edit.js +0 -72
- package/dist/tools/coding/file-outline.js +0 -227
- package/dist/tools/coding/file-read-protocol.js +0 -425
- package/dist/tools/coding/file-read.js +0 -35
- package/dist/tools/coding/file-write.js +0 -43
- package/dist/tools/coding/glob-tool.js +0 -109
- package/dist/tools/coding/grep.js +0 -162
- package/dist/tools/coding/index.js +0 -27
- package/dist/tools/intelligence/index.js +0 -269
- package/dist/tools/intelligence/record-fact.js +0 -48
- package/dist/tools/intelligence/timeline-markers.js +0 -130
- package/dist/tools/registry.js +0 -47
- package/dist/tools/types.js +0 -8
- package/dist/tracking/auto-snapshot-triggers.js +0 -246
- package/dist/tracking/branch-context.js +0 -115
- package/dist/tracking/branch-snapshot.js +0 -217
- package/dist/tracking/causal-bridge.js +0 -317
- package/dist/tracking/circuit-breaker.js +0 -147
- package/dist/tracking/commit-watcher.js +0 -114
- package/dist/tracking/context-ledger.js +0 -119
- package/dist/tracking/correction-detector.js +0 -324
- package/dist/tracking/drift-tracker.js +0 -874
- package/dist/tracking/durability-tracker.js +0 -94
- package/dist/tracking/entity-rewind.js +0 -200
- package/dist/tracking/file-hash-state.js +0 -114
- package/dist/tracking/git-attribution.js +0 -132
- package/dist/tracking/git-trailers.js +0 -171
- package/dist/tracking/intelligence-counter.js +0 -46
- package/dist/tracking/intent-correlator.js +0 -202
- package/dist/tracking/intent-encoder.js +0 -52
- package/dist/tracking/intent-token-tracker.js +0 -159
- package/dist/tracking/ledger-archiver.js +0 -94
- package/dist/tracking/ledger-chains.js +0 -245
- package/dist/tracking/metrics-store.js +0 -361
- package/dist/tracking/native-watcher.js +0 -131
- package/dist/tracking/offline-rewind.js +0 -295
- package/dist/tracking/pending-violations.js +0 -74
- package/dist/tracking/persistence-effectiveness.js +0 -167
- package/dist/tracking/prompt-durability.js +0 -202
- package/dist/tracking/quality-signals.js +0 -213
- package/dist/tracking/redactor.js +0 -73
- package/dist/tracking/rewind-engine.js +0 -161
- package/dist/tracking/session-history.js +0 -128
- package/dist/tracking/session-receipt.js +0 -88
- package/dist/tracking/session-summary-writer.js +0 -157
- package/dist/tracking/shadow-ledger.js +0 -321
- package/dist/tracking/stash-manager.js +0 -258
- package/dist/tracking/timeline-fork.js +0 -213
- package/dist/tracking/timeline.js +0 -69
- package/dist/tracking/token-flow.js +0 -276
- package/dist/tracking/turn-segmenter.js +0 -122
- package/dist/tracking/weekly-accumulator.js +0 -179
- package/dist/tracking/working-snapshots.js +0 -188
- package/dist/tracking/workspace-manifest.js +0 -176
- package/dist/transport/http.js +0 -102
- package/dist/utils/counterfactual.js +0 -65
- package/dist/utils/deep-link.js +0 -34
- package/dist/utils/detect.js +0 -193
- package/dist/utils/exec.js +0 -73
- package/dist/utils/file-logger.js +0 -87
- package/dist/utils/format-error.js +0 -29
- package/dist/utils/git.js +0 -181
- package/dist/utils/log.js +0 -57
- package/dist/utils/logger.js +0 -35
- package/dist/utils/mcp-content-json.js +0 -8
- package/dist/utils/session-logger.js +0 -154
- package/dist/utils/startup-log.js +0 -512
- package/dist/utils/ui.js +0 -56
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 10 TF-D.1: Token Flow API routes for the dashboard.
|
|
3
|
-
*
|
|
4
|
-
* GET /api/token-flow/session → Current session summary with mechanism breakdown
|
|
5
|
-
* GET /api/token-flow/sessions → All session IDs with summary stats
|
|
6
|
-
* GET /api/token-flow/history → Last 20 sessions with tokenFlowSummary
|
|
7
|
-
* GET /api/token-flow/events → Events (optional ?turn=N, ?mechanism=X, ?session_id=Y)
|
|
8
|
-
* GET /api/token-flow/cumulative → Per-turn cumulative totals for chart rendering
|
|
9
|
-
*
|
|
10
|
-
* RC1 fix: All reads come from `.unerr/metrics.db` (readTokenFlowEvents), NOT
|
|
11
|
-
* the proxy's in-memory buffer. The proxy process never handles tool calls —
|
|
12
|
-
* `unerr --mcp` does. The proxy's TokenFlowWriter in-memory buffer is always
|
|
13
|
-
* empty for tool-call events.
|
|
14
|
-
*/
|
|
15
|
-
import { Hono } from "hono";
|
|
16
|
-
import { readSessionHistory } from "../../tracking/session-history.js";
|
|
17
|
-
import { aggregateSession, readTokenFlowEvents, } from "../../tracking/token-flow.js";
|
|
18
|
-
/**
|
|
19
|
-
* Token-trace surfaces *byte savings* — persistent_memory events carry
|
|
20
|
-
* effectiveness verdicts (zero saved bytes by design) and belong exclusively
|
|
21
|
-
* to the /reasoning page. Strip them from every read in this route.
|
|
22
|
-
*/
|
|
23
|
-
function stripPersistentMemory(events) {
|
|
24
|
-
return events.filter((e) => e.mechanism !== "persistent_memory");
|
|
25
|
-
}
|
|
26
|
-
export function createTokenFlowRoutes(deps) {
|
|
27
|
-
const app = new Hono();
|
|
28
|
-
// ── /session — Current session summary ───────────────────────────
|
|
29
|
-
// RC1 fix: Read from disk JSONL, not in-memory buffer.
|
|
30
|
-
// The proxy's writer has the correct sessionId but no events (tool calls
|
|
31
|
-
// happen in the --mcp process). Disk has all events from all processes.
|
|
32
|
-
app.get("/session", (c) => {
|
|
33
|
-
const start = performance.now();
|
|
34
|
-
const writer = deps.getTokenFlowWriter();
|
|
35
|
-
const querySessionId = c.req.query("session_id");
|
|
36
|
-
// Read ALL events from disk (persistent_memory excluded — /reasoning only)
|
|
37
|
-
const allEvents = stripPersistentMemory(readTokenFlowEvents(deps.unerrDir));
|
|
38
|
-
// Priority: query param > proxy writer > most recent from disk
|
|
39
|
-
let sessionId = querySessionId || writer?.sessionId;
|
|
40
|
-
if (!sessionId && allEvents.length > 0) {
|
|
41
|
-
sessionId = allEvents[allEvents.length - 1]?.session_id;
|
|
42
|
-
}
|
|
43
|
-
// No events AND no resolvable session id — return zeroed summary
|
|
44
|
-
// (empty shape, not null, so consumers can read fields uniformly).
|
|
45
|
-
if (allEvents.length === 0 || !sessionId) {
|
|
46
|
-
return c.json({
|
|
47
|
-
data: {
|
|
48
|
-
...aggregateSession([], sessionId ?? "unknown"),
|
|
49
|
-
event_count: 0,
|
|
50
|
-
},
|
|
51
|
-
_meta: {
|
|
52
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
53
|
-
},
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
// Filter events for this session. When using query param (explicit selection),
|
|
57
|
-
// don't include "unknown" events — only include them for current session.
|
|
58
|
-
const sessionEvents = querySessionId
|
|
59
|
-
? allEvents.filter((e) => e.session_id === sessionId)
|
|
60
|
-
: allEvents.filter((e) => e.session_id === sessionId || e.session_id === "unknown");
|
|
61
|
-
const summary = aggregateSession(sessionEvents, sessionId);
|
|
62
|
-
// If filtering by sessionId yields nothing, try aggregating all "unknown" events
|
|
63
|
-
// as they likely belong to the current session
|
|
64
|
-
if (summary.total_tokens_saved === 0) {
|
|
65
|
-
const unknownEvents = allEvents.filter((e) => e.session_id === "unknown");
|
|
66
|
-
if (unknownEvents.length > 0) {
|
|
67
|
-
const fallbackSummary = aggregateSession(unknownEvents, "unknown");
|
|
68
|
-
return c.json({
|
|
69
|
-
data: {
|
|
70
|
-
...fallbackSummary,
|
|
71
|
-
session_id: sessionId,
|
|
72
|
-
event_count: unknownEvents.length,
|
|
73
|
-
},
|
|
74
|
-
_meta: {
|
|
75
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return c.json({
|
|
81
|
-
data: {
|
|
82
|
-
...summary,
|
|
83
|
-
event_count: sessionEvents.length,
|
|
84
|
-
},
|
|
85
|
-
_meta: {
|
|
86
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
// ── /global — Cross-session aggregate (all time or date range) ──
|
|
91
|
-
// Powers the Global view KPIs: total saved/delivered across ALL sessions.
|
|
92
|
-
// Supports: ?from_ts=ISO&to_ts=ISO
|
|
93
|
-
app.get("/global", (c) => {
|
|
94
|
-
const start = performance.now();
|
|
95
|
-
const fromTs = c.req.query("from_ts");
|
|
96
|
-
const toTs = c.req.query("to_ts");
|
|
97
|
-
const allEvents = stripPersistentMemory(readTokenFlowEvents(deps.unerrDir, {
|
|
98
|
-
from_ts: fromTs || undefined,
|
|
99
|
-
to_ts: toTs || undefined,
|
|
100
|
-
}));
|
|
101
|
-
const emptyCompound = {
|
|
102
|
-
total_sessions: 0,
|
|
103
|
-
total_turns: 0,
|
|
104
|
-
total_tokens_without: 0,
|
|
105
|
-
total_tokens_with: 0,
|
|
106
|
-
total_tokens_saved: 0,
|
|
107
|
-
efficiency_pct: 0,
|
|
108
|
-
by_mechanism: {},
|
|
109
|
-
event_count: 0,
|
|
110
|
-
avg_context_reduction: 0,
|
|
111
|
-
peak_context_reduction: 0,
|
|
112
|
-
total_context_avoided: 0,
|
|
113
|
-
};
|
|
114
|
-
if (allEvents.length === 0) {
|
|
115
|
-
return c.json({ data: emptyCompound, _meta: { latency_ms: 0 } });
|
|
116
|
-
}
|
|
117
|
-
let totalWithout = 0;
|
|
118
|
-
let totalWith = 0;
|
|
119
|
-
let totalSaved = 0;
|
|
120
|
-
const sessions = new Set();
|
|
121
|
-
const turns = new Set(); // "session:turn" for dedup
|
|
122
|
-
const mechMap = new Map();
|
|
123
|
-
for (const e of allEvents) {
|
|
124
|
-
totalWithout += e.tokens_without;
|
|
125
|
-
totalWith += e.tokens_with;
|
|
126
|
-
totalSaved += e.tokens_saved;
|
|
127
|
-
sessions.add(e.session_id);
|
|
128
|
-
turns.add(`${e.session_id}:${e.turn}`);
|
|
129
|
-
const m = mechMap.get(e.mechanism) ?? {
|
|
130
|
-
saved: 0,
|
|
131
|
-
delivered: 0,
|
|
132
|
-
count: 0,
|
|
133
|
-
};
|
|
134
|
-
m.saved += e.tokens_saved;
|
|
135
|
-
m.delivered += e.tokens_with;
|
|
136
|
-
m.count++;
|
|
137
|
-
mechMap.set(e.mechanism, m);
|
|
138
|
-
}
|
|
139
|
-
// Compound savings: per-session, per-turn cumulative sums.
|
|
140
|
-
// Context resets between sessions, so compute independently per session.
|
|
141
|
-
const sessionTurnSaved = new Map();
|
|
142
|
-
for (const e of allEvents) {
|
|
143
|
-
let turnMap = sessionTurnSaved.get(e.session_id);
|
|
144
|
-
if (!turnMap) {
|
|
145
|
-
turnMap = new Map();
|
|
146
|
-
sessionTurnSaved.set(e.session_id, turnMap);
|
|
147
|
-
}
|
|
148
|
-
turnMap.set(e.turn, (turnMap.get(e.turn) ?? 0) + e.tokens_saved);
|
|
149
|
-
}
|
|
150
|
-
let compoundTotal = 0;
|
|
151
|
-
for (const turnMap of sessionTurnSaved.values()) {
|
|
152
|
-
const sortedTurns = [...turnMap.entries()].sort(([a], [b]) => a - b);
|
|
153
|
-
let cumSaved = 0;
|
|
154
|
-
for (const [, saved] of sortedTurns) {
|
|
155
|
-
cumSaved += saved;
|
|
156
|
-
compoundTotal += cumSaved;
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
const byMechanism = {};
|
|
160
|
-
for (const [mech, data] of mechMap) {
|
|
161
|
-
byMechanism[mech] = {
|
|
162
|
-
tokens_saved: data.saved,
|
|
163
|
-
tokens_delivered: data.delivered,
|
|
164
|
-
event_count: data.count,
|
|
165
|
-
pct_of_total: totalSaved > 0 ? Math.round((data.saved / totalSaved) * 100) : 0,
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
return c.json({
|
|
169
|
-
data: {
|
|
170
|
-
total_sessions: sessions.size,
|
|
171
|
-
total_turns: turns.size,
|
|
172
|
-
total_tokens_without: totalWithout,
|
|
173
|
-
total_tokens_with: totalWith,
|
|
174
|
-
total_tokens_saved: totalSaved,
|
|
175
|
-
efficiency_pct: totalWithout > 0 ? Math.round((totalSaved / totalWithout) * 100) : 0,
|
|
176
|
-
by_mechanism: byMechanism,
|
|
177
|
-
event_count: allEvents.length,
|
|
178
|
-
avg_context_reduction: turns.size > 0 ? Math.round(compoundTotal / turns.size) : 0,
|
|
179
|
-
peak_context_reduction: totalSaved,
|
|
180
|
-
total_context_avoided: compoundTotal,
|
|
181
|
-
},
|
|
182
|
-
_meta: {
|
|
183
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
184
|
-
},
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
// ── /sessions — List all unique session IDs ─────────────────────
|
|
188
|
-
// Supports: ?limit=N&offset=N&from_ts=ISO&to_ts=ISO
|
|
189
|
-
app.get("/sessions", (c) => {
|
|
190
|
-
const start = performance.now();
|
|
191
|
-
const fromTs = c.req.query("from_ts");
|
|
192
|
-
const toTs = c.req.query("to_ts");
|
|
193
|
-
// Pagination is opt-in: callers without a `limit` get all sessions so they
|
|
194
|
-
// can compute aggregates that match `/global`. UI clients pass an explicit
|
|
195
|
-
// limit when they want a paginated view (capped at 200).
|
|
196
|
-
const rawLimit = c.req.query("limit");
|
|
197
|
-
const limit = rawLimit !== undefined
|
|
198
|
-
? Math.min(Math.max(Number(rawLimit), 1), 200)
|
|
199
|
-
: Number.MAX_SAFE_INTEGER;
|
|
200
|
-
const offset = Math.max(Number(c.req.query("offset") ?? 0), 0);
|
|
201
|
-
const allEvents = stripPersistentMemory(readTokenFlowEvents(deps.unerrDir, {
|
|
202
|
-
from_ts: fromTs || undefined,
|
|
203
|
-
to_ts: toTs || undefined,
|
|
204
|
-
}));
|
|
205
|
-
// Build agent lookup from session history
|
|
206
|
-
const historyEntries = readSessionHistory(deps.unerrDir);
|
|
207
|
-
const agentBySession = new Map();
|
|
208
|
-
for (const h of historyEntries) {
|
|
209
|
-
if (h.agentName)
|
|
210
|
-
agentBySession.set(h.sessionId, h.agentName);
|
|
211
|
-
}
|
|
212
|
-
const sessionMap = new Map();
|
|
213
|
-
for (const e of allEvents) {
|
|
214
|
-
const entry = sessionMap.get(e.session_id) ?? {
|
|
215
|
-
event_count: 0,
|
|
216
|
-
total_saved: 0,
|
|
217
|
-
first_ts: e.ts,
|
|
218
|
-
last_ts: e.ts,
|
|
219
|
-
mechanisms: new Set(),
|
|
220
|
-
turnSaved: new Map(),
|
|
221
|
-
};
|
|
222
|
-
entry.event_count++;
|
|
223
|
-
entry.total_saved += e.tokens_saved;
|
|
224
|
-
if (e.ts < entry.first_ts)
|
|
225
|
-
entry.first_ts = e.ts;
|
|
226
|
-
if (e.ts > entry.last_ts)
|
|
227
|
-
entry.last_ts = e.ts;
|
|
228
|
-
entry.mechanisms.add(e.mechanism);
|
|
229
|
-
entry.turnSaved.set(e.turn, (entry.turnSaved.get(e.turn) ?? 0) + e.tokens_saved);
|
|
230
|
-
sessionMap.set(e.session_id, entry);
|
|
231
|
-
}
|
|
232
|
-
const allSessions = [...sessionMap.entries()]
|
|
233
|
-
.map(([id, data]) => {
|
|
234
|
-
// Compute avg context reduction: compound total / num turns
|
|
235
|
-
const sortedTurns = [...data.turnSaved.entries()].sort(([a], [b]) => a - b);
|
|
236
|
-
let cumSaved = 0;
|
|
237
|
-
let compoundTotal = 0;
|
|
238
|
-
for (const [, saved] of sortedTurns) {
|
|
239
|
-
cumSaved += saved;
|
|
240
|
-
compoundTotal += cumSaved;
|
|
241
|
-
}
|
|
242
|
-
const numTurns = sortedTurns.length;
|
|
243
|
-
return {
|
|
244
|
-
session_id: id,
|
|
245
|
-
event_count: data.event_count,
|
|
246
|
-
total_saved: data.total_saved,
|
|
247
|
-
total_turns: numTurns,
|
|
248
|
-
avg_context_reduction: numTurns > 0 ? Math.round(compoundTotal / numTurns) : 0,
|
|
249
|
-
first_ts: data.first_ts,
|
|
250
|
-
last_ts: data.last_ts,
|
|
251
|
-
mechanisms: [...data.mechanisms],
|
|
252
|
-
agent_name: agentBySession.get(id) ?? deps.getAgentName?.(id) ?? null,
|
|
253
|
-
};
|
|
254
|
-
})
|
|
255
|
-
.sort((a, b) => b.last_ts.localeCompare(a.last_ts));
|
|
256
|
-
const paginated = allSessions.slice(offset, offset + limit);
|
|
257
|
-
return c.json({
|
|
258
|
-
data: paginated,
|
|
259
|
-
total: allSessions.length,
|
|
260
|
-
limit,
|
|
261
|
-
offset,
|
|
262
|
-
_meta: {
|
|
263
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
264
|
-
},
|
|
265
|
-
});
|
|
266
|
-
});
|
|
267
|
-
// ── /history — Recent sessions from session-history.jsonl ───────
|
|
268
|
-
app.get("/history", (c) => {
|
|
269
|
-
const start = performance.now();
|
|
270
|
-
const limit = Math.min(Number(c.req.query("limit") ?? 20), 50);
|
|
271
|
-
const entries = readSessionHistory(deps.unerrDir);
|
|
272
|
-
const withFlow = entries
|
|
273
|
-
.filter((e) => e.tokenFlowSummary)
|
|
274
|
-
.slice(-limit)
|
|
275
|
-
.reverse();
|
|
276
|
-
return c.json({
|
|
277
|
-
data: withFlow.map((e) => ({
|
|
278
|
-
session_id: e.sessionId,
|
|
279
|
-
started_at: e.startedAt,
|
|
280
|
-
ended_at: e.endedAt,
|
|
281
|
-
duration_ms: e.durationMs,
|
|
282
|
-
tool_calls: e.toolCalls,
|
|
283
|
-
tokens_saved: e.tokensSaved,
|
|
284
|
-
efficiency: e.efficiency,
|
|
285
|
-
token_flow: e.tokenFlowSummary,
|
|
286
|
-
})),
|
|
287
|
-
total: withFlow.length,
|
|
288
|
-
_meta: {
|
|
289
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
290
|
-
},
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
// ── /events — Events with filters ───────────────────────────────
|
|
294
|
-
// Supports: ?session_id=X&turn=N&mechanism=X&from_ts=ISO&to_ts=ISO&limit=N&offset=N
|
|
295
|
-
// RC1 fix: Always read from disk, not in-memory.
|
|
296
|
-
app.get("/events", (c) => {
|
|
297
|
-
const start = performance.now();
|
|
298
|
-
const turnFilter = c.req.query("turn");
|
|
299
|
-
const mechanismFilter = c.req.query("mechanism");
|
|
300
|
-
const sessionFilter = c.req.query("session_id");
|
|
301
|
-
const fromTs = c.req.query("from_ts");
|
|
302
|
-
const toTs = c.req.query("to_ts");
|
|
303
|
-
const limit = Math.min(Number(c.req.query("limit") ?? 200), 500);
|
|
304
|
-
const offset = Math.max(Number(c.req.query("offset") ?? 0), 0);
|
|
305
|
-
// Always read from disk — the proxy process has no in-memory events
|
|
306
|
-
let events = stripPersistentMemory(readTokenFlowEvents(deps.unerrDir, {
|
|
307
|
-
session_id: sessionFilter || undefined,
|
|
308
|
-
mechanism: mechanismFilter || undefined,
|
|
309
|
-
from_ts: fromTs || undefined,
|
|
310
|
-
to_ts: toTs || undefined,
|
|
311
|
-
}));
|
|
312
|
-
// If no session filter provided, include current session + "unknown"
|
|
313
|
-
if (!sessionFilter) {
|
|
314
|
-
const writer = deps.getTokenFlowWriter();
|
|
315
|
-
if (writer) {
|
|
316
|
-
events = events.filter((e) => e.session_id === writer.sessionId || e.session_id === "unknown");
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
if (turnFilter) {
|
|
320
|
-
const turnNum = Number(turnFilter);
|
|
321
|
-
if (!Number.isNaN(turnNum)) {
|
|
322
|
-
events = events.filter((e) => e.turn === turnNum);
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
const paginated = events.slice(offset, offset + limit);
|
|
326
|
-
return c.json({
|
|
327
|
-
data: paginated,
|
|
328
|
-
total: events.length,
|
|
329
|
-
limit,
|
|
330
|
-
offset,
|
|
331
|
-
_meta: {
|
|
332
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
333
|
-
},
|
|
334
|
-
});
|
|
335
|
-
});
|
|
336
|
-
// ── /cumulative — Per-turn cumulative totals for charts ─────────
|
|
337
|
-
app.get("/cumulative", (c) => {
|
|
338
|
-
const start = performance.now();
|
|
339
|
-
const sessionFilter = c.req.query("session_id");
|
|
340
|
-
const writer = deps.getTokenFlowWriter();
|
|
341
|
-
let events = stripPersistentMemory(readTokenFlowEvents(deps.unerrDir));
|
|
342
|
-
// Filter to relevant session
|
|
343
|
-
const targetSession = sessionFilter ?? writer?.sessionId;
|
|
344
|
-
if (targetSession) {
|
|
345
|
-
events = events.filter((e) => e.session_id === targetSession || e.session_id === "unknown");
|
|
346
|
-
}
|
|
347
|
-
// Sort by turn, then by timestamp within turn
|
|
348
|
-
events.sort((a, b) => a.turn - b.turn || a.ts.localeCompare(b.ts));
|
|
349
|
-
// Build cumulative per-turn data
|
|
350
|
-
const turnData = [];
|
|
351
|
-
const cumulativeMechanisms = {};
|
|
352
|
-
let cumulativeSaved = 0;
|
|
353
|
-
let contextTurnsTotal = 0; // sum of cumulative savings at each turn
|
|
354
|
-
// Group events by turn
|
|
355
|
-
const turnGroups = new Map();
|
|
356
|
-
for (const e of events) {
|
|
357
|
-
const group = turnGroups.get(e.turn) ?? [];
|
|
358
|
-
group.push(e);
|
|
359
|
-
turnGroups.set(e.turn, group);
|
|
360
|
-
}
|
|
361
|
-
for (const [turn, turnEvents] of [...turnGroups.entries()].sort(([a], [b]) => a - b)) {
|
|
362
|
-
let turnSaved = 0;
|
|
363
|
-
const mechanismsThisTurn = {};
|
|
364
|
-
const tools = new Set();
|
|
365
|
-
for (const e of turnEvents) {
|
|
366
|
-
turnSaved += e.tokens_saved;
|
|
367
|
-
mechanismsThisTurn[e.mechanism] =
|
|
368
|
-
(mechanismsThisTurn[e.mechanism] ?? 0) + e.tokens_saved;
|
|
369
|
-
cumulativeMechanisms[e.mechanism] =
|
|
370
|
-
(cumulativeMechanisms[e.mechanism] ?? 0) + e.tokens_saved;
|
|
371
|
-
if (e.tool)
|
|
372
|
-
tools.add(e.tool);
|
|
373
|
-
}
|
|
374
|
-
cumulativeSaved += turnSaved;
|
|
375
|
-
contextTurnsTotal += cumulativeSaved;
|
|
376
|
-
turnData.push({
|
|
377
|
-
turn,
|
|
378
|
-
tools: [...tools],
|
|
379
|
-
tokens_saved_this_turn: turnSaved,
|
|
380
|
-
cumulative_tokens_saved: cumulativeSaved,
|
|
381
|
-
context_avoided: cumulativeSaved,
|
|
382
|
-
mechanisms_this_turn: mechanismsThisTurn,
|
|
383
|
-
cumulative_by_mechanism: { ...cumulativeMechanisms },
|
|
384
|
-
event_count: turnEvents.length,
|
|
385
|
-
});
|
|
386
|
-
}
|
|
387
|
-
// Avg context reduction per turn: how much smaller context was on average
|
|
388
|
-
const numTurns = turnData.length;
|
|
389
|
-
const avgContextReduction = numTurns > 0 ? Math.round(contextTurnsTotal / numTurns) : 0;
|
|
390
|
-
return c.json({
|
|
391
|
-
data: turnData,
|
|
392
|
-
total_turns: numTurns,
|
|
393
|
-
total_saved: cumulativeSaved,
|
|
394
|
-
avg_context_reduction: avgContextReduction,
|
|
395
|
-
peak_context_reduction: cumulativeSaved, // max = final cumulative
|
|
396
|
-
total_context_avoided: contextTurnsTotal, // sum of cumulative savings at each turn
|
|
397
|
-
_meta: {
|
|
398
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
});
|
|
402
|
-
return app;
|
|
403
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Skill Effectiveness Tracker — Phase 4.2
|
|
3
|
-
*
|
|
4
|
-
* Tracks skill activations and correlates them with outcomes:
|
|
5
|
-
* - Skill suggested by behavior → did the agent invoke related graph tools?
|
|
6
|
-
* - How many file reads were prevented (proxy for exploration loops avoided)?
|
|
7
|
-
*
|
|
8
|
-
* Surfaced in session summary: "blast-radius-first prevented 3 cascade failures"
|
|
9
|
-
*/
|
|
10
|
-
/**
|
|
11
|
-
* Session-scoped tracker for skill effectiveness.
|
|
12
|
-
*/
|
|
13
|
-
export class SkillEffectivenessTracker {
|
|
14
|
-
activations = [];
|
|
15
|
-
pendingSkillIds = new Set();
|
|
16
|
-
/**
|
|
17
|
-
* Record that a skill was suggested (by behavior or always-on injection).
|
|
18
|
-
*/
|
|
19
|
-
recordActivation(skillId, triggeredBy, behaviorId) {
|
|
20
|
-
this.activations.push({
|
|
21
|
-
skillId,
|
|
22
|
-
triggeredBy,
|
|
23
|
-
behaviorId,
|
|
24
|
-
timestamp: Date.now(),
|
|
25
|
-
graphToolsCalled: [],
|
|
26
|
-
followed: false,
|
|
27
|
-
});
|
|
28
|
-
this.pendingSkillIds.add(skillId);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Record that a graph tool was called (correlates with pending skill activations).
|
|
32
|
-
* Call this from the proxy when any graph tool is invoked.
|
|
33
|
-
*/
|
|
34
|
-
recordGraphToolCall(toolName) {
|
|
35
|
-
// Attribute to the most recent pending activation
|
|
36
|
-
for (let i = this.activations.length - 1; i >= 0; i--) {
|
|
37
|
-
const activation = this.activations[i];
|
|
38
|
-
if (this.pendingSkillIds.has(activation.skillId)) {
|
|
39
|
-
activation.graphToolsCalled.push(toolName);
|
|
40
|
-
activation.followed = true;
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Clear pending state (e.g., when a new tool call starts).
|
|
47
|
-
*/
|
|
48
|
-
clearPending() {
|
|
49
|
-
this.pendingSkillIds.clear();
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Get effectiveness stats for session summary.
|
|
53
|
-
*/
|
|
54
|
-
getStats() {
|
|
55
|
-
const perSkillMap = new Map();
|
|
56
|
-
for (const a of this.activations) {
|
|
57
|
-
const existing = perSkillMap.get(a.skillId) ?? {
|
|
58
|
-
activations: 0,
|
|
59
|
-
graphToolsCalled: 0,
|
|
60
|
-
followed: 0,
|
|
61
|
-
};
|
|
62
|
-
existing.activations++;
|
|
63
|
-
existing.graphToolsCalled += a.graphToolsCalled.length;
|
|
64
|
-
if (a.followed)
|
|
65
|
-
existing.followed++;
|
|
66
|
-
perSkillMap.set(a.skillId, existing);
|
|
67
|
-
}
|
|
68
|
-
const perSkill = Array.from(perSkillMap.entries()).map(([skillId, stats]) => ({
|
|
69
|
-
skillId,
|
|
70
|
-
activations: stats.activations,
|
|
71
|
-
graphToolsCalled: stats.graphToolsCalled,
|
|
72
|
-
followRate: stats.activations > 0 ? stats.followed / stats.activations : 0,
|
|
73
|
-
}));
|
|
74
|
-
// Build summary line
|
|
75
|
-
const behaviorTriggered = this.activations.filter((a) => a.triggeredBy === "behavior");
|
|
76
|
-
const preventions = behaviorTriggered.filter((a) => a.followed).length;
|
|
77
|
-
const summaryLine = preventions > 0
|
|
78
|
-
? `${preventions} skill-guided prevention(s) this session`
|
|
79
|
-
: "No skill-guided preventions this session";
|
|
80
|
-
return {
|
|
81
|
-
totalActivations: this.activations.length,
|
|
82
|
-
perSkill,
|
|
83
|
-
summaryLine,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Reset for new session.
|
|
88
|
-
*/
|
|
89
|
-
reset() {
|
|
90
|
-
this.activations = [];
|
|
91
|
-
this.pendingSkillIds.clear();
|
|
92
|
-
}
|
|
93
|
-
}
|