@unerr-ai/unerr 0.2.1 → 0.2.3
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 +36 -45
- package/dist/cli.js +37443 -36022
- package/package.json +2 -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,444 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reasoning Quality API routes for the dashboard.
|
|
3
|
-
*
|
|
4
|
-
* Derives quality/reasoning metrics from existing token flow data.
|
|
5
|
-
* No new tracking infrastructure — all computed from TokenFlowEvent records.
|
|
6
|
-
*
|
|
7
|
-
* GET /api/reasoning-quality/global → Cross-session quality metrics
|
|
8
|
-
* GET /api/reasoning-quality/session → Single session quality metrics
|
|
9
|
-
* GET /api/reasoning-quality/sessions → Per-session quality scores for list view
|
|
10
|
-
*/
|
|
11
|
-
import { Hono } from "hono";
|
|
12
|
-
import { readSessionHistory } from "../../tracking/session-history.js";
|
|
13
|
-
import { readTokenFlowEvents, } from "../../tracking/token-flow.js";
|
|
14
|
-
/** Mechanisms that indicate graph-backed precision queries */
|
|
15
|
-
const GRAPH_MECHANISMS = new Set(["graph_query", "file_read"]);
|
|
16
|
-
/** Mechanisms that indicate shell/exec compression */
|
|
17
|
-
const SHELL_MECHANISMS = new Set(["shell_compression"]);
|
|
18
|
-
/** Mechanisms that prevent breakages */
|
|
19
|
-
const SAFETY_MECHANISMS = new Set(["behavior_automation"]);
|
|
20
|
-
/** Mechanism that carries persistent-memory effectiveness verdicts */
|
|
21
|
-
const PERSISTENT_MEMORY_MECHANISM = "persistent_memory";
|
|
22
|
-
function computeQualityMetrics(events) {
|
|
23
|
-
if (events.length === 0) {
|
|
24
|
-
return {
|
|
25
|
-
signal_to_noise_ratio: 0,
|
|
26
|
-
noise_removed_pct: 0,
|
|
27
|
-
context_density: 0,
|
|
28
|
-
entities_resolved: 0,
|
|
29
|
-
graph_tokens_delivered: 0,
|
|
30
|
-
attention_multiplier: 1,
|
|
31
|
-
first_call_resolution_rate: 0,
|
|
32
|
-
graph_calls: 0,
|
|
33
|
-
total_tool_calls: 0,
|
|
34
|
-
turns_saved: 0,
|
|
35
|
-
exploration_loops_prevented: 0,
|
|
36
|
-
blast_radius_warnings: 0,
|
|
37
|
-
circuit_breaker_activations: 0,
|
|
38
|
-
convention_injections: 0,
|
|
39
|
-
prevention_score: 0,
|
|
40
|
-
reasoning_quality_multiplier: 0,
|
|
41
|
-
total_sessions: 0,
|
|
42
|
-
total_turns: 0,
|
|
43
|
-
total_events: 0,
|
|
44
|
-
facts_surfaced: 0,
|
|
45
|
-
facts_recalled: 0,
|
|
46
|
-
facts_recorded: 0,
|
|
47
|
-
conventions_surfaced: 0,
|
|
48
|
-
resume_hits: 0,
|
|
49
|
-
negative_warnings: 0,
|
|
50
|
-
memory_signals_fired: 0,
|
|
51
|
-
verdicts_reinforced: 0,
|
|
52
|
-
verdicts_acted_on: 0,
|
|
53
|
-
verdicts_ignored: 0,
|
|
54
|
-
verdicts_corrected: 0,
|
|
55
|
-
verdicts_caught: 0,
|
|
56
|
-
memory_verdicts_total: 0,
|
|
57
|
-
memory_effectiveness_pct: 0,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
let totalWithout = 0;
|
|
61
|
-
let totalWith = 0;
|
|
62
|
-
let graphCalls = 0;
|
|
63
|
-
let graphTokensDelivered = 0;
|
|
64
|
-
let shellCompressionEvents = 0;
|
|
65
|
-
let behaviorEvents = 0;
|
|
66
|
-
let blastRadiusWarnings = 0;
|
|
67
|
-
let circuitBreakerActivations = 0;
|
|
68
|
-
let conventionInjections = 0;
|
|
69
|
-
let explorationLoopsPrevented = 0;
|
|
70
|
-
let dedupEvents = 0;
|
|
71
|
-
// Persistent-memory counters
|
|
72
|
-
let factsSurfaced = 0;
|
|
73
|
-
let factsRecalled = 0;
|
|
74
|
-
let factsRecorded = 0;
|
|
75
|
-
let conventionsSurfaced = 0;
|
|
76
|
-
let resumeHits = 0;
|
|
77
|
-
let negativeWarnings = 0;
|
|
78
|
-
let memorySignalsFired = 0;
|
|
79
|
-
let verdictsReinforced = 0;
|
|
80
|
-
let verdictsActedOn = 0;
|
|
81
|
-
let verdictsIgnored = 0;
|
|
82
|
-
let verdictsCorrected = 0;
|
|
83
|
-
let verdictsCaught = 0;
|
|
84
|
-
const sessions = new Set();
|
|
85
|
-
const turns = new Set();
|
|
86
|
-
for (const e of events) {
|
|
87
|
-
totalWithout += e.tokens_without;
|
|
88
|
-
totalWith += e.tokens_with;
|
|
89
|
-
sessions.add(e.session_id);
|
|
90
|
-
turns.add(`${e.session_id}:${e.turn}`);
|
|
91
|
-
if (GRAPH_MECHANISMS.has(e.mechanism)) {
|
|
92
|
-
graphCalls++;
|
|
93
|
-
graphTokensDelivered += e.tokens_with;
|
|
94
|
-
}
|
|
95
|
-
if (SHELL_MECHANISMS.has(e.mechanism)) {
|
|
96
|
-
shellCompressionEvents++;
|
|
97
|
-
}
|
|
98
|
-
if (SAFETY_MECHANISMS.has(e.mechanism)) {
|
|
99
|
-
behaviorEvents++;
|
|
100
|
-
}
|
|
101
|
-
// Extract detail-based counts
|
|
102
|
-
const d = e.detail;
|
|
103
|
-
if (d) {
|
|
104
|
-
// Blast radius warnings: file_read events with callers/references injected
|
|
105
|
-
if (d.counterfactual === "blast_radius" || d.blast_radius) {
|
|
106
|
-
blastRadiusWarnings++;
|
|
107
|
-
}
|
|
108
|
-
// Circuit breaker activations
|
|
109
|
-
if (d.circuit_breaker || d.counterfactual === "circuit_breaker") {
|
|
110
|
-
circuitBreakerActivations++;
|
|
111
|
-
}
|
|
112
|
-
// Convention injections: file_read with conventions injected
|
|
113
|
-
if (d.conventions_injected ||
|
|
114
|
-
d.counterfactual === "convention_injection") {
|
|
115
|
-
conventionInjections++;
|
|
116
|
-
}
|
|
117
|
-
// Hook redirects (exploration loop prevention)
|
|
118
|
-
if (d.hook_redirect || d.counterfactual === "hook_redirect") {
|
|
119
|
-
explorationLoopsPrevented++;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (e.mechanism === "session_dedup") {
|
|
123
|
-
dedupEvents++;
|
|
124
|
-
}
|
|
125
|
-
if (e.mechanism === PERSISTENT_MEMORY_MECHANISM && d) {
|
|
126
|
-
const kind = d.kind;
|
|
127
|
-
const verdict = d.verdict;
|
|
128
|
-
if (verdict === "fired") {
|
|
129
|
-
memorySignalsFired++;
|
|
130
|
-
switch (kind) {
|
|
131
|
-
case "fact_injected":
|
|
132
|
-
factsSurfaced++;
|
|
133
|
-
break;
|
|
134
|
-
case "fact_recalled":
|
|
135
|
-
factsRecalled++;
|
|
136
|
-
break;
|
|
137
|
-
case "fact_recorded":
|
|
138
|
-
factsRecorded++;
|
|
139
|
-
break;
|
|
140
|
-
case "convention_injected":
|
|
141
|
-
conventionsSurfaced++;
|
|
142
|
-
break;
|
|
143
|
-
case "resume_injected":
|
|
144
|
-
resumeHits++;
|
|
145
|
-
break;
|
|
146
|
-
case "negative_warned":
|
|
147
|
-
negativeWarnings++;
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
switch (verdict) {
|
|
153
|
-
case "reinforced":
|
|
154
|
-
verdictsReinforced++;
|
|
155
|
-
break;
|
|
156
|
-
case "acted_on":
|
|
157
|
-
verdictsActedOn++;
|
|
158
|
-
break;
|
|
159
|
-
case "ignored":
|
|
160
|
-
verdictsIgnored++;
|
|
161
|
-
break;
|
|
162
|
-
case "corrected":
|
|
163
|
-
verdictsCorrected++;
|
|
164
|
-
break;
|
|
165
|
-
case "caught":
|
|
166
|
-
verdictsCaught++;
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
const totalSaved = totalWithout - totalWith;
|
|
173
|
-
const totalToolCalls = events.length;
|
|
174
|
-
// ── Category 1: Context Quality ──
|
|
175
|
-
// SNR: what fraction of original content was signal (delivered / original)
|
|
176
|
-
const snr = totalWithout > 0 ? totalWith / totalWithout : 0;
|
|
177
|
-
const noiseRemovedPct = totalWithout > 0
|
|
178
|
-
? Math.round(((totalWithout - totalWith) / totalWithout) * 100)
|
|
179
|
-
: 0;
|
|
180
|
-
// Context density: graph events per 1K tokens delivered
|
|
181
|
-
const contextDensity = graphTokensDelivered > 0
|
|
182
|
-
? Math.round((graphCalls / (graphTokensDelivered / 1000)) * 10) / 10
|
|
183
|
-
: 0;
|
|
184
|
-
// Attention multiplier: based on the insight that removing N tokens from a
|
|
185
|
-
// context of size C improves attention quality by approximately C/(C-N).
|
|
186
|
-
// Conservative estimate: sqrt of compression ratio.
|
|
187
|
-
const compressionRatio = totalWith > 0 ? totalWithout / totalWith : 1;
|
|
188
|
-
const attentionMultiplier = Math.round(Math.sqrt(compressionRatio) * 100) / 100;
|
|
189
|
-
// ── Category 2: Fewer Turns ──
|
|
190
|
-
const firstCallRate = totalToolCalls > 0 ? Math.round((graphCalls / totalToolCalls) * 100) : 0;
|
|
191
|
-
// Each graph call saves ~2.5 turns on average (vs grep→read→grep→read cycle)
|
|
192
|
-
const turnsSaved = Math.round(graphCalls * 2.5);
|
|
193
|
-
// ── Category 3: Fewer Breakages ──
|
|
194
|
-
// Also count behavior_automation events as convention injections if not
|
|
195
|
-
// otherwise categorized
|
|
196
|
-
const totalConventionInjections = conventionInjections + behaviorEvents;
|
|
197
|
-
const preventionScore = blastRadiusWarnings +
|
|
198
|
-
circuitBreakerActivations * 10 +
|
|
199
|
-
totalConventionInjections;
|
|
200
|
-
// ── Category 4: Compound ──
|
|
201
|
-
const firstCallRateDecimal = firstCallRate / 100;
|
|
202
|
-
const reasoningMultiplier = compressionRatio > 0 && firstCallRateDecimal > 0
|
|
203
|
-
? Math.round(compressionRatio * (1 + firstCallRateDecimal) * 100) / 100
|
|
204
|
-
: 0;
|
|
205
|
-
// ── Category 5: Persistent Memory ──
|
|
206
|
-
const verdictsTotal = verdictsReinforced +
|
|
207
|
-
verdictsActedOn +
|
|
208
|
-
verdictsIgnored +
|
|
209
|
-
verdictsCorrected +
|
|
210
|
-
verdictsCaught;
|
|
211
|
-
const loadBearing = verdictsReinforced + verdictsActedOn + verdictsCaught;
|
|
212
|
-
const memoryEffectivenessPct = verdictsTotal > 0 ? Math.round((loadBearing / verdictsTotal) * 100) : 0;
|
|
213
|
-
return {
|
|
214
|
-
signal_to_noise_ratio: Math.round(snr * 1000) / 1000,
|
|
215
|
-
noise_removed_pct: noiseRemovedPct,
|
|
216
|
-
context_density: contextDensity,
|
|
217
|
-
entities_resolved: graphCalls,
|
|
218
|
-
graph_tokens_delivered: graphTokensDelivered,
|
|
219
|
-
attention_multiplier: attentionMultiplier,
|
|
220
|
-
first_call_resolution_rate: firstCallRate,
|
|
221
|
-
graph_calls: graphCalls,
|
|
222
|
-
total_tool_calls: totalToolCalls,
|
|
223
|
-
turns_saved: turnsSaved,
|
|
224
|
-
exploration_loops_prevented: explorationLoopsPrevented,
|
|
225
|
-
blast_radius_warnings: blastRadiusWarnings,
|
|
226
|
-
circuit_breaker_activations: circuitBreakerActivations,
|
|
227
|
-
convention_injections: totalConventionInjections,
|
|
228
|
-
prevention_score: preventionScore,
|
|
229
|
-
reasoning_quality_multiplier: reasoningMultiplier,
|
|
230
|
-
total_sessions: sessions.size,
|
|
231
|
-
total_turns: turns.size,
|
|
232
|
-
total_events: events.length,
|
|
233
|
-
facts_surfaced: factsSurfaced,
|
|
234
|
-
facts_recalled: factsRecalled,
|
|
235
|
-
facts_recorded: factsRecorded,
|
|
236
|
-
conventions_surfaced: conventionsSurfaced,
|
|
237
|
-
resume_hits: resumeHits,
|
|
238
|
-
negative_warnings: negativeWarnings,
|
|
239
|
-
memory_signals_fired: memorySignalsFired,
|
|
240
|
-
verdicts_reinforced: verdictsReinforced,
|
|
241
|
-
verdicts_acted_on: verdictsActedOn,
|
|
242
|
-
verdicts_ignored: verdictsIgnored,
|
|
243
|
-
verdicts_corrected: verdictsCorrected,
|
|
244
|
-
verdicts_caught: verdictsCaught,
|
|
245
|
-
memory_verdicts_total: verdictsTotal,
|
|
246
|
-
memory_effectiveness_pct: memoryEffectivenessPct,
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
export function createReasoningQualityRoutes(deps) {
|
|
250
|
-
const app = new Hono();
|
|
251
|
-
// ── /global — Cross-session quality metrics ─────────────────────
|
|
252
|
-
app.get("/global", (c) => {
|
|
253
|
-
const start = performance.now();
|
|
254
|
-
const fromTs = c.req.query("from_ts");
|
|
255
|
-
const toTs = c.req.query("to_ts");
|
|
256
|
-
const events = readTokenFlowEvents(deps.unerrDir, {
|
|
257
|
-
from_ts: fromTs || undefined,
|
|
258
|
-
to_ts: toTs || undefined,
|
|
259
|
-
});
|
|
260
|
-
const metrics = computeQualityMetrics(events);
|
|
261
|
-
return c.json({
|
|
262
|
-
data: metrics,
|
|
263
|
-
_meta: {
|
|
264
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
265
|
-
},
|
|
266
|
-
});
|
|
267
|
-
});
|
|
268
|
-
// ── /session — Single session quality metrics ───────────────────
|
|
269
|
-
app.get("/session", (c) => {
|
|
270
|
-
const start = performance.now();
|
|
271
|
-
const querySessionId = c.req.query("session_id");
|
|
272
|
-
const writer = deps.getTokenFlowWriter();
|
|
273
|
-
const allEvents = readTokenFlowEvents(deps.unerrDir);
|
|
274
|
-
if (allEvents.length === 0) {
|
|
275
|
-
return c.json({ data: null, _meta: { latency_ms: 0 } });
|
|
276
|
-
}
|
|
277
|
-
let sessionId = querySessionId || writer?.sessionId;
|
|
278
|
-
if (!sessionId) {
|
|
279
|
-
sessionId = allEvents[allEvents.length - 1]?.session_id;
|
|
280
|
-
}
|
|
281
|
-
if (!sessionId) {
|
|
282
|
-
return c.json({ data: null, _meta: { latency_ms: 0 } });
|
|
283
|
-
}
|
|
284
|
-
const sessionEvents = querySessionId
|
|
285
|
-
? allEvents.filter((e) => e.session_id === sessionId)
|
|
286
|
-
: allEvents.filter((e) => e.session_id === sessionId || e.session_id === "unknown");
|
|
287
|
-
const metrics = computeQualityMetrics(sessionEvents);
|
|
288
|
-
// Per-turn quality trajectory (for session health chart)
|
|
289
|
-
const turnGroups = new Map();
|
|
290
|
-
for (const e of sessionEvents) {
|
|
291
|
-
const group = turnGroups.get(e.turn) ?? [];
|
|
292
|
-
group.push(e);
|
|
293
|
-
turnGroups.set(e.turn, group);
|
|
294
|
-
}
|
|
295
|
-
const trajectory = [];
|
|
296
|
-
let cumWithout = 0;
|
|
297
|
-
let cumWith = 0;
|
|
298
|
-
for (const [turn, turnEvents] of [...turnGroups.entries()].sort(([a], [b]) => a - b)) {
|
|
299
|
-
let turnGraphCalls = 0;
|
|
300
|
-
let turnGraphDelivered = 0;
|
|
301
|
-
for (const e of turnEvents) {
|
|
302
|
-
cumWithout += e.tokens_without;
|
|
303
|
-
cumWith += e.tokens_with;
|
|
304
|
-
if (GRAPH_MECHANISMS.has(e.mechanism)) {
|
|
305
|
-
turnGraphCalls++;
|
|
306
|
-
turnGraphDelivered += e.tokens_with;
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
trajectory.push({
|
|
310
|
-
turn,
|
|
311
|
-
snr: cumWithout > 0 ? Math.round((cumWith / cumWithout) * 1000) / 1000 : 0,
|
|
312
|
-
cumulative_noise_removed_pct: cumWithout > 0
|
|
313
|
-
? Math.round(((cumWithout - cumWith) / cumWithout) * 100)
|
|
314
|
-
: 0,
|
|
315
|
-
graph_calls_this_turn: turnGraphCalls,
|
|
316
|
-
context_density: turnGraphDelivered > 0
|
|
317
|
-
? Math.round((turnGraphCalls / (turnGraphDelivered / 1000)) * 10) /
|
|
318
|
-
10
|
|
319
|
-
: 0,
|
|
320
|
-
});
|
|
321
|
-
}
|
|
322
|
-
return c.json({
|
|
323
|
-
data: {
|
|
324
|
-
...metrics,
|
|
325
|
-
session_id: sessionId,
|
|
326
|
-
trajectory,
|
|
327
|
-
},
|
|
328
|
-
_meta: {
|
|
329
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
});
|
|
333
|
-
// ── /sessions — Quality scores per session for list view ────────
|
|
334
|
-
app.get("/sessions", (c) => {
|
|
335
|
-
const start = performance.now();
|
|
336
|
-
const fromTs = c.req.query("from_ts");
|
|
337
|
-
const toTs = c.req.query("to_ts");
|
|
338
|
-
const limit = Math.min(Number(c.req.query("limit") ?? 50), 200);
|
|
339
|
-
const offset = Math.max(Number(c.req.query("offset") ?? 0), 0);
|
|
340
|
-
const allEvents = readTokenFlowEvents(deps.unerrDir, {
|
|
341
|
-
from_ts: fromTs || undefined,
|
|
342
|
-
to_ts: toTs || undefined,
|
|
343
|
-
});
|
|
344
|
-
// Build agent lookup
|
|
345
|
-
const historyEntries = readSessionHistory(deps.unerrDir);
|
|
346
|
-
const agentBySession = new Map();
|
|
347
|
-
for (const h of historyEntries) {
|
|
348
|
-
if (h.agentName)
|
|
349
|
-
agentBySession.set(h.sessionId, h.agentName);
|
|
350
|
-
}
|
|
351
|
-
// Group events by session
|
|
352
|
-
const sessionMap = new Map();
|
|
353
|
-
for (const e of allEvents) {
|
|
354
|
-
const group = sessionMap.get(e.session_id) ?? [];
|
|
355
|
-
group.push(e);
|
|
356
|
-
sessionMap.set(e.session_id, group);
|
|
357
|
-
}
|
|
358
|
-
const allSessions = [...sessionMap.entries()]
|
|
359
|
-
.map(([sessionId, events]) => {
|
|
360
|
-
const m = computeQualityMetrics(events);
|
|
361
|
-
const lastTs = events.reduce((max, e) => (e.ts > max ? e.ts : max), events[0].ts);
|
|
362
|
-
const firstTs = events.reduce((min, e) => (e.ts < min ? e.ts : min), events[0].ts);
|
|
363
|
-
return {
|
|
364
|
-
session_id: sessionId,
|
|
365
|
-
first_ts: firstTs,
|
|
366
|
-
last_ts: lastTs,
|
|
367
|
-
agent_name: agentBySession.get(sessionId) ??
|
|
368
|
-
deps.getAgentName?.(sessionId) ??
|
|
369
|
-
null,
|
|
370
|
-
noise_removed_pct: m.noise_removed_pct,
|
|
371
|
-
first_call_resolution_rate: m.first_call_resolution_rate,
|
|
372
|
-
prevention_score: m.prevention_score,
|
|
373
|
-
reasoning_quality_multiplier: m.reasoning_quality_multiplier,
|
|
374
|
-
context_density: m.context_density,
|
|
375
|
-
turns_saved: m.turns_saved,
|
|
376
|
-
total_events: m.total_events,
|
|
377
|
-
total_turns: m.total_turns,
|
|
378
|
-
memory_effectiveness_pct: m.memory_effectiveness_pct,
|
|
379
|
-
memory_signals_fired: m.memory_signals_fired,
|
|
380
|
-
memory_verdicts_total: m.memory_verdicts_total,
|
|
381
|
-
};
|
|
382
|
-
})
|
|
383
|
-
.sort((a, b) => (b.last_ts ?? "").localeCompare(a.last_ts ?? ""));
|
|
384
|
-
const paginated = allSessions.slice(offset, offset + limit);
|
|
385
|
-
return c.json({
|
|
386
|
-
data: paginated,
|
|
387
|
-
total: allSessions.length,
|
|
388
|
-
limit,
|
|
389
|
-
offset,
|
|
390
|
-
_meta: {
|
|
391
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
});
|
|
395
|
-
// ── /trend — Quality metrics over time (per-session chronological) ─
|
|
396
|
-
// Powers the "Quality Over Time" temporal chart in the global view.
|
|
397
|
-
app.get("/trend", (c) => {
|
|
398
|
-
const start = performance.now();
|
|
399
|
-
const fromTs = c.req.query("from_ts");
|
|
400
|
-
const toTs = c.req.query("to_ts");
|
|
401
|
-
const allEvents = readTokenFlowEvents(deps.unerrDir, {
|
|
402
|
-
from_ts: fromTs || undefined,
|
|
403
|
-
to_ts: toTs || undefined,
|
|
404
|
-
});
|
|
405
|
-
// Group by session
|
|
406
|
-
const sessionMap = new Map();
|
|
407
|
-
for (const e of allEvents) {
|
|
408
|
-
const group = sessionMap.get(e.session_id) ?? [];
|
|
409
|
-
group.push(e);
|
|
410
|
-
sessionMap.set(e.session_id, group);
|
|
411
|
-
}
|
|
412
|
-
// Build chronological trend — one data point per session, ordered by time
|
|
413
|
-
const trend = [...sessionMap.entries()]
|
|
414
|
-
.map(([sessionId, events]) => {
|
|
415
|
-
const m = computeQualityMetrics(events);
|
|
416
|
-
const firstTs = events.reduce((min, e) => (e.ts < min ? e.ts : min), events[0].ts);
|
|
417
|
-
const lastTs = events.reduce((max, e) => (e.ts > max ? e.ts : max), events[0].ts);
|
|
418
|
-
return {
|
|
419
|
-
session_id: sessionId,
|
|
420
|
-
first_ts: firstTs,
|
|
421
|
-
last_ts: lastTs,
|
|
422
|
-
noise_removed_pct: m.noise_removed_pct,
|
|
423
|
-
first_call_resolution_rate: m.first_call_resolution_rate,
|
|
424
|
-
prevention_score: m.prevention_score,
|
|
425
|
-
reasoning_quality_multiplier: m.reasoning_quality_multiplier,
|
|
426
|
-
context_density: m.context_density,
|
|
427
|
-
attention_multiplier: m.attention_multiplier,
|
|
428
|
-
turns_saved: m.turns_saved,
|
|
429
|
-
total_events: m.total_events,
|
|
430
|
-
memory_effectiveness_pct: m.memory_effectiveness_pct,
|
|
431
|
-
memory_signals_fired: m.memory_signals_fired,
|
|
432
|
-
};
|
|
433
|
-
})
|
|
434
|
-
.sort((a, b) => a.first_ts.localeCompare(b.first_ts));
|
|
435
|
-
return c.json({
|
|
436
|
-
data: trend,
|
|
437
|
-
total: trend.length,
|
|
438
|
-
_meta: {
|
|
439
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
440
|
-
},
|
|
441
|
-
});
|
|
442
|
-
});
|
|
443
|
-
return app;
|
|
444
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 7: Session API — live counters, efficiency, intents, shadow ledger.
|
|
3
|
-
*/
|
|
4
|
-
import { Hono } from "hono";
|
|
5
|
-
import { computePercentiles, totalCaughtEvents, } from "../../proxy/session-stats.js";
|
|
6
|
-
function parseLimit(raw, fallback, max) {
|
|
7
|
-
const n = Number.parseInt(raw ?? "", 10);
|
|
8
|
-
if (Number.isNaN(n) || n < 1)
|
|
9
|
-
return fallback;
|
|
10
|
-
return Math.min(n, max);
|
|
11
|
-
}
|
|
12
|
-
export function createSessionRoutes(deps) {
|
|
13
|
-
const app = new Hono();
|
|
14
|
-
app.get("/stats", async (c) => {
|
|
15
|
-
const start = performance.now();
|
|
16
|
-
const s = deps.stats;
|
|
17
|
-
const localP = computePercentiles(s.latency.localSamples, s.latency.localTotalSamples);
|
|
18
|
-
return c.json({
|
|
19
|
-
data: {
|
|
20
|
-
tool_calls: s.toolCallsLocal,
|
|
21
|
-
estimated_tokens_saved: s.estimatedTokensSaved,
|
|
22
|
-
violations_caught: s.violationsCaught,
|
|
23
|
-
risk_warnings_issued: s.riskWarningsIssued,
|
|
24
|
-
session_started_at: new Date(s.sessionStartedAt).toISOString(),
|
|
25
|
-
is_resumed_session: s.isResumedSession,
|
|
26
|
-
previous_session: s.previousSession,
|
|
27
|
-
caught_events: {
|
|
28
|
-
...s.events,
|
|
29
|
-
total: totalCaughtEvents(s.events),
|
|
30
|
-
},
|
|
31
|
-
latency_ms: localP
|
|
32
|
-
? {
|
|
33
|
-
p50: localP.p50,
|
|
34
|
-
p95: localP.p95,
|
|
35
|
-
p99: localP.p99,
|
|
36
|
-
min: localP.min,
|
|
37
|
-
max: localP.max,
|
|
38
|
-
count: localP.count,
|
|
39
|
-
}
|
|
40
|
-
: null,
|
|
41
|
-
local_mode: s.localMode,
|
|
42
|
-
},
|
|
43
|
-
_meta: {
|
|
44
|
-
source: "local",
|
|
45
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
app.get("/efficiency", async (c) => {
|
|
50
|
-
const start = performance.now();
|
|
51
|
-
const snap = deps.getEfficiencySnapshot();
|
|
52
|
-
return c.json({
|
|
53
|
-
data: snap,
|
|
54
|
-
_meta: {
|
|
55
|
-
source: "local",
|
|
56
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
57
|
-
},
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
app.get("/intents", async (c) => {
|
|
61
|
-
const start = performance.now();
|
|
62
|
-
const groups = deps.getIntentGroups();
|
|
63
|
-
return c.json({
|
|
64
|
-
data: groups,
|
|
65
|
-
_meta: {
|
|
66
|
-
source: "local",
|
|
67
|
-
count: groups.length,
|
|
68
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
69
|
-
},
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
app.get("/ledger", async (c) => {
|
|
73
|
-
const start = performance.now();
|
|
74
|
-
const limit = parseLimit(c.req.query("limit"), 50, 200);
|
|
75
|
-
const entries = deps.getRecentLedgerEntries(limit);
|
|
76
|
-
return c.json({
|
|
77
|
-
data: entries,
|
|
78
|
-
_meta: {
|
|
79
|
-
source: "local",
|
|
80
|
-
count: entries.length,
|
|
81
|
-
latency_ms: Math.round((performance.now() - start) * 100) / 100,
|
|
82
|
-
},
|
|
83
|
-
});
|
|
84
|
-
});
|
|
85
|
-
return app;
|
|
86
|
-
}
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Layer 7: SSE endpoint for real-time dashboard updates.
|
|
3
|
-
*
|
|
4
|
-
* GET /api/stream — Server-Sent Events transport.
|
|
5
|
-
*
|
|
6
|
-
* Lifecycle:
|
|
7
|
-
* 1. Send backfill (last 20 events from circular buffer)
|
|
8
|
-
* 2. Subscribe to EventBus for live events
|
|
9
|
-
* 3. Send keepalive ping every 25s
|
|
10
|
-
* 4. On disconnect: unsubscribe, cleanup
|
|
11
|
-
*/
|
|
12
|
-
import { Hono } from "hono";
|
|
13
|
-
import { streamSSE } from "hono/streaming";
|
|
14
|
-
import { totalCaughtEvents, } from "../../proxy/session-stats.js";
|
|
15
|
-
import { eventBus } from "../event-bus.js";
|
|
16
|
-
export function createStreamRoutes(deps) {
|
|
17
|
-
const app = new Hono();
|
|
18
|
-
app.get("/", (c) => {
|
|
19
|
-
return streamSSE(c, async (stream) => {
|
|
20
|
-
let id = 0;
|
|
21
|
-
let aborted = false;
|
|
22
|
-
stream.onAbort(() => {
|
|
23
|
-
aborted = true;
|
|
24
|
-
});
|
|
25
|
-
// 1. Send initial session stats
|
|
26
|
-
await stream.writeSSE({
|
|
27
|
-
id: String(++id),
|
|
28
|
-
event: "session_stats",
|
|
29
|
-
data: JSON.stringify({
|
|
30
|
-
tool_calls: deps.stats.toolCallsLocal,
|
|
31
|
-
tokens_saved: deps.stats.estimatedTokensSaved,
|
|
32
|
-
violations_caught: deps.stats.violationsCaught,
|
|
33
|
-
risk_warnings: deps.stats.riskWarningsIssued,
|
|
34
|
-
duration_s: Math.round((Date.now() - deps.stats.sessionStartedAt) / 1000),
|
|
35
|
-
session_events: deps.stats.events,
|
|
36
|
-
caught_total: totalCaughtEvents(deps.stats.events),
|
|
37
|
-
}),
|
|
38
|
-
});
|
|
39
|
-
// 2. Backfill recent events
|
|
40
|
-
const recent = eventBus.getRecentEvents(20);
|
|
41
|
-
for (const event of recent) {
|
|
42
|
-
if (aborted)
|
|
43
|
-
return;
|
|
44
|
-
await stream.writeSSE({
|
|
45
|
-
id: String(++id),
|
|
46
|
-
event: event.type,
|
|
47
|
-
data: JSON.stringify(event.data),
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
// 3. Subscribe to live events
|
|
51
|
-
const unsubscribe = eventBus.subscribe(async (event) => {
|
|
52
|
-
if (aborted)
|
|
53
|
-
return;
|
|
54
|
-
try {
|
|
55
|
-
await stream.writeSSE({
|
|
56
|
-
id: String(++id),
|
|
57
|
-
event: event.type,
|
|
58
|
-
data: JSON.stringify(event.data),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
aborted = true;
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
// 4. Keepalive ping every 25s
|
|
66
|
-
const pingInterval = setInterval(async () => {
|
|
67
|
-
if (aborted) {
|
|
68
|
-
clearInterval(pingInterval);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
try {
|
|
72
|
-
await stream.writeSSE({
|
|
73
|
-
id: String(++id),
|
|
74
|
-
event: "ping",
|
|
75
|
-
data: JSON.stringify({}),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
aborted = true;
|
|
80
|
-
clearInterval(pingInterval);
|
|
81
|
-
}
|
|
82
|
-
}, 25_000);
|
|
83
|
-
// 5. Session stats tick every 30s
|
|
84
|
-
const statsInterval = setInterval(async () => {
|
|
85
|
-
if (aborted) {
|
|
86
|
-
clearInterval(statsInterval);
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
try {
|
|
90
|
-
await stream.writeSSE({
|
|
91
|
-
id: String(++id),
|
|
92
|
-
event: "session_stats",
|
|
93
|
-
data: JSON.stringify({
|
|
94
|
-
tool_calls: deps.stats.toolCallsLocal,
|
|
95
|
-
tokens_saved: deps.stats.estimatedTokensSaved,
|
|
96
|
-
violations_caught: deps.stats.violationsCaught,
|
|
97
|
-
risk_warnings: deps.stats.riskWarningsIssued,
|
|
98
|
-
duration_s: Math.round((Date.now() - deps.stats.sessionStartedAt) / 1000),
|
|
99
|
-
session_events: deps.stats.events,
|
|
100
|
-
caught_total: totalCaughtEvents(deps.stats.events),
|
|
101
|
-
}),
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
aborted = true;
|
|
106
|
-
clearInterval(statsInterval);
|
|
107
|
-
}
|
|
108
|
-
}, 30_000);
|
|
109
|
-
// Block until client disconnects
|
|
110
|
-
while (!aborted) {
|
|
111
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
112
|
-
}
|
|
113
|
-
// Cleanup
|
|
114
|
-
clearInterval(pingInterval);
|
|
115
|
-
clearInterval(statsInterval);
|
|
116
|
-
unsubscribe();
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
return app;
|
|
120
|
-
}
|