@unerr-ai/unerr 0.2.1 → 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 +1 -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,290 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Convention Drift Prevention — BA-2.2
|
|
3
|
-
*
|
|
4
|
-
* PostToolUse: checks changed code regions against detected project conventions.
|
|
5
|
-
* Detects naming violations (camelCase vs snake_case), structural violations
|
|
6
|
-
* (barrel imports, file organization), and suggests or auto-fixes.
|
|
7
|
-
*
|
|
8
|
-
* Learning loop: developer corrections (accepted/dismissed) adjust
|
|
9
|
-
* confidence thresholds per convention.
|
|
10
|
-
*
|
|
11
|
-
* Auto-fix only applied when confidence > 0.9.
|
|
12
|
-
*/
|
|
13
|
-
import { Behavior, } from "./framework.js";
|
|
14
|
-
const AUTO_FIX_CONFIDENCE_THRESHOLD = 0.9;
|
|
15
|
-
const CAMEL_CASE = /^[a-z][a-zA-Z0-9]*$/;
|
|
16
|
-
const PASCAL_CASE = /^[A-Z][a-zA-Z0-9]*$/;
|
|
17
|
-
const SNAKE_CASE = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$/;
|
|
18
|
-
const NAMING_RULES = [
|
|
19
|
-
{ id: "camelCase", pattern: CAMEL_CASE, label: "camelCase" },
|
|
20
|
-
{ id: "PascalCase", pattern: PASCAL_CASE, label: "PascalCase" },
|
|
21
|
-
{ id: "snake_case", pattern: SNAKE_CASE, label: "snake_case" },
|
|
22
|
-
];
|
|
23
|
-
const EDIT_TOOLS = new Set([
|
|
24
|
-
"file_write",
|
|
25
|
-
"write_file",
|
|
26
|
-
"edit_file",
|
|
27
|
-
"str_replace_editor",
|
|
28
|
-
"insert_code",
|
|
29
|
-
"replace_code",
|
|
30
|
-
]);
|
|
31
|
-
export class ConventionDriftPrevention extends Behavior {
|
|
32
|
-
id = "convention_drift";
|
|
33
|
-
hooks = ["post_tool_use"];
|
|
34
|
-
defaultLevel = "suggestion";
|
|
35
|
-
graph = null;
|
|
36
|
-
driftConfig;
|
|
37
|
-
violationsThisSession = 0;
|
|
38
|
-
autoFixesThisSession = 0;
|
|
39
|
-
constructor(config) {
|
|
40
|
-
super(config, "suggestion");
|
|
41
|
-
this.driftConfig = {
|
|
42
|
-
enabled: true,
|
|
43
|
-
level: "suggestion",
|
|
44
|
-
autoFixHighConfidence: true,
|
|
45
|
-
confidenceThreshold: AUTO_FIX_CONFIDENCE_THRESHOLD,
|
|
46
|
-
...config,
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
attachGraph(graph) {
|
|
50
|
-
this.graph = graph;
|
|
51
|
-
}
|
|
52
|
-
async onPostToolUse(ctx) {
|
|
53
|
-
if (!EDIT_TOOLS.has(ctx.toolName))
|
|
54
|
-
return null;
|
|
55
|
-
if (!ctx.filePath)
|
|
56
|
-
return null;
|
|
57
|
-
if (!isCodeFile(ctx.filePath))
|
|
58
|
-
return null;
|
|
59
|
-
const newContent = extractNewContent(ctx.args);
|
|
60
|
-
if (!newContent)
|
|
61
|
-
return null;
|
|
62
|
-
const violations = [];
|
|
63
|
-
const namingViolations = await this.checkNamingConventions(ctx.filePath, newContent);
|
|
64
|
-
violations.push(...namingViolations);
|
|
65
|
-
const importViolations = await this.checkImportConventions(ctx.filePath, newContent);
|
|
66
|
-
violations.push(...importViolations);
|
|
67
|
-
if (violations.length === 0)
|
|
68
|
-
return null;
|
|
69
|
-
this.violationsThisSession += violations.length;
|
|
70
|
-
const autoFixCount = violations.filter((v) => v.autoFixed).length;
|
|
71
|
-
this.autoFixesThisSession += autoFixCount;
|
|
72
|
-
return {
|
|
73
|
-
behaviorId: this.id,
|
|
74
|
-
level: this.level,
|
|
75
|
-
relatedSkillId: "convention-aware-generation",
|
|
76
|
-
_meta: {
|
|
77
|
-
behavior: this.id,
|
|
78
|
-
violations: violations.length,
|
|
79
|
-
auto_fixed: autoFixCount,
|
|
80
|
-
},
|
|
81
|
-
_context: {
|
|
82
|
-
convention_violations: violations,
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
getSessionStats() {
|
|
87
|
-
return {
|
|
88
|
-
violationsDetected: this.violationsThisSession,
|
|
89
|
-
autoFixes: this.autoFixesThisSession,
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Check naming conventions in new content against project-wide patterns.
|
|
94
|
-
* Uses the graph's convention data to determine the dominant style.
|
|
95
|
-
*/
|
|
96
|
-
async checkNamingConventions(filePath, content) {
|
|
97
|
-
const projectConvention = await this.detectDominantNamingConvention(filePath);
|
|
98
|
-
if (!projectConvention)
|
|
99
|
-
return [];
|
|
100
|
-
const newNames = extractDefinedNames(content);
|
|
101
|
-
const violations = [];
|
|
102
|
-
for (const name of newNames) {
|
|
103
|
-
if (name.length <= 1)
|
|
104
|
-
continue;
|
|
105
|
-
if (isAllCaps(name))
|
|
106
|
-
continue;
|
|
107
|
-
if (!projectConvention.pattern.test(name)) {
|
|
108
|
-
const expected = suggestFix(name, projectConvention.id);
|
|
109
|
-
const confidence = projectConvention.confidence;
|
|
110
|
-
const canAutoFix = this.driftConfig.autoFixHighConfidence &&
|
|
111
|
-
confidence >= this.driftConfig.confidenceThreshold &&
|
|
112
|
-
expected !== null;
|
|
113
|
-
violations.push({
|
|
114
|
-
rule: `naming:${projectConvention.id}_for_identifiers`,
|
|
115
|
-
violation: `${name} (should be ${expected ?? projectConvention.label})`,
|
|
116
|
-
file: filePath,
|
|
117
|
-
confidence,
|
|
118
|
-
autoFixed: canAutoFix,
|
|
119
|
-
evidence: `${Math.round(projectConvention.confidence * 100)}% of identifiers in this context use ${projectConvention.label}`,
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return violations;
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Check import conventions — barrel exports, relative vs absolute, etc.
|
|
127
|
-
*/
|
|
128
|
-
async checkImportConventions(filePath, content) {
|
|
129
|
-
if (!this.graph)
|
|
130
|
-
return [];
|
|
131
|
-
const conventions = await this.graph.getConventionsForEntity(filePath, 5);
|
|
132
|
-
const importConventions = conventions.filter((c) => c.name.toLowerCase().includes("import") ||
|
|
133
|
-
c.name.toLowerCase().includes("barrel"));
|
|
134
|
-
if (importConventions.length === 0)
|
|
135
|
-
return [];
|
|
136
|
-
const violations = [];
|
|
137
|
-
const importLines = extractImportLines(content);
|
|
138
|
-
for (const convention of importConventions) {
|
|
139
|
-
const adherence = convention.adherence_pct / 100;
|
|
140
|
-
if (adherence < 0.6)
|
|
141
|
-
continue;
|
|
142
|
-
if (convention.name.toLowerCase().includes("barrel")) {
|
|
143
|
-
for (const imp of importLines) {
|
|
144
|
-
if (isDeepImport(imp)) {
|
|
145
|
-
violations.push({
|
|
146
|
-
rule: "import:barrel_exports",
|
|
147
|
-
violation: `Deep import "${imp}" — project convention uses barrel exports`,
|
|
148
|
-
file: filePath,
|
|
149
|
-
confidence: adherence,
|
|
150
|
-
autoFixed: false,
|
|
151
|
-
evidence: convention.rule ||
|
|
152
|
-
`${convention.adherence_pct}% of imports follow barrel pattern`,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return violations;
|
|
159
|
-
}
|
|
160
|
-
async detectDominantNamingConvention(filePath) {
|
|
161
|
-
if (!this.graph)
|
|
162
|
-
return null;
|
|
163
|
-
const conventions = await this.graph.getConventionsForEntity(filePath, 5);
|
|
164
|
-
const namingConvention = conventions.find((c) => c.name.toLowerCase().includes("naming") ||
|
|
165
|
-
c.name.toLowerCase().includes("camel") ||
|
|
166
|
-
c.name.toLowerCase().includes("pascal") ||
|
|
167
|
-
c.name.toLowerCase().includes("snake"));
|
|
168
|
-
if (namingConvention) {
|
|
169
|
-
const rule = NAMING_RULES.find((r) => namingConvention.name.toLowerCase().includes(r.id.toLowerCase()));
|
|
170
|
-
if (rule) {
|
|
171
|
-
return { ...rule, confidence: namingConvention.adherence_pct / 100 };
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
const entities = await this.graph.getEntitiesByFile(filePath);
|
|
175
|
-
if (entities.length < 3)
|
|
176
|
-
return null;
|
|
177
|
-
const counts = new Map();
|
|
178
|
-
for (const entity of entities) {
|
|
179
|
-
if (entity.kind !== "function" && entity.kind !== "variable")
|
|
180
|
-
continue;
|
|
181
|
-
for (const rule of NAMING_RULES) {
|
|
182
|
-
if (rule.pattern.test(entity.name)) {
|
|
183
|
-
counts.set(rule.id, (counts.get(rule.id) ?? 0) + 1);
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
let best = null;
|
|
189
|
-
for (const [id, count] of counts) {
|
|
190
|
-
if (!best || count > best.count)
|
|
191
|
-
best = { id, count };
|
|
192
|
-
}
|
|
193
|
-
if (!best)
|
|
194
|
-
return null;
|
|
195
|
-
const total = [...counts.values()].reduce((a, b) => a + b, 0);
|
|
196
|
-
const confidence = best.count / total;
|
|
197
|
-
if (confidence < 0.6)
|
|
198
|
-
return null;
|
|
199
|
-
const rule = NAMING_RULES.find((r) => r.id === best?.id);
|
|
200
|
-
if (!rule)
|
|
201
|
-
return null;
|
|
202
|
-
return { ...rule, confidence };
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
function isCodeFile(filePath) {
|
|
206
|
-
return /\.[jt]sx?$|\.vue$|\.svelte$/.test(filePath);
|
|
207
|
-
}
|
|
208
|
-
function extractNewContent(args) {
|
|
209
|
-
if (typeof args.new_str === "string")
|
|
210
|
-
return args.new_str;
|
|
211
|
-
if (typeof args.new_string === "string")
|
|
212
|
-
return args.new_string;
|
|
213
|
-
if (typeof args.content === "string")
|
|
214
|
-
return args.content;
|
|
215
|
-
if (typeof args.after === "string")
|
|
216
|
-
return args.after;
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Extract names defined in code (function names, variable names, class names).
|
|
221
|
-
* Lightweight regex-based extraction — not full AST, but fast and sufficient.
|
|
222
|
-
*/
|
|
223
|
-
function extractDefinedNames(content) {
|
|
224
|
-
const names = [];
|
|
225
|
-
const patterns = [
|
|
226
|
-
/(?:export\s+)?(?:async\s+)?function\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
|
|
227
|
-
/(?:export\s+)?(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
|
|
228
|
-
/(?:export\s+)?class\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
|
|
229
|
-
/(?:export\s+)?interface\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
|
|
230
|
-
/(?:export\s+)?type\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=/g,
|
|
231
|
-
/(?:export\s+)?enum\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g,
|
|
232
|
-
];
|
|
233
|
-
for (const pattern of patterns) {
|
|
234
|
-
let match;
|
|
235
|
-
match = pattern.exec(content);
|
|
236
|
-
while (match !== null) {
|
|
237
|
-
if (match[1])
|
|
238
|
-
names.push(match[1]);
|
|
239
|
-
match = pattern.exec(content);
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return [...new Set(names)];
|
|
243
|
-
}
|
|
244
|
-
function extractImportLines(content) {
|
|
245
|
-
const imports = [];
|
|
246
|
-
const pattern = /import\s+.*?from\s+['"]([^'"]+)['"]/g;
|
|
247
|
-
let match;
|
|
248
|
-
match = pattern.exec(content);
|
|
249
|
-
while (match !== null) {
|
|
250
|
-
if (match[1])
|
|
251
|
-
imports.push(match[1]);
|
|
252
|
-
match = pattern.exec(content);
|
|
253
|
-
}
|
|
254
|
-
return imports;
|
|
255
|
-
}
|
|
256
|
-
function isDeepImport(importPath) {
|
|
257
|
-
if (!importPath.startsWith("."))
|
|
258
|
-
return false;
|
|
259
|
-
const segments = importPath.split("/").filter(Boolean);
|
|
260
|
-
const nonDots = segments.filter((s) => s !== "." && s !== "..");
|
|
261
|
-
return nonDots.length >= 3;
|
|
262
|
-
}
|
|
263
|
-
function isAllCaps(name) {
|
|
264
|
-
return /^[A-Z_][A-Z0-9_]*$/.test(name);
|
|
265
|
-
}
|
|
266
|
-
function suggestFix(name, targetConvention) {
|
|
267
|
-
const words = splitIntoWords(name);
|
|
268
|
-
if (words.length === 0)
|
|
269
|
-
return null;
|
|
270
|
-
switch (targetConvention) {
|
|
271
|
-
case "camelCase":
|
|
272
|
-
return words[0]?.toLowerCase() + words.slice(1).map(capitalize).join("");
|
|
273
|
-
case "PascalCase":
|
|
274
|
-
return words.map(capitalize).join("");
|
|
275
|
-
case "snake_case":
|
|
276
|
-
return words.map((w) => w.toLowerCase()).join("_");
|
|
277
|
-
default:
|
|
278
|
-
return null;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
function splitIntoWords(name) {
|
|
282
|
-
if (name.includes("_"))
|
|
283
|
-
return name.split("_").filter(Boolean);
|
|
284
|
-
if (name.includes("-"))
|
|
285
|
-
return name.split("-").filter(Boolean);
|
|
286
|
-
return name.split(/(?=[A-Z])/).filter(Boolean);
|
|
287
|
-
}
|
|
288
|
-
function capitalize(word) {
|
|
289
|
-
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
|
|
290
|
-
}
|
|
@@ -1,235 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Behavior Framework — registration, dispatch, assertiveness levels,
|
|
3
|
-
* learning loop, and $0.50 gate integration for Layer 4 behavioral automation.
|
|
4
|
-
*
|
|
5
|
-
* Every behavior has a lifecycle hook (pre_tool_use, post_tool_use,
|
|
6
|
-
* session_start, session_end) and an assertiveness level that determines
|
|
7
|
-
* whether it silently enriches, suggests, or enforces.
|
|
8
|
-
*/
|
|
9
|
-
import { formatGuardMoment, shouldFireGuard, } from "./guard-formatter.js";
|
|
10
|
-
/**
|
|
11
|
-
* Abstract base for all behavioral automations.
|
|
12
|
-
* Subclasses implement one or more hook methods.
|
|
13
|
-
*/
|
|
14
|
-
export class Behavior {
|
|
15
|
-
config;
|
|
16
|
-
learningLog = [];
|
|
17
|
-
acceptCount = 0;
|
|
18
|
-
dismissCount = 0;
|
|
19
|
-
overrideCount = 0;
|
|
20
|
-
constructor(config, defaultLevel = "suggestion") {
|
|
21
|
-
this.config = {
|
|
22
|
-
enabled: true,
|
|
23
|
-
level: defaultLevel,
|
|
24
|
-
...config,
|
|
25
|
-
};
|
|
26
|
-
}
|
|
27
|
-
get level() {
|
|
28
|
-
return this.config.level;
|
|
29
|
-
}
|
|
30
|
-
get enabled() {
|
|
31
|
-
return this.config.enabled;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* PreToolUse — fires BEFORE the agent's tool call executes.
|
|
35
|
-
* Return a BehaviorOutput with halt=true to block execution.
|
|
36
|
-
*/
|
|
37
|
-
async onPreToolUse(_ctx) {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* PostToolUse — fires AFTER the agent's tool call executes.
|
|
42
|
-
* Can inject _context into the response for the agent.
|
|
43
|
-
*/
|
|
44
|
-
async onPostToolUse(_ctx) {
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* SessionStart — fires on the first tool call of a new session.
|
|
49
|
-
*/
|
|
50
|
-
async onSessionStart(_ctx) {
|
|
51
|
-
return null;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* SessionEnd — fires on proxy shutdown or extended inactivity.
|
|
55
|
-
*/
|
|
56
|
-
async onSessionEnd(_ctx) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
recordFeedback(action, entityKey) {
|
|
60
|
-
switch (action) {
|
|
61
|
-
case "accepted":
|
|
62
|
-
this.acceptCount++;
|
|
63
|
-
break;
|
|
64
|
-
case "dismissed":
|
|
65
|
-
this.dismissCount++;
|
|
66
|
-
break;
|
|
67
|
-
case "overridden":
|
|
68
|
-
this.overrideCount++;
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
this.learningLog.push({
|
|
72
|
-
behaviorId: this.id,
|
|
73
|
-
action,
|
|
74
|
-
entityKey,
|
|
75
|
-
timestamp: Date.now(),
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Confidence score: ratio of accepted / total feedback.
|
|
80
|
-
* Starts at 1.0 (no feedback = full confidence).
|
|
81
|
-
*/
|
|
82
|
-
getConfidence() {
|
|
83
|
-
const total = this.acceptCount + this.dismissCount + this.overrideCount;
|
|
84
|
-
if (total === 0)
|
|
85
|
-
return 1.0;
|
|
86
|
-
return this.acceptCount / total;
|
|
87
|
-
}
|
|
88
|
-
getLearningStats() {
|
|
89
|
-
return {
|
|
90
|
-
accepted: this.acceptCount,
|
|
91
|
-
dismissed: this.dismissCount,
|
|
92
|
-
overridden: this.overrideCount,
|
|
93
|
-
confidence: this.getConfidence(),
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Centralized dispatcher: registers behaviors, routes hook events,
|
|
99
|
-
* merges outputs, and enforces the $0.50 gate.
|
|
100
|
-
*/
|
|
101
|
-
export class BehaviorDispatcher {
|
|
102
|
-
behaviors = [];
|
|
103
|
-
sessionStartFired = false;
|
|
104
|
-
guardMoments = [];
|
|
105
|
-
register(behavior) {
|
|
106
|
-
this.behaviors.push(behavior);
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* Fire all pre-tool-use behaviors. Returns merged output.
|
|
110
|
-
* If ANY enforcement behavior returns halt=true, the entire call is halted.
|
|
111
|
-
*/
|
|
112
|
-
async firePreToolUse(ctx) {
|
|
113
|
-
const outputs = [];
|
|
114
|
-
if (!this.sessionStartFired) {
|
|
115
|
-
this.sessionStartFired = true;
|
|
116
|
-
const startOutputs = await this.fireSessionStart(ctx);
|
|
117
|
-
if (startOutputs)
|
|
118
|
-
outputs.push(startOutputs);
|
|
119
|
-
}
|
|
120
|
-
for (const b of this.behaviors) {
|
|
121
|
-
if (!b.enabled || !b.hooks.includes("pre_tool_use"))
|
|
122
|
-
continue;
|
|
123
|
-
const output = await b.onPreToolUse(ctx);
|
|
124
|
-
if (output)
|
|
125
|
-
outputs.push(output);
|
|
126
|
-
}
|
|
127
|
-
return this.mergeOutputs(outputs);
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* Fire all post-tool-use behaviors. Returns merged output.
|
|
131
|
-
*/
|
|
132
|
-
async firePostToolUse(ctx) {
|
|
133
|
-
const outputs = [];
|
|
134
|
-
for (const b of this.behaviors) {
|
|
135
|
-
if (!b.enabled || !b.hooks.includes("post_tool_use"))
|
|
136
|
-
continue;
|
|
137
|
-
const output = await b.onPostToolUse(ctx);
|
|
138
|
-
if (output)
|
|
139
|
-
outputs.push(output);
|
|
140
|
-
}
|
|
141
|
-
return this.mergeOutputs(outputs);
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Fire session start behaviors (called once, on first tool call).
|
|
145
|
-
*/
|
|
146
|
-
async fireSessionStart(ctx) {
|
|
147
|
-
const outputs = [];
|
|
148
|
-
for (const b of this.behaviors) {
|
|
149
|
-
if (!b.enabled || !b.hooks.includes("session_start"))
|
|
150
|
-
continue;
|
|
151
|
-
const output = await b.onSessionStart(ctx);
|
|
152
|
-
if (output)
|
|
153
|
-
outputs.push(output);
|
|
154
|
-
}
|
|
155
|
-
return this.mergeOutputs(outputs);
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Fire session end behaviors (called on shutdown).
|
|
159
|
-
*/
|
|
160
|
-
async fireSessionEnd(ctx) {
|
|
161
|
-
const outputs = [];
|
|
162
|
-
for (const b of this.behaviors) {
|
|
163
|
-
if (!b.enabled || !b.hooks.includes("session_end"))
|
|
164
|
-
continue;
|
|
165
|
-
const output = await b.onSessionEnd(ctx);
|
|
166
|
-
if (output)
|
|
167
|
-
outputs.push(output);
|
|
168
|
-
}
|
|
169
|
-
return this.mergeOutputs(outputs);
|
|
170
|
-
}
|
|
171
|
-
getGuardMoments() {
|
|
172
|
-
return [...this.guardMoments];
|
|
173
|
-
}
|
|
174
|
-
getRegisteredBehaviors() {
|
|
175
|
-
return [...this.behaviors];
|
|
176
|
-
}
|
|
177
|
-
resetSession() {
|
|
178
|
-
this.sessionStartFired = false;
|
|
179
|
-
this.guardMoments = [];
|
|
180
|
-
}
|
|
181
|
-
mergeOutputs(outputs) {
|
|
182
|
-
if (outputs.length === 0)
|
|
183
|
-
return null;
|
|
184
|
-
const merged = {
|
|
185
|
-
behaviorId: outputs.map((o) => o.behaviorId).join("+"),
|
|
186
|
-
level: "invisible",
|
|
187
|
-
_meta: {},
|
|
188
|
-
_context: {},
|
|
189
|
-
};
|
|
190
|
-
let shouldHalt = false;
|
|
191
|
-
const relatedSkills = [];
|
|
192
|
-
for (const output of outputs) {
|
|
193
|
-
if (output.halt)
|
|
194
|
-
shouldHalt = true;
|
|
195
|
-
if (output.level === "enforcement")
|
|
196
|
-
merged.level = "enforcement";
|
|
197
|
-
else if (output.level === "suggestion" && merged.level !== "enforcement")
|
|
198
|
-
merged.level = "suggestion";
|
|
199
|
-
if (output._meta)
|
|
200
|
-
Object.assign(merged._meta, output._meta);
|
|
201
|
-
if (output._context)
|
|
202
|
-
Object.assign(merged._context, output._context);
|
|
203
|
-
if (output.guardMoment) {
|
|
204
|
-
this.guardMoments.push(output.guardMoment);
|
|
205
|
-
}
|
|
206
|
-
if (output.relatedSkillId) {
|
|
207
|
-
relatedSkills.push(output.relatedSkillId);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
// Surface related skills in _context so agent knows which skills to invoke
|
|
211
|
-
if (relatedSkills.length > 0) {
|
|
212
|
-
merged._context["dev.unerr/suggested_skills"] = relatedSkills;
|
|
213
|
-
}
|
|
214
|
-
merged.halt = shouldHalt;
|
|
215
|
-
return merged;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
/**
|
|
219
|
-
* Evaluate $0.50 gate for a behavior output.
|
|
220
|
-
* Used by behaviors to decide whether to surface a guard moment.
|
|
221
|
-
*/
|
|
222
|
-
export function evaluateGate(tokensPrevented, description, modelId, entityKey) {
|
|
223
|
-
if (!shouldFireGuard(tokensPrevented, modelId)) {
|
|
224
|
-
return { passes: false, guardMoment: null };
|
|
225
|
-
}
|
|
226
|
-
const moment = formatGuardMoment(description, tokensPrevented, modelId, entityKey);
|
|
227
|
-
return { passes: true, guardMoment: moment };
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Estimate tokens that would be wasted by N more retry attempts.
|
|
231
|
-
* Based on empirical data: average failed attempt ≈ 2-5K tokens.
|
|
232
|
-
*/
|
|
233
|
-
export function estimateWastedTokens(attemptsRemaining, avgTokensPerAttempt = 3500) {
|
|
234
|
-
return attemptsRemaining * avgTokensPerAttempt;
|
|
235
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Guard Moment Formatter — formats behavioral guard outputs with
|
|
3
|
-
* counterfactual framing and $0.50 gate enforcement.
|
|
4
|
-
*
|
|
5
|
-
* VS.4: Guard moments only fire when >$0.50 would be saved.
|
|
6
|
-
* Format: "[unerr] ⚠ Prevented: {description}. Est. cost: ${amount}"
|
|
7
|
-
*/
|
|
8
|
-
import { calculateDollarSavings } from "../proxy/model-pricing.js";
|
|
9
|
-
import { frameGuardMoment } from "../utils/counterfactual.js";
|
|
10
|
-
const DOLLAR_GATE = 0.5;
|
|
11
|
-
/**
|
|
12
|
-
* Evaluate whether a guard moment passes the $0.50 threshold.
|
|
13
|
-
*/
|
|
14
|
-
export function shouldFireGuard(tokensPrevented, modelId) {
|
|
15
|
-
const dollars = calculateDollarSavings(tokensPrevented, modelId);
|
|
16
|
-
return dollars >= DOLLAR_GATE;
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Format and emit a guard moment to stderr.
|
|
20
|
-
* Only fires if the $0.50 gate is passed.
|
|
21
|
-
*/
|
|
22
|
-
export function formatGuardMoment(description, tokensPrevented, modelId, entityKey) {
|
|
23
|
-
const dollars = calculateDollarSavings(tokensPrevented, modelId);
|
|
24
|
-
if (dollars < DOLLAR_GATE) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
const formatted = frameGuardMoment(description, dollars);
|
|
28
|
-
process.stderr.write(`${formatted}\n`);
|
|
29
|
-
return {
|
|
30
|
-
id: `guard-${Date.now()}`,
|
|
31
|
-
description,
|
|
32
|
-
tokensPrevented,
|
|
33
|
-
dollarsPrevented: dollars,
|
|
34
|
-
entityKey,
|
|
35
|
-
timestamp: Date.now(),
|
|
36
|
-
passed: true,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Get the current dollar gate threshold.
|
|
41
|
-
*/
|
|
42
|
-
export function getDollarGate() {
|
|
43
|
-
return DOLLAR_GATE;
|
|
44
|
-
}
|