@rhei-team/rhei 1.0.0-beta.0 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js
CHANGED
|
@@ -30320,8 +30320,9 @@ async function buildLocalCodeContextForGoalSnapshot(args) {
|
|
|
30320
30320
|
const backendIntelligenceExactRefsForSelection = routeSlotContextGoal ? [] : backendIntelligenceExactRefs;
|
|
30321
30321
|
const evidenceInputPresent = Boolean(args.evidenceIntake?.length || args.refinementEvidence?.length || args.diagnosticEvidence?.length);
|
|
30322
30322
|
const goalExactRefsForSelection = evidenceInputPresent ? [] : goalExactRefs;
|
|
30323
|
-
const
|
|
30324
|
-
const
|
|
30323
|
+
const goalScopeFileRefsForSelection = evidenceInputPresent ? [] : goalScopeFileRefs;
|
|
30324
|
+
const exactRefs = uniqueStrings16([...explicitExactRefs, ...recentTouchedRefs, ...goalScopeFileRefsForSelection, ...explicitScopeFileRefs, ...goalExactRefsForSelection, ...boringMcpExactRefs, ...backendIntelligenceExactRefsForSelection]);
|
|
30325
|
+
const collectionExactRefs = uniqueStrings16([...exactRefs, ...goalScopeFileRefs, ...goalExactRefs, ...boringMcpEvidenceRefs, ...backendIntelligenceExactRefs, ...backendIntelligenceEvidenceRefs, ...categoryCollectionRefs]);
|
|
30325
30326
|
const maxFilesToScan = positiveInteger5(args.maxFilesToScan, DEFAULT_MAX_FILES_TO_SCAN);
|
|
30326
30327
|
const collectStartedAt = Date.now();
|
|
30327
30328
|
let collected = await collectCandidates(repoPath, terms, maxFilesToScan, core, { ...args, exactRefs: collectionExactRefs, allowEvalReportEvidenceRefs });
|
|
@@ -42985,10 +42986,20 @@ var rheiStatusToolOutputSchema = toolOutputSchema({
|
|
|
42985
42986
|
var rheiLedgerToolInputSchema = toolObjectSchema({
|
|
42986
42987
|
op: {
|
|
42987
42988
|
type: "string",
|
|
42988
|
-
enum: [
|
|
42989
|
-
|
|
42989
|
+
enum: [
|
|
42990
|
+
"overview",
|
|
42991
|
+
"report_cards",
|
|
42992
|
+
"trust_diff",
|
|
42993
|
+
"explain_line",
|
|
42994
|
+
"why_symbol_changed",
|
|
42995
|
+
"what_changed_since"
|
|
42996
|
+
],
|
|
42997
|
+
description: "Ledger projection. Defaults to overview. why_symbol_changed needs `symbol`; what_changed_since needs `sinceMs` or `sinceRef`."
|
|
42990
42998
|
},
|
|
42991
|
-
repoPath: STRING_SCHEMA
|
|
42999
|
+
repoPath: STRING_SCHEMA,
|
|
43000
|
+
symbol: { type: "string", description: "why_symbol_changed: symbol name or qualified name." },
|
|
43001
|
+
sinceMs: { type: "number", description: "what_changed_since: inclusive lower bound (epoch ms)." },
|
|
43002
|
+
sinceRef: { type: "string", description: "what_changed_since: git ref (e.g. HEAD~1, main) resolved to its commit time." }
|
|
42992
43003
|
}, { additionalProperties: true });
|
|
42993
43004
|
var rheiLedgerToolOutputSchema = toolOutputSchema({
|
|
42994
43005
|
kind: STRING_SCHEMA,
|
|
@@ -42999,6 +43010,8 @@ var rheiLedgerToolOutputSchema = toolOutputSchema({
|
|
|
42999
43010
|
reportCards: OBJECT_ARRAY_SCHEMA,
|
|
43000
43011
|
trustDiff: OBJECT_SCHEMA,
|
|
43001
43012
|
line: STRING_SCHEMA,
|
|
43013
|
+
whySymbolChanged: OBJECT_SCHEMA,
|
|
43014
|
+
whatChangedSince: OBJECT_SCHEMA,
|
|
43002
43015
|
reportOnly: BOOLEAN_SCHEMA,
|
|
43003
43016
|
advisoryOnly: BOOLEAN_SCHEMA,
|
|
43004
43017
|
noAuthority: BOOLEAN_SCHEMA,
|
|
@@ -44262,7 +44275,7 @@ function briefRheiCodeIoHits(search) {
|
|
|
44262
44275
|
return;
|
|
44263
44276
|
const sqlSymbolSearch = search["searchMode"] === "sql_symbol_index" || search["searchTargetMode"] === "symbol_path";
|
|
44264
44277
|
const results = codeIoRecordList(search["results"]).slice(0, 5).map((result) => {
|
|
44265
|
-
const matchedLines = arrayValue4(result["matchedLines"]).map((line) => asRecord8(line)).filter((line) => Boolean(line)).slice(0,
|
|
44278
|
+
const matchedLines = arrayValue4(result["matchedLines"]).map((line) => asRecord8(line)).filter((line) => Boolean(line)).slice(0, 1).map((line) => removeUndefined13({
|
|
44266
44279
|
line: line["line"],
|
|
44267
44280
|
preview: line["preview"]
|
|
44268
44281
|
}));
|
|
@@ -45727,6 +45740,9 @@ function repoPromptSearchDto(search) {
|
|
|
45727
45740
|
const omittedTotal = codeIoNonNegativeNumber(search["omittedResultCount"] ?? coverage?.["matchedRefsOmitted"]) ?? (omittedByResultLimit > 0 ? omittedByResultLimit : undefined);
|
|
45728
45741
|
const omittedContentMatches = returnedMatchCount === undefined ? undefined : Math.max(0, totalMatchCount - returnedMatchCount);
|
|
45729
45742
|
const sizeLimitHit = coverage?.["truncated"] === true;
|
|
45743
|
+
const carriesNonRedundantInfo = omittedTotal !== undefined && omittedTotal > 0 || omittedContentMatches !== undefined && omittedContentMatches > 0 || sizeLimitHit || Boolean(cleanString23(scopeDiagnostic?.["summary"])) || Boolean(cleanString23(scopeDiagnostic?.["suggestion"])) || arrayValue4(search["warnings"]).length > 0 || perFileTotals.length > 0 || pathMatchLines.length > 0;
|
|
45744
|
+
if (!carriesNonRedundantInfo)
|
|
45745
|
+
return;
|
|
45730
45746
|
return removeUndefined13({
|
|
45731
45747
|
total_matches: totalMatchCount,
|
|
45732
45748
|
total_files: codeIoNonNegativeNumber(coverage?.["searchedFiles"]) ?? resultCount,
|
|
@@ -46572,7 +46588,7 @@ function briefRheiCodeIoInspect(inspectResult) {
|
|
|
46572
46588
|
const factCounts = asRecord8(codeStructureCoverage?.["factCounts"]);
|
|
46573
46589
|
const emittedFactCounts = asRecord8(codeStructureCoverage?.["emittedFactCounts"]);
|
|
46574
46590
|
const fullSymbolCount = positiveNumber12(factCounts?.["symbols"]) ?? symbols.length;
|
|
46575
|
-
const emittedSymbolCount = positiveNumber12(emittedFactCounts?.["symbols"])
|
|
46591
|
+
const emittedSymbolCount = positiveNumber12(emittedFactCounts?.["symbols"]);
|
|
46576
46592
|
const nextReadPlan = arrayValue4(inspectResult["nextReadPlan"]);
|
|
46577
46593
|
const codeStructureDiagnostic = asRecord8(inspect["codeStructureDiagnostic"]) ?? asRecord8(inspectResult["codeStructureDiagnostic"]);
|
|
46578
46594
|
const imports = [];
|
|
@@ -46611,9 +46627,8 @@ function briefRheiCodeIoInspect(inspectResult) {
|
|
|
46611
46627
|
mode: inspect["mode"],
|
|
46612
46628
|
codemapProfile: inspect["codemapProfile"],
|
|
46613
46629
|
fileCount: files.length,
|
|
46614
|
-
symbolCount:
|
|
46615
|
-
|
|
46616
|
-
emittedSymbolCount: emittedSymbolCount !== symbols.length && emittedSymbolCount !== fullSymbolCount ? emittedSymbolCount : undefined,
|
|
46630
|
+
symbolCount: fullSymbolCount,
|
|
46631
|
+
emittedSymbolCount: emittedSymbolCount !== undefined && emittedSymbolCount !== fullSymbolCount ? emittedSymbolCount : undefined,
|
|
46617
46632
|
importCount: imports.length,
|
|
46618
46633
|
functionSignatureCount: functionSignatures.length,
|
|
46619
46634
|
codeStructureContent,
|
|
@@ -48946,10 +48961,10 @@ function compactObservability(observability) {
|
|
|
48946
48961
|
categoryCollectionRefCount: observability["categoryCollectionRefCount"]
|
|
48947
48962
|
});
|
|
48948
48963
|
}
|
|
48949
|
-
function compactContextReadPlanItem(value) {
|
|
48964
|
+
function compactContextReadPlanItem(value, includeContent = true) {
|
|
48950
48965
|
if (!value)
|
|
48951
48966
|
return;
|
|
48952
|
-
const content = cleanString23(value["content"]);
|
|
48967
|
+
const content = includeContent ? cleanString23(value["content"]) : undefined;
|
|
48953
48968
|
return removeUndefined13({
|
|
48954
48969
|
readId: value["readId"],
|
|
48955
48970
|
path: value["path"],
|
|
@@ -49055,7 +49070,7 @@ function compactContextForGoalRead(value) {
|
|
|
49055
49070
|
const maxContentChars = 12000;
|
|
49056
49071
|
const readItems = arrayValue4(read["reads"]).map((item) => asRecord8(item)).filter((item) => Boolean(item));
|
|
49057
49072
|
const singleRead = readItems.length === 0 && (cleanString23(read["path"]) || cleanString23(read["content"])) ? [read] : [];
|
|
49058
|
-
const readPlan = arrayValue4(read["readPlan"]).slice(0, 12).map((item) => compactContextReadPlanItem(asRecord8(item))).filter((item) => Boolean(item));
|
|
49073
|
+
const readPlan = arrayValue4(read["readPlan"]).slice(0, 12).map((item) => compactContextReadPlanItem(asRecord8(item), false)).filter((item) => Boolean(item));
|
|
49059
49074
|
const compactReads = [...readItems, ...singleRead].slice(0, 12).map((item) => compactContextReadPlanItem(asRecord8(item))).filter((item) => Boolean(item));
|
|
49060
49075
|
const paths = read["paths"] ?? singleRead.map((item) => item["path"]);
|
|
49061
49076
|
const compactRead = removeUndefined13({
|
|
@@ -62181,13 +62196,19 @@ async function resolveMultiEditFrontier(input) {
|
|
|
62181
62196
|
if (proposedPaths.length === 0)
|
|
62182
62197
|
return unavailable("no_proposed_paths");
|
|
62183
62198
|
const advisoryDepth = Math.min(activation2.effectiveDepth, 3);
|
|
62199
|
+
const runAdvisory = input.includeAdvisory ?? true;
|
|
62184
62200
|
let gateResult;
|
|
62185
62201
|
let advisoryResult;
|
|
62186
62202
|
try {
|
|
62187
|
-
|
|
62188
|
-
|
|
62189
|
-
|
|
62190
|
-
|
|
62203
|
+
if (runAdvisory) {
|
|
62204
|
+
[gateResult, advisoryResult] = await Promise.all([
|
|
62205
|
+
runQuery({ repoPath: input.repoPath, op: "impact", changedPaths: proposedPaths, depth: 1, artifactRoot: input.artifactRoot, env }),
|
|
62206
|
+
runQuery({ repoPath: input.repoPath, op: "impact", changedPaths: proposedPaths, depth: advisoryDepth, artifactRoot: input.artifactRoot, env })
|
|
62207
|
+
]);
|
|
62208
|
+
} else {
|
|
62209
|
+
gateResult = await runQuery({ repoPath: input.repoPath, op: "impact", changedPaths: proposedPaths, depth: 1, artifactRoot: input.artifactRoot, env });
|
|
62210
|
+
advisoryResult = undefined;
|
|
62211
|
+
}
|
|
62191
62212
|
} catch (error) {
|
|
62192
62213
|
return unavailable(`graph_query_threw: ${String(error).slice(0, 200)}`);
|
|
62193
62214
|
}
|
|
@@ -62288,7 +62309,8 @@ async function goalStartReadPlanWithFrontier(contextForGoal, options = {}) {
|
|
|
62288
62309
|
return { ...plan, frontier: { status: verdict.status, reason: verdict.reason } };
|
|
62289
62310
|
}
|
|
62290
62311
|
const planPaths = new Set(items.map((item) => cleanString23(item["path"])).filter(Boolean));
|
|
62291
|
-
const
|
|
62312
|
+
const frontierPaths = uniqueStrings24([...verdict.gateEdges, ...verdict.advisoryFrontier]);
|
|
62313
|
+
const addedPaths = frontierPaths.filter((path) => !planPaths.has(path)).slice(0, 4);
|
|
62292
62314
|
const frontierItems = addedPaths.map((path, index) => ({
|
|
62293
62315
|
order: items.length + index + 1,
|
|
62294
62316
|
path,
|
|
@@ -62301,9 +62323,9 @@ async function goalStartReadPlanWithFrontier(contextForGoal, options = {}) {
|
|
|
62301
62323
|
coverage: { ...asRecord8(plan["coverage"]) ?? {}, frontier: frontierItems.length },
|
|
62302
62324
|
frontier: {
|
|
62303
62325
|
status: verdict.status,
|
|
62304
|
-
|
|
62305
|
-
|
|
62306
|
-
|
|
62326
|
+
addedPaths,
|
|
62327
|
+
gateEdgeCount: verdict.gateEdges.length,
|
|
62328
|
+
frontierPathCount: frontierPaths.length
|
|
62307
62329
|
}
|
|
62308
62330
|
};
|
|
62309
62331
|
}
|
|
@@ -62868,6 +62890,7 @@ async function buildWritebackGraduation(input) {
|
|
|
62868
62890
|
repoPath,
|
|
62869
62891
|
proposedPaths: changedPaths,
|
|
62870
62892
|
gateEligible: true,
|
|
62893
|
+
includeAdvisory: false,
|
|
62871
62894
|
runQuery: input.runQuery
|
|
62872
62895
|
});
|
|
62873
62896
|
const verdictRecorded = input.synapseVerdictReceipt?.["status"] === "recorded";
|
|
@@ -66984,6 +67007,64 @@ function directCodeIoDeleteFile(args) {
|
|
|
66984
67007
|
});
|
|
66985
67008
|
}
|
|
66986
67009
|
|
|
67010
|
+
// src/codeContextTools/inverseCapture.ts
|
|
67011
|
+
import { deriveInverseEdit } from "./vendor/rhei-core/changeEpisode.js";
|
|
67012
|
+
function str2(value) {
|
|
67013
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
67014
|
+
}
|
|
67015
|
+
function firstString2(record, keys) {
|
|
67016
|
+
for (const key of keys) {
|
|
67017
|
+
const value = str2(record[key]);
|
|
67018
|
+
if (value !== undefined)
|
|
67019
|
+
return value;
|
|
67020
|
+
}
|
|
67021
|
+
return;
|
|
67022
|
+
}
|
|
67023
|
+
var SEARCH_KEYS = ["search", "find", "old_string", "oldString", "oldText"];
|
|
67024
|
+
var REPLACE_KEYS = ["replace", "replacement", "with", "new_string", "newString", "newText"];
|
|
67025
|
+
function inverseEditsForFile(file) {
|
|
67026
|
+
const path = str2(file["path"]);
|
|
67027
|
+
if (!path)
|
|
67028
|
+
return [];
|
|
67029
|
+
const isDelete = file["delete"] === true || file["mode"] === "delete";
|
|
67030
|
+
if (isDelete)
|
|
67031
|
+
return [deriveInverseEdit({ kind: "delete", path })];
|
|
67032
|
+
if (file["create"] === true)
|
|
67033
|
+
return [deriveInverseEdit({ kind: "create", path })];
|
|
67034
|
+
const rewrite = str2(file["rewrite"]) ?? str2(file["content"]);
|
|
67035
|
+
if (rewrite !== undefined)
|
|
67036
|
+
return [deriveInverseEdit({ kind: "rewrite", path, afterContent: rewrite })];
|
|
67037
|
+
const edits = Array.isArray(file["edits"]) ? file["edits"] : [];
|
|
67038
|
+
const inverses = [];
|
|
67039
|
+
for (const raw of edits) {
|
|
67040
|
+
if (typeof raw !== "object" || raw === null)
|
|
67041
|
+
continue;
|
|
67042
|
+
const edit = raw;
|
|
67043
|
+
const search = firstString2(edit, SEARCH_KEYS);
|
|
67044
|
+
const replace = firstString2(edit, REPLACE_KEYS);
|
|
67045
|
+
if (search === undefined || replace === undefined)
|
|
67046
|
+
continue;
|
|
67047
|
+
inverses.push(deriveInverseEdit({ kind: "search_replace", path, search, replace }));
|
|
67048
|
+
}
|
|
67049
|
+
return inverses;
|
|
67050
|
+
}
|
|
67051
|
+
function inverseEditsForFiles(files) {
|
|
67052
|
+
if (!Array.isArray(files))
|
|
67053
|
+
return [];
|
|
67054
|
+
const bundles = [];
|
|
67055
|
+
for (const file of files) {
|
|
67056
|
+
if (typeof file !== "object" || file === null)
|
|
67057
|
+
continue;
|
|
67058
|
+
const path = str2(file["path"]);
|
|
67059
|
+
if (!path)
|
|
67060
|
+
continue;
|
|
67061
|
+
const inverse = inverseEditsForFile(file);
|
|
67062
|
+
if (inverse.length > 0)
|
|
67063
|
+
bundles.push({ path, inverse });
|
|
67064
|
+
}
|
|
67065
|
+
return bundles;
|
|
67066
|
+
}
|
|
67067
|
+
|
|
66987
67068
|
// src/codeContextTools/codeIoToolRuntimeAndServiceIntelligence.ts
|
|
66988
67069
|
var RECENT_SEARCH_TRIAL_TTL_MS = 10 * 60 * 1000;
|
|
66989
67070
|
var recentSearchTrials = new Map;
|
|
@@ -67804,12 +67885,18 @@ function codeIoRuntimeRestartInfo() {
|
|
|
67804
67885
|
restartAction: asRecord8(runtime["restartAction"])
|
|
67805
67886
|
};
|
|
67806
67887
|
}
|
|
67807
|
-
function codeIoRuntimeWriteBlockRequired(op,
|
|
67808
|
-
|
|
67888
|
+
function codeIoRuntimeWriteBlockRequired(op, reloadStatus, targetPaths = []) {
|
|
67889
|
+
if (op !== "edit" && op !== "publish" && op !== "revert")
|
|
67890
|
+
return false;
|
|
67891
|
+
if (reloadStatus["editEngineStale"] === true)
|
|
67892
|
+
return true;
|
|
67893
|
+
if (targetPaths.length === 0)
|
|
67894
|
+
return true;
|
|
67895
|
+
return targetPaths.some((path) => /(?:^|\/)packages\/mcp-server\/src\//.test(path));
|
|
67809
67896
|
}
|
|
67810
67897
|
function codeIoRuntimeFreshnessAdvisory(op, targetPaths = []) {
|
|
67811
67898
|
const restartInfo = codeIoRuntimeRestartInfo();
|
|
67812
|
-
if (!restartInfo || codeIoRuntimeWriteBlockRequired(op, restartInfo.reloadStatus))
|
|
67899
|
+
if (!restartInfo || codeIoRuntimeWriteBlockRequired(op, restartInfo.reloadStatus, targetPaths))
|
|
67813
67900
|
return;
|
|
67814
67901
|
return removeUndefined13({
|
|
67815
67902
|
kind: "rhei_code_io_runtime_freshness",
|
|
@@ -67861,7 +67948,7 @@ function codeIoRuntimeStaleWriteBlock(op, targetPaths = []) {
|
|
|
67861
67948
|
const restartInfo = codeIoRuntimeRestartInfo();
|
|
67862
67949
|
if (!restartInfo)
|
|
67863
67950
|
return;
|
|
67864
|
-
if (!codeIoRuntimeWriteBlockRequired(op, restartInfo.reloadStatus))
|
|
67951
|
+
if (!codeIoRuntimeWriteBlockRequired(op, restartInfo.reloadStatus, targetPaths))
|
|
67865
67952
|
return;
|
|
67866
67953
|
return removeUndefined13({
|
|
67867
67954
|
kind: op === "publish" || op === "revert" ? "rhei_code_git_publish" : "rhei_code_edit",
|
|
@@ -68657,6 +68744,7 @@ async function recordCodeIoEditProvenance(args, edit, files, dirtyEventReceipt)
|
|
|
68657
68744
|
goal: cleanString23(args["goal"]) ?? cleanString23(args["message"]),
|
|
68658
68745
|
createdAt
|
|
68659
68746
|
})}`;
|
|
68747
|
+
const inverseByPath = new Map(inverseEditsForFiles(files).map((bundle) => [bundle.path, bundle.inverse]));
|
|
68660
68748
|
await getLedgerSink({ repoPath }).recordEditProvenance(buildEditProvenanceV1({
|
|
68661
68749
|
provenanceId,
|
|
68662
68750
|
source: "codex_mcp",
|
|
@@ -68669,7 +68757,11 @@ async function recordCodeIoEditProvenance(args, edit, files, dirtyEventReceipt)
|
|
|
68669
68757
|
repoEpoch: cleanString23(asRecord8(edit["receipt"])?.["repoEpoch"]) ?? "repo_epoch_unknown_v1",
|
|
68670
68758
|
scopePolicyHash: cleanString23(edit["scopePolicyHash"]) ?? UNKNOWN_SCOPE_POLICY_HASH,
|
|
68671
68759
|
files: changedPaths,
|
|
68672
|
-
hunks: changedPaths.map((path) =>
|
|
68760
|
+
hunks: changedPaths.map((path) => {
|
|
68761
|
+
const inverse = inverseByPath.get(path);
|
|
68762
|
+
const hunk = { filePath: path, startLine: 0, endLine: 0, kind: codeIoEditHunkKind(args) };
|
|
68763
|
+
return inverse === undefined ? hunk : { ...hunk, inverse };
|
|
68764
|
+
}),
|
|
68673
68765
|
symbols: codeIoTouchedSymbols(files, edit),
|
|
68674
68766
|
mcpReceipts: receiptRefs,
|
|
68675
68767
|
nonMcpCommands: [],
|
|
@@ -69259,6 +69351,8 @@ async function buildRheiCodeIoPayload(args) {
|
|
|
69259
69351
|
const postEditIndexWarmup = codeIoPostEditIndexWarmupReceipt(args, edit, files, dirtyEventReceipt);
|
|
69260
69352
|
const frontierGateForResponse = frontierGate ? removeUndefined13({
|
|
69261
69353
|
...frontierGate,
|
|
69354
|
+
advisoryFrontier: undefined,
|
|
69355
|
+
advisoryFrontierCount: frontierGate.advisoryFrontier.length,
|
|
69262
69356
|
evidence: undefined,
|
|
69263
69357
|
evidenceSummary: frontierGate.evidence ? removeUndefined13({
|
|
69264
69358
|
sourceEpochHash: frontierGate.evidence.sourceEpochHash,
|
|
@@ -70252,8 +70346,6 @@ var RHEI_MCP_PUBLIC_LOCAL_TOOL_NAMES = [
|
|
|
70252
70346
|
"index",
|
|
70253
70347
|
"status",
|
|
70254
70348
|
"ledger",
|
|
70255
|
-
"agent_run",
|
|
70256
|
-
"agent_manage",
|
|
70257
70349
|
"oracle_send",
|
|
70258
70350
|
"oracle_utils"
|
|
70259
70351
|
];
|
|
@@ -73226,8 +73318,18 @@ var rheiStatusOutputSchema = z3.object({
|
|
|
73226
73318
|
productionAuthority: z3.literal(false).optional()
|
|
73227
73319
|
}).passthrough();
|
|
73228
73320
|
var rheiLedgerInputSchema = z3.object({
|
|
73229
|
-
op: z3.enum([
|
|
73230
|
-
|
|
73321
|
+
op: z3.enum([
|
|
73322
|
+
"overview",
|
|
73323
|
+
"report_cards",
|
|
73324
|
+
"trust_diff",
|
|
73325
|
+
"explain_line",
|
|
73326
|
+
"why_symbol_changed",
|
|
73327
|
+
"what_changed_since"
|
|
73328
|
+
]).optional(),
|
|
73329
|
+
repoPath: z3.string().min(1).optional(),
|
|
73330
|
+
symbol: z3.string().optional(),
|
|
73331
|
+
sinceMs: z3.number().optional(),
|
|
73332
|
+
sinceRef: z3.string().optional()
|
|
73231
73333
|
}).passthrough();
|
|
73232
73334
|
var rheiLedgerOutputSchema = z3.object({
|
|
73233
73335
|
kind: z3.string().optional(),
|
|
@@ -76775,31 +76877,31 @@ async function emitGoalMemoryCandidates(syncConfig, args) {
|
|
|
76775
76877
|
const raw = args.structured["memoryCandidates"];
|
|
76776
76878
|
if (!Array.isArray(raw) || raw.length === 0)
|
|
76777
76879
|
return;
|
|
76778
|
-
const
|
|
76880
|
+
const str3 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
76779
76881
|
const strArray = (value) => Array.isArray(value) ? value.filter((v) => typeof v === "string") : undefined;
|
|
76780
|
-
const topGoal =
|
|
76781
|
-
const topSession =
|
|
76882
|
+
const topGoal = str3(args.structured["goal"]);
|
|
76883
|
+
const topSession = str3(args.structured["goalSessionId"]) ?? str3(args.structured["activeGoalId"]);
|
|
76782
76884
|
for (const entry of raw.slice(0, 20)) {
|
|
76783
76885
|
if (!entry || typeof entry !== "object")
|
|
76784
76886
|
continue;
|
|
76785
76887
|
const candidate = entry;
|
|
76786
|
-
const title =
|
|
76787
|
-
const summary =
|
|
76888
|
+
const title = str3(candidate["title"]) ?? str3(candidate["summary"]);
|
|
76889
|
+
const summary = str3(candidate["summary"]) ?? str3(candidate["title"]);
|
|
76788
76890
|
if (!title || !summary)
|
|
76789
76891
|
continue;
|
|
76790
76892
|
try {
|
|
76791
76893
|
await postRetryableRheiSyncReceipt(syncConfig, "/v1/mcp/sync/goal-memory", {
|
|
76792
76894
|
projectId: args.projectId,
|
|
76793
76895
|
repoId: args.repoId,
|
|
76794
|
-
sourceKind:
|
|
76795
|
-
candidateKind:
|
|
76896
|
+
sourceKind: str3(candidate["sourceKind"]) ?? "goal_finish",
|
|
76897
|
+
candidateKind: str3(candidate["candidateKind"]) ?? "goal_session_learning",
|
|
76796
76898
|
title,
|
|
76797
76899
|
summary,
|
|
76798
|
-
subjectKind: ["path", "symbol", "service"].includes(String(candidate["subjectKind"])) ?
|
|
76799
|
-
subjectRef:
|
|
76800
|
-
normalizedRule:
|
|
76801
|
-
goal:
|
|
76802
|
-
goalSessionId:
|
|
76900
|
+
subjectKind: ["path", "symbol", "service"].includes(String(candidate["subjectKind"])) ? str3(candidate["subjectKind"]) : undefined,
|
|
76901
|
+
subjectRef: str3(candidate["subjectRef"]),
|
|
76902
|
+
normalizedRule: str3(candidate["normalizedRule"]) ?? str3(candidate["rule"]),
|
|
76903
|
+
goal: str3(candidate["goal"]) ?? topGoal,
|
|
76904
|
+
goalSessionId: str3(candidate["goalSessionId"]) ?? topSession,
|
|
76803
76905
|
filePaths: strArray(candidate["filePaths"]) ?? strArray(candidate["files"]),
|
|
76804
76906
|
targetRefs: strArray(candidate["targetRefs"]),
|
|
76805
76907
|
evidenceRefs: strArray(candidate["evidenceRefs"])
|
|
@@ -79497,14 +79599,14 @@ async function readFocusLedger(repoPath) {
|
|
|
79497
79599
|
const raw = JSON.parse(await readFile22(path, "utf8"));
|
|
79498
79600
|
const items = asArray(raw.items).flatMap((item) => {
|
|
79499
79601
|
const record = asRecord11(item);
|
|
79500
|
-
const stableKey =
|
|
79501
|
-
const source =
|
|
79602
|
+
const stableKey = str3(record?.["stableKey"]);
|
|
79603
|
+
const source = str3(record?.["source"]);
|
|
79502
79604
|
if (!stableKey || !source)
|
|
79503
79605
|
return [];
|
|
79504
79606
|
return [{
|
|
79505
79607
|
stableKey,
|
|
79506
79608
|
source,
|
|
79507
|
-
goalId:
|
|
79609
|
+
goalId: str3(record?.["goalId"]),
|
|
79508
79610
|
showCount: num(record?.["showCount"]) ?? 0,
|
|
79509
79611
|
lastShownAt: num(record?.["lastShownAt"]),
|
|
79510
79612
|
snoozedUntil: num(record?.["snoozedUntil"]),
|
|
@@ -79792,7 +79894,7 @@ function extractReuse(report) {
|
|
|
79792
79894
|
continue;
|
|
79793
79895
|
const evidence2 = asRecord11(record["evidence"]);
|
|
79794
79896
|
const dup = asRecord11(evidence2?.["duplicateEvidence"]);
|
|
79795
|
-
const subtype =
|
|
79897
|
+
const subtype = str3(dup?.["reuseSubtype"]) ?? str3(record["candidateKind"]) ?? "other";
|
|
79796
79898
|
counts.set(subtype, (counts.get(subtype) ?? 0) + 1);
|
|
79797
79899
|
const member = representativeMember(asArray(evidence2?.["memberSet"]));
|
|
79798
79900
|
if (member)
|
|
@@ -79815,13 +79917,13 @@ function extractDeadCode(report) {
|
|
|
79815
79917
|
if (!record)
|
|
79816
79918
|
continue;
|
|
79817
79919
|
const evidence2 = asRecord11(record["evidence"]);
|
|
79818
|
-
const kind =
|
|
79920
|
+
const kind = str3(evidence2?.["exportKind"]) ?? str3(record["candidateKind"]) ?? "other";
|
|
79819
79921
|
counts.set(kind, (counts.get(kind) ?? 0) + 1);
|
|
79820
|
-
const path =
|
|
79922
|
+
const path = str3(evidence2?.["path"]);
|
|
79821
79923
|
if (path)
|
|
79822
79924
|
paths.push(path);
|
|
79823
79925
|
if (!top) {
|
|
79824
|
-
const name =
|
|
79926
|
+
const name = str3(record["displayName"]) ?? str3(evidence2?.["exportName"]) ?? "symbol";
|
|
79825
79927
|
top = `${name} (${kind})${path ? ` · ${path}` : ""}`;
|
|
79826
79928
|
}
|
|
79827
79929
|
}
|
|
@@ -79835,14 +79937,14 @@ function extractCoverage(report) {
|
|
|
79835
79937
|
const record = asRecord11(entry);
|
|
79836
79938
|
if (!record)
|
|
79837
79939
|
continue;
|
|
79838
|
-
const path =
|
|
79940
|
+
const path = str3(record["path"]);
|
|
79839
79941
|
if (path) {
|
|
79840
79942
|
paths.push(path);
|
|
79841
79943
|
if (!top)
|
|
79842
79944
|
top = path;
|
|
79843
79945
|
}
|
|
79844
79946
|
for (const type of asArray(record["evidenceTypes"])) {
|
|
79845
|
-
const label =
|
|
79947
|
+
const label = str3(type);
|
|
79846
79948
|
if (label)
|
|
79847
79949
|
counts.set(label, (counts.get(label) ?? 0) + 1);
|
|
79848
79950
|
}
|
|
@@ -79852,7 +79954,7 @@ function extractCoverage(report) {
|
|
|
79852
79954
|
function extractGeneric(report) {
|
|
79853
79955
|
const paths = [];
|
|
79854
79956
|
for (const candidate of asArray(report["candidates"])) {
|
|
79855
|
-
const path =
|
|
79957
|
+
const path = str3(asRecord11(candidate)?.["path"]) ?? str3(asRecord11(asRecord11(candidate)?.["evidence"])?.["path"]);
|
|
79856
79958
|
if (path)
|
|
79857
79959
|
paths.push(path);
|
|
79858
79960
|
}
|
|
@@ -79927,21 +80029,21 @@ function extractHotspots(report) {
|
|
|
79927
80029
|
const evidence2 = asRecord11(asRecord11(candidate)?.["evidence"]);
|
|
79928
80030
|
for (const member of asArray(evidence2?.["memberSet"])) {
|
|
79929
80031
|
const record = asRecord11(member);
|
|
79930
|
-
const symbol =
|
|
80032
|
+
const symbol = str3(record?.["displayName"]);
|
|
79931
80033
|
const inEdges = num(record?.["inEdgeCount"]);
|
|
79932
80034
|
if (!symbol || inEdges === undefined)
|
|
79933
80035
|
continue;
|
|
79934
80036
|
const display = symbol.includes("/") ? basename18(symbol) : symbol;
|
|
79935
80037
|
const existing = best.get(symbol);
|
|
79936
80038
|
if (!existing || inEdges > existing.inEdges)
|
|
79937
|
-
best.set(symbol, { symbol: display, inEdges, path:
|
|
80039
|
+
best.set(symbol, { symbol: display, inEdges, path: str3(record?.["path"]) });
|
|
79938
80040
|
}
|
|
79939
80041
|
}
|
|
79940
80042
|
return [...best.values()].sort((a, b) => b.inEdges - a.inEdges).slice(0, 4);
|
|
79941
80043
|
}
|
|
79942
80044
|
function extractDriftTop(report) {
|
|
79943
80045
|
const first = asRecord11(asArray(report?.["findings"])[0]);
|
|
79944
|
-
return
|
|
80046
|
+
return str3(first?.["path"]) ?? str3(first?.["summary"]) ?? str3(first?.["title"]) ?? str3(first?.["detail"]);
|
|
79945
80047
|
}
|
|
79946
80048
|
async function buildModuleRows(repoPath, findings) {
|
|
79947
80049
|
const weights = await scanModuleWeights(repoPath);
|
|
@@ -80035,10 +80137,10 @@ function representativeMember(members) {
|
|
|
80035
80137
|
let fallback;
|
|
80036
80138
|
for (const member of members) {
|
|
80037
80139
|
const record = asRecord11(member);
|
|
80038
|
-
const path =
|
|
80140
|
+
const path = str3(record?.["path"]);
|
|
80039
80141
|
if (!path)
|
|
80040
80142
|
continue;
|
|
80041
|
-
if (
|
|
80143
|
+
if (str3(record?.["role"]) === "representative")
|
|
80042
80144
|
return path;
|
|
80043
80145
|
fallback ??= path;
|
|
80044
80146
|
}
|
|
@@ -80056,7 +80158,7 @@ function asArray(value) {
|
|
|
80056
80158
|
function asRecord11(value) {
|
|
80057
80159
|
return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
|
|
80058
80160
|
}
|
|
80059
|
-
function
|
|
80161
|
+
function str3(value) {
|
|
80060
80162
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
80061
80163
|
}
|
|
80062
80164
|
function num(value) {
|
|
@@ -80080,6 +80182,173 @@ function tidyPath(absolute, repoPath) {
|
|
|
80080
80182
|
return rel && !rel.startsWith("..") ? rel : absolute;
|
|
80081
80183
|
}
|
|
80082
80184
|
|
|
80185
|
+
// src/codeContextTools/symbolHistoryReader.ts
|
|
80186
|
+
import { join as join25 } from "node:path";
|
|
80187
|
+
import {
|
|
80188
|
+
resolveProvenanceSymbols,
|
|
80189
|
+
whatChangedSince,
|
|
80190
|
+
whySymbolChanged
|
|
80191
|
+
} from "./vendor/rhei-core/evidenceLedger.js";
|
|
80192
|
+
function resolveSymbolHistoryDeps(repoPath, env = process.env) {
|
|
80193
|
+
const root = localSqlArtifactRoot(repoPath, env);
|
|
80194
|
+
return {
|
|
80195
|
+
ledgerDbPath: join25(root, "evidence-ledger.sqlite"),
|
|
80196
|
+
codeIndexDbPath: join25(root, "code-index.sqlite")
|
|
80197
|
+
};
|
|
80198
|
+
}
|
|
80199
|
+
async function loadSqlite() {
|
|
80200
|
+
try {
|
|
80201
|
+
return await import("node:sqlite");
|
|
80202
|
+
} catch {
|
|
80203
|
+
return;
|
|
80204
|
+
}
|
|
80205
|
+
}
|
|
80206
|
+
function openReadOnly(mod, path) {
|
|
80207
|
+
try {
|
|
80208
|
+
return new mod.DatabaseSync(path, { readOnly: true });
|
|
80209
|
+
} catch {
|
|
80210
|
+
return;
|
|
80211
|
+
}
|
|
80212
|
+
}
|
|
80213
|
+
function parseStringArray(value) {
|
|
80214
|
+
if (typeof value !== "string" || value.length === 0)
|
|
80215
|
+
return [];
|
|
80216
|
+
try {
|
|
80217
|
+
const parsed = JSON.parse(value);
|
|
80218
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string") : [];
|
|
80219
|
+
} catch {
|
|
80220
|
+
return [];
|
|
80221
|
+
}
|
|
80222
|
+
}
|
|
80223
|
+
function toProvenanceRow(raw) {
|
|
80224
|
+
return {
|
|
80225
|
+
provenanceId: raw.provenance_id,
|
|
80226
|
+
goal: raw.goal ?? undefined,
|
|
80227
|
+
actor: raw.actor ?? undefined,
|
|
80228
|
+
createdAt: raw.created_at ?? undefined,
|
|
80229
|
+
files: parseStringArray(raw.files_json),
|
|
80230
|
+
symbols: parseStringArray(raw.symbols_json)
|
|
80231
|
+
};
|
|
80232
|
+
}
|
|
80233
|
+
function symbolSpansForPaths(db, paths) {
|
|
80234
|
+
const unique = [...new Set(paths)].filter(Boolean);
|
|
80235
|
+
if (unique.length === 0)
|
|
80236
|
+
return [];
|
|
80237
|
+
const placeholders = unique.map(() => "?").join(",");
|
|
80238
|
+
const rows = db.prepare(`SELECT COALESCE(s.qualified_name, s.name) AS key, f.path AS path,
|
|
80239
|
+
s.start_line AS startLine, s.end_line AS endLine
|
|
80240
|
+
FROM symbols s JOIN files f ON f.file_id = s.file_id
|
|
80241
|
+
WHERE f.path IN (${placeholders})`).all(...unique);
|
|
80242
|
+
return rows.map((row) => ({
|
|
80243
|
+
key: String(row["key"]),
|
|
80244
|
+
path: String(row["path"]),
|
|
80245
|
+
startLine: Number(row["startLine"]),
|
|
80246
|
+
endLine: Number(row["endLine"])
|
|
80247
|
+
}));
|
|
80248
|
+
}
|
|
80249
|
+
function definingPathsForSymbol(db, symbol) {
|
|
80250
|
+
const rows = db.prepare(`SELECT DISTINCT f.path AS path FROM symbols s JOIN files f ON f.file_id = s.file_id
|
|
80251
|
+
WHERE s.qualified_name = ? OR s.name = ?`).all(symbol, symbol);
|
|
80252
|
+
return rows.map((row) => String(row["path"]));
|
|
80253
|
+
}
|
|
80254
|
+
function provenanceRowsTouchingPaths(db, paths) {
|
|
80255
|
+
const unique = [...new Set(paths)].filter(Boolean);
|
|
80256
|
+
if (unique.length === 0)
|
|
80257
|
+
return [];
|
|
80258
|
+
const likeClause = unique.map(() => "files_json LIKE ?").join(" OR ");
|
|
80259
|
+
const likeParams = unique.map((path) => `%${JSON.stringify(path)}%`);
|
|
80260
|
+
const rows = db.prepare(`SELECT provenance_id, goal, actor, created_at, files_json, symbols_json
|
|
80261
|
+
FROM edit_provenance WHERE ${likeClause} ORDER BY created_at DESC`).all(...likeParams);
|
|
80262
|
+
const want = new Set(unique);
|
|
80263
|
+
return rows.map(toProvenanceRow).filter((row) => (row.files ?? []).some((file) => want.has(file)));
|
|
80264
|
+
}
|
|
80265
|
+
async function resolveSinceMs(query2) {
|
|
80266
|
+
if (typeof query2.sinceMs === "number" && Number.isFinite(query2.sinceMs))
|
|
80267
|
+
return query2.sinceMs;
|
|
80268
|
+
if (query2.sinceRef && query2.repoPath) {
|
|
80269
|
+
try {
|
|
80270
|
+
const { execFileSync: execFileSync5 } = await import("node:child_process");
|
|
80271
|
+
const out = execFileSync5("git", ["-C", query2.repoPath, "log", "-1", "--format=%ct", query2.sinceRef], {
|
|
80272
|
+
encoding: "utf8",
|
|
80273
|
+
timeout: 2000,
|
|
80274
|
+
windowsHide: true
|
|
80275
|
+
}).trim();
|
|
80276
|
+
const seconds = Number.parseInt(out, 10);
|
|
80277
|
+
if (Number.isFinite(seconds))
|
|
80278
|
+
return seconds * 1000;
|
|
80279
|
+
} catch {}
|
|
80280
|
+
}
|
|
80281
|
+
return;
|
|
80282
|
+
}
|
|
80283
|
+
var EMPTY_WHAT_CHANGED = {
|
|
80284
|
+
sinceMs: 0,
|
|
80285
|
+
editCount: 0,
|
|
80286
|
+
files: [],
|
|
80287
|
+
symbols: [],
|
|
80288
|
+
goals: []
|
|
80289
|
+
};
|
|
80290
|
+
async function whatChangedSinceFromLedger(query2, deps) {
|
|
80291
|
+
const sinceMs = await resolveSinceMs(query2);
|
|
80292
|
+
const mod = await loadSqlite();
|
|
80293
|
+
if (!mod || sinceMs === undefined)
|
|
80294
|
+
return { ...EMPTY_WHAT_CHANGED, sinceMs: sinceMs ?? 0, available: false };
|
|
80295
|
+
const ledger = openReadOnly(mod, deps.ledgerDbPath);
|
|
80296
|
+
if (!ledger)
|
|
80297
|
+
return { ...EMPTY_WHAT_CHANGED, sinceMs, available: false };
|
|
80298
|
+
try {
|
|
80299
|
+
const raw = ledger.prepare(`SELECT provenance_id, goal, actor, created_at, files_json, symbols_json
|
|
80300
|
+
FROM edit_provenance WHERE created_at >= ? ORDER BY created_at DESC`).all(sinceMs);
|
|
80301
|
+
const rows = raw.map(toProvenanceRow);
|
|
80302
|
+
const resolved = withResolvedSymbols(mod, deps, rows);
|
|
80303
|
+
return { ...whatChangedSince({ sinceMs }, resolved), available: true };
|
|
80304
|
+
} catch {
|
|
80305
|
+
return { ...EMPTY_WHAT_CHANGED, sinceMs, available: false };
|
|
80306
|
+
} finally {
|
|
80307
|
+
ledger.close();
|
|
80308
|
+
}
|
|
80309
|
+
}
|
|
80310
|
+
async function whySymbolChangedFromLedger(symbol, deps) {
|
|
80311
|
+
const empty = { symbol, editCount: 0, edits: [], goals: [] };
|
|
80312
|
+
const mod = await loadSqlite();
|
|
80313
|
+
if (!mod)
|
|
80314
|
+
return { ...empty, available: false };
|
|
80315
|
+
const index = openReadOnly(mod, deps.codeIndexDbPath);
|
|
80316
|
+
const ledger = openReadOnly(mod, deps.ledgerDbPath);
|
|
80317
|
+
if (!index || !ledger) {
|
|
80318
|
+
index?.close();
|
|
80319
|
+
ledger?.close();
|
|
80320
|
+
return { ...empty, available: false };
|
|
80321
|
+
}
|
|
80322
|
+
try {
|
|
80323
|
+
const paths = definingPathsForSymbol(index, symbol);
|
|
80324
|
+
if (paths.length === 0)
|
|
80325
|
+
return { ...empty, available: true };
|
|
80326
|
+
const rows = provenanceRowsTouchingPaths(ledger, paths).map((row) => ({
|
|
80327
|
+
...row,
|
|
80328
|
+
symbols: [...new Set([...row.symbols ?? [], symbol])]
|
|
80329
|
+
}));
|
|
80330
|
+
return { ...whySymbolChanged(symbol, rows), available: true };
|
|
80331
|
+
} catch {
|
|
80332
|
+
return { ...empty, available: false };
|
|
80333
|
+
} finally {
|
|
80334
|
+
index.close();
|
|
80335
|
+
ledger.close();
|
|
80336
|
+
}
|
|
80337
|
+
}
|
|
80338
|
+
function withResolvedSymbols(mod, deps, rows) {
|
|
80339
|
+
const paths = rows.flatMap((row) => row.files ?? []);
|
|
80340
|
+
if (paths.length === 0)
|
|
80341
|
+
return [...rows];
|
|
80342
|
+
const index = openReadOnly(mod, deps.codeIndexDbPath);
|
|
80343
|
+
if (!index)
|
|
80344
|
+
return [...rows];
|
|
80345
|
+
try {
|
|
80346
|
+
return resolveProvenanceSymbols(rows, symbolSpansForPaths(index, paths));
|
|
80347
|
+
} finally {
|
|
80348
|
+
index.close();
|
|
80349
|
+
}
|
|
80350
|
+
}
|
|
80351
|
+
|
|
80083
80352
|
// src/tools.ts
|
|
80084
80353
|
var ENTITY_TYPES = [
|
|
80085
80354
|
"character",
|
|
@@ -81741,8 +82010,46 @@ async function executeRemoteBetaTool(toolName, args, config) {
|
|
|
81741
82010
|
});
|
|
81742
82011
|
}
|
|
81743
82012
|
if (toolName === "ledger") {
|
|
82013
|
+
const repoPath = typeof args["repoPath"] === "string" ? args["repoPath"] : undefined;
|
|
82014
|
+
const opRaw = typeof args["op"] === "string" ? args["op"].trim().toLowerCase() : "overview";
|
|
82015
|
+
if (opRaw === "why_symbol_changed" || opRaw === "what_changed_since") {
|
|
82016
|
+
const deps = resolveSymbolHistoryDeps(repoPath ?? process.cwd());
|
|
82017
|
+
if (opRaw === "why_symbol_changed") {
|
|
82018
|
+
const symbol = typeof args["symbol"] === "string" ? args["symbol"].trim() : "";
|
|
82019
|
+
const result2 = symbol ? await whySymbolChangedFromLedger(symbol, deps) : { symbol: "", editCount: 0, edits: [], goals: [], available: false };
|
|
82020
|
+
return textJsonResult({
|
|
82021
|
+
kind: "rhei_ledger",
|
|
82022
|
+
schemaVersion: 1,
|
|
82023
|
+
op: "why_symbol_changed",
|
|
82024
|
+
status: result2.available ? "ready" : "unavailable",
|
|
82025
|
+
whySymbolChanged: result2,
|
|
82026
|
+
reportOnly: true,
|
|
82027
|
+
advisoryOnly: true,
|
|
82028
|
+
noAuthority: true,
|
|
82029
|
+
candidatesNotProof: true,
|
|
82030
|
+
sourceWrites: false,
|
|
82031
|
+
productionAuthority: false
|
|
82032
|
+
});
|
|
82033
|
+
}
|
|
82034
|
+
const sinceMs = typeof args["sinceMs"] === "number" ? args["sinceMs"] : undefined;
|
|
82035
|
+
const sinceRef = typeof args["sinceRef"] === "string" ? args["sinceRef"] : undefined;
|
|
82036
|
+
const result = await whatChangedSinceFromLedger({ sinceMs, sinceRef, repoPath }, deps);
|
|
82037
|
+
return textJsonResult({
|
|
82038
|
+
kind: "rhei_ledger",
|
|
82039
|
+
schemaVersion: 1,
|
|
82040
|
+
op: "what_changed_since",
|
|
82041
|
+
status: result.available ? "ready" : "unavailable",
|
|
82042
|
+
whatChangedSince: result,
|
|
82043
|
+
reportOnly: true,
|
|
82044
|
+
advisoryOnly: true,
|
|
82045
|
+
noAuthority: true,
|
|
82046
|
+
candidatesNotProof: true,
|
|
82047
|
+
sourceWrites: false,
|
|
82048
|
+
productionAuthority: false
|
|
82049
|
+
});
|
|
82050
|
+
}
|
|
81744
82051
|
const overview = await buildStatusOverview({
|
|
81745
|
-
repoPath
|
|
82052
|
+
repoPath,
|
|
81746
82053
|
full: true,
|
|
81747
82054
|
skipMcpSmoke: true,
|
|
81748
82055
|
mcp: {
|
|
@@ -81753,7 +82060,6 @@ async function executeRemoteBetaTool(toolName, args, config) {
|
|
|
81753
82060
|
}
|
|
81754
82061
|
});
|
|
81755
82062
|
const evidenceLedger = overview.overview["evidenceLedger"];
|
|
81756
|
-
const opRaw = typeof args["op"] === "string" ? args["op"].trim().toLowerCase() : "overview";
|
|
81757
82063
|
const op = opRaw === "report_cards" || opRaw === "trust_diff" || opRaw === "explain_line" ? opRaw : "overview";
|
|
81758
82064
|
const line = evidenceLedger ? evidenceLedgerLine(evidenceLedger) : undefined;
|
|
81759
82065
|
return textJsonResult({
|
|
@@ -82538,8 +82844,6 @@ function createLocalCodeContextServer(args) {
|
|
|
82538
82844
|
};
|
|
82539
82845
|
const explicitAllowlist = process.env["RHEI_MCP_LOCAL_TOOL_ALLOWLIST"] !== undefined;
|
|
82540
82846
|
for (const tool of RHEI_LOCAL_STATUS_TOOLS) {
|
|
82541
|
-
if (!publicLocalToolNameSet.has(tool.name))
|
|
82542
|
-
continue;
|
|
82543
82847
|
if (explicitAllowlist && localToolAllowlist && !localToolAllowlist.has(tool.name))
|
|
82544
82848
|
continue;
|
|
82545
82849
|
if (!explicitAllowlist && toolSurface !== "top_level_orchestrator")
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// ../core/src/changeEpisode/types.ts
|
|
2
|
+
var CHANGE_EPISODE_SCHEMA_VERSION = 1;
|
|
3
|
+
// ../core/src/changeEpisode/builders.ts
|
|
4
|
+
var CHANGE_EPISODE_ACTORS = ["human", "agent", "mixed"];
|
|
5
|
+
var CLOSED_EPISODE_STATUSES = [
|
|
6
|
+
"validated",
|
|
7
|
+
"failed",
|
|
8
|
+
"reverted",
|
|
9
|
+
"committed"
|
|
10
|
+
];
|
|
11
|
+
function openChangeEpisode(input) {
|
|
12
|
+
if (!CHANGE_EPISODE_ACTORS.includes(input.actor)) {
|
|
13
|
+
throw new Error(`openChangeEpisode: invalid actor "${String(input.actor)}"`);
|
|
14
|
+
}
|
|
15
|
+
if (!isValidTimestamp(input.startedAt)) {
|
|
16
|
+
throw new Error(`openChangeEpisode: invalid startedAt ${String(input.startedAt)}`);
|
|
17
|
+
}
|
|
18
|
+
const episodeId = cleanString(input.episodeId) ?? `change-episode:${stableHash({
|
|
19
|
+
goalId: input.goalId,
|
|
20
|
+
actor: input.actor,
|
|
21
|
+
startedAt: input.startedAt,
|
|
22
|
+
goal: input.goal,
|
|
23
|
+
gitBaseSha: input.gitBaseSha
|
|
24
|
+
})}`;
|
|
25
|
+
return removeUndefined({
|
|
26
|
+
episodeId,
|
|
27
|
+
goalId: cleanString(input.goalId),
|
|
28
|
+
actor: input.actor,
|
|
29
|
+
startedAt: input.startedAt,
|
|
30
|
+
goal: cleanString(input.goal),
|
|
31
|
+
contextReceipts: uniqueStrings(input.contextReceipts ?? []),
|
|
32
|
+
fileDeltas: uniqueStrings(input.fileDeltas ?? []),
|
|
33
|
+
symbolDeltas: uniqueStrings(input.symbolDeltas ?? []),
|
|
34
|
+
edgeDeltas: uniqueStrings(input.edgeDeltas ?? []),
|
|
35
|
+
memoryDeltas: uniqueStrings(input.memoryDeltas ?? []),
|
|
36
|
+
validationRuns: uniqueStrings(input.validationRuns ?? []),
|
|
37
|
+
gitBaseSha: cleanString(input.gitBaseSha),
|
|
38
|
+
worktreeDirtyHashBefore: cleanString(input.worktreeDirtyHashBefore),
|
|
39
|
+
status: "open"
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function attachEpisodeRows(episode, rows) {
|
|
43
|
+
return {
|
|
44
|
+
...episode,
|
|
45
|
+
contextReceipts: uniqueStrings([...episode.contextReceipts, ...rows.contextReceipts ?? []]),
|
|
46
|
+
fileDeltas: uniqueStrings([...episode.fileDeltas, ...rows.fileDeltas ?? []]),
|
|
47
|
+
symbolDeltas: uniqueStrings([...episode.symbolDeltas, ...rows.symbolDeltas ?? []]),
|
|
48
|
+
edgeDeltas: uniqueStrings([...episode.edgeDeltas, ...rows.edgeDeltas ?? []]),
|
|
49
|
+
memoryDeltas: uniqueStrings([...episode.memoryDeltas, ...rows.memoryDeltas ?? []]),
|
|
50
|
+
validationRuns: uniqueStrings([...episode.validationRuns, ...rows.validationRuns ?? []])
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function closeChangeEpisode(episode, input) {
|
|
54
|
+
if (episode.status !== "open") {
|
|
55
|
+
throw new Error(`closeChangeEpisode: episode ${episode.episodeId} is already closed (status "${episode.status}")`);
|
|
56
|
+
}
|
|
57
|
+
if (!CLOSED_EPISODE_STATUSES.includes(input.status)) {
|
|
58
|
+
throw new Error(`closeChangeEpisode: invalid target status "${String(input.status)}"`);
|
|
59
|
+
}
|
|
60
|
+
if (!isValidTimestamp(input.endedAt) || input.endedAt < episode.startedAt) {
|
|
61
|
+
throw new Error(`closeChangeEpisode: invalid endedAt ${String(input.endedAt)} (startedAt ${episode.startedAt})`);
|
|
62
|
+
}
|
|
63
|
+
return removeUndefined({
|
|
64
|
+
...episode,
|
|
65
|
+
status: input.status,
|
|
66
|
+
endedAt: input.endedAt,
|
|
67
|
+
gitHeadSha: cleanString(input.gitHeadSha) ?? episode.gitHeadSha,
|
|
68
|
+
worktreeDirtyHashAfter: cleanString(input.worktreeDirtyHashAfter) ?? episode.worktreeDirtyHashAfter
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
function planEpisodeRevert(episode, fileDeltas, opts = {}) {
|
|
72
|
+
const deltasById = new Map;
|
|
73
|
+
for (const delta of fileDeltas) {
|
|
74
|
+
if (!deltasById.has(delta.fileDeltaId))
|
|
75
|
+
deltasById.set(delta.fileDeltaId, delta);
|
|
76
|
+
}
|
|
77
|
+
const deltasToInvert = [];
|
|
78
|
+
const deltasExcluded = [];
|
|
79
|
+
const conflicts = [];
|
|
80
|
+
for (const fileDeltaId of uniqueStrings(episode.fileDeltas)) {
|
|
81
|
+
const delta = deltasById.get(fileDeltaId);
|
|
82
|
+
if (!delta) {
|
|
83
|
+
conflicts.push({ fileDeltaId, reason: "missing_delta_row" });
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const currentPath = cleanString(delta.pathAfter) ?? cleanString(delta.pathBefore);
|
|
87
|
+
if (currentPath !== undefined && opts.excludePaths?.(currentPath) === true) {
|
|
88
|
+
deltasExcluded.push({ id: fileDeltaId, reason: "excluded_by_filter" });
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (cleanString(delta.inversePatch) === undefined) {
|
|
92
|
+
conflicts.push({ fileDeltaId, reason: "no_inverse_patch" });
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
deltasToInvert.push(fileDeltaId);
|
|
96
|
+
}
|
|
97
|
+
const status = deltasToInvert.length === 0 ? "blocked" : conflicts.length > 0 || deltasExcluded.length > 0 ? "partial" : "plannable";
|
|
98
|
+
return {
|
|
99
|
+
episodeId: episode.episodeId,
|
|
100
|
+
deltasToInvert,
|
|
101
|
+
deltasExcluded,
|
|
102
|
+
conflicts,
|
|
103
|
+
status
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function fileDeltasFromProvenance(rows) {
|
|
107
|
+
return rows.map((row) => {
|
|
108
|
+
const actor = (() => {
|
|
109
|
+
const value = cleanString(row.actor);
|
|
110
|
+
return value === "human" || value?.startsWith("human_") ? "human" : "agent";
|
|
111
|
+
})();
|
|
112
|
+
return removeUndefined({
|
|
113
|
+
fileDeltaId: row.provenanceId,
|
|
114
|
+
pathBefore: cleanString(row.pathBefore),
|
|
115
|
+
pathAfter: cleanString(row.pathAfter),
|
|
116
|
+
changeKind: row.changeKind ?? "modify",
|
|
117
|
+
beforeHash: cleanString(row.beforeHash),
|
|
118
|
+
afterHash: cleanString(row.afterHash),
|
|
119
|
+
forwardPatch: cleanString(row.forwardPatch),
|
|
120
|
+
inversePatch: cleanString(row.inversePatch),
|
|
121
|
+
actor,
|
|
122
|
+
receiptId: row.provenanceId
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function uniqueStrings(values) {
|
|
127
|
+
return Array.from(new Set(values.map((value) => value?.trim()).filter((value) => Boolean(value))));
|
|
128
|
+
}
|
|
129
|
+
function cleanString(value) {
|
|
130
|
+
if (typeof value !== "string")
|
|
131
|
+
return;
|
|
132
|
+
const text = value.trim();
|
|
133
|
+
return text.length > 0 ? text : undefined;
|
|
134
|
+
}
|
|
135
|
+
function isValidTimestamp(value) {
|
|
136
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0;
|
|
137
|
+
}
|
|
138
|
+
function removeUndefined(value) {
|
|
139
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
140
|
+
}
|
|
141
|
+
function stableHash(value) {
|
|
142
|
+
const text = stableJson(value);
|
|
143
|
+
let hash = 2166136261;
|
|
144
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
145
|
+
hash ^= text.charCodeAt(index);
|
|
146
|
+
hash = Math.imul(hash, 16777619);
|
|
147
|
+
}
|
|
148
|
+
return (hash >>> 0).toString(16).padStart(8, "0").slice(0, 12);
|
|
149
|
+
}
|
|
150
|
+
function stableJson(value) {
|
|
151
|
+
if (Array.isArray(value))
|
|
152
|
+
return `[${value.map(stableJson).join(",")}]`;
|
|
153
|
+
if (value && typeof value === "object") {
|
|
154
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableJson(value[key])}`).join(",")}}`;
|
|
155
|
+
}
|
|
156
|
+
return JSON.stringify(value);
|
|
157
|
+
}
|
|
158
|
+
// ../core/src/changeEpisode/fromLedgerRows.ts
|
|
159
|
+
function assembleChangeEpisodeFromLedgerRows(input) {
|
|
160
|
+
const provenance = input.editProvenance.filter((row) => row.goalId === undefined || row.goalId === input.goalId);
|
|
161
|
+
const timestamps = [
|
|
162
|
+
...input.trials.map((trial) => trial.createdAt),
|
|
163
|
+
...provenance.map((row) => row.createdAt)
|
|
164
|
+
].filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
165
|
+
const startedAt = timestamps.length > 0 ? Math.min(...timestamps) : input.fallbackStartedAt ?? 0;
|
|
166
|
+
const episode = openChangeEpisode({
|
|
167
|
+
goalId: input.goalId,
|
|
168
|
+
goal: input.goal,
|
|
169
|
+
actor: inferEpisodeActor(provenance),
|
|
170
|
+
startedAt,
|
|
171
|
+
gitBaseSha: input.gitBaseSha,
|
|
172
|
+
worktreeDirtyHashBefore: input.worktreeDirtyHashBefore
|
|
173
|
+
});
|
|
174
|
+
return attachEpisodeRows(episode, {
|
|
175
|
+
contextReceipts: uniqueStrings([
|
|
176
|
+
...input.packItems.map((item) => item.trialId),
|
|
177
|
+
...input.trials.map((trial) => trial.receiptId)
|
|
178
|
+
]),
|
|
179
|
+
fileDeltas: provenance.map((row) => row.provenanceId),
|
|
180
|
+
symbolDeltas: provenance.flatMap((row) => [...row.symbols ?? []]),
|
|
181
|
+
validationRuns: [...input.validationRefs]
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function inferEpisodeActor(provenance) {
|
|
185
|
+
let hasAgent = false;
|
|
186
|
+
let hasHuman = false;
|
|
187
|
+
for (const row of provenance) {
|
|
188
|
+
const actor = cleanString(row.actor);
|
|
189
|
+
if (actor === undefined)
|
|
190
|
+
continue;
|
|
191
|
+
if (actor === "human" || actor.startsWith("human_"))
|
|
192
|
+
hasHuman = true;
|
|
193
|
+
else
|
|
194
|
+
hasAgent = true;
|
|
195
|
+
}
|
|
196
|
+
if (hasAgent && hasHuman)
|
|
197
|
+
return "mixed";
|
|
198
|
+
if (hasHuman)
|
|
199
|
+
return "human";
|
|
200
|
+
return "agent";
|
|
201
|
+
}
|
|
202
|
+
// ../core/src/changeEpisode/deltaComputation.ts
|
|
203
|
+
function isUsableRange(range) {
|
|
204
|
+
return Number.isFinite(range.startLine) && Number.isFinite(range.endLine) && range.endLine >= range.startLine && (range.startLine > 0 || range.endLine > 0);
|
|
205
|
+
}
|
|
206
|
+
function overlaps(span, range) {
|
|
207
|
+
return span.startLine <= range.endLine && span.endLine >= range.startLine;
|
|
208
|
+
}
|
|
209
|
+
function resolveTouchedSymbols(changedPaths, symbols, hunks) {
|
|
210
|
+
const changed = new Set(changedPaths.map((path) => path.trim()).filter(Boolean));
|
|
211
|
+
const usableHunksByPath = new Map;
|
|
212
|
+
for (const hunk of hunks ?? []) {
|
|
213
|
+
if (!isUsableRange(hunk))
|
|
214
|
+
continue;
|
|
215
|
+
const list = usableHunksByPath.get(hunk.path) ?? [];
|
|
216
|
+
list.push(hunk);
|
|
217
|
+
usableHunksByPath.set(hunk.path, list);
|
|
218
|
+
}
|
|
219
|
+
const seen = new Set;
|
|
220
|
+
const touched = [];
|
|
221
|
+
for (const symbol of symbols) {
|
|
222
|
+
if (!changed.has(symbol.path))
|
|
223
|
+
continue;
|
|
224
|
+
const pathHunks = usableHunksByPath.get(symbol.path);
|
|
225
|
+
const isTouched = pathHunks === undefined ? true : pathHunks.some((range) => overlaps(symbol, range));
|
|
226
|
+
if (!isTouched)
|
|
227
|
+
continue;
|
|
228
|
+
const key = symbol.key.trim();
|
|
229
|
+
if (!key || seen.has(key))
|
|
230
|
+
continue;
|
|
231
|
+
seen.add(key);
|
|
232
|
+
touched.push(key);
|
|
233
|
+
}
|
|
234
|
+
return touched;
|
|
235
|
+
}
|
|
236
|
+
function deriveInverseEdit(spec) {
|
|
237
|
+
switch (spec.kind) {
|
|
238
|
+
case "search_replace":
|
|
239
|
+
return { kind: "search_replace", path: spec.path, search: spec.replace, replace: spec.search };
|
|
240
|
+
case "rewrite":
|
|
241
|
+
return spec.beforeContent === undefined ? { kind: "unavailable", path: spec.path, reason: "missing_before_content" } : { kind: "rewrite", path: spec.path, rewrite: spec.beforeContent };
|
|
242
|
+
case "create":
|
|
243
|
+
return { kind: "delete", path: spec.path };
|
|
244
|
+
case "delete":
|
|
245
|
+
return spec.beforeContent === undefined ? { kind: "unavailable", path: spec.path, reason: "missing_before_content" } : { kind: "create", path: spec.path, content: spec.beforeContent };
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function edgeIdentity(edge) {
|
|
249
|
+
return `${edge.from}\x00${edge.to}\x00${edge.kind}`;
|
|
250
|
+
}
|
|
251
|
+
function diffGraphEdges(prev, next) {
|
|
252
|
+
const prevIds = new Set(prev.map(edgeIdentity));
|
|
253
|
+
const nextIds = new Set(next.map(edgeIdentity));
|
|
254
|
+
const added = [];
|
|
255
|
+
const seenAdded = new Set;
|
|
256
|
+
for (const edge of next) {
|
|
257
|
+
const id = edgeIdentity(edge);
|
|
258
|
+
if (prevIds.has(id) || seenAdded.has(id))
|
|
259
|
+
continue;
|
|
260
|
+
seenAdded.add(id);
|
|
261
|
+
added.push({ change: "added", from: edge.from, to: edge.to, kind: edge.kind });
|
|
262
|
+
}
|
|
263
|
+
const removed = [];
|
|
264
|
+
const seenRemoved = new Set;
|
|
265
|
+
for (const edge of prev) {
|
|
266
|
+
const id = edgeIdentity(edge);
|
|
267
|
+
if (nextIds.has(id) || seenRemoved.has(id))
|
|
268
|
+
continue;
|
|
269
|
+
seenRemoved.add(id);
|
|
270
|
+
removed.push({ change: "removed", from: edge.from, to: edge.to, kind: edge.kind });
|
|
271
|
+
}
|
|
272
|
+
return [...added, ...removed];
|
|
273
|
+
}
|
|
274
|
+
export {
|
|
275
|
+
uniqueStrings,
|
|
276
|
+
stableHash,
|
|
277
|
+
resolveTouchedSymbols,
|
|
278
|
+
planEpisodeRevert,
|
|
279
|
+
openChangeEpisode,
|
|
280
|
+
fileDeltasFromProvenance,
|
|
281
|
+
diffGraphEdges,
|
|
282
|
+
deriveInverseEdit,
|
|
283
|
+
closeChangeEpisode,
|
|
284
|
+
cleanString,
|
|
285
|
+
attachEpisodeRows,
|
|
286
|
+
assembleChangeEpisodeFromLedgerRows,
|
|
287
|
+
CHANGE_EPISODE_SCHEMA_VERSION
|
|
288
|
+
};
|
|
@@ -4092,11 +4092,13 @@ var EditSourceV1Schema = exports_external.enum([
|
|
|
4092
4092
|
"shell_script",
|
|
4093
4093
|
"unknown_external_agent"
|
|
4094
4094
|
]);
|
|
4095
|
+
var EditHunkInverseV1Schema = exports_external.object({ kind: exports_external.string() }).passthrough();
|
|
4095
4096
|
var EditHunkV1Schema = exports_external.object({
|
|
4096
4097
|
filePath: exports_external.string(),
|
|
4097
4098
|
startLine: exports_external.number().int().nonnegative(),
|
|
4098
4099
|
endLine: exports_external.number().int().nonnegative(),
|
|
4099
|
-
kind: exports_external.enum(["insert", "update", "delete", "mixed"]).optional()
|
|
4100
|
+
kind: exports_external.enum(["insert", "update", "delete", "mixed"]).optional(),
|
|
4101
|
+
inverse: exports_external.array(EditHunkInverseV1Schema).optional()
|
|
4100
4102
|
}).strict();
|
|
4101
4103
|
var EditProvenanceV1Schema = exports_external.object({
|
|
4102
4104
|
schemaVersion: exports_external.literal(EVIDENCE_LEDGER_SCHEMA_VERSION),
|
|
@@ -5084,6 +5086,100 @@ function buildProvenanceSourceReportCards(provenances, events, options = {}) {
|
|
|
5084
5086
|
return card;
|
|
5085
5087
|
}).sort((a, b) => b.totalEdits - a.totalEdits || a.source.localeCompare(b.source));
|
|
5086
5088
|
}
|
|
5089
|
+
// ../core/src/changeEpisode/deltaComputation.ts
|
|
5090
|
+
function isUsableRange(range) {
|
|
5091
|
+
return Number.isFinite(range.startLine) && Number.isFinite(range.endLine) && range.endLine >= range.startLine && (range.startLine > 0 || range.endLine > 0);
|
|
5092
|
+
}
|
|
5093
|
+
function overlaps(span, range) {
|
|
5094
|
+
return span.startLine <= range.endLine && span.endLine >= range.startLine;
|
|
5095
|
+
}
|
|
5096
|
+
function resolveTouchedSymbols(changedPaths, symbols, hunks) {
|
|
5097
|
+
const changed = new Set(changedPaths.map((path) => path.trim()).filter(Boolean));
|
|
5098
|
+
const usableHunksByPath = new Map;
|
|
5099
|
+
for (const hunk of hunks ?? []) {
|
|
5100
|
+
if (!isUsableRange(hunk))
|
|
5101
|
+
continue;
|
|
5102
|
+
const list = usableHunksByPath.get(hunk.path) ?? [];
|
|
5103
|
+
list.push(hunk);
|
|
5104
|
+
usableHunksByPath.set(hunk.path, list);
|
|
5105
|
+
}
|
|
5106
|
+
const seen = new Set;
|
|
5107
|
+
const touched = [];
|
|
5108
|
+
for (const symbol of symbols) {
|
|
5109
|
+
if (!changed.has(symbol.path))
|
|
5110
|
+
continue;
|
|
5111
|
+
const pathHunks = usableHunksByPath.get(symbol.path);
|
|
5112
|
+
const isTouched = pathHunks === undefined ? true : pathHunks.some((range) => overlaps(symbol, range));
|
|
5113
|
+
if (!isTouched)
|
|
5114
|
+
continue;
|
|
5115
|
+
const key = symbol.key.trim();
|
|
5116
|
+
if (!key || seen.has(key))
|
|
5117
|
+
continue;
|
|
5118
|
+
seen.add(key);
|
|
5119
|
+
touched.push(key);
|
|
5120
|
+
}
|
|
5121
|
+
return touched;
|
|
5122
|
+
}
|
|
5123
|
+
// ../core/src/evidenceLedger/symbolHistory.ts
|
|
5124
|
+
function sortRowsMostRecentFirst(rows) {
|
|
5125
|
+
return [...rows].sort((left, right) => timestamp(right.createdAt) - timestamp(left.createdAt));
|
|
5126
|
+
}
|
|
5127
|
+
function timestamp(value) {
|
|
5128
|
+
return typeof value === "number" && Number.isFinite(value) ? value : Number.NEGATIVE_INFINITY;
|
|
5129
|
+
}
|
|
5130
|
+
function pushUnique(seen, into, value) {
|
|
5131
|
+
const text = value?.trim();
|
|
5132
|
+
if (!text || seen.has(text))
|
|
5133
|
+
return;
|
|
5134
|
+
seen.add(text);
|
|
5135
|
+
into.push(text);
|
|
5136
|
+
}
|
|
5137
|
+
function resolveProvenanceSymbols(rows, indexSymbols) {
|
|
5138
|
+
return rows.map((row) => {
|
|
5139
|
+
if ((row.symbols ?? []).length > 0)
|
|
5140
|
+
return row;
|
|
5141
|
+
const symbols = resolveTouchedSymbols([...row.files ?? []], indexSymbols);
|
|
5142
|
+
return { ...row, symbols };
|
|
5143
|
+
});
|
|
5144
|
+
}
|
|
5145
|
+
function whySymbolChanged(symbolKey, rows) {
|
|
5146
|
+
const target = symbolKey.trim();
|
|
5147
|
+
const ordered = sortRowsMostRecentFirst(rows);
|
|
5148
|
+
const edits = [];
|
|
5149
|
+
const goalSeen = new Set;
|
|
5150
|
+
const goals = [];
|
|
5151
|
+
for (const row of ordered) {
|
|
5152
|
+
if (!(row.symbols ?? []).some((symbol) => symbol.trim() === target))
|
|
5153
|
+
continue;
|
|
5154
|
+
edits.push({
|
|
5155
|
+
provenanceId: row.provenanceId,
|
|
5156
|
+
goalId: row.goalId,
|
|
5157
|
+
goal: row.goal,
|
|
5158
|
+
actor: row.actor,
|
|
5159
|
+
changedAt: row.createdAt,
|
|
5160
|
+
files: [...row.files ?? []]
|
|
5161
|
+
});
|
|
5162
|
+
pushUnique(goalSeen, goals, row.goal);
|
|
5163
|
+
}
|
|
5164
|
+
return { symbol: target, editCount: edits.length, edits, goals };
|
|
5165
|
+
}
|
|
5166
|
+
function whatChangedSince(input, rows) {
|
|
5167
|
+
const ordered = sortRowsMostRecentFirst(rows).filter((row) => timestamp(row.createdAt) >= input.sinceMs);
|
|
5168
|
+
const fileSeen = new Set;
|
|
5169
|
+
const files = [];
|
|
5170
|
+
const symbolSeen = new Set;
|
|
5171
|
+
const symbols = [];
|
|
5172
|
+
const goalSeen = new Set;
|
|
5173
|
+
const goals = [];
|
|
5174
|
+
for (const row of ordered) {
|
|
5175
|
+
for (const file of row.files ?? [])
|
|
5176
|
+
pushUnique(fileSeen, files, file);
|
|
5177
|
+
for (const symbol of row.symbols ?? [])
|
|
5178
|
+
pushUnique(symbolSeen, symbols, symbol);
|
|
5179
|
+
pushUnique(goalSeen, goals, row.goal);
|
|
5180
|
+
}
|
|
5181
|
+
return { sinceMs: input.sinceMs, editCount: ordered.length, files, symbols, goals };
|
|
5182
|
+
}
|
|
5087
5183
|
// ../core/src/evidenceLedger/ddl.ts
|
|
5088
5184
|
var EVIDENCE_LEDGER_DDL = `-- Evidence Ledger canonical DDL (SPEC ONLY).
|
|
5089
5185
|
--
|
|
@@ -5236,8 +5332,11 @@ var EVIDENCE_LEDGER_TABLE_NAMES = [
|
|
|
5236
5332
|
"memory_signals"
|
|
5237
5333
|
];
|
|
5238
5334
|
export {
|
|
5335
|
+
whySymbolChanged,
|
|
5336
|
+
whatChangedSince,
|
|
5239
5337
|
usefulnessForIntent,
|
|
5240
5338
|
unsafeTeamEvidenceMetadataPayloadPaths,
|
|
5339
|
+
resolveProvenanceSymbols,
|
|
5241
5340
|
rankSubjectsForIntent,
|
|
5242
5341
|
parseGitNameOnlyLog,
|
|
5243
5342
|
nextMemoryStatus,
|
|
@@ -5325,6 +5424,7 @@ export {
|
|
|
5325
5424
|
EditSourceV1Schema,
|
|
5326
5425
|
EditProvenanceV1Schema,
|
|
5327
5426
|
EditHunkV1Schema,
|
|
5427
|
+
EditHunkInverseV1Schema,
|
|
5328
5428
|
EVIDENCE_LEDGER_TABLE_NAMES,
|
|
5329
5429
|
EVIDENCE_LEDGER_SCHEMA_VERSION,
|
|
5330
5430
|
EVIDENCE_LEDGER_DDL,
|