evrex-mcp 0.8.0 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/account.js +1 -1
- package/dist/capture.js +243 -27
- package/dist/continuity.js +1 -1
- package/dist/hook.js +5 -2
- package/dist/import.js +280 -27
- package/dist/index.js +398 -76
- package/dist/pretool.js +1 -1
- package/dist/tickets.js +1 -1
- package/package.json +16 -15
package/dist/account.js
CHANGED
|
@@ -49,7 +49,7 @@ var evrexApi = {
|
|
|
49
49
|
// Evidence-only retrieval (BM25 + embedding), no LLM synthesis call — see
|
|
50
50
|
// apps/backend/src/query/query.service.ts#search. Used by evrex_search,
|
|
51
51
|
// which wants ranked hits fast, not a synthesized paragraph.
|
|
52
|
-
search: (repoPath, text, filePaths) => post("/search", { repoPath, text, filePaths }),
|
|
52
|
+
search: (repoPath, text, filePaths, boost) => post("/search", { repoPath, text, filePaths, boost }),
|
|
53
53
|
// Everything that happened in a repo, newest first, bounded by days — the
|
|
54
54
|
// same query the desktop Timeline screen makes. Sessions and commits
|
|
55
55
|
// interleaved, each with the handle evrex_expand takes.
|
package/dist/capture.js
CHANGED
|
@@ -180,17 +180,38 @@ function commitMeta(repoPath, sha) {
|
|
|
180
180
|
const line = git(repoPath, [
|
|
181
181
|
"show",
|
|
182
182
|
"-s",
|
|
183
|
-
`--format=%H${FIELD_SEP}%an${FIELD_SEP}%ae${FIELD_SEP}%aI`,
|
|
183
|
+
`--format=%H${FIELD_SEP}%an${FIELD_SEP}%ae${FIELD_SEP}%aI${FIELD_SEP}%P`,
|
|
184
184
|
sha
|
|
185
185
|
]).trim();
|
|
186
186
|
const parts = line.split(FIELD_SEP);
|
|
187
|
-
const
|
|
187
|
+
const body = git(repoPath, ["show", "-s", "--notes", `--format=%B${FIELD_SEP}%N`, sha]);
|
|
188
|
+
const sep2 = body.indexOf(FIELD_SEP);
|
|
189
|
+
let message = (sep2 === -1 ? body : body.slice(0, sep2)).replace(/\n+$/, "");
|
|
190
|
+
const note = sep2 === -1 ? "" : body.slice(sep2 + FIELD_SEP.length);
|
|
191
|
+
const noted = /^Evrex-Session:\s*(\S+)\s*$/m.exec(note);
|
|
192
|
+
if (noted && !/^Evrex-Session:/m.test(message)) {
|
|
193
|
+
message = `${message}
|
|
194
|
+
|
|
195
|
+
${noted[0].trim()}`;
|
|
196
|
+
}
|
|
188
197
|
return {
|
|
189
198
|
sha: parts[0] ?? sha,
|
|
190
199
|
author: parts[1] ?? "",
|
|
191
200
|
authorEmail: parts[2] ?? "",
|
|
192
201
|
ts: parts[3] ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
193
|
-
|
|
202
|
+
parents: (parts[4] ?? "").split(" ").filter(Boolean),
|
|
203
|
+
message,
|
|
204
|
+
// The note's session, exposed on its own as well as folded above. When
|
|
205
|
+
// the message ALREADY carries a trailer, the fold declines (never rewrite
|
|
206
|
+
// what was stamped at commit time) — but the parse must still prefer the
|
|
207
|
+
// note: it exists only because an operator pressed "verify" on a commit
|
|
208
|
+
// whose message trailer names a session no ingest can resolve. Commit
|
|
209
|
+
// 843dabb3 is the standing example — its at-commit-time trailer names a
|
|
210
|
+
// session with no transcript anywhere, so the folding guard alone left it
|
|
211
|
+
// permanently un-verifiable: every promote re-ingested the dangling id,
|
|
212
|
+
// bestLink demoted it below the resolvable match, and the button offered
|
|
213
|
+
// the same commit again.
|
|
214
|
+
notedSession: noted?.[1] ?? null
|
|
194
215
|
};
|
|
195
216
|
}
|
|
196
217
|
function parseTrailers(repoPath, message) {
|
|
@@ -238,6 +259,10 @@ function trailerValue(trailers, key) {
|
|
|
238
259
|
const k = key.toLowerCase();
|
|
239
260
|
return trailers.find((t) => t.key.toLowerCase() === k)?.value ?? null;
|
|
240
261
|
}
|
|
262
|
+
function originOf(trailers) {
|
|
263
|
+
const v = trailerValue(trailers, EVREX_ORIGIN_TRAILER_KEY);
|
|
264
|
+
return v?.trim().toLowerCase() === "human" ? "human" : null;
|
|
265
|
+
}
|
|
241
266
|
function agentTrailersOf(trailers) {
|
|
242
267
|
const out = [];
|
|
243
268
|
for (const t of trailers) {
|
|
@@ -307,6 +332,9 @@ function commitFiles(repoPath, sha) {
|
|
|
307
332
|
}
|
|
308
333
|
function parseGitLog(repoPath, repoId, known) {
|
|
309
334
|
const shas = listShas(repoPath).filter((sha) => !known?.has(sha));
|
|
335
|
+
return parseCommitsBySha(repoPath, shas, repoId);
|
|
336
|
+
}
|
|
337
|
+
function parseCommitsBySha(repoPath, shas, repoId) {
|
|
310
338
|
const id = repoId ?? deriveRepoId(repoPath);
|
|
311
339
|
return shas.map((sha) => {
|
|
312
340
|
const meta = commitMeta(repoPath, sha);
|
|
@@ -319,15 +347,19 @@ function parseGitLog(repoPath, repoId, known) {
|
|
|
319
347
|
authorEmail: meta.authorEmail,
|
|
320
348
|
ts: meta.ts,
|
|
321
349
|
message: meta.message,
|
|
350
|
+
parents: meta.parents,
|
|
322
351
|
branch: commitBranch(repoPath, sha),
|
|
323
|
-
|
|
352
|
+
// A note is the operator's explicit later correction, so where both
|
|
353
|
+
// exist it names the session — see commitMeta's notedSession.
|
|
354
|
+
evrexSessionTrailer: meta.notedSession ?? trailerValue(trailers, EVREX_SESSION_TRAILER_KEY),
|
|
355
|
+
origin: originOf(trailers),
|
|
324
356
|
agentTrailers: agentTrailersOf(trailers),
|
|
325
357
|
statedInsights: statedInsightsOf(trailers),
|
|
326
358
|
files: commitFiles(repoPath, sha)
|
|
327
359
|
};
|
|
328
360
|
});
|
|
329
361
|
}
|
|
330
|
-
var DIFF_CAP, FIELD_SEP, EVREX_SESSION_TRAILER_KEY;
|
|
362
|
+
var DIFF_CAP, FIELD_SEP, EVREX_SESSION_TRAILER_KEY, EVREX_ORIGIN_TRAILER_KEY;
|
|
331
363
|
var init_git_history = __esm({
|
|
332
364
|
"../../packages/ingest-core/src/git-history.ts"() {
|
|
333
365
|
"use strict";
|
|
@@ -335,6 +367,7 @@ var init_git_history = __esm({
|
|
|
335
367
|
DIFF_CAP = 2e4;
|
|
336
368
|
FIELD_SEP = "";
|
|
337
369
|
EVREX_SESSION_TRAILER_KEY = "Evrex-Session";
|
|
370
|
+
EVREX_ORIGIN_TRAILER_KEY = "Evrex-Origin";
|
|
338
371
|
}
|
|
339
372
|
});
|
|
340
373
|
|
|
@@ -399,7 +432,7 @@ var PARSER_VERSION;
|
|
|
399
432
|
var init_incremental = __esm({
|
|
400
433
|
"../../packages/ingest-core/src/incremental.ts"() {
|
|
401
434
|
"use strict";
|
|
402
|
-
PARSER_VERSION =
|
|
435
|
+
PARSER_VERSION = 7;
|
|
403
436
|
}
|
|
404
437
|
});
|
|
405
438
|
|
|
@@ -514,11 +547,54 @@ var init_redact = __esm({
|
|
|
514
547
|
|
|
515
548
|
// ../../packages/ingest-core/src/subject-linking.ts
|
|
516
549
|
function matchCommitToSession(commit, sessions) {
|
|
550
|
+
const saw = sessions.filter(
|
|
551
|
+
(s) => (s.committedShas ?? []).some(
|
|
552
|
+
(short) => short.length >= 7 && commit.sha.startsWith(short)
|
|
553
|
+
)
|
|
554
|
+
);
|
|
555
|
+
if (saw.length > 0) {
|
|
556
|
+
const oneLineage2 = saw.every((s) => s.startedAt === saw[0].startedAt);
|
|
557
|
+
if (!oneLineage2) {
|
|
558
|
+
return { sha: commit.sha, sessionId: null, reason: "ambiguous" };
|
|
559
|
+
}
|
|
560
|
+
const original2 = [...saw].sort((a, b) => a.endedAt - b.endedAt)[0];
|
|
561
|
+
return {
|
|
562
|
+
sha: commit.sha,
|
|
563
|
+
sessionId: original2.sessionId,
|
|
564
|
+
evidence: "saw-the-sha",
|
|
565
|
+
confidence: SHA_CONFIDENCE
|
|
566
|
+
};
|
|
567
|
+
}
|
|
517
568
|
const subject = commit.subject.trim();
|
|
569
|
+
const prMatch = /\(#(\d+)\)$/.exec(subject) ?? /^Merge pull request #(\d+)\b/.exec(subject);
|
|
570
|
+
if (prMatch) {
|
|
571
|
+
const pr = Number(prMatch[1]);
|
|
572
|
+
const mergedIt = sessions.filter(
|
|
573
|
+
(s) => (s.mergedPrNumbers ?? []).includes(pr)
|
|
574
|
+
);
|
|
575
|
+
if (mergedIt.length > 0) {
|
|
576
|
+
const oneLineage2 = mergedIt.every(
|
|
577
|
+
(s) => s.startedAt === mergedIt[0].startedAt
|
|
578
|
+
);
|
|
579
|
+
if (!oneLineage2) {
|
|
580
|
+
return { sha: commit.sha, sessionId: null, reason: "ambiguous" };
|
|
581
|
+
}
|
|
582
|
+
const original2 = [...mergedIt].sort((a, b) => a.endedAt - b.endedAt)[0];
|
|
583
|
+
return {
|
|
584
|
+
sha: commit.sha,
|
|
585
|
+
sessionId: original2.sessionId,
|
|
586
|
+
evidence: "merged-the-pr",
|
|
587
|
+
confidence: SHA_CONFIDENCE
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
}
|
|
518
591
|
if (subject.length === 0) {
|
|
519
592
|
return { sha: commit.sha, sessionId: null, reason: "absent" };
|
|
520
593
|
}
|
|
521
|
-
const
|
|
594
|
+
const unsquashed = subject.replace(/\s+\(#\d+\)$/, "");
|
|
595
|
+
const ran = sessions.filter(
|
|
596
|
+
(s) => s.committedSubjects.includes(subject) || unsquashed !== subject && s.committedSubjects.includes(unsquashed)
|
|
597
|
+
);
|
|
522
598
|
if (ran.length === 0) {
|
|
523
599
|
return { sha: commit.sha, sessionId: null, reason: "absent" };
|
|
524
600
|
}
|
|
@@ -572,15 +648,61 @@ function committedSubjects(command) {
|
|
|
572
648
|
if (inline) {
|
|
573
649
|
const subject = inline[2].split("\n")[0].trim();
|
|
574
650
|
if (subject) out.push(subject);
|
|
651
|
+
continue;
|
|
652
|
+
}
|
|
653
|
+
const marginal = /^[^\n]*?-am\s*(['"])([\s\S]*?)\1/.exec(rest) ?? /^[^\n]*?--message[= ]\s*(['"])([\s\S]*?)\1/.exec(rest) ?? /^[^\n]*?-m(['"])([\s\S]*?)\1/.exec(rest);
|
|
654
|
+
if (marginal) {
|
|
655
|
+
const subject = marginal[2].split("\n")[0].trim();
|
|
656
|
+
if (subject) out.push(subject);
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
const fromFile = /^[^\n]*?(?:-F|--file)[= ]\s*(\S+)/.exec(rest);
|
|
660
|
+
if (fromFile) {
|
|
661
|
+
const file = fromFile[1].replace(/^["']|["']$/g, "");
|
|
662
|
+
const escaped = file.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
663
|
+
const wrote = new RegExp(
|
|
664
|
+
`(?:cat\\s*>>?|tee\\s+(?:-a\\s+)?)\\s*["']?${escaped}["']?[^\\n]*<<-?\\s*(['"]?)(\\w+)\\1[^\\n]*\\r?\\n([^\\n]*)`
|
|
665
|
+
).exec(command.slice(0, found.index));
|
|
666
|
+
if (wrote) {
|
|
667
|
+
const subject = wrote[3].trim();
|
|
668
|
+
if (subject) out.push(subject);
|
|
669
|
+
}
|
|
575
670
|
}
|
|
576
671
|
}
|
|
577
672
|
return out;
|
|
578
673
|
}
|
|
579
|
-
|
|
674
|
+
function mergeResponseShas(output) {
|
|
675
|
+
const out = /* @__PURE__ */ new Set();
|
|
676
|
+
const line = /"sha"\s*:\s*"([0-9a-f]{40})"/g;
|
|
677
|
+
for (let m = line.exec(output); m !== null; m = line.exec(output)) {
|
|
678
|
+
out.add(m[1]);
|
|
679
|
+
}
|
|
680
|
+
return [...out];
|
|
681
|
+
}
|
|
682
|
+
function commitShaOutputs(output) {
|
|
683
|
+
const out = /* @__PURE__ */ new Set();
|
|
684
|
+
const line = /\[[\w./+-]+ (?:\(root-commit\) )?([0-9a-f]{7,12})\]/g;
|
|
685
|
+
for (let m = line.exec(output); m !== null; m = line.exec(output)) {
|
|
686
|
+
out.add(m[1]);
|
|
687
|
+
}
|
|
688
|
+
return [...out];
|
|
689
|
+
}
|
|
690
|
+
function mergedPrFromCommand(command) {
|
|
691
|
+
const api = /\/pulls\/(\d+)\/merge\b/.exec(command);
|
|
692
|
+
if (api) return Number(api[1]);
|
|
693
|
+
const gh = /\bgh\s+pr\s+merge\s+(?:.*?\/pull\/(\d+)\b|(\d+)\b)/.exec(command);
|
|
694
|
+
if (gh) return Number(gh[1] ?? gh[2]);
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
function mergeSucceeded(output) {
|
|
698
|
+
return /"merged"\s*:\s*true/.test(output) || /(Squashed and |Rebased and )?[Mm]erged pull request #\d+/.test(output);
|
|
699
|
+
}
|
|
700
|
+
var INFERRED_CONFIDENCE, SHA_CONFIDENCE;
|
|
580
701
|
var init_subject_linking = __esm({
|
|
581
702
|
"../../packages/ingest-core/src/subject-linking.ts"() {
|
|
582
703
|
"use strict";
|
|
583
704
|
INFERRED_CONFIDENCE = 0.9;
|
|
705
|
+
SHA_CONFIDENCE = 0.95;
|
|
584
706
|
}
|
|
585
707
|
});
|
|
586
708
|
|
|
@@ -811,6 +933,7 @@ function parseSessionFile(filePath, repoPath, repoId = deriveRepoId(repoPath), a
|
|
|
811
933
|
let totalRedactions = 0;
|
|
812
934
|
let branch = null;
|
|
813
935
|
let agentId = null;
|
|
936
|
+
const seenIds = /* @__PURE__ */ new Set();
|
|
814
937
|
for (const line of lines) {
|
|
815
938
|
let record;
|
|
816
939
|
try {
|
|
@@ -826,6 +949,8 @@ function parseSessionFile(filePath, repoPath, repoId = deriveRepoId(repoPath), a
|
|
|
826
949
|
const id = record.uuid;
|
|
827
950
|
const ts = record.timestamp;
|
|
828
951
|
if (!id || !ts) continue;
|
|
952
|
+
if (seenIds.has(id)) continue;
|
|
953
|
+
seenIds.add(id);
|
|
829
954
|
sessionId ??= record.sessionId ?? record.session_id ?? null;
|
|
830
955
|
if (typeof record.gitBranch === "string" && record.gitBranch) branch = record.gitBranch;
|
|
831
956
|
if (typeof record.agentId === "string" && record.agentId) agentId ??= record.agentId;
|
|
@@ -879,6 +1004,7 @@ function parseSessionFile(filePath, repoPath, repoId = deriveRepoId(repoPath), a
|
|
|
879
1004
|
const subagent = agentId ? { agentId, agentType: meta?.agentType ?? null, description: meta?.description ?? null } : null;
|
|
880
1005
|
if (agentId && !aiTitle && meta?.description) aiTitle = meta.description;
|
|
881
1006
|
const sortedTs = turns.map((t) => t.ts).sort();
|
|
1007
|
+
const commitEvidence = collectCommitEvidence(lines);
|
|
882
1008
|
const rawContent = capped ? "" : lines.map((line) => {
|
|
883
1009
|
try {
|
|
884
1010
|
return JSON.stringify(redactJsonValue(JSON.parse(line)).value);
|
|
@@ -902,7 +1028,9 @@ function parseSessionFile(filePath, repoPath, repoId = deriveRepoId(repoPath), a
|
|
|
902
1028
|
// filled in by the caller — see getGitUserName in git-history.ts
|
|
903
1029
|
sourceFile: filePath,
|
|
904
1030
|
redactionCount: totalRedactions,
|
|
905
|
-
committedSubjects:
|
|
1031
|
+
committedSubjects: commitEvidence.subjects,
|
|
1032
|
+
committedShas: commitEvidence.shas,
|
|
1033
|
+
mergedPrNumbers: commitEvidence.prNumbers,
|
|
906
1034
|
branch,
|
|
907
1035
|
parentSessionId,
|
|
908
1036
|
subagent,
|
|
@@ -911,10 +1039,14 @@ function parseSessionFile(filePath, repoPath, repoId = deriveRepoId(repoPath), a
|
|
|
911
1039
|
turns
|
|
912
1040
|
};
|
|
913
1041
|
}
|
|
914
|
-
function
|
|
1042
|
+
function collectCommitEvidence(lines) {
|
|
915
1043
|
const subjects = /* @__PURE__ */ new Set();
|
|
1044
|
+
const shas = /* @__PURE__ */ new Set();
|
|
1045
|
+
const prNumbers = /* @__PURE__ */ new Set();
|
|
1046
|
+
const commitCallIds = /* @__PURE__ */ new Set();
|
|
1047
|
+
const mergeCallIds = /* @__PURE__ */ new Map();
|
|
916
1048
|
for (const line of lines) {
|
|
917
|
-
if (!line.includes("git commit")) continue;
|
|
1049
|
+
if (!line.includes("git commit") && !line.includes("/merge") && !line.includes("tool_use_id")) continue;
|
|
918
1050
|
let record;
|
|
919
1051
|
try {
|
|
920
1052
|
record = JSON.parse(line);
|
|
@@ -924,13 +1056,40 @@ function collectCommittedSubjects(lines) {
|
|
|
924
1056
|
const content = record?.message?.content;
|
|
925
1057
|
if (!Array.isArray(content)) continue;
|
|
926
1058
|
for (const block of content) {
|
|
927
|
-
if (block?.type
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1059
|
+
if (block?.type === "tool_use") {
|
|
1060
|
+
const command = block?.input?.command;
|
|
1061
|
+
if (typeof command !== "string") continue;
|
|
1062
|
+
const found = committedSubjects(command);
|
|
1063
|
+
for (const subject of found) subjects.add(subject);
|
|
1064
|
+
if (/\bgit\s+(?:-C\s+\S+\s+)?commit\b/.test(command) && typeof block?.id === "string") {
|
|
1065
|
+
commitCallIds.add(block.id);
|
|
1066
|
+
}
|
|
1067
|
+
if ((/\/pulls\/\d+\/merge\b/.test(command) || /\bgh\s+pr\s+merge\b/.test(command)) && typeof block?.id === "string") {
|
|
1068
|
+
mergeCallIds.set(block.id, mergedPrFromCommand(command));
|
|
1069
|
+
}
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
if (block?.type === "tool_result" && mergeCallIds.has(block?.tool_use_id)) {
|
|
1073
|
+
const raw = block?.content;
|
|
1074
|
+
const text = typeof raw === "string" ? raw : Array.isArray(raw) ? raw.map(
|
|
1075
|
+
(x) => typeof x?.text === "string" ? x.text : ""
|
|
1076
|
+
).join(" ") : "";
|
|
1077
|
+
for (const sha of mergeResponseShas(text)) shas.add(sha);
|
|
1078
|
+
if (mergeSucceeded(text)) {
|
|
1079
|
+
const pr = mergeCallIds.get(block?.tool_use_id);
|
|
1080
|
+
if (typeof pr === "number") prNumbers.add(pr);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
if (block?.type === "tool_result" && commitCallIds.has(block?.tool_use_id)) {
|
|
1084
|
+
const raw = block?.content;
|
|
1085
|
+
const text = typeof raw === "string" ? raw : Array.isArray(raw) ? raw.map(
|
|
1086
|
+
(x) => typeof x?.text === "string" ? x.text : ""
|
|
1087
|
+
).join(" ") : "";
|
|
1088
|
+
for (const sha of commitShaOutputs(text)) shas.add(sha);
|
|
1089
|
+
}
|
|
931
1090
|
}
|
|
932
1091
|
}
|
|
933
|
-
return [...subjects];
|
|
1092
|
+
return { subjects: [...subjects], shas: [...shas], prNumbers: [...prNumbers] };
|
|
934
1093
|
}
|
|
935
1094
|
function parseAllSessions(repoPath, repoId, cursors = {}) {
|
|
936
1095
|
const id = repoId ?? deriveRepoId(repoPath);
|
|
@@ -1896,16 +2055,58 @@ function addUsage(into, next) {
|
|
|
1896
2055
|
model: into.model ?? next.model
|
|
1897
2056
|
};
|
|
1898
2057
|
}
|
|
1899
|
-
function
|
|
2058
|
+
function commitEvidenceFromToolCalls(lines) {
|
|
1900
2059
|
const subjects = /* @__PURE__ */ new Set();
|
|
2060
|
+
const shas = /* @__PURE__ */ new Set();
|
|
2061
|
+
const prNumbers = /* @__PURE__ */ new Set();
|
|
2062
|
+
const commitCallIds = /* @__PURE__ */ new Set();
|
|
2063
|
+
const mergeCallIds = /* @__PURE__ */ new Map();
|
|
1901
2064
|
for (const line of lines) {
|
|
1902
2065
|
if (line.type !== "response_item") continue;
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
2066
|
+
const kind = payloadType(line);
|
|
2067
|
+
if (kind === "custom_tool_call") {
|
|
2068
|
+
const input = asString(line.payload?.input);
|
|
2069
|
+
if (!input) continue;
|
|
2070
|
+
const cmd = execCmdFrom(input) ?? input;
|
|
2071
|
+
const callId = asString(line.payload?.call_id);
|
|
2072
|
+
for (const subject of committedSubjects(cmd)) subjects.add(subject);
|
|
2073
|
+
if (/\bgit\s+(?:-C\s+\S+\s+)?commit\b/.test(cmd) && callId) {
|
|
2074
|
+
commitCallIds.add(callId);
|
|
2075
|
+
}
|
|
2076
|
+
if ((/\/pulls\/\d+\/merge\b/.test(cmd) || /\bgh\s+pr\s+merge\b/.test(cmd)) && callId) {
|
|
2077
|
+
mergeCallIds.set(callId, mergedPrFromCommand(cmd));
|
|
2078
|
+
}
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
if (kind === "custom_tool_call_output") {
|
|
2082
|
+
const callId = asString(line.payload?.call_id);
|
|
2083
|
+
if (!callId) continue;
|
|
2084
|
+
const raw = line.payload?.output;
|
|
2085
|
+
const text = Array.isArray(raw) ? raw.map(
|
|
2086
|
+
(x) => typeof x?.text === "string" ? x.text : ""
|
|
2087
|
+
).join(" ") : asString(raw) ?? "";
|
|
2088
|
+
if (commitCallIds.has(callId)) {
|
|
2089
|
+
for (const sha of commitShaOutputs(text)) shas.add(sha);
|
|
2090
|
+
}
|
|
2091
|
+
if (mergeCallIds.has(callId)) {
|
|
2092
|
+
for (const sha of mergeResponseShas(text)) shas.add(sha);
|
|
2093
|
+
if (mergeSucceeded(text)) {
|
|
2094
|
+
const pr = mergeCallIds.get(callId);
|
|
2095
|
+
if (typeof pr === "number") prNumbers.add(pr);
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
}
|
|
2100
|
+
return { subjects: [...subjects], shas: [...shas], prNumbers: [...prNumbers] };
|
|
2101
|
+
}
|
|
2102
|
+
function execCmdFrom(input) {
|
|
2103
|
+
const m = /cmd\s*:\s*"((?:[^"\\]|\\.)*)"/.exec(input);
|
|
2104
|
+
if (!m) return null;
|
|
2105
|
+
try {
|
|
2106
|
+
return JSON.parse(`"${m[1]}"`);
|
|
2107
|
+
} catch {
|
|
2108
|
+
return null;
|
|
1907
2109
|
}
|
|
1908
|
-
return [...subjects];
|
|
1909
2110
|
}
|
|
1910
2111
|
function filesFromToolCalls(lines) {
|
|
1911
2112
|
const byPath = /* @__PURE__ */ new Map();
|
|
@@ -1964,20 +2165,28 @@ function parseCodexSessionFile(filePath, repoPath, repoId) {
|
|
|
1964
2165
|
const { content: rawContent, count: rawRedactions } = redactRollout(raw);
|
|
1965
2166
|
redactionCount += rawRedactions;
|
|
1966
2167
|
const firstUser = events.find((e) => e.role === "user");
|
|
2168
|
+
const codexCommitEvidence = commitEvidenceFromToolCalls(lines);
|
|
1967
2169
|
return {
|
|
1968
2170
|
id: sessionId,
|
|
1969
2171
|
agentKind: "codex",
|
|
1970
2172
|
branch: null,
|
|
1971
2173
|
parentSessionId: null,
|
|
1972
2174
|
subagent: null,
|
|
1973
|
-
//
|
|
1974
|
-
//
|
|
1975
|
-
|
|
2175
|
+
// The caller's derivation first: it reads the checkout's remote NOW,
|
|
2176
|
+
// which is the identity every other ingest path agrees on. The remote
|
|
2177
|
+
// recorded in session_meta is a historical fact — after the repo's
|
|
2178
|
+
// GitHub transfer, month-old rollouts kept re-minting the dead
|
|
2179
|
+
// jeffcheema id on every re-index, resurrecting a "third evrex-app"
|
|
2180
|
+
// as fast as migrations folded it. Meta only serves a rollout whose
|
|
2181
|
+
// checkout is gone, where a stale identity beats none.
|
|
2182
|
+
repoId: repoId.startsWith("path:") ? repoIdFromMeta(meta) ?? repoId : repoId,
|
|
1976
2183
|
cwd,
|
|
1977
2184
|
startedAt: asString(meta.timestamp) ?? events[0]?.ts ?? null,
|
|
1978
2185
|
endedAt: events[events.length - 1]?.ts ?? null,
|
|
1979
2186
|
turnCount: turns.length,
|
|
1980
|
-
committedSubjects:
|
|
2187
|
+
committedSubjects: codexCommitEvidence.subjects,
|
|
2188
|
+
committedShas: codexCommitEvidence.shas,
|
|
2189
|
+
mergedPrNumbers: codexCommitEvidence.prNumbers,
|
|
1981
2190
|
aiTitle: firstUser ? firstUser.text.slice(0, 120).trim() : null,
|
|
1982
2191
|
author: null,
|
|
1983
2192
|
sourceFile: filePath,
|
|
@@ -2928,6 +3137,7 @@ var src_exports = {};
|
|
|
2928
3137
|
__export(src_exports, {
|
|
2929
3138
|
CONVERSATION_KINDS: () => CONVERSATION_KINDS,
|
|
2930
3139
|
EMPTY_USAGE: () => EMPTY_USAGE,
|
|
3140
|
+
EVREX_ORIGIN_TRAILER_KEY: () => EVREX_ORIGIN_TRAILER_KEY,
|
|
2931
3141
|
EVREX_SESSION_TRAILER_KEY: () => EVREX_SESSION_TRAILER_KEY,
|
|
2932
3142
|
JIRA_FIELDS: () => JIRA_FIELDS,
|
|
2933
3143
|
JIRA_SEARCH_PATH: () => JIRA_SEARCH_PATH,
|
|
@@ -2950,6 +3160,7 @@ __export(src_exports, {
|
|
|
2950
3160
|
changedSessions: () => changedSessions,
|
|
2951
3161
|
codexSessionsDir: () => codexSessionsDir,
|
|
2952
3162
|
collectRepoData: () => collectRepoData,
|
|
3163
|
+
commitShaOutputs: () => commitShaOutputs,
|
|
2953
3164
|
commitsOlderThan: () => commitsOlderThan,
|
|
2954
3165
|
committedSubjects: () => committedSubjects,
|
|
2955
3166
|
copilotSessionsDir: () => copilotSessionsDir,
|
|
@@ -2984,14 +3195,19 @@ __export(src_exports, {
|
|
|
2984
3195
|
loadComposers: () => loadComposers,
|
|
2985
3196
|
matchCommitToSession: () => matchCommitToSession,
|
|
2986
3197
|
measureLineSurvival: () => measureLineSurvival,
|
|
3198
|
+
mergeResponseShas: () => mergeResponseShas,
|
|
3199
|
+
mergeSucceeded: () => mergeSucceeded,
|
|
3200
|
+
mergedPrFromCommand: () => mergedPrFromCommand,
|
|
2987
3201
|
normalizeRepoRemote: () => normalizeRepoRemote,
|
|
2988
3202
|
opencodeDbPath: () => opencodeDbPath,
|
|
3203
|
+
originOf: () => originOf,
|
|
2989
3204
|
parseAllCodexSessions: () => parseAllCodexSessions,
|
|
2990
3205
|
parseAllCopilotSessions: () => parseAllCopilotSessions,
|
|
2991
3206
|
parseAllCursorSessions: () => parseAllCursorSessions,
|
|
2992
3207
|
parseAllOpenCodeSessions: () => parseAllOpenCodeSessions,
|
|
2993
3208
|
parseAllSessions: () => parseAllSessions,
|
|
2994
3209
|
parseCodexSessionFile: () => parseCodexSessionFile,
|
|
3210
|
+
parseCommitsBySha: () => parseCommitsBySha,
|
|
2995
3211
|
parseCopilotSessionDir: () => parseCopilotSessionDir,
|
|
2996
3212
|
parseCopilotWorkspace: () => parseCopilotWorkspace,
|
|
2997
3213
|
parseCursorComposer: () => parseCursorComposer,
|
|
@@ -3158,7 +3374,7 @@ var init_client = __esm({
|
|
|
3158
3374
|
// Evidence-only retrieval (BM25 + embedding), no LLM synthesis call — see
|
|
3159
3375
|
// apps/backend/src/query/query.service.ts#search. Used by evrex_search,
|
|
3160
3376
|
// which wants ranked hits fast, not a synthesized paragraph.
|
|
3161
|
-
search: (repoPath, text, filePaths) => post("/search", { repoPath, text, filePaths }),
|
|
3377
|
+
search: (repoPath, text, filePaths, boost) => post("/search", { repoPath, text, filePaths, boost }),
|
|
3162
3378
|
// Everything that happened in a repo, newest first, bounded by days — the
|
|
3163
3379
|
// same query the desktop Timeline screen makes. Sessions and commits
|
|
3164
3380
|
// interleaved, each with the handle evrex_expand takes.
|
package/dist/continuity.js
CHANGED
|
@@ -51,7 +51,7 @@ var evrexApi = {
|
|
|
51
51
|
// Evidence-only retrieval (BM25 + embedding), no LLM synthesis call — see
|
|
52
52
|
// apps/backend/src/query/query.service.ts#search. Used by evrex_search,
|
|
53
53
|
// which wants ranked hits fast, not a synthesized paragraph.
|
|
54
|
-
search: (repoPath, text, filePaths) => post("/search", { repoPath, text, filePaths }),
|
|
54
|
+
search: (repoPath, text, filePaths, boost) => post("/search", { repoPath, text, filePaths, boost }),
|
|
55
55
|
// Everything that happened in a repo, newest first, bounded by days — the
|
|
56
56
|
// same query the desktop Timeline screen makes. Sessions and commits
|
|
57
57
|
// interleaved, each with the handle evrex_expand takes.
|
package/dist/hook.js
CHANGED
|
@@ -49,7 +49,7 @@ var evrexApi = {
|
|
|
49
49
|
// Evidence-only retrieval (BM25 + embedding), no LLM synthesis call — see
|
|
50
50
|
// apps/backend/src/query/query.service.ts#search. Used by evrex_search,
|
|
51
51
|
// which wants ranked hits fast, not a synthesized paragraph.
|
|
52
|
-
search: (repoPath, text, filePaths) => post("/search", { repoPath, text, filePaths }),
|
|
52
|
+
search: (repoPath, text, filePaths, boost) => post("/search", { repoPath, text, filePaths, boost }),
|
|
53
53
|
// Everything that happened in a repo, newest first, bounded by days — the
|
|
54
54
|
// same query the desktop Timeline screen makes. Sessions and commits
|
|
55
55
|
// interleaved, each with the handle evrex_expand takes.
|
|
@@ -245,7 +245,10 @@ async function main() {
|
|
|
245
245
|
if (!repoId) return;
|
|
246
246
|
const remaining = DEADLINE_MS - (Date.now() - started);
|
|
247
247
|
if (remaining < 500) return;
|
|
248
|
-
const result = await withDeadline(
|
|
248
|
+
const result = await withDeadline(
|
|
249
|
+
evrexApi.search(repoId, prompt, void 0, false),
|
|
250
|
+
remaining
|
|
251
|
+
);
|
|
249
252
|
if (!result?.evidence?.length) return;
|
|
250
253
|
const sessionId = input.session_id ?? "";
|
|
251
254
|
const path = statePath();
|