@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,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Durability Tracker — tracks AI modifications and their survival time.
|
|
3
|
-
*
|
|
4
|
-
* U.7: Records when AI modifies an entity and whether it survives 24h.
|
|
5
|
-
* U.8: Computes durability score = survived / total modifications.
|
|
6
|
-
*
|
|
7
|
-
* A modification "survives" if the entity's body_hash is unchanged 24h later.
|
|
8
|
-
* Entities with low durability (<0.5) trigger warnings surfaced as `ur|wrn` prefix lines.
|
|
9
|
-
*/
|
|
10
|
-
const SURVIVAL_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
11
|
-
export function createDurabilityTracker() {
|
|
12
|
-
const records = new Map();
|
|
13
|
-
function recordModification(entityKey, sessionId, bodyHash) {
|
|
14
|
-
if (!records.has(entityKey))
|
|
15
|
-
records.set(entityKey, []);
|
|
16
|
-
records.get(entityKey)?.push({
|
|
17
|
-
entityKey,
|
|
18
|
-
modifiedAt: Date.now(),
|
|
19
|
-
sessionId,
|
|
20
|
-
bodyHashAtModification: bodyHash,
|
|
21
|
-
survived: null,
|
|
22
|
-
evaluatedAt: null,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
function evaluateSurvival(entityKey, currentBodyHash) {
|
|
26
|
-
const mods = records.get(entityKey);
|
|
27
|
-
if (!mods)
|
|
28
|
-
return;
|
|
29
|
-
const now = Date.now();
|
|
30
|
-
for (const mod of mods) {
|
|
31
|
-
if (mod.survived !== null)
|
|
32
|
-
continue;
|
|
33
|
-
if (now - mod.modifiedAt < SURVIVAL_WINDOW_MS)
|
|
34
|
-
continue;
|
|
35
|
-
mod.survived = mod.bodyHashAtModification === currentBodyHash;
|
|
36
|
-
mod.evaluatedAt = now;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
function getScore(entityKey) {
|
|
40
|
-
const mods = records.get(entityKey);
|
|
41
|
-
if (!mods || mods.length === 0)
|
|
42
|
-
return null;
|
|
43
|
-
let survived = 0;
|
|
44
|
-
let reverted = 0;
|
|
45
|
-
let pending = 0;
|
|
46
|
-
for (const mod of mods) {
|
|
47
|
-
if (mod.survived === null)
|
|
48
|
-
pending++;
|
|
49
|
-
else if (mod.survived)
|
|
50
|
-
survived++;
|
|
51
|
-
else
|
|
52
|
-
reverted++;
|
|
53
|
-
}
|
|
54
|
-
const evaluated = survived + reverted;
|
|
55
|
-
const score = evaluated > 0 ? survived / evaluated : 0.5;
|
|
56
|
-
return {
|
|
57
|
-
entityKey,
|
|
58
|
-
score: Math.round(score * 100) / 100,
|
|
59
|
-
totalModifications: mods.length,
|
|
60
|
-
survived,
|
|
61
|
-
reverted,
|
|
62
|
-
pending,
|
|
63
|
-
lastModified: mods[mods.length - 1]?.modifiedAt ?? 0,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function getLowDurabilityEntities(threshold = 0.5) {
|
|
67
|
-
const results = [];
|
|
68
|
-
for (const entityKey of records.keys()) {
|
|
69
|
-
const result = getScore(entityKey);
|
|
70
|
-
if (result &&
|
|
71
|
-
result.score < threshold &&
|
|
72
|
-
result.totalModifications >= 2) {
|
|
73
|
-
results.push(result);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
return results.sort((a, b) => a.score - b.score);
|
|
77
|
-
}
|
|
78
|
-
function getAllScores() {
|
|
79
|
-
const scores = new Map();
|
|
80
|
-
for (const entityKey of records.keys()) {
|
|
81
|
-
const result = getScore(entityKey);
|
|
82
|
-
if (result)
|
|
83
|
-
scores.set(entityKey, result);
|
|
84
|
-
}
|
|
85
|
-
return scores;
|
|
86
|
-
}
|
|
87
|
-
return {
|
|
88
|
-
recordModification,
|
|
89
|
-
evaluateSurvival,
|
|
90
|
-
getScore,
|
|
91
|
-
getLowDurabilityEntities,
|
|
92
|
-
getAllScores,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Sprint 7.4: Entity-level rewind — surgically restore a single entity to its base state.
|
|
3
|
-
*
|
|
4
|
-
* Flow:
|
|
5
|
-
* 1. Look up entity in drift_overlay by name (+ optional file_path)
|
|
6
|
-
* 2. Read current file content
|
|
7
|
-
* 3. Replace the entity's current body with previous_body at its line range
|
|
8
|
-
* 4. Write the file back
|
|
9
|
-
* 5. Remove drift_overlay entry
|
|
10
|
-
*
|
|
11
|
-
* For ADDED entities (no previous_body): delete the entity lines from the file.
|
|
12
|
-
* For DELETED entities: restore previous_body at the original line position.
|
|
13
|
-
* For MODIFIED entities: replace current lines with previous_body.
|
|
14
|
-
*/
|
|
15
|
-
import { readFileSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { join } from "node:path";
|
|
17
|
-
/**
|
|
18
|
-
* Revert a single entity to its base (pre-drift) state.
|
|
19
|
-
*/
|
|
20
|
-
export async function revertEntity(entityName, localGraph, projectRoot, filePath) {
|
|
21
|
-
// Find the drift entity by name
|
|
22
|
-
const driftEntity = await findDriftEntity(localGraph, entityName, filePath);
|
|
23
|
-
if (!driftEntity) {
|
|
24
|
-
return {
|
|
25
|
-
reverted: false,
|
|
26
|
-
file: filePath ?? "",
|
|
27
|
-
lines: "",
|
|
28
|
-
drift_status: "",
|
|
29
|
-
error: `No drifted entity found: "${entityName}"${filePath ? ` in ${filePath}` : ""}`,
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
const absPath = join(projectRoot, driftEntity.file_path);
|
|
33
|
-
if (driftEntity.drift_status === "added") {
|
|
34
|
-
// Entity was added locally — remove it from the file
|
|
35
|
-
return removeAddedEntity(absPath, driftEntity, localGraph);
|
|
36
|
-
}
|
|
37
|
-
if (driftEntity.drift_status === "deleted") {
|
|
38
|
-
// Entity was deleted locally — restore previous_body
|
|
39
|
-
return restoreDeletedEntity(absPath, driftEntity, localGraph);
|
|
40
|
-
}
|
|
41
|
-
if (driftEntity.drift_status === "modified") {
|
|
42
|
-
// Entity was modified — replace current body with previous_body
|
|
43
|
-
return restoreModifiedEntity(absPath, driftEntity, localGraph);
|
|
44
|
-
}
|
|
45
|
-
// dependency_changed — nothing to revert in the file itself
|
|
46
|
-
await localGraph.removeDriftEntity(driftEntity.key);
|
|
47
|
-
return {
|
|
48
|
-
reverted: true,
|
|
49
|
-
file: driftEntity.file_path,
|
|
50
|
-
lines: "",
|
|
51
|
-
drift_status: driftEntity.drift_status,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Find a drift entity by name, optionally filtered by file path.
|
|
56
|
-
*/
|
|
57
|
-
async function findDriftEntity(localGraph, entityName, filePath) {
|
|
58
|
-
// Query drift_overlay for entities matching the name
|
|
59
|
-
const result = await localGraph.db.run(filePath
|
|
60
|
-
? `?[key, name, kind, sig, body, fp, ls, le, ch, ds, iid, ma, origin, pb, ps] :=
|
|
61
|
-
*drift_overlay[key, name, kind, sig, body, fp, ls, le, ch, ds, iid, ma, origin, pb, ps],
|
|
62
|
-
name = $name, fp = $fp`
|
|
63
|
-
: `?[key, name, kind, sig, body, fp, ls, le, ch, ds, iid, ma, origin, pb, ps] :=
|
|
64
|
-
*drift_overlay[key, name, kind, sig, body, fp, ls, le, ch, ds, iid, ma, origin, pb, ps],
|
|
65
|
-
name = $name`, filePath ? { name: entityName, fp: filePath } : { name: entityName });
|
|
66
|
-
if (result.rows.length === 0)
|
|
67
|
-
return null;
|
|
68
|
-
const [key, name, kind, signature, body, file_path, line_start, line_end, content_hash, drift_status, intent_id, modified_at, origin, previous_body, previous_signature,] = result.rows[0];
|
|
69
|
-
return {
|
|
70
|
-
key,
|
|
71
|
-
name,
|
|
72
|
-
kind,
|
|
73
|
-
signature,
|
|
74
|
-
body,
|
|
75
|
-
file_path,
|
|
76
|
-
line_start,
|
|
77
|
-
line_end,
|
|
78
|
-
content_hash,
|
|
79
|
-
drift_status: drift_status,
|
|
80
|
-
intent_id,
|
|
81
|
-
modified_at,
|
|
82
|
-
origin: origin,
|
|
83
|
-
previous_body,
|
|
84
|
-
previous_signature,
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Remove an ADDED entity from the file (it didn't exist in base).
|
|
89
|
-
*/
|
|
90
|
-
async function removeAddedEntity(absPath, entity, localGraph) {
|
|
91
|
-
try {
|
|
92
|
-
const content = readFileSync(absPath, "utf-8");
|
|
93
|
-
const lines = content.split("\n");
|
|
94
|
-
// Remove the entity's lines (1-indexed → 0-indexed)
|
|
95
|
-
const startIdx = entity.line_start - 1;
|
|
96
|
-
const endIdx = entity.line_end;
|
|
97
|
-
lines.splice(startIdx, endIdx - startIdx);
|
|
98
|
-
writeFileSync(absPath, lines.join("\n"), "utf-8");
|
|
99
|
-
await localGraph.removeDriftEntity(entity.key);
|
|
100
|
-
return {
|
|
101
|
-
reverted: true,
|
|
102
|
-
file: entity.file_path,
|
|
103
|
-
lines: `${entity.line_start}-${entity.line_end}`,
|
|
104
|
-
drift_status: "added",
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
catch (err) {
|
|
108
|
-
return {
|
|
109
|
-
reverted: false,
|
|
110
|
-
file: entity.file_path,
|
|
111
|
-
lines: "",
|
|
112
|
-
drift_status: "added",
|
|
113
|
-
error: `Failed to remove added entity: ${err instanceof Error ? err.message : String(err)}`,
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Restore a DELETED entity by inserting previous_body back into the file.
|
|
119
|
-
*/
|
|
120
|
-
async function restoreDeletedEntity(absPath, entity, localGraph) {
|
|
121
|
-
if (!entity.previous_body) {
|
|
122
|
-
// No previous body stored — can't restore
|
|
123
|
-
await localGraph.removeDriftEntity(entity.key);
|
|
124
|
-
return {
|
|
125
|
-
reverted: false,
|
|
126
|
-
file: entity.file_path,
|
|
127
|
-
lines: "",
|
|
128
|
-
drift_status: "deleted",
|
|
129
|
-
error: "No previous body available for restoration",
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
const content = readFileSync(absPath, "utf-8");
|
|
134
|
-
const lines = content.split("\n");
|
|
135
|
-
const previousLines = entity.previous_body.split("\n");
|
|
136
|
-
// Insert at the original line position (or end of file if beyond current length)
|
|
137
|
-
const insertIdx = Math.min(entity.line_start - 1, lines.length);
|
|
138
|
-
lines.splice(insertIdx, 0, ...previousLines);
|
|
139
|
-
writeFileSync(absPath, lines.join("\n"), "utf-8");
|
|
140
|
-
await localGraph.removeDriftEntity(entity.key);
|
|
141
|
-
const endLine = insertIdx + previousLines.length;
|
|
142
|
-
return {
|
|
143
|
-
reverted: true,
|
|
144
|
-
file: entity.file_path,
|
|
145
|
-
lines: `${insertIdx + 1}-${endLine}`,
|
|
146
|
-
drift_status: "deleted",
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
catch (err) {
|
|
150
|
-
return {
|
|
151
|
-
reverted: false,
|
|
152
|
-
file: entity.file_path,
|
|
153
|
-
lines: "",
|
|
154
|
-
drift_status: "deleted",
|
|
155
|
-
error: `Failed to restore deleted entity: ${err instanceof Error ? err.message : String(err)}`,
|
|
156
|
-
};
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Restore a MODIFIED entity by replacing current lines with previous_body.
|
|
161
|
-
*/
|
|
162
|
-
async function restoreModifiedEntity(absPath, entity, localGraph) {
|
|
163
|
-
if (!entity.previous_body) {
|
|
164
|
-
await localGraph.removeDriftEntity(entity.key);
|
|
165
|
-
return {
|
|
166
|
-
reverted: false,
|
|
167
|
-
file: entity.file_path,
|
|
168
|
-
lines: "",
|
|
169
|
-
drift_status: "modified",
|
|
170
|
-
error: "No previous body available for restoration",
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
try {
|
|
174
|
-
const content = readFileSync(absPath, "utf-8");
|
|
175
|
-
const lines = content.split("\n");
|
|
176
|
-
const previousLines = entity.previous_body.split("\n");
|
|
177
|
-
// Replace the entity's current lines with the previous body
|
|
178
|
-
const startIdx = entity.line_start - 1;
|
|
179
|
-
const deleteCount = entity.line_end - entity.line_start + 1;
|
|
180
|
-
lines.splice(startIdx, deleteCount, ...previousLines);
|
|
181
|
-
writeFileSync(absPath, lines.join("\n"), "utf-8");
|
|
182
|
-
await localGraph.removeDriftEntity(entity.key);
|
|
183
|
-
const endLine = startIdx + previousLines.length;
|
|
184
|
-
return {
|
|
185
|
-
reverted: true,
|
|
186
|
-
file: entity.file_path,
|
|
187
|
-
lines: `${entity.line_start}-${endLine}`,
|
|
188
|
-
drift_status: "modified",
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
catch (err) {
|
|
192
|
-
return {
|
|
193
|
-
reverted: false,
|
|
194
|
-
file: entity.file_path,
|
|
195
|
-
lines: "",
|
|
196
|
-
drift_status: "modified",
|
|
197
|
-
error: `Failed to restore modified entity: ${err instanceof Error ? err.message : String(err)}`,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
}
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File Hash State Manager — tracks per-file content SHAs for state-aware skipping.
|
|
3
|
-
*
|
|
4
|
-
* Manages `.unerr/state/file_hashes.json` with the skip decision tree:
|
|
5
|
-
* - Content SHA unchanged + HEAD SHA unchanged → SKIP
|
|
6
|
-
* - Content SHA unchanged + HEAD SHA changed → SKIP scan, UPDATE head_sha
|
|
7
|
-
* - Content SHA changed → PROCESS
|
|
8
|
-
*
|
|
9
|
-
* Uses atomic write (write to .tmp, rename) for crash safety.
|
|
10
|
-
*/
|
|
11
|
-
import { createHash } from "node:crypto";
|
|
12
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
13
|
-
import { join } from "node:path";
|
|
14
|
-
const STATE_FILE = "file_hashes.json";
|
|
15
|
-
export class FileHashManager {
|
|
16
|
-
stateDir;
|
|
17
|
-
statePath;
|
|
18
|
-
state;
|
|
19
|
-
constructor(unerrDir) {
|
|
20
|
-
this.stateDir = join(unerrDir, "state");
|
|
21
|
-
this.statePath = join(this.stateDir, STATE_FILE);
|
|
22
|
-
this.state = this.load();
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Determine if a file should be processed or skipped.
|
|
26
|
-
*/
|
|
27
|
-
shouldProcess(filePath, contentSha, headSha) {
|
|
28
|
-
const entry = this.state.files[filePath];
|
|
29
|
-
if (!entry)
|
|
30
|
-
return "process";
|
|
31
|
-
if (entry.contentSha !== contentSha) {
|
|
32
|
-
// Content changed → must process
|
|
33
|
-
return "process";
|
|
34
|
-
}
|
|
35
|
-
if (entry.headSha !== headSha) {
|
|
36
|
-
// Content same but HEAD moved (e.g., commit or rebase) → skip but update headSha
|
|
37
|
-
entry.headSha = headSha;
|
|
38
|
-
entry.processedAt = new Date().toISOString();
|
|
39
|
-
// Defer save — caller should call save() after batch
|
|
40
|
-
}
|
|
41
|
-
return "skip";
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Mark a file as processed with the given content and HEAD SHAs.
|
|
45
|
-
*/
|
|
46
|
-
markProcessed(filePath, contentSha, headSha) {
|
|
47
|
-
this.state.files[filePath] = {
|
|
48
|
-
contentSha,
|
|
49
|
-
headSha,
|
|
50
|
-
processedAt: new Date().toISOString(),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Remove tracking for a specific file.
|
|
55
|
-
*/
|
|
56
|
-
remove(filePath) {
|
|
57
|
-
delete this.state.files[filePath];
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Clear all file hash state.
|
|
61
|
-
*/
|
|
62
|
-
clearAll() {
|
|
63
|
-
this.state.files = {};
|
|
64
|
-
this.save();
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Persist state to disk atomically (write .tmp → rename).
|
|
68
|
-
*/
|
|
69
|
-
save() {
|
|
70
|
-
if (!existsSync(this.stateDir)) {
|
|
71
|
-
mkdirSync(this.stateDir, { recursive: true });
|
|
72
|
-
}
|
|
73
|
-
const tmpPath = `${this.statePath}.tmp`;
|
|
74
|
-
writeFileSync(tmpPath, JSON.stringify(this.state, null, 2), "utf-8");
|
|
75
|
-
renameSync(tmpPath, this.statePath);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Get the number of tracked files.
|
|
79
|
-
*/
|
|
80
|
-
get trackedCount() {
|
|
81
|
-
return Object.keys(this.state.files).length;
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Get the full state (for debugging / `unerr debug`).
|
|
85
|
-
*/
|
|
86
|
-
getState() {
|
|
87
|
-
return this.state;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Restore state from a snapshot (e.g., branch switch restore).
|
|
91
|
-
* Replaces current in-memory state without persisting to disk.
|
|
92
|
-
*/
|
|
93
|
-
restoreState(snapshot) {
|
|
94
|
-
this.state = { files: { ...snapshot.files } };
|
|
95
|
-
}
|
|
96
|
-
load() {
|
|
97
|
-
if (!existsSync(this.statePath)) {
|
|
98
|
-
return { files: {} };
|
|
99
|
-
}
|
|
100
|
-
try {
|
|
101
|
-
const raw = readFileSync(this.statePath, "utf-8");
|
|
102
|
-
return JSON.parse(raw);
|
|
103
|
-
}
|
|
104
|
-
catch {
|
|
105
|
-
return { files: {} };
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Compute SHA-256 hex digest of file content.
|
|
111
|
-
*/
|
|
112
|
-
export function contentSha256(content) {
|
|
113
|
-
return createHash("sha256").update(content).digest("hex");
|
|
114
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Git-Native Attribution — Phase 5.5 §1.5
|
|
3
|
-
*
|
|
4
|
-
* Attaches ledger metadata to Git commits using:
|
|
5
|
-
* 1. Git Trailers (in commit messages) — lightweight, `git log --format="%(trailers)"` parseable
|
|
6
|
-
* 2. Git Notes (under refs/notes/unerr) — rich JSON metadata, aligned with git-ai v3 standard
|
|
7
|
-
*
|
|
8
|
-
* Both mechanisms survive rebases, merges, squashes, and cherry-picks.
|
|
9
|
-
* This makes ledger data git-mergeable by construction — no sidecar JSONL files.
|
|
10
|
-
*
|
|
11
|
-
* Integration point: Called from `unerr push` before git push (P13 bridge).
|
|
12
|
-
*/
|
|
13
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
import { writeNote } from "../utils/git.js";
|
|
16
|
-
// ── Git Trailers ───────────────────────────────────────────────────────────
|
|
17
|
-
/**
|
|
18
|
-
* Build a commit message with Git Trailers appended.
|
|
19
|
-
* Trailers are standardized key-value pairs at the end of a commit message,
|
|
20
|
-
* separated from the body by a blank line. Parseable via `git log --format="%(trailers)"`.
|
|
21
|
-
*
|
|
22
|
-
* Aligned with git-ai v3 standard: session-based attribution with model + tool metadata.
|
|
23
|
-
*/
|
|
24
|
-
export function buildCommitMessageWithTrailers(baseMessage, attribution) {
|
|
25
|
-
const trailers = [];
|
|
26
|
-
trailers.push(`Unerr-Session: ${attribution.sessionId}`);
|
|
27
|
-
if (attribution.ledgerEntryIds.length > 0) {
|
|
28
|
-
// Only include first entry ID to keep message short; full list in Git Notes
|
|
29
|
-
trailers.push(`Unerr-Ledger-Id: ${attribution.ledgerEntryIds[0]}`);
|
|
30
|
-
}
|
|
31
|
-
if (attribution.changeType) {
|
|
32
|
-
trailers.push(`Unerr-Change-Type: ${attribution.changeType}`);
|
|
33
|
-
}
|
|
34
|
-
if (attribution.featureArea) {
|
|
35
|
-
trailers.push(`Unerr-Feature: ${attribution.featureArea}`);
|
|
36
|
-
}
|
|
37
|
-
if (attribution.planSummary) {
|
|
38
|
-
// Truncate plan to 72 chars for trailer (single-line convention)
|
|
39
|
-
const truncated = attribution.planSummary.length > 72
|
|
40
|
-
? `${attribution.planSummary.slice(0, 69)}...`
|
|
41
|
-
: attribution.planSummary;
|
|
42
|
-
trailers.push(`Unerr-Plan: ${truncated}`);
|
|
43
|
-
}
|
|
44
|
-
if (attribution.agentModel) {
|
|
45
|
-
trailers.push(`Unerr-Agent-Model: ${attribution.agentModel}`);
|
|
46
|
-
}
|
|
47
|
-
if (attribution.agentTool) {
|
|
48
|
-
trailers.push(`Unerr-Agent-Tool: ${attribution.agentTool}`);
|
|
49
|
-
}
|
|
50
|
-
// Git trailer format: blank line + trailers (one per line)
|
|
51
|
-
return `${baseMessage}\n\n${trailers.join("\n")}\n`;
|
|
52
|
-
}
|
|
53
|
-
// ── Git Notes ──────────────────────────────────────────────────────────────
|
|
54
|
-
/**
|
|
55
|
-
* Build the Git Note payload (JSON) for a commit.
|
|
56
|
-
* Written to refs/notes/unerr — a custom namespace that won't conflict
|
|
57
|
-
* with the default refs/notes/commits.
|
|
58
|
-
*
|
|
59
|
-
* Contains rich metadata: full prompt, plan, entities affected, etc.
|
|
60
|
-
* The git-ai v3 standard uses a similar approach under refs/notes/ai.
|
|
61
|
-
*/
|
|
62
|
-
export function buildGitNotePayload(attribution) {
|
|
63
|
-
return {
|
|
64
|
-
version: "1.0",
|
|
65
|
-
session_id: attribution.sessionId,
|
|
66
|
-
ledger_entry_ids: attribution.ledgerEntryIds,
|
|
67
|
-
prompt: attribution.prompt,
|
|
68
|
-
plan_summary: attribution.planSummary,
|
|
69
|
-
change_type: attribution.changeType,
|
|
70
|
-
feature_area: attribution.featureArea,
|
|
71
|
-
entities_affected: [], // Resolved from SCIP index
|
|
72
|
-
files_changed: attribution.filesChanged,
|
|
73
|
-
agent_model: attribution.agentModel,
|
|
74
|
-
agent_tool: attribution.agentTool,
|
|
75
|
-
created_at: new Date().toISOString(),
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Write a Git Note under refs/notes/unerr for a specific commit.
|
|
80
|
-
* Uses simple-git via the centralized git utility.
|
|
81
|
-
*
|
|
82
|
-
* Target latency: <50ms (see §1.8.1).
|
|
83
|
-
*/
|
|
84
|
-
export async function writeGitNote(dir, _gitdir, commitSha, payload) {
|
|
85
|
-
const noteContent = JSON.stringify(payload, null, 2);
|
|
86
|
-
await writeNote(dir, "unerr", commitSha, noteContent);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Read the attribution context from the local workspace manifest.
|
|
90
|
-
* The manifest (.unerr/manifest.json) is maintained by P10's Local Proxy
|
|
91
|
-
* and contains session-scoped attribution data.
|
|
92
|
-
*
|
|
93
|
-
* Returns null if no manifest exists (non-AI push).
|
|
94
|
-
*/
|
|
95
|
-
export function readAttributionFromManifest(cwd) {
|
|
96
|
-
const manifestPath = join(cwd, ".unerr", "manifest.json");
|
|
97
|
-
if (!existsSync(manifestPath))
|
|
98
|
-
return null;
|
|
99
|
-
try {
|
|
100
|
-
const raw = readFileSync(manifestPath, "utf-8");
|
|
101
|
-
const manifest = JSON.parse(raw);
|
|
102
|
-
if (!manifest.sessionId || !manifest.attributions?.length)
|
|
103
|
-
return null;
|
|
104
|
-
// Merge all unflushed attributions into a single context
|
|
105
|
-
const allEntryIds = [];
|
|
106
|
-
const allFiles = [];
|
|
107
|
-
const prompts = [];
|
|
108
|
-
for (const attr of manifest.attributions) {
|
|
109
|
-
if (attr.ledgerEntryIds)
|
|
110
|
-
allEntryIds.push(...attr.ledgerEntryIds);
|
|
111
|
-
if (attr.filesChanged)
|
|
112
|
-
allFiles.push(...attr.filesChanged);
|
|
113
|
-
if (attr.prompt)
|
|
114
|
-
prompts.push(attr.prompt);
|
|
115
|
-
}
|
|
116
|
-
const latest = manifest.attributions[manifest.attributions.length - 1];
|
|
117
|
-
return {
|
|
118
|
-
sessionId: manifest.sessionId,
|
|
119
|
-
ledgerEntryIds: [...new Set(allEntryIds)],
|
|
120
|
-
prompt: prompts.join(" → "),
|
|
121
|
-
planSummary: latest.planSummary,
|
|
122
|
-
changeType: latest.changeType,
|
|
123
|
-
featureArea: latest.featureArea,
|
|
124
|
-
agentModel: latest.agentModel,
|
|
125
|
-
agentTool: latest.agentTool,
|
|
126
|
-
filesChanged: [...new Set(allFiles)],
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
return null;
|
|
131
|
-
}
|
|
132
|
-
}
|