patchrelay 0.36.7 → 0.36.9

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.
@@ -0,0 +1,125 @@
1
+ import { extractLatestAssistantSummary } from "../issue-session-events.js";
2
+ import { isoNow } from "./shared.js";
3
+ export class RunStore {
4
+ connection;
5
+ mapRunRow;
6
+ issues;
7
+ syncIssueSessionFromIssue;
8
+ constructor(connection, mapRunRow, issues, syncIssueSessionFromIssue) {
9
+ this.connection = connection;
10
+ this.mapRunRow = mapRunRow;
11
+ this.issues = issues;
12
+ this.syncIssueSessionFromIssue = syncIssueSessionFromIssue;
13
+ }
14
+ createRun(params) {
15
+ const now = isoNow();
16
+ const result = this.connection.prepare(`
17
+ INSERT INTO runs (issue_id, project_id, linear_issue_id, run_type, status, source_head_sha, prompt_text, started_at)
18
+ VALUES (?, ?, ?, ?, 'queued', ?, ?, ?)
19
+ `).run(params.issueId, params.projectId, params.linearIssueId, params.runType, params.sourceHeadSha ?? null, params.promptText ?? null, now);
20
+ const run = this.getRunById(Number(result.lastInsertRowid));
21
+ const issue = this.issues.getIssue(params.projectId, params.linearIssueId);
22
+ if (issue) {
23
+ this.syncIssueSessionFromIssue(issue, { lastRunType: run.runType });
24
+ }
25
+ return run;
26
+ }
27
+ getRunById(id) {
28
+ const row = this.connection.prepare("SELECT * FROM runs WHERE id = ?").get(id);
29
+ return row ? this.mapRunRow(row) : undefined;
30
+ }
31
+ getRunByThreadId(threadId) {
32
+ const row = this.connection.prepare("SELECT * FROM runs WHERE thread_id = ?").get(threadId);
33
+ return row ? this.mapRunRow(row) : undefined;
34
+ }
35
+ listRunsForIssue(projectId, linearIssueId) {
36
+ const rows = this.connection
37
+ .prepare("SELECT * FROM runs WHERE project_id = ? AND linear_issue_id = ? ORDER BY id")
38
+ .all(projectId, linearIssueId);
39
+ return rows.map(this.mapRunRow);
40
+ }
41
+ getLatestRunForIssue(projectId, linearIssueId) {
42
+ const row = this.connection
43
+ .prepare("SELECT * FROM runs WHERE project_id = ? AND linear_issue_id = ? ORDER BY id DESC LIMIT 1")
44
+ .get(projectId, linearIssueId);
45
+ return row ? this.mapRunRow(row) : undefined;
46
+ }
47
+ listActiveRuns() {
48
+ const rows = this.connection
49
+ .prepare("SELECT * FROM runs WHERE status IN ('queued', 'running')")
50
+ .all();
51
+ return rows.map(this.mapRunRow);
52
+ }
53
+ listRunningRuns() {
54
+ const rows = this.connection
55
+ .prepare("SELECT * FROM runs WHERE status IN ('running', 'queued')")
56
+ .all();
57
+ return rows.map(this.mapRunRow);
58
+ }
59
+ updateRunThread(runId, params) {
60
+ this.connection.prepare(`
61
+ UPDATE runs SET
62
+ thread_id = ?,
63
+ parent_thread_id = COALESCE(?, parent_thread_id),
64
+ turn_id = COALESCE(?, turn_id),
65
+ status = 'running'
66
+ WHERE id = ?
67
+ AND ended_at IS NULL
68
+ AND status IN ('queued', 'running')
69
+ `).run(params.threadId, params.parentThreadId ?? null, params.turnId ?? null, runId);
70
+ const run = this.getRunById(runId);
71
+ if (!run)
72
+ return;
73
+ const issue = this.issues.getIssue(run.projectId, run.linearIssueId);
74
+ if (issue) {
75
+ this.syncIssueSessionFromIssue(issue);
76
+ }
77
+ }
78
+ updateRunTurnId(runId, turnId) {
79
+ this.connection.prepare("UPDATE runs SET turn_id = ? WHERE id = ?").run(turnId, runId);
80
+ }
81
+ finishRun(runId, params) {
82
+ const now = isoNow();
83
+ this.connection.prepare(`
84
+ UPDATE runs SET
85
+ status = ?,
86
+ thread_id = COALESCE(?, thread_id),
87
+ turn_id = COALESCE(?, turn_id),
88
+ failure_reason = COALESCE(?, failure_reason),
89
+ summary_json = COALESCE(?, summary_json),
90
+ report_json = COALESCE(?, report_json),
91
+ ended_at = ?
92
+ WHERE id = ?
93
+ `).run(params.status, params.threadId ?? null, params.turnId ?? null, params.failureReason ?? null, params.summaryJson ?? null, params.reportJson ?? null, now, runId);
94
+ const run = this.getRunById(runId);
95
+ if (!run)
96
+ return;
97
+ const issue = this.issues.getIssue(run.projectId, run.linearIssueId);
98
+ if (issue) {
99
+ this.syncIssueSessionFromIssue(issue, {
100
+ summaryText: extractLatestAssistantSummary(this.getRunById(runId) ?? run),
101
+ lastRunType: run.runType,
102
+ });
103
+ }
104
+ }
105
+ saveThreadEvent(params) {
106
+ this.connection.prepare(`
107
+ INSERT INTO run_thread_events (run_id, thread_id, turn_id, method, event_json, created_at)
108
+ VALUES (?, ?, ?, ?, ?, ?)
109
+ `).run(params.runId, params.threadId, params.turnId ?? null, params.method, params.eventJson, isoNow());
110
+ }
111
+ listThreadEvents(runId) {
112
+ const rows = this.connection
113
+ .prepare("SELECT * FROM run_thread_events WHERE run_id = ? ORDER BY id")
114
+ .all(runId);
115
+ return rows.map((row) => ({
116
+ id: Number(row.id),
117
+ runId: Number(row.run_id),
118
+ threadId: String(row.thread_id),
119
+ ...(row.turn_id !== null ? { turnId: String(row.turn_id) } : {}),
120
+ method: String(row.method),
121
+ eventJson: String(row.event_json),
122
+ createdAt: String(row.created_at),
123
+ }));
124
+ }
125
+ }
@@ -0,0 +1,71 @@
1
+ export class WebhookEventStore {
2
+ connection;
3
+ constructor(connection) {
4
+ this.connection = connection;
5
+ }
6
+ insertWebhookEvent(webhookId, receivedAt) {
7
+ const existing = this.connection
8
+ .prepare("SELECT id FROM webhook_events WHERE webhook_id = ?")
9
+ .get(webhookId);
10
+ if (existing) {
11
+ return { id: existing.id, duplicate: true };
12
+ }
13
+ const result = this.connection
14
+ .prepare("INSERT INTO webhook_events (webhook_id, received_at, processing_status) VALUES (?, ?, 'processed')")
15
+ .run(webhookId, receivedAt);
16
+ return { id: Number(result.lastInsertRowid), duplicate: false };
17
+ }
18
+ insertFullWebhookEvent(params) {
19
+ const existing = this.connection
20
+ .prepare("SELECT id FROM webhook_events WHERE webhook_id = ?")
21
+ .get(params.webhookId);
22
+ if (existing) {
23
+ return { id: existing.id, dedupeStatus: "duplicate" };
24
+ }
25
+ const result = this.connection
26
+ .prepare("INSERT INTO webhook_events (webhook_id, received_at, payload_json) VALUES (?, ?, ?)")
27
+ .run(params.webhookId, params.receivedAt, params.payloadJson);
28
+ return { id: Number(result.lastInsertRowid), dedupeStatus: "accepted" };
29
+ }
30
+ getWebhookPayload(id) {
31
+ const row = this.connection.prepare("SELECT webhook_id, payload_json FROM webhook_events WHERE id = ?").get(id);
32
+ if (!row || !row.payload_json)
33
+ return undefined;
34
+ return { webhookId: String(row.webhook_id), payloadJson: String(row.payload_json) };
35
+ }
36
+ isWebhookDuplicate(webhookId) {
37
+ return this.connection.prepare("SELECT 1 FROM webhook_events WHERE webhook_id = ?").get(webhookId) !== undefined;
38
+ }
39
+ markWebhookProcessed(id, status) {
40
+ this.connection.prepare("UPDATE webhook_events SET processing_status = ? WHERE id = ?").run(status, id);
41
+ }
42
+ assignWebhookProject(id, projectId) {
43
+ this.connection.prepare("UPDATE webhook_events SET project_id = ? WHERE id = ?").run(projectId, id);
44
+ }
45
+ findLatestAgentSessionIdForIssue(linearIssueId) {
46
+ const row = this.connection.prepare(`
47
+ SELECT COALESCE(
48
+ json_extract(payload_json, '$.agentSession.id'),
49
+ json_extract(payload_json, '$.data.agentSession.id'),
50
+ json_extract(payload_json, '$.agentSessionId'),
51
+ json_extract(payload_json, '$.data.agentSessionId')
52
+ ) AS agent_session_id
53
+ FROM webhook_events
54
+ WHERE COALESCE(
55
+ json_extract(payload_json, '$.agentSession.issueId'),
56
+ json_extract(payload_json, '$.data.agentSession.issueId'),
57
+ json_extract(payload_json, '$.agentSession.issue.id'),
58
+ json_extract(payload_json, '$.data.agentSession.issue.id')
59
+ ) = ?
60
+ AND COALESCE(
61
+ json_extract(payload_json, '$.agentSession.id'),
62
+ json_extract(payload_json, '$.data.agentSession.id'),
63
+ json_extract(payload_json, '$.agentSessionId'),
64
+ json_extract(payload_json, '$.data.agentSessionId')
65
+ ) IS NOT NULL
66
+ ORDER BY id DESC
67
+ LIMIT 1
68
+ `).get(linearIssueId);
69
+ return row?.agent_session_id != null ? String(row.agent_session_id) : undefined;
70
+ }
71
+ }