@titan-design/session-graph 0.9.1 → 0.11.0
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 +122 -7
- package/dist/index.d.ts +83 -8
- package/dist/index.js +411 -23
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -254,6 +254,45 @@ function applyEpisodeTranscriptSchema(db) {
|
|
|
254
254
|
addColumnIfMissing(db, EPISODE_TABLE, "end_transcript_id", "INTEGER");
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
// src/audit-schema-v7.ts
|
|
258
|
+
var ORIGIN_TASK_LINK_MIGRATION_NAME = "origin task link";
|
|
259
|
+
function applyOriginTaskLinkSchema(db) {
|
|
260
|
+
addColumnIfMissing(db, "session_origin", "task_ids", "TEXT");
|
|
261
|
+
addColumnIfMissing(db, "session_origin", "task_source", "TEXT");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// src/audit-schema-v8.ts
|
|
265
|
+
var REVIEW_VERDICT_MIGRATION_NAME = "review verdicts";
|
|
266
|
+
var REVIEW_TABLE = "pr_review";
|
|
267
|
+
var REVIEW_DDL = `
|
|
268
|
+
CREATE TABLE IF NOT EXISTS ${REVIEW_TABLE} (
|
|
269
|
+
source_key TEXT PRIMARY KEY,
|
|
270
|
+
surface TEXT NOT NULL,
|
|
271
|
+
verdict TEXT NOT NULL,
|
|
272
|
+
ts TEXT NOT NULL,
|
|
273
|
+
session_id TEXT,
|
|
274
|
+
transcript_id INTEGER,
|
|
275
|
+
repo TEXT,
|
|
276
|
+
repo_hint TEXT,
|
|
277
|
+
cwd_repo TEXT,
|
|
278
|
+
number INTEGER NOT NULL,
|
|
279
|
+
pr_ref TEXT
|
|
280
|
+
);
|
|
281
|
+
CREATE INDEX IF NOT EXISTS idx_${REVIEW_TABLE}_pr_ref ON ${REVIEW_TABLE}(pr_ref);
|
|
282
|
+
CREATE INDEX IF NOT EXISTS idx_${REVIEW_TABLE}_transcript ON ${REVIEW_TABLE}(transcript_id);
|
|
283
|
+
`;
|
|
284
|
+
var PR_COLUMNS = [
|
|
285
|
+
["review_rounds_gh", "INTEGER"],
|
|
286
|
+
["review_rounds_chat", "INTEGER"],
|
|
287
|
+
["commit_times", "TEXT"]
|
|
288
|
+
];
|
|
289
|
+
function applyReviewVerdictSchema(db) {
|
|
290
|
+
db.exec(REVIEW_DDL);
|
|
291
|
+
for (const [column, type] of PR_COLUMNS) addColumnIfMissing(db, "pr", column, type);
|
|
292
|
+
db.exec("UPDATE pr SET review_rounds_gh = review_rounds");
|
|
293
|
+
db.exec("UPDATE pr SET outcome_checked_at = NULL");
|
|
294
|
+
}
|
|
295
|
+
|
|
257
296
|
// src/normalized-schema.ts
|
|
258
297
|
import { conversationRef } from "@titan-design/agent-protocol";
|
|
259
298
|
var NORMALIZED_DDL = `
|
|
@@ -451,6 +490,7 @@ var DERIVED_TABLES = [
|
|
|
451
490
|
"pr",
|
|
452
491
|
"pr_merge_observation",
|
|
453
492
|
"pr_create_observation",
|
|
493
|
+
REVIEW_TABLE,
|
|
454
494
|
"branch",
|
|
455
495
|
"file",
|
|
456
496
|
"task",
|
|
@@ -465,7 +505,9 @@ var MIGRATIONS = [
|
|
|
465
505
|
} },
|
|
466
506
|
{ version: 4, name: AUDIT_MIGRATION_NAME, up: applyAuditSchema },
|
|
467
507
|
{ version: 5, name: ORIGIN_MIGRATION_NAME, up: applyOriginSchema },
|
|
468
|
-
{ version: 6, name: EPISODE_TRANSCRIPT_MIGRATION_NAME, up: applyEpisodeTranscriptSchema }
|
|
508
|
+
{ version: 6, name: EPISODE_TRANSCRIPT_MIGRATION_NAME, up: applyEpisodeTranscriptSchema },
|
|
509
|
+
{ version: 7, name: ORIGIN_TASK_LINK_MIGRATION_NAME, up: applyOriginTaskLinkSchema },
|
|
510
|
+
{ version: 8, name: REVIEW_VERDICT_MIGRATION_NAME, up: applyReviewVerdictSchema }
|
|
469
511
|
];
|
|
470
512
|
|
|
471
513
|
// src/graph.ts
|
|
@@ -625,8 +667,91 @@ function advanceFacet(db, transcriptId, fromByteOffset, indexedTo, version = EXT
|
|
|
625
667
|
db.prepare(`UPDATE ${FACET_TABLE} SET indexed_to = ? WHERE transcript_id = ? AND facet = ? AND version = ?`).run(indexedTo, transcriptId, AUDIT_FACET, version);
|
|
626
668
|
}
|
|
627
669
|
|
|
670
|
+
// src/injected-text.ts
|
|
671
|
+
import { hasMarker, INJECTED_MARKERS } from "@titan-design/session-read";
|
|
672
|
+
var INJECTED_CAUSES = /* @__PURE__ */ new Set([
|
|
673
|
+
"compaction",
|
|
674
|
+
"channel_message",
|
|
675
|
+
"channel_system",
|
|
676
|
+
"task_notification",
|
|
677
|
+
"scheduled_wakeup",
|
|
678
|
+
"usage_limit_resume",
|
|
679
|
+
"harness_resume",
|
|
680
|
+
"local_command",
|
|
681
|
+
"image_meta",
|
|
682
|
+
"hook_or_reminder"
|
|
683
|
+
]);
|
|
684
|
+
function isInjectedCause(cause) {
|
|
685
|
+
return INJECTED_CAUSES.has(cause);
|
|
686
|
+
}
|
|
687
|
+
function isUntypedPrompt(inbound, indexSdkPrompts = false) {
|
|
688
|
+
return isInjectedCause(inbound.cause) || !indexSdkPrompts && inbound.promptSource === "sdk";
|
|
689
|
+
}
|
|
690
|
+
var CUT_TAGS = [
|
|
691
|
+
...Object.values(INJECTED_MARKERS).flatMap((m) => m.close ? [m.close.slice(2, -1)] : []),
|
|
692
|
+
"user-prompt-submit-hook",
|
|
693
|
+
"command-name",
|
|
694
|
+
"command-message",
|
|
695
|
+
"local-command-caveat",
|
|
696
|
+
"local-command-stdout",
|
|
697
|
+
"local-command-stderr",
|
|
698
|
+
"bash-stdout",
|
|
699
|
+
"bash-stderr"
|
|
700
|
+
].join("|");
|
|
701
|
+
var CUT_OPEN = new RegExp(`<(${CUT_TAGS})(?=[\\s>])[^>]*>`, "g");
|
|
702
|
+
var LINE_END_OR_BLOCK = new RegExp(`^[ \\t]*(?:\\r?\\n|$|<(?:${CUT_TAGS})(?=[\\s>]))`);
|
|
703
|
+
function opensBlock(text, at, keptFrom) {
|
|
704
|
+
const lead = text.slice(Math.max(text.lastIndexOf("\n", at - 1) + 1, keptFrom), at);
|
|
705
|
+
return lead.trim() === "";
|
|
706
|
+
}
|
|
707
|
+
function balancedClose(text, tag, from) {
|
|
708
|
+
const tags = new RegExp(`<(/?)${tag}(?=[\\s>])[^>]*>`, "g");
|
|
709
|
+
tags.lastIndex = from;
|
|
710
|
+
let depth = 1;
|
|
711
|
+
for (let m = tags.exec(text); m; m = tags.exec(text)) {
|
|
712
|
+
depth += m[1] ? -1 : 1;
|
|
713
|
+
if (depth === 0) return tags.lastIndex;
|
|
714
|
+
}
|
|
715
|
+
return null;
|
|
716
|
+
}
|
|
717
|
+
function closesLine(text, at) {
|
|
718
|
+
return LINE_END_OR_BLOCK.test(text.slice(at));
|
|
719
|
+
}
|
|
720
|
+
function cutBlocks(text) {
|
|
721
|
+
let kept = "";
|
|
722
|
+
let keptFrom = 0;
|
|
723
|
+
CUT_OPEN.lastIndex = 0;
|
|
724
|
+
for (let open = CUT_OPEN.exec(text); open; open = CUT_OPEN.exec(text)) {
|
|
725
|
+
if (!opensBlock(text, open.index, keptFrom)) continue;
|
|
726
|
+
const end = balancedClose(text, open[1], CUT_OPEN.lastIndex);
|
|
727
|
+
if (end === null || !closesLine(text, end)) continue;
|
|
728
|
+
kept += text.slice(keptFrom, open.index);
|
|
729
|
+
keptFrom = end;
|
|
730
|
+
CUT_OPEN.lastIndex = end;
|
|
731
|
+
}
|
|
732
|
+
return kept + text.slice(keptFrom);
|
|
733
|
+
}
|
|
734
|
+
var INTERRUPTION = /\[Request interrupted by user[^\]\n]*\]/g;
|
|
735
|
+
var HUMAN_WRAPPERS = ["command-args", "bash-input"].map((tag) => new RegExp(`</?${tag}>`, "g"));
|
|
736
|
+
var WHOLE_TURN_MARKERS = Object.keys(INJECTED_MARKERS).filter((name) => INJECTED_MARKERS[name].close === null);
|
|
737
|
+
var SPAWN_BRIEF_FRAMING = [
|
|
738
|
+
/^# Predecessor: .*\n\nYou are taking over from /,
|
|
739
|
+
/Commit your work there; nothing outside it is yours to change\.\s*$/,
|
|
740
|
+
/You may be sharing it with other agents, so stay inside the paths you were given\.\s*$/
|
|
741
|
+
];
|
|
742
|
+
function isWholeTurnInjected(text) {
|
|
743
|
+
const trimmed = text.trim();
|
|
744
|
+
return WHOLE_TURN_MARKERS.some((name) => hasMarker(trimmed, name)) || SPAWN_BRIEF_FRAMING.some((re) => re.test(trimmed));
|
|
745
|
+
}
|
|
746
|
+
function stripInjected(text) {
|
|
747
|
+
let kept = cutBlocks(text);
|
|
748
|
+
for (const re of [INTERRUPTION, ...HUMAN_WRAPPERS]) kept = kept.replace(re, "");
|
|
749
|
+
if (isWholeTurnInjected(kept)) return "";
|
|
750
|
+
return kept.replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
751
|
+
}
|
|
752
|
+
|
|
628
753
|
// src/apply.ts
|
|
629
|
-
import { sessionRef } from "@titan-design/session-read";
|
|
754
|
+
import { RELATIONS, sessionRef } from "@titan-design/session-read";
|
|
630
755
|
function applyDelta(graph, transcriptId, delta, source = {}) {
|
|
631
756
|
graph.db.transaction(() => {
|
|
632
757
|
applyFacts(graph.db, transcriptId, delta);
|
|
@@ -635,6 +760,8 @@ function applyDelta(graph, transcriptId, delta, source = {}) {
|
|
|
635
760
|
applyAssets(graph.db, delta);
|
|
636
761
|
applyPhases(graph.db, transcriptId, delta);
|
|
637
762
|
applyLinkedRows(graph, transcriptId, delta);
|
|
763
|
+
applySpans(graph, transcriptId, delta, source.indexSdkPrompts ?? false);
|
|
764
|
+
applyReviewVerdicts(graph.db, transcriptId, delta);
|
|
638
765
|
applyAudit(graph.db, transcriptId, delta);
|
|
639
766
|
})();
|
|
640
767
|
}
|
|
@@ -702,6 +829,43 @@ function pick(row, sql) {
|
|
|
702
829
|
const names = new Set([...sql.matchAll(/@(\w+)/g)].map((m) => m[1]));
|
|
703
830
|
return Object.fromEntries([...names].map((name) => [name, row[name] ?? null]));
|
|
704
831
|
}
|
|
832
|
+
var UPSERT_CHAT_VERDICT = `
|
|
833
|
+
INSERT INTO ${REVIEW_TABLE} (source_key, surface, verdict, ts, session_id, transcript_id, repo, repo_hint, cwd_repo, number)
|
|
834
|
+
VALUES (@sourceKey, 'chat', @verdict, @ts, @sessionId, @transcriptId, @repo, @repoHint, @cwdRepo, @number)
|
|
835
|
+
ON CONFLICT (source_key) DO UPDATE SET verdict = excluded.verdict, ts = excluded.ts, session_id = excluded.session_id,
|
|
836
|
+
transcript_id = excluded.transcript_id, repo = excluded.repo, repo_hint = excluded.repo_hint,
|
|
837
|
+
cwd_repo = excluded.cwd_repo, number = excluded.number`;
|
|
838
|
+
function applyReviewVerdicts(db, transcriptId, delta) {
|
|
839
|
+
const upsert = db.prepare(UPSERT_CHAT_VERDICT);
|
|
840
|
+
const keys = chatVerdictKeys(delta.reviewVerdicts);
|
|
841
|
+
delta.reviewVerdicts.forEach((v, i) => {
|
|
842
|
+
upsert.run({
|
|
843
|
+
sourceKey: keys[i],
|
|
844
|
+
verdict: v.verdict,
|
|
845
|
+
ts: v.ts,
|
|
846
|
+
sessionId: v.sessionId,
|
|
847
|
+
transcriptId,
|
|
848
|
+
repo: v.repo,
|
|
849
|
+
repoHint: v.repoHint,
|
|
850
|
+
cwdRepo: v.cwdRepo,
|
|
851
|
+
number: v.number
|
|
852
|
+
});
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
var hasOrdinal = (v) => Number.isInteger(v.ordinal) && v.ordinal >= 0;
|
|
856
|
+
function chatVerdictKeys(verdicts) {
|
|
857
|
+
const used = /* @__PURE__ */ new Map();
|
|
858
|
+
const usedBy = (id) => used.get(id) ?? used.set(id, /* @__PURE__ */ new Set()).get(id);
|
|
859
|
+
for (const v of verdicts) if (hasOrdinal(v)) usedBy(v.toolUseId).add(v.ordinal);
|
|
860
|
+
return verdicts.map((v) => {
|
|
861
|
+
if (hasOrdinal(v)) return `chat:${v.toolUseId}:${v.ordinal}`;
|
|
862
|
+
const taken = usedBy(v.toolUseId);
|
|
863
|
+
let n = 0;
|
|
864
|
+
while (taken.has(n)) n += 1;
|
|
865
|
+
taken.add(n);
|
|
866
|
+
return `chat:${v.toolUseId}:${n}`;
|
|
867
|
+
});
|
|
868
|
+
}
|
|
705
869
|
function applyPhases(db, transcriptId, delta) {
|
|
706
870
|
const current = db.prepare("SELECT phase_id, to_mode FROM permission_phase WHERE session_id = ? AND trigger = ? AND t_invalid IS NULL ORDER BY phase_id DESC LIMIT 1");
|
|
707
871
|
const close = db.prepare("UPDATE permission_phase SET t_invalid = ? WHERE phase_id = ?");
|
|
@@ -734,11 +898,22 @@ function applyLinkedRows(graph, transcriptId, delta) {
|
|
|
734
898
|
for (const s of delta.subagents) upsertSubagent.run(s.agentRef, s.sessionId, s.agentType, s.label, s.ts || null, factId(s.byteOffset));
|
|
735
899
|
const linkTranscript = db.prepare("INSERT INTO subagent (agent_ref, child_session_id) VALUES (?, ?) ON CONFLICT (agent_ref) DO UPDATE SET child_session_id = excluded.child_session_id");
|
|
736
900
|
for (const l of delta.subagentTranscripts) linkTranscript.run(l.agentRef, l.childSessionId);
|
|
737
|
-
for (const e of delta.edges) graph
|
|
901
|
+
for (const e of delta.edges) assertTranscriptEdge(graph, { sourceRef: e.sourceRef, relation: e.relation, targetRef: e.targetRef, tValid: e.ts, factId: factId(e.byteOffset) });
|
|
902
|
+
}
|
|
903
|
+
function applySpans(graph, transcriptId, delta, indexSdkPrompts) {
|
|
904
|
+
const injectedLines = new Set(delta.inbound.filter((i) => isUntypedPrompt(i, indexSdkPrompts)).map((i) => i.byteOffset));
|
|
738
905
|
for (const span of delta.spans) {
|
|
739
|
-
|
|
906
|
+
const isPrompt = span.field === "prompt";
|
|
907
|
+
const text = !isPrompt ? span.text : injectedLines.has(span.byteOffset) ? "" : stripInjected(span.text);
|
|
908
|
+
if (isPrompt && !text) continue;
|
|
909
|
+
graph.spans.index({ ownerRef: sessionRef(span.sessionId), field: span.field, sourceId: transcriptId, byteOffset: span.byteOffset, byteLength: span.byteLength }, text);
|
|
740
910
|
}
|
|
741
911
|
}
|
|
912
|
+
function assertTranscriptEdge(graph, edge) {
|
|
913
|
+
const existing = edge.relation === RELATIONS.RAN ? graph.edges.current(edge.sourceRef, edge.relation, edge.targetRef) : void 0;
|
|
914
|
+
if (existing?.attrs?.via === "origin") graph.edges.supersede(edge);
|
|
915
|
+
else graph.edges.assert(edge);
|
|
916
|
+
}
|
|
742
917
|
function factIdFor(db, transcriptId, byteOffset) {
|
|
743
918
|
const row = db.prepare("SELECT fact_id FROM fact WHERE transcript_id = ? AND byte_offset = ?").get(transcriptId, byteOffset);
|
|
744
919
|
return row?.fact_id ?? null;
|
|
@@ -755,7 +930,7 @@ function purgeTranscript(graph, transcriptId) {
|
|
|
755
930
|
graph.db.prepare(`DELETE FROM "${table}" WHERE session_id IN (SELECT session_id FROM session WHERE transcript_id = ?)`).run(transcriptId);
|
|
756
931
|
}
|
|
757
932
|
graph.db.prepare(`DELETE FROM "${KIT.edge}" WHERE fact_id IN (SELECT fact_id FROM fact WHERE transcript_id = ?)`).run(transcriptId);
|
|
758
|
-
for (const table of [...AUDIT_TABLES, FACET_TABLE]) {
|
|
933
|
+
for (const table of [...AUDIT_TABLES, FACET_TABLE, REVIEW_TABLE]) {
|
|
759
934
|
graph.db.prepare(`DELETE FROM "${table}" WHERE transcript_id = ?`).run(transcriptId);
|
|
760
935
|
}
|
|
761
936
|
graph.db.prepare("DELETE FROM fact WHERE transcript_id = ?").run(transcriptId);
|
|
@@ -921,18 +1096,126 @@ import { promises as fs } from "fs";
|
|
|
921
1096
|
import { contentHash, resumePoint } from "@titan-design/locator";
|
|
922
1097
|
import { TranscriptParseError as TranscriptParseError2, extractTranscript as extractTranscript2 } from "@titan-design/session-read";
|
|
923
1098
|
|
|
1099
|
+
// src/review-rounds.ts
|
|
1100
|
+
import { RELATIONS as RELATIONS2 } from "@titan-design/session-read";
|
|
1101
|
+
var isReviewerProfile = (profile) => profile === "reviewer" || profile.endsWith("-reviewer");
|
|
1102
|
+
function countRounds(changesRequestedAt, commitTimes) {
|
|
1103
|
+
const commits = parseCommitTimes(commitTimes);
|
|
1104
|
+
return commits ? answeredHeads(changesRequestedAt, commits).heads.size : null;
|
|
1105
|
+
}
|
|
1106
|
+
function parseCommitTimes(commitTimes) {
|
|
1107
|
+
const commits = commitTimes.map(Date.parse);
|
|
1108
|
+
return commits.some(Number.isNaN) ? null : commits;
|
|
1109
|
+
}
|
|
1110
|
+
function answeredHeads(reviewTimes, commits) {
|
|
1111
|
+
const heads = /* @__PURE__ */ new Set();
|
|
1112
|
+
let ignored = 0;
|
|
1113
|
+
for (const ts of reviewTimes) {
|
|
1114
|
+
const at = Date.parse(ts);
|
|
1115
|
+
if (Number.isNaN(at)) {
|
|
1116
|
+
ignored += 1;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1119
|
+
const head = commits.filter((c) => c <= at).length;
|
|
1120
|
+
if (head < commits.length) heads.add(head);
|
|
1121
|
+
}
|
|
1122
|
+
return { heads, ignored };
|
|
1123
|
+
}
|
|
1124
|
+
function projectReviewRounds(graph, options = {}) {
|
|
1125
|
+
const isReviewer = options.isReviewerProfile ?? isReviewerProfile;
|
|
1126
|
+
return graph.db.transaction(() => {
|
|
1127
|
+
const resolved = resolveChatVerdicts(graph.db);
|
|
1128
|
+
const invalidTimes = writeRounds(graph.db, isReviewer);
|
|
1129
|
+
const { n } = graph.db.prepare(`SELECT count(*) AS n FROM ${REVIEW_TABLE} WHERE surface = 'chat' AND pr_ref IS NULL`).get();
|
|
1130
|
+
return { resolved, unresolved: n, invalidTimes };
|
|
1131
|
+
})();
|
|
1132
|
+
}
|
|
1133
|
+
function resolveChatVerdicts(db) {
|
|
1134
|
+
const rows = db.prepare(`SELECT source_key, session_id, repo, repo_hint, cwd_repo, number FROM ${REVIEW_TABLE} WHERE surface = 'chat' AND pr_ref IS NULL`).all();
|
|
1135
|
+
const update = db.prepare(`UPDATE ${REVIEW_TABLE} SET pr_ref = ? WHERE source_key = ?`);
|
|
1136
|
+
let resolved = 0;
|
|
1137
|
+
for (const row of rows) {
|
|
1138
|
+
const prRef = resolvePrRef(db, row);
|
|
1139
|
+
if (prRef) resolved += update.run(prRef, row.source_key).changes;
|
|
1140
|
+
}
|
|
1141
|
+
return resolved;
|
|
1142
|
+
}
|
|
1143
|
+
function resolvePrRef(db, row) {
|
|
1144
|
+
const candidates = db.prepare("SELECT pr_ref, repo FROM pr WHERE number = ? AND repo IS NOT NULL").all(row.number);
|
|
1145
|
+
if (row.repo) return candidates.find((c) => c.repo === row.repo)?.pr_ref ?? null;
|
|
1146
|
+
const hinted = candidates.filter((c) => sameRepoName(c.repo, row.repo_hint));
|
|
1147
|
+
if (hinted.length === 1) return hinted[0].pr_ref;
|
|
1148
|
+
const pool = hinted.length > 0 ? hinted : candidates;
|
|
1149
|
+
const linked = familyLinks(db, row.session_id);
|
|
1150
|
+
const bySibling = pool.filter((c) => linked.has(c.pr_ref));
|
|
1151
|
+
if (bySibling.length === 1) return bySibling[0].pr_ref;
|
|
1152
|
+
const byCwd = (bySibling.length > 0 ? bySibling : pool).filter((c) => sameRepoName(c.repo, row.cwd_repo));
|
|
1153
|
+
return byCwd.length === 1 ? byCwd[0].pr_ref : null;
|
|
1154
|
+
}
|
|
1155
|
+
function sameRepoName(repo, name) {
|
|
1156
|
+
return name !== null && repo.split("/").pop().toLowerCase() === name.toLowerCase();
|
|
1157
|
+
}
|
|
1158
|
+
var FAMILY_LINKS = `
|
|
1159
|
+
WITH parent(id) AS (
|
|
1160
|
+
SELECT parent_session_id FROM session_origin WHERE session_id = @sessionId AND parent_session_id IS NOT NULL
|
|
1161
|
+
UNION SELECT session_id FROM subagent WHERE child_session_id = @sessionId AND session_id IS NOT NULL
|
|
1162
|
+
), family(id) AS (
|
|
1163
|
+
SELECT @sessionId UNION SELECT id FROM parent
|
|
1164
|
+
UNION SELECT session_id FROM session_origin WHERE parent_session_id IN (SELECT id FROM parent)
|
|
1165
|
+
UNION SELECT child_session_id FROM subagent WHERE session_id IN (SELECT id FROM parent) AND child_session_id IS NOT NULL
|
|
1166
|
+
UNION SELECT substr(target_ref, 9) FROM "${KIT.edge}" WHERE relation = '${RELATIONS2.SPAWNED}' AND t_expired IS NULL
|
|
1167
|
+
AND target_ref LIKE 'session:%' AND source_ref IN (SELECT 'session:' || id FROM parent)
|
|
1168
|
+
)
|
|
1169
|
+
SELECT DISTINCT target_ref FROM "${KIT.edge}" WHERE relation = '${RELATIONS2.LINKED}' AND t_expired IS NULL
|
|
1170
|
+
AND source_ref IN (SELECT 'session:' || id FROM family)`;
|
|
1171
|
+
function familyLinks(db, sessionId) {
|
|
1172
|
+
if (!sessionId) return /* @__PURE__ */ new Set();
|
|
1173
|
+
const rows = db.prepare(FAMILY_LINKS).all({ sessionId });
|
|
1174
|
+
return new Set(rows.map((r) => r.target_ref));
|
|
1175
|
+
}
|
|
1176
|
+
var CHANGES_REQUESTED = `
|
|
1177
|
+
SELECT r.pr_ref, r.surface, r.ts, o.profile FROM ${REVIEW_TABLE} r LEFT JOIN session_origin o ON o.session_id = r.session_id
|
|
1178
|
+
WHERE r.verdict = 'changes_requested' AND r.pr_ref IS NOT NULL`;
|
|
1179
|
+
function writeRounds(db, isReviewer) {
|
|
1180
|
+
const byPr = /* @__PURE__ */ new Map();
|
|
1181
|
+
for (const review of db.prepare(CHANGES_REQUESTED).all()) {
|
|
1182
|
+
if (review.surface === "chat" && !(review.profile && isReviewer(review.profile))) continue;
|
|
1183
|
+
byPr.set(review.pr_ref, [...byPr.get(review.pr_ref) ?? [], review]);
|
|
1184
|
+
}
|
|
1185
|
+
const update = db.prepare("UPDATE pr SET review_rounds_chat = ?, review_rounds = ? WHERE pr_ref = ?");
|
|
1186
|
+
let invalidTimes = 0;
|
|
1187
|
+
for (const pr of db.prepare("SELECT pr_ref, commit_times, review_rounds_gh FROM pr").all()) {
|
|
1188
|
+
const { chat, total, invalid } = roundsFor(pr, byPr.get(pr.pr_ref) ?? []);
|
|
1189
|
+
update.run(chat, total, pr.pr_ref);
|
|
1190
|
+
invalidTimes += invalid;
|
|
1191
|
+
}
|
|
1192
|
+
return invalidTimes;
|
|
1193
|
+
}
|
|
1194
|
+
function roundsFor(pr, reviews) {
|
|
1195
|
+
const commits = pr.commit_times === null ? null : parseCommitTimes(JSON.parse(pr.commit_times));
|
|
1196
|
+
if (!commits) return { chat: null, total: pr.review_rounds_gh, invalid: pr.commit_times === null ? 0 : 1 };
|
|
1197
|
+
const timesOn = (surface) => reviews.filter((r) => r.surface === surface).map((r) => r.ts);
|
|
1198
|
+
const forge = answeredHeads(timesOn("github"), commits);
|
|
1199
|
+
const chat = answeredHeads(timesOn("chat"), commits);
|
|
1200
|
+
const chatOnly = [...chat.heads].filter((head) => !forge.heads.has(head)).length;
|
|
1201
|
+
return { chat: chat.heads.size, total: (pr.review_rounds_gh ?? 0) + chatOnly, invalid: forge.ignored + chat.ignored };
|
|
1202
|
+
}
|
|
1203
|
+
|
|
924
1204
|
// src/outcomes.ts
|
|
925
1205
|
var NO_PR_OUTCOMES = Object.freeze({ requested: 0, applied: 0, failed: false });
|
|
926
1206
|
var PRS_NEEDING_OUTCOME = `
|
|
927
1207
|
SELECT pr_ref, repo, number FROM pr
|
|
928
|
-
WHERE repo IS NOT NULL AND number IS NOT NULL
|
|
929
|
-
|
|
1208
|
+
WHERE repo IS NOT NULL AND number IS NOT NULL
|
|
1209
|
+
AND (outcome_checked_at IS NULL OR state IS NOT 'merged' OR commit_times IS NULL)
|
|
1210
|
+
ORDER BY CASE WHEN outcome_checked_at IS NULL THEN 0 WHEN state IS NOT 'merged' THEN 1 ELSE 2 END, pr_ref`;
|
|
930
1211
|
var APPLY_OUTCOME = `
|
|
931
1212
|
UPDATE pr SET
|
|
932
1213
|
state = CASE WHEN state = 'merged' THEN state ELSE COALESCE(lower(@state), state) END,
|
|
933
1214
|
merged_at = COALESCE(@mergedAt, merged_at),
|
|
934
1215
|
closed_at = COALESCE(@closedAt, closed_at),
|
|
935
1216
|
review_rounds = COALESCE(@reviewRounds, review_rounds),
|
|
1217
|
+
review_rounds_gh = COALESCE(@reviewRounds, review_rounds_gh),
|
|
1218
|
+
commit_times = COALESCE(@commitTimes, commit_times),
|
|
936
1219
|
outcome_checked_at = @checkedAt
|
|
937
1220
|
WHERE pr_ref = @prRef`;
|
|
938
1221
|
function prsNeedingOutcome(graph) {
|
|
@@ -952,6 +1235,7 @@ async function enrichPrs(graph, resolver) {
|
|
|
952
1235
|
}
|
|
953
1236
|
function write(graph, resolved, checkedAt) {
|
|
954
1237
|
const apply = graph.db.prepare(APPLY_OUTCOME);
|
|
1238
|
+
const stored = graph.db.prepare("SELECT commit_times FROM pr WHERE pr_ref = ?");
|
|
955
1239
|
return graph.db.transaction(() => {
|
|
956
1240
|
let applied = 0;
|
|
957
1241
|
for (const [prRef, fields] of resolved) {
|
|
@@ -962,27 +1246,79 @@ function write(graph, resolved, checkedAt) {
|
|
|
962
1246
|
state: fields.state ?? null,
|
|
963
1247
|
mergedAt: fields.mergedAt ?? null,
|
|
964
1248
|
closedAt: fields.closedAt ?? null,
|
|
965
|
-
reviewRounds: fields
|
|
1249
|
+
reviewRounds: forgeRounds(fields, () => storedCommitTimes(stored.get(prRef))),
|
|
1250
|
+
commitTimes: fields.commitTimes ? JSON.stringify(fields.commitTimes) : null
|
|
966
1251
|
}).changes;
|
|
1252
|
+
if (fields.reviews) replaceForgeReviews(graph, prRef, fields.reviews);
|
|
967
1253
|
}
|
|
968
1254
|
return applied;
|
|
969
1255
|
})();
|
|
970
1256
|
}
|
|
1257
|
+
var isChangesRequested = (review) => review.state.toUpperCase() === "CHANGES_REQUESTED";
|
|
1258
|
+
function forgeRounds(fields, storedTimes) {
|
|
1259
|
+
if (!fields.reviews) return fields.reviewRounds ?? null;
|
|
1260
|
+
const commitTimes = fields.commitTimes ?? storedTimes();
|
|
1261
|
+
const counted = commitTimes ? countRounds(fields.reviews.filter(isChangesRequested).map((r) => r.submittedAt), commitTimes) : null;
|
|
1262
|
+
return counted ?? fields.reviewRounds ?? null;
|
|
1263
|
+
}
|
|
1264
|
+
function storedCommitTimes(row) {
|
|
1265
|
+
const times = row?.commit_times;
|
|
1266
|
+
return times ? JSON.parse(times) : null;
|
|
1267
|
+
}
|
|
1268
|
+
var FORGE_VERDICTS = { APPROVED: "approve", CHANGES_REQUESTED: "changes_requested" };
|
|
1269
|
+
var INSERT_FORGE_REVIEW = `
|
|
1270
|
+
INSERT INTO ${REVIEW_TABLE} (source_key, surface, verdict, ts, repo, number, pr_ref)
|
|
1271
|
+
SELECT 'gh:' || pr_ref || ':' || @ts, 'github', @verdict, @ts, repo, number, pr_ref FROM pr WHERE pr_ref = @prRef
|
|
1272
|
+
ON CONFLICT (source_key) DO UPDATE SET verdict = CASE WHEN verdict = 'changes_requested' THEN verdict ELSE excluded.verdict END`;
|
|
1273
|
+
function replaceForgeReviews(graph, prRef, reviews) {
|
|
1274
|
+
graph.db.prepare(`DELETE FROM ${REVIEW_TABLE} WHERE surface = 'github' AND pr_ref = ?`).run(prRef);
|
|
1275
|
+
const insert = graph.db.prepare(INSERT_FORGE_REVIEW);
|
|
1276
|
+
for (const review of reviews) {
|
|
1277
|
+
const verdict = FORGE_VERDICTS[review.state.toUpperCase()];
|
|
1278
|
+
if (verdict) insert.run({ prRef, verdict, ts: review.submittedAt });
|
|
1279
|
+
}
|
|
1280
|
+
}
|
|
971
1281
|
|
|
972
1282
|
// src/origin.ts
|
|
973
|
-
import { RELATIONS, sessionRef as sessionRef3 } from "@titan-design/session-read";
|
|
1283
|
+
import { RELATIONS as RELATIONS3, sessionRef as sessionRef3, taskRef } from "@titan-design/session-read";
|
|
1284
|
+
var NO_TASK_LINK = "none";
|
|
974
1285
|
var NO_ORIGINS = Object.freeze({ requested: 0, applied: 0, events: 0, failed: false });
|
|
975
1286
|
var SESSIONS_NEEDING_ORIGIN = `
|
|
976
1287
|
SELECT s.session_id FROM session s LEFT JOIN session_origin o ON o.session_id = s.session_id
|
|
977
|
-
WHERE o.session_id IS NULL OR o.resolved_at < s.ended_at
|
|
1288
|
+
WHERE o.session_id IS NULL OR o.resolved_at < s.ended_at OR o.task_source IS NULL
|
|
978
1289
|
ORDER BY s.session_id`;
|
|
1290
|
+
var ORIGIN_COLUMNS = [
|
|
1291
|
+
"origin_system",
|
|
1292
|
+
"agent_id",
|
|
1293
|
+
"agent_name",
|
|
1294
|
+
"parent_name",
|
|
1295
|
+
"parent_session_id",
|
|
1296
|
+
"profile",
|
|
1297
|
+
"model_alias",
|
|
1298
|
+
"surface",
|
|
1299
|
+
"isolation",
|
|
1300
|
+
"depth",
|
|
1301
|
+
"origin_kind",
|
|
1302
|
+
"config_dir",
|
|
1303
|
+
"spawn_cwd",
|
|
1304
|
+
"spawned_at",
|
|
1305
|
+
"brief_chars",
|
|
1306
|
+
"brief_excerpt",
|
|
1307
|
+
"brief_path",
|
|
1308
|
+
"launch_args",
|
|
1309
|
+
"resolved_at"
|
|
1310
|
+
];
|
|
1311
|
+
var TASK_LINK_UPDATE = `task_source = COALESCE(excluded.task_source, task_source),
|
|
1312
|
+
task_ids = CASE WHEN excluded.task_source IS NOT NULL THEN excluded.task_ids ELSE task_ids END`;
|
|
979
1313
|
var UPSERT_ORIGIN = `
|
|
980
|
-
INSERT
|
|
1314
|
+
INSERT INTO session_origin (session_id, origin_system, agent_id, agent_name, parent_name, parent_session_id,
|
|
981
1315
|
profile, model_alias, surface, isolation, depth, origin_kind, config_dir, spawn_cwd, spawned_at,
|
|
982
|
-
brief_chars, brief_excerpt, brief_path, launch_args, resolved_at)
|
|
1316
|
+
brief_chars, brief_excerpt, brief_path, launch_args, task_ids, task_source, resolved_at)
|
|
983
1317
|
VALUES (@sessionId, @originSystem, @agentId, @agentName, @parentName, @parentSessionId,
|
|
984
1318
|
@profile, @modelAlias, @surface, @isolation, @depth, @originKind, @configDir, @spawnCwd, @spawnedAt,
|
|
985
|
-
@briefChars, @briefExcerpt, @briefPath, @launchArgs, @resolvedAt)
|
|
1319
|
+
@briefChars, @briefExcerpt, @briefPath, @launchArgs, @taskIds, @taskSource, @resolvedAt)
|
|
1320
|
+
ON CONFLICT (session_id) DO UPDATE SET ${ORIGIN_COLUMNS.map((c) => `${c} = excluded.${c}`).join(", ")},
|
|
1321
|
+
${TASK_LINK_UPDATE}`;
|
|
986
1322
|
var UPSERT_EVENT = `
|
|
987
1323
|
INSERT INTO session_external_event (session_id, ts, origin_system, kind, detail)
|
|
988
1324
|
VALUES (@sessionId, @ts, COALESCE(@originSystem, (SELECT origin_system FROM session_origin WHERE session_id = @sessionId), 'unknown'), @kind, @detail)
|
|
@@ -999,14 +1335,16 @@ function sessionsNeedingOrigin(graph) {
|
|
|
999
1335
|
async function resolveOrigins(graph, resolver) {
|
|
1000
1336
|
if (!resolver) return NO_ORIGINS;
|
|
1001
1337
|
const sessionIds = sessionsNeedingOrigin(graph);
|
|
1338
|
+
let resolved = [];
|
|
1002
1339
|
try {
|
|
1003
1340
|
const resolution = await resolver(sessionIds);
|
|
1004
1341
|
const written = writeResolution(graph, resolution, (/* @__PURE__ */ new Date()).toISOString());
|
|
1342
|
+
resolved = Object.keys(resolution.origins);
|
|
1005
1343
|
return { requested: sessionIds.length, ...written, failed: false };
|
|
1006
1344
|
} catch (err) {
|
|
1007
1345
|
return { requested: sessionIds.length, applied: 0, events: 0, failed: true, error: err instanceof Error ? err.message : String(err) };
|
|
1008
1346
|
} finally {
|
|
1009
|
-
projectOrigins(graph);
|
|
1347
|
+
projectOrigins(graph, resolved);
|
|
1010
1348
|
}
|
|
1011
1349
|
}
|
|
1012
1350
|
function writeResolution(graph, resolution, resolvedAt) {
|
|
@@ -1045,21 +1383,54 @@ function originRow(sessionId, o, resolvedAt) {
|
|
|
1045
1383
|
briefChars: o.briefChars ?? null,
|
|
1046
1384
|
briefExcerpt: o.briefExcerpt ?? null,
|
|
1047
1385
|
briefPath: o.briefPath ?? null,
|
|
1048
|
-
launchArgs: o.launchArgs ?? null
|
|
1386
|
+
launchArgs: o.launchArgs ?? null,
|
|
1387
|
+
...taskLinkColumns(o)
|
|
1049
1388
|
};
|
|
1050
1389
|
}
|
|
1051
|
-
function
|
|
1390
|
+
function taskLinkColumns(o) {
|
|
1391
|
+
if (o.taskIds === void 0) return { taskIds: null, taskSource: null };
|
|
1392
|
+
if (!o.taskIds?.length) return { taskIds: null, taskSource: NO_TASK_LINK };
|
|
1393
|
+
return { taskIds: JSON.stringify(o.taskIds), taskSource: o.taskSource ?? NO_TASK_LINK };
|
|
1394
|
+
}
|
|
1395
|
+
function projectOrigins(graph, resolved) {
|
|
1052
1396
|
const spawned = graph.db.prepare("SELECT session_id, parent_session_id, spawned_at, resolved_at FROM session_origin WHERE parent_session_id IS NOT NULL").all();
|
|
1053
1397
|
graph.db.transaction(() => {
|
|
1054
1398
|
graph.db.prepare(PROJECT_SUBAGENTS).run();
|
|
1055
1399
|
for (const o of spawned) {
|
|
1056
|
-
graph.edges.assert({ sourceRef: sessionRef3(o.parent_session_id), relation:
|
|
1400
|
+
graph.edges.assert({ sourceRef: sessionRef3(o.parent_session_id), relation: RELATIONS3.SPAWNED, targetRef: sessionRef3(o.session_id), tValid: o.spawned_at ?? o.resolved_at });
|
|
1057
1401
|
}
|
|
1402
|
+
projectTaskLinks(graph, resolved);
|
|
1058
1403
|
})();
|
|
1059
1404
|
}
|
|
1405
|
+
var LINK_CONFIDENCE = { name: 1, "name-over-brief": 1, "brief-anchor": 0.9, "brief-paragraph": 0.6 };
|
|
1406
|
+
var LOWEST_CONFIDENCE = 0.6;
|
|
1407
|
+
function projectTaskLinks(graph, resolved) {
|
|
1408
|
+
const insertTask = graph.db.prepare("INSERT OR IGNORE INTO task (task_ref, task_id) VALUES (?, ?)");
|
|
1409
|
+
const select = graph.db.prepare("SELECT session_id, task_ids, task_source, spawned_at, resolved_at FROM session_origin WHERE session_id = ?");
|
|
1410
|
+
for (const sessionId of resolved) {
|
|
1411
|
+
const o = select.get(sessionId);
|
|
1412
|
+
if (!o) continue;
|
|
1413
|
+
const taskIds = o.task_ids ? JSON.parse(o.task_ids) : [];
|
|
1414
|
+
const source = sessionRef3(o.session_id);
|
|
1415
|
+
const attrs = { via: "origin", source: o.task_source };
|
|
1416
|
+
const confidence = LINK_CONFIDENCE[o.task_source ?? ""] ?? LOWEST_CONFIDENCE;
|
|
1417
|
+
for (const taskId of taskIds) {
|
|
1418
|
+
insertTask.run(taskRef(taskId), taskId);
|
|
1419
|
+
graph.edges.assert({ sourceRef: source, relation: RELATIONS3.RAN, targetRef: taskRef(taskId), tValid: o.spawned_at ?? o.resolved_at, attrs, confidence });
|
|
1420
|
+
}
|
|
1421
|
+
expireStaleTaskLinks(graph, source, new Set(taskIds.map(taskRef)));
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
function expireStaleTaskLinks(graph, source, linked) {
|
|
1425
|
+
for (const edge of graph.edges.from(source)) {
|
|
1426
|
+
if (edge.relation === RELATIONS3.RAN && edge.attrs?.via === "origin" && !linked.has(edge.targetRef)) {
|
|
1427
|
+
graph.edges.expire(source, RELATIONS3.RAN, edge.targetRef);
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1060
1431
|
|
|
1061
1432
|
// src/tasks.ts
|
|
1062
|
-
import { taskRef } from "@titan-design/session-read";
|
|
1433
|
+
import { taskRef as taskRef2 } from "@titan-design/session-read";
|
|
1063
1434
|
var NO_ENRICHMENT = Object.freeze({ requested: 0, applied: 0, failed: false });
|
|
1064
1435
|
var UPSERT_TASK = `
|
|
1065
1436
|
INSERT INTO task (task_ref, task_id, initiative, title, status, estimate) VALUES (@taskRef, @taskId, @initiative, @title, @status, @estimate)
|
|
@@ -1088,7 +1459,7 @@ function write2(graph, resolved) {
|
|
|
1088
1459
|
for (const [taskId, fields] of resolved) {
|
|
1089
1460
|
if (!fields) continue;
|
|
1090
1461
|
applied += upsert.run({
|
|
1091
|
-
taskRef:
|
|
1462
|
+
taskRef: taskRef2(taskId),
|
|
1092
1463
|
taskId,
|
|
1093
1464
|
initiative: fields.initiative ?? null,
|
|
1094
1465
|
title: fields.title ?? null,
|
|
@@ -1122,7 +1493,7 @@ async function indexTranscript(graph, transcript, options = {}) {
|
|
|
1122
1493
|
});
|
|
1123
1494
|
const rewound = point.state === "rewritten" || delta.restartedFromZero;
|
|
1124
1495
|
if (rewound) purgeTranscript(graph, row.sourceId);
|
|
1125
|
-
applyDelta(graph, row.sourceId, delta, { account: transcript.account });
|
|
1496
|
+
applyDelta(graph, row.sourceId, delta, { account: transcript.account, indexSdkPrompts: options.indexSdkPrompts });
|
|
1126
1497
|
advanceFacet(graph.db, row.sourceId, rewound ? 0 : point.start, delta.lastByteOffset);
|
|
1127
1498
|
const stat2 = await fs.stat(transcript.absolutePath);
|
|
1128
1499
|
graph.transcripts.advance(transcript.displayPath, {
|
|
@@ -1143,7 +1514,7 @@ async function indexTranscript(graph, transcript, options = {}) {
|
|
|
1143
1514
|
}
|
|
1144
1515
|
}
|
|
1145
1516
|
async function refreshCorpus(graph, transcripts, options = {}) {
|
|
1146
|
-
const { resolveTasks, resolveOrigins: originResolver, resolvePrs, full, facetLimit, ...perTranscript } = options;
|
|
1517
|
+
const { resolveTasks, resolveOrigins: originResolver, resolvePrs, isReviewerProfile: isReviewerProfile2, full, facetLimit, ...perTranscript } = options;
|
|
1147
1518
|
const counts = { indexed: 0, unchanged: 0, rewound: 0, missing: 0, quarantined: 0 };
|
|
1148
1519
|
const touched = [];
|
|
1149
1520
|
let facts = 0;
|
|
@@ -1159,10 +1530,11 @@ async function refreshCorpus(graph, transcripts, options = {}) {
|
|
|
1159
1530
|
const origins = await resolveOrigins(graph, originResolver);
|
|
1160
1531
|
const reconciled = reconcile(graph);
|
|
1161
1532
|
const prs = await enrichPrs(graph, resolvePrs);
|
|
1533
|
+
const reviews = projectReviewRounds(graph, { isReviewerProfile: isReviewerProfile2 });
|
|
1162
1534
|
const tasks = await enrichTasks(graph, resolveTasks, allTaskIds(graph));
|
|
1163
1535
|
const markedMissing = await markMissing(graph, transcripts);
|
|
1164
1536
|
const facetSummary = { facetsBackfilled: facets.backfilled, facetBacklog: facets.backlog };
|
|
1165
|
-
return { transcripts: transcripts.length, ...counts, facts, turnsRolledUp, reconciled, tasks, origins, prs, markedMissing, ...facetSummary };
|
|
1537
|
+
return { transcripts: transcripts.length, ...counts, facts, turnsRolledUp, reconciled, tasks, origins, prs, reviews, markedMissing, ...facetSummary };
|
|
1166
1538
|
}
|
|
1167
1539
|
async function markMissing(graph, discovered) {
|
|
1168
1540
|
const present = new Set(discovered.map((t) => t.displayPath));
|
|
@@ -1274,7 +1646,8 @@ function insertObservation(graph, transcriptId, o) {
|
|
|
1274
1646
|
);
|
|
1275
1647
|
}
|
|
1276
1648
|
function observationText(o) {
|
|
1277
|
-
if (o.kind === "message") return o.content.map((p) => ({ field:
|
|
1649
|
+
if (o.kind === "message" && o.role === "user") return o.content.map((p) => ({ field: "prompt", text: stripInjected(p.text).slice(0, SPAN_TEXT_CAP), locator: p.locator })).filter((p) => p.text);
|
|
1650
|
+
if (o.kind === "message") return o.content.map((p) => ({ field: "assistant_response", text: p.text.slice(0, SPAN_TEXT_CAP), locator: p.locator }));
|
|
1278
1651
|
if (o.kind === "tool_call" && o.inputLocator) return [{ field: "tool_input", text: normalizedSearchText(o.input), locator: o.inputLocator }];
|
|
1279
1652
|
if (o.kind === "tool_result" && o.outputLocator) return [{ field: "tool_result", text: normalizedSearchText(o.output), locator: o.outputLocator }];
|
|
1280
1653
|
return [];
|
|
@@ -1377,6 +1750,10 @@ function* stagedRows(graph, table) {
|
|
|
1377
1750
|
import { contentHash as contentHash3, prefixHash } from "@titan-design/locator";
|
|
1378
1751
|
import { readSessionSourceText, readSessionText, SessionUsageAccumulator } from "@titan-design/session-read";
|
|
1379
1752
|
async function readIndexedText(graph, span) {
|
|
1753
|
+
const text = await readSourceText(graph, span);
|
|
1754
|
+
return text !== null && span.field === "prompt" ? stripInjected(text) : text;
|
|
1755
|
+
}
|
|
1756
|
+
async function readSourceText(graph, span) {
|
|
1380
1757
|
const row = graph.db.prepare(`SELECT n.locators FROM normalized_span n JOIN search_span s USING(span_id)
|
|
1381
1758
|
WHERE s.source_id = ? AND s.byte_offset = ? AND s.field = ?`).get(span.sourceId, span.byteOffset, span.field);
|
|
1382
1759
|
if (row) {
|
|
@@ -1465,22 +1842,32 @@ export {
|
|
|
1465
1842
|
NO_ENRICHMENT,
|
|
1466
1843
|
NO_ORIGINS,
|
|
1467
1844
|
NO_PR_OUTCOMES,
|
|
1845
|
+
NO_TASK_LINK,
|
|
1468
1846
|
ORIGIN_DDL,
|
|
1469
1847
|
ORIGIN_MIGRATION_NAME,
|
|
1470
1848
|
ORIGIN_TABLES,
|
|
1849
|
+
ORIGIN_TASK_LINK_MIGRATION_NAME,
|
|
1471
1850
|
ORIGIN_VIEWS,
|
|
1851
|
+
REVIEW_DDL,
|
|
1852
|
+
REVIEW_TABLE,
|
|
1853
|
+
REVIEW_VERDICT_MIGRATION_NAME,
|
|
1472
1854
|
allSessionIds,
|
|
1473
1855
|
allTaskIds,
|
|
1474
1856
|
applyAudit,
|
|
1475
1857
|
applyDelta,
|
|
1476
1858
|
backfillFacets,
|
|
1859
|
+
countRounds,
|
|
1477
1860
|
enrichPrs,
|
|
1478
1861
|
enrichTasks,
|
|
1479
1862
|
indexCodexSource,
|
|
1480
1863
|
indexTranscript,
|
|
1864
|
+
isInjectedCause,
|
|
1865
|
+
isReviewerProfile,
|
|
1866
|
+
isUntypedPrompt,
|
|
1481
1867
|
normalizedSessions,
|
|
1482
1868
|
normalizedUsage,
|
|
1483
1869
|
openSessionGraph,
|
|
1870
|
+
projectReviewRounds,
|
|
1484
1871
|
prsNeedingOutcome,
|
|
1485
1872
|
purgeTranscript,
|
|
1486
1873
|
readIndexedText,
|
|
@@ -1492,6 +1879,7 @@ export {
|
|
|
1492
1879
|
resolveOrigins,
|
|
1493
1880
|
rollupSessions,
|
|
1494
1881
|
sessionsNeedingOrigin,
|
|
1882
|
+
stripInjected,
|
|
1495
1883
|
syncPrices
|
|
1496
1884
|
};
|
|
1497
1885
|
//# sourceMappingURL=index.js.map
|