agent-trace 0.2.9 → 0.2.11
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/agent-trace.cjs +158 -7
- package/package.json +1 -1
package/agent-trace.cjs
CHANGED
|
@@ -24122,6 +24122,10 @@ th{color:var(--text-dim);font-size:10px;text-transform:uppercase;letter-spacing:
|
|
|
24122
24122
|
.outcome-prs{border-top:1px solid var(--panel-border);padding-top:6px;margin-top:4px}
|
|
24123
24123
|
.outcome-pr{display:flex;align-items:center;gap:8px;padding:3px 0;font-size:12px}
|
|
24124
24124
|
.pr-badge{font-size:10px;padding:1px 5px;border-radius:3px;font-weight:600;text-transform:uppercase;border:1px solid rgba(74,222,128,.3);color:var(--green)}
|
|
24125
|
+
.pr-badge.open{border-color:rgba(34,211,238,.3);color:var(--cyan)}
|
|
24126
|
+
.pr-badge.merged{border-color:rgba(192,132,252,.3);color:var(--purple)}
|
|
24127
|
+
.pr-badge.closed{border-color:rgba(248,113,113,.3);color:var(--red)}
|
|
24128
|
+
.pr-badge.draft{border-color:rgba(102,102,102,.3);color:var(--text-muted)}
|
|
24125
24129
|
.pr-label{color:var(--cyan);font-weight:600}
|
|
24126
24130
|
.pr-repo{color:var(--text-muted)}
|
|
24127
24131
|
.pr-link{color:var(--text-dim);text-decoration:none;font-size:11px}
|
|
@@ -24470,7 +24474,7 @@ function renderReplay() {
|
|
|
24470
24474
|
}
|
|
24471
24475
|
if (replay.pullRequests.length > 0) {
|
|
24472
24476
|
h += '<div class="outcome-prs">';
|
|
24473
|
-
replay.pullRequests.forEach(function(pr){ h += '<div class="outcome-pr"><span class="pr-badge">' + esc(pr.state) + '</span><span class="pr-label">PR #' + pr.prNumber + '</span><span class="pr-repo">' + esc(pr.repo) + '</span>' + (pr.url ? '<a class="pr-link" href="' + esc(pr.url) + '" target="_blank" rel="noopener noreferrer">' + esc(pr.url) + '</a>' : '') + '</div>'; });
|
|
24477
|
+
replay.pullRequests.forEach(function(pr){ h += '<div class="outcome-pr"><span class="pr-badge ' + esc(pr.state) + '">' + esc(pr.state) + '</span><span class="pr-label">PR #' + pr.prNumber + '</span><span class="pr-repo">' + esc(pr.repo) + '</span>' + (pr.url ? '<a class="pr-link" href="' + esc(pr.url) + '" target="_blank" rel="noopener noreferrer">' + esc(pr.url) + '</a>' : '') + '</div>'; });
|
|
24474
24478
|
h += '</div>';
|
|
24475
24479
|
}
|
|
24476
24480
|
h += '</div>';
|
|
@@ -25073,6 +25077,18 @@ CREATE TABLE IF NOT EXISTS commits (
|
|
|
25073
25077
|
);
|
|
25074
25078
|
|
|
25075
25079
|
CREATE INDEX IF NOT EXISTS idx_commits_session ON commits(session_id);
|
|
25080
|
+
|
|
25081
|
+
CREATE TABLE IF NOT EXISTS pull_requests (
|
|
25082
|
+
session_id TEXT NOT NULL,
|
|
25083
|
+
repo TEXT NOT NULL,
|
|
25084
|
+
pr_number INTEGER NOT NULL,
|
|
25085
|
+
state TEXT NOT NULL DEFAULT 'open',
|
|
25086
|
+
url TEXT,
|
|
25087
|
+
merged_at TEXT,
|
|
25088
|
+
PRIMARY KEY (session_id, repo, pr_number)
|
|
25089
|
+
);
|
|
25090
|
+
|
|
25091
|
+
CREATE INDEX IF NOT EXISTS idx_pull_requests_session ON pull_requests(session_id);
|
|
25076
25092
|
`;
|
|
25077
25093
|
function toJsonArray(value) {
|
|
25078
25094
|
return JSON.stringify(value);
|
|
@@ -25255,6 +25271,37 @@ var SqliteClient = class {
|
|
|
25255
25271
|
});
|
|
25256
25272
|
transaction(rows);
|
|
25257
25273
|
}
|
|
25274
|
+
async upsertPullRequests(rows) {
|
|
25275
|
+
if (rows.length === 0) return;
|
|
25276
|
+
const upsert = this.db.prepare(`
|
|
25277
|
+
INSERT INTO pull_requests
|
|
25278
|
+
(session_id, repo, pr_number, state, url, merged_at)
|
|
25279
|
+
VALUES (?, ?, ?, ?, ?, ?)
|
|
25280
|
+
ON CONFLICT(session_id, repo, pr_number) DO UPDATE SET
|
|
25281
|
+
state = excluded.state,
|
|
25282
|
+
url = COALESCE(excluded.url, pull_requests.url),
|
|
25283
|
+
merged_at = COALESCE(excluded.merged_at, pull_requests.merged_at)
|
|
25284
|
+
`);
|
|
25285
|
+
const transaction = this.db.transaction((prRows) => {
|
|
25286
|
+
for (const row of prRows) {
|
|
25287
|
+
upsert.run(
|
|
25288
|
+
row.session_id,
|
|
25289
|
+
row.repo,
|
|
25290
|
+
row.pr_number,
|
|
25291
|
+
row.state,
|
|
25292
|
+
row.url,
|
|
25293
|
+
row.merged_at
|
|
25294
|
+
);
|
|
25295
|
+
}
|
|
25296
|
+
});
|
|
25297
|
+
transaction(rows);
|
|
25298
|
+
}
|
|
25299
|
+
listPullRequestsBySessionId(sessionId) {
|
|
25300
|
+
const rows = this.db.prepare(
|
|
25301
|
+
"SELECT session_id, repo, pr_number, state, url, merged_at FROM pull_requests WHERE session_id = ? ORDER BY pr_number ASC"
|
|
25302
|
+
).all(sessionId);
|
|
25303
|
+
return rows;
|
|
25304
|
+
}
|
|
25258
25305
|
listCommitsBySessionId(sessionId) {
|
|
25259
25306
|
const rows = this.db.prepare(
|
|
25260
25307
|
"SELECT sha, session_id, prompt_id, message, lines_added, lines_removed, committed_at FROM commits WHERE session_id = ? ORDER BY committed_at ASC"
|
|
@@ -25972,6 +26019,19 @@ function toPostgresCommitRow(trace, commit) {
|
|
|
25972
26019
|
function toPostgresCommitRows(trace) {
|
|
25973
26020
|
return trace.git.commits.map((commit) => toPostgresCommitRow(trace, commit));
|
|
25974
26021
|
}
|
|
26022
|
+
function toPostgresPullRequestRow(trace, pr) {
|
|
26023
|
+
return {
|
|
26024
|
+
session_id: trace.sessionId,
|
|
26025
|
+
repo: pr.repo,
|
|
26026
|
+
pr_number: pr.prNumber,
|
|
26027
|
+
state: pr.state,
|
|
26028
|
+
url: toNullableString(pr.url),
|
|
26029
|
+
merged_at: toNullableString(pr.mergedAt)
|
|
26030
|
+
};
|
|
26031
|
+
}
|
|
26032
|
+
function toPostgresPullRequestRows(trace) {
|
|
26033
|
+
return trace.git.pullRequests.map((pr) => toPostgresPullRequestRow(trace, pr));
|
|
26034
|
+
}
|
|
25975
26035
|
function dedupeBySessionId(rows) {
|
|
25976
26036
|
const bySession = /* @__PURE__ */ new Map();
|
|
25977
26037
|
rows.forEach((row) => {
|
|
@@ -25986,6 +26046,13 @@ function dedupeBySha(rows) {
|
|
|
25986
26046
|
});
|
|
25987
26047
|
return [...bySha.values()];
|
|
25988
26048
|
}
|
|
26049
|
+
function dedupePullRequests(rows) {
|
|
26050
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
26051
|
+
rows.forEach((row) => {
|
|
26052
|
+
byKey.set(`${row.session_id}:${row.repo}:${String(row.pr_number)}`, row);
|
|
26053
|
+
});
|
|
26054
|
+
return [...byKey.values()];
|
|
26055
|
+
}
|
|
25989
26056
|
var PostgresSessionWriter = class {
|
|
25990
26057
|
client;
|
|
25991
26058
|
constructor(client) {
|
|
@@ -26003,9 +26070,11 @@ var PostgresSessionWriter = class {
|
|
|
26003
26070
|
}
|
|
26004
26071
|
const sessions = dedupeBySessionId(traces.map(toPostgresSessionRow));
|
|
26005
26072
|
const commitRows = dedupeBySha(traces.flatMap((trace) => toPostgresCommitRows(trace)));
|
|
26073
|
+
const prRows = dedupePullRequests(traces.flatMap((trace) => toPostgresPullRequestRows(trace)));
|
|
26006
26074
|
const sessionsPromise = this.client.upsertSessions(sessions);
|
|
26007
26075
|
const commitsPromise = this.client.upsertCommits(commitRows);
|
|
26008
|
-
|
|
26076
|
+
const prsPromise = this.client.upsertPullRequests(prRows);
|
|
26077
|
+
await Promise.all([sessionsPromise, commitsPromise, prsPromise]);
|
|
26009
26078
|
return {
|
|
26010
26079
|
writtenSessions: sessions.length,
|
|
26011
26080
|
writtenCommits: commitRows.length
|
|
@@ -28222,6 +28291,7 @@ var InMemoryRuntimeClickHouseClient = class {
|
|
|
28222
28291
|
var InMemoryRuntimePostgresClient = class {
|
|
28223
28292
|
sessionsById = /* @__PURE__ */ new Map();
|
|
28224
28293
|
commitsBySha = /* @__PURE__ */ new Map();
|
|
28294
|
+
pullRequestsByKey = /* @__PURE__ */ new Map();
|
|
28225
28295
|
async upsertSessions(rows) {
|
|
28226
28296
|
rows.forEach((row) => {
|
|
28227
28297
|
this.sessionsById.set(row.session_id, row);
|
|
@@ -28232,12 +28302,20 @@ var InMemoryRuntimePostgresClient = class {
|
|
|
28232
28302
|
this.commitsBySha.set(row.sha, row);
|
|
28233
28303
|
});
|
|
28234
28304
|
}
|
|
28305
|
+
async upsertPullRequests(rows) {
|
|
28306
|
+
rows.forEach((row) => {
|
|
28307
|
+
this.pullRequestsByKey.set(`${row.session_id}:${row.repo}:${String(row.pr_number)}`, row);
|
|
28308
|
+
});
|
|
28309
|
+
}
|
|
28235
28310
|
listSessions() {
|
|
28236
28311
|
return [...this.sessionsById.values()];
|
|
28237
28312
|
}
|
|
28238
28313
|
listCommits() {
|
|
28239
28314
|
return [...this.commitsBySha.values()];
|
|
28240
28315
|
}
|
|
28316
|
+
listPullRequests() {
|
|
28317
|
+
return [...this.pullRequestsByKey.values()];
|
|
28318
|
+
}
|
|
28241
28319
|
};
|
|
28242
28320
|
var InMemoryRuntimeSessionTraceClient = class {
|
|
28243
28321
|
rows = [];
|
|
@@ -28545,15 +28623,35 @@ function toUpdatedTrace(existing, envelope) {
|
|
|
28545
28623
|
const prUrl = readString4(payload, ["pr_url", "prUrl"]);
|
|
28546
28624
|
const prRepo = readString4(payload, ["pr_repo", "prRepo"]);
|
|
28547
28625
|
const prNumberRaw = readNumber3(payload, ["pr_number", "prNumber"]);
|
|
28626
|
+
const prState = readString4(payload, ["pr_state", "prState"]) ?? "open";
|
|
28627
|
+
const prMergedAt = readString4(payload, ["pr_merged_at", "prMergedAt"]);
|
|
28548
28628
|
if (prUrl !== void 0 && prRepo !== void 0 && prNumberRaw !== void 0) {
|
|
28549
|
-
const
|
|
28550
|
-
if (
|
|
28629
|
+
const trackedIndex = existingPullRequests.findIndex((pr) => pr.prNumber === prNumberRaw && pr.repo === prRepo);
|
|
28630
|
+
if (trackedIndex === -1) {
|
|
28551
28631
|
existingPullRequests.push({
|
|
28552
28632
|
repo: prRepo,
|
|
28553
28633
|
prNumber: prNumberRaw,
|
|
28554
|
-
state:
|
|
28555
|
-
url: prUrl
|
|
28634
|
+
state: prState,
|
|
28635
|
+
url: prUrl,
|
|
28636
|
+
...prState === "merged" && prMergedAt !== void 0 ? { mergedAt: prMergedAt } : {}
|
|
28556
28637
|
});
|
|
28638
|
+
} else if (prState !== "open") {
|
|
28639
|
+
const tracked = existingPullRequests[trackedIndex];
|
|
28640
|
+
existingPullRequests[trackedIndex] = {
|
|
28641
|
+
...tracked,
|
|
28642
|
+
state: prState,
|
|
28643
|
+
...prState === "merged" && prMergedAt !== void 0 ? { mergedAt: prMergedAt } : {}
|
|
28644
|
+
};
|
|
28645
|
+
}
|
|
28646
|
+
} else if (prState !== "open" && prRepo !== void 0 && prNumberRaw !== void 0) {
|
|
28647
|
+
const trackedIndex = existingPullRequests.findIndex((pr) => pr.prNumber === prNumberRaw && pr.repo === prRepo);
|
|
28648
|
+
if (trackedIndex !== -1) {
|
|
28649
|
+
const tracked = existingPullRequests[trackedIndex];
|
|
28650
|
+
existingPullRequests[trackedIndex] = {
|
|
28651
|
+
...tracked,
|
|
28652
|
+
state: prState,
|
|
28653
|
+
...prState === "merged" && prMergedAt !== void 0 ? { mergedAt: prMergedAt } : {}
|
|
28654
|
+
};
|
|
28557
28655
|
}
|
|
28558
28656
|
}
|
|
28559
28657
|
return {
|
|
@@ -28827,10 +28925,21 @@ function hydrateFromSqlite(runtime, sqlite, limit, eventLimit) {
|
|
|
28827
28925
|
const extra = commits.filter((c) => !pgShas.has(c.sha));
|
|
28828
28926
|
commits = [...mapped, ...extra];
|
|
28829
28927
|
}
|
|
28928
|
+
let pullRequests = trace.git.pullRequests;
|
|
28929
|
+
const pgPrs = sqlite.listPullRequestsBySessionId(row.session_id);
|
|
28930
|
+
if (pgPrs.length > 0) {
|
|
28931
|
+
pullRequests = pgPrs.map((pr) => ({
|
|
28932
|
+
repo: pr.repo,
|
|
28933
|
+
prNumber: pr.pr_number,
|
|
28934
|
+
state: pr.state,
|
|
28935
|
+
...pr.url !== null ? { url: pr.url } : {},
|
|
28936
|
+
...pr.merged_at !== null ? { mergedAt: pr.merged_at } : {}
|
|
28937
|
+
}));
|
|
28938
|
+
}
|
|
28830
28939
|
let hydratedTrace = {
|
|
28831
28940
|
...trace,
|
|
28832
28941
|
timeline,
|
|
28833
|
-
git: { ...trace.git, commits }
|
|
28942
|
+
git: { ...trace.git, commits, pullRequests }
|
|
28834
28943
|
};
|
|
28835
28944
|
if (hydratedTrace.metrics.totalCostUsd === 0 && (hydratedTrace.metrics.totalInputTokens > 0 || hydratedTrace.metrics.totalOutputTokens > 0) && hydratedTrace.metrics.modelsUsed.length > 0) {
|
|
28836
28945
|
const model = String(hydratedTrace.metrics.modelsUsed[0]);
|
|
@@ -29426,6 +29535,40 @@ function extractPrUrl(payload) {
|
|
|
29426
29535
|
if (prUrlMatch !== null && prUrlMatch[0] !== void 0) return prUrlMatch[0];
|
|
29427
29536
|
return void 0;
|
|
29428
29537
|
}
|
|
29538
|
+
function extractPrState(payload) {
|
|
29539
|
+
const record = payload;
|
|
29540
|
+
const explicit = readString5(record, "pr_state") ?? readString5(record, "prState");
|
|
29541
|
+
if (explicit !== void 0) return explicit;
|
|
29542
|
+
const output = readString5(record, "tool_response") ?? readString5(record, "toolResponse") ?? readString5(record, "stdout") ?? readString5(record, "output");
|
|
29543
|
+
const command = pickCommand2(payload);
|
|
29544
|
+
const combined = [command, output].filter((s) => s !== void 0).join("\n");
|
|
29545
|
+
if (combined.length === 0) return void 0;
|
|
29546
|
+
const isGhPr = /\bgh\s+pr\b/.test(combined);
|
|
29547
|
+
if (!isGhPr) return void 0;
|
|
29548
|
+
const jsonState = combined.match(/"state"\s*:\s*"(MERGED|CLOSED|OPEN|DRAFT)"/i);
|
|
29549
|
+
if (jsonState?.[1] !== void 0) {
|
|
29550
|
+
const s = jsonState[1].toLowerCase();
|
|
29551
|
+
return s === "open" ? "open" : s;
|
|
29552
|
+
}
|
|
29553
|
+
const isDraft = /"isDraft"\s*:\s*true/i.test(combined);
|
|
29554
|
+
if (isDraft) return "draft";
|
|
29555
|
+
if (/\bgh\s+pr\s+merge\b/.test(combined) && output !== void 0 && !/error|failed|not merged/i.test(output)) {
|
|
29556
|
+
return "merged";
|
|
29557
|
+
}
|
|
29558
|
+
if (/\bMerged\b/.test(combined)) return "merged";
|
|
29559
|
+
if (/\bClosed\b/.test(combined)) return "closed";
|
|
29560
|
+
if (/\bDraft\b/.test(combined)) return "draft";
|
|
29561
|
+
return void 0;
|
|
29562
|
+
}
|
|
29563
|
+
function extractPrMergedAt(payload) {
|
|
29564
|
+
const record = payload;
|
|
29565
|
+
const explicit = readString5(record, "pr_merged_at") ?? readString5(record, "prMergedAt");
|
|
29566
|
+
if (explicit !== void 0) return explicit;
|
|
29567
|
+
const output = readString5(record, "tool_response") ?? readString5(record, "toolResponse") ?? readString5(record, "stdout") ?? readString5(record, "output");
|
|
29568
|
+
if (output === void 0) return void 0;
|
|
29569
|
+
const match = output.match(/"mergedAt"\s*:\s*"([^"]+)"/);
|
|
29570
|
+
return match?.[1] ?? void 0;
|
|
29571
|
+
}
|
|
29429
29572
|
function parsePrFromUrl(url) {
|
|
29430
29573
|
const match = url.match(/https:\/\/github\.com\/([^/]+\/[^/]+)\/pull\/(\d+)/);
|
|
29431
29574
|
if (match === null || match[1] === void 0 || match[2] === void 0) return void 0;
|
|
@@ -29831,6 +29974,14 @@ function enrichHookPayloadWithGitContext(payload, provider, baselineStore, now)
|
|
|
29831
29974
|
}
|
|
29832
29975
|
}
|
|
29833
29976
|
}
|
|
29977
|
+
const prState = extractPrState(payload);
|
|
29978
|
+
if (prState !== void 0) {
|
|
29979
|
+
patch["pr_state"] = prState;
|
|
29980
|
+
}
|
|
29981
|
+
const prMergedAt = extractPrMergedAt(payload);
|
|
29982
|
+
if (prMergedAt !== void 0) {
|
|
29983
|
+
patch["pr_merged_at"] = prMergedAt;
|
|
29984
|
+
}
|
|
29834
29985
|
if (Object.keys(patch).length === 0) {
|
|
29835
29986
|
return {
|
|
29836
29987
|
payload,
|