patchrelay 0.38.0 → 0.38.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/build-info.json +3 -3
- package/dist/github-linear-session-sync.js +57 -0
- package/dist/github-pr-comment-handler.js +74 -0
- package/dist/github-webhook-failure-context.js +70 -0
- package/dist/github-webhook-handler.js +41 -976
- package/dist/github-webhook-issue-resolution.js +46 -0
- package/dist/github-webhook-policy.js +105 -0
- package/dist/github-webhook-reactive-run.js +302 -0
- package/dist/github-webhook-state-projector.js +231 -0
- package/dist/github-webhook-terminal-handler.js +111 -0
- package/dist/issue-overview-query.js +8 -57
- package/dist/legacy-issue-overview.js +58 -0
- package/dist/reactive-pr-state.js +65 -0
- package/dist/reactive-run-policy.js +35 -118
- package/dist/remote-pr-state.js +11 -0
- package/dist/run-orchestrator.js +21 -7
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { buildAgentSessionExternalUrls } from "./agent-session-presentation.js";
|
|
2
|
+
import { buildAgentSessionPlanForIssue } from "./agent-session-plan.js";
|
|
3
|
+
import { buildGitHubStateActivity } from "./linear-session-reporting.js";
|
|
4
|
+
export async function emitGitHubLinearActivity(params) {
|
|
5
|
+
const { issue, newState, event, linearProvider, logger, feed } = params;
|
|
6
|
+
if (!issue.agentSessionId)
|
|
7
|
+
return;
|
|
8
|
+
try {
|
|
9
|
+
const linear = await linearProvider.forProject(issue.projectId);
|
|
10
|
+
if (!linear?.createAgentActivity)
|
|
11
|
+
return;
|
|
12
|
+
const content = buildGitHubStateActivity(issue.factoryState, event);
|
|
13
|
+
if (!content)
|
|
14
|
+
return;
|
|
15
|
+
const allowEphemeral = content.type === "thought" || content.type === "action";
|
|
16
|
+
await linear.createAgentActivity({
|
|
17
|
+
agentSessionId: issue.agentSessionId,
|
|
18
|
+
content,
|
|
19
|
+
...(allowEphemeral ? { ephemeral: false } : {}),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
24
|
+
logger.warn({ issueKey: issue.issueKey, newState, error: msg }, "Failed to emit Linear activity from GitHub webhook");
|
|
25
|
+
feed?.publish({
|
|
26
|
+
level: "warn",
|
|
27
|
+
kind: "linear",
|
|
28
|
+
issueKey: issue.issueKey,
|
|
29
|
+
projectId: issue.projectId,
|
|
30
|
+
status: "linear_error",
|
|
31
|
+
summary: `Linear activity failed: ${msg}`,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export async function syncGitHubLinearSession(params) {
|
|
36
|
+
const { issue, linearProvider, logger, config } = params;
|
|
37
|
+
if (!issue.agentSessionId)
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
const linear = await linearProvider.forProject(issue.projectId);
|
|
41
|
+
if (!linear?.updateAgentSession)
|
|
42
|
+
return;
|
|
43
|
+
const externalUrls = buildAgentSessionExternalUrls(config, {
|
|
44
|
+
...(issue.issueKey ? { issueKey: issue.issueKey } : {}),
|
|
45
|
+
...(issue.prUrl ? { prUrl: issue.prUrl } : {}),
|
|
46
|
+
});
|
|
47
|
+
await linear.updateAgentSession({
|
|
48
|
+
agentSessionId: issue.agentSessionId,
|
|
49
|
+
plan: buildAgentSessionPlanForIssue(issue),
|
|
50
|
+
...(externalUrls ? { externalUrls } : {}),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
55
|
+
logger.warn({ issueKey: issue.issueKey, error: msg }, "Failed to sync Linear session from GitHub webhook");
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export class GitHubPrCommentHandler {
|
|
2
|
+
db;
|
|
3
|
+
enqueueIssue;
|
|
4
|
+
logger;
|
|
5
|
+
codex;
|
|
6
|
+
feed;
|
|
7
|
+
constructor(db, enqueueIssue, logger, codex, feed) {
|
|
8
|
+
this.db = db;
|
|
9
|
+
this.enqueueIssue = enqueueIssue;
|
|
10
|
+
this.logger = logger;
|
|
11
|
+
this.codex = codex;
|
|
12
|
+
this.feed = feed;
|
|
13
|
+
}
|
|
14
|
+
async handleCreatedComment(payload) {
|
|
15
|
+
if (payload.action !== "created")
|
|
16
|
+
return;
|
|
17
|
+
const issuePayload = payload.issue;
|
|
18
|
+
const comment = payload.comment;
|
|
19
|
+
if (!issuePayload || !comment)
|
|
20
|
+
return;
|
|
21
|
+
if (!issuePayload.pull_request)
|
|
22
|
+
return;
|
|
23
|
+
const body = typeof comment.body === "string" ? comment.body : "";
|
|
24
|
+
if (!body.trim())
|
|
25
|
+
return;
|
|
26
|
+
const user = comment.user;
|
|
27
|
+
const author = typeof user?.login === "string" ? user.login : "unknown";
|
|
28
|
+
if (typeof user?.type === "string" && user.type === "Bot")
|
|
29
|
+
return;
|
|
30
|
+
const prNumber = typeof issuePayload.number === "number" ? issuePayload.number : undefined;
|
|
31
|
+
if (!prNumber)
|
|
32
|
+
return;
|
|
33
|
+
const issue = this.db.issues.getIssueByPrNumber(prNumber);
|
|
34
|
+
if (!issue)
|
|
35
|
+
return;
|
|
36
|
+
this.feed?.publish({
|
|
37
|
+
level: "info",
|
|
38
|
+
kind: "comment",
|
|
39
|
+
issueKey: issue.issueKey,
|
|
40
|
+
projectId: issue.projectId,
|
|
41
|
+
stage: issue.factoryState,
|
|
42
|
+
status: "pr_comment",
|
|
43
|
+
summary: `GitHub PR comment from ${author}`,
|
|
44
|
+
detail: body.slice(0, 200),
|
|
45
|
+
});
|
|
46
|
+
if (issue.activeRunId) {
|
|
47
|
+
const run = this.db.runs.getRunById(issue.activeRunId);
|
|
48
|
+
if (run?.threadId && run.turnId) {
|
|
49
|
+
try {
|
|
50
|
+
await this.codex.steerTurn({
|
|
51
|
+
threadId: run.threadId,
|
|
52
|
+
turnId: run.turnId,
|
|
53
|
+
input: `GitHub PR comment from ${author}:\n\n${body}`,
|
|
54
|
+
});
|
|
55
|
+
this.logger.info({ issueKey: issue.issueKey, author }, "Forwarded GitHub PR comment to active run");
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
60
|
+
this.logger.warn({ issueKey: issue.issueKey, error: msg }, "Failed to forward GitHub PR comment");
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
this.db.issueSessions.appendIssueSessionEvent({
|
|
65
|
+
projectId: issue.projectId,
|
|
66
|
+
linearIssueId: issue.linearIssueId,
|
|
67
|
+
eventType: "followup_comment",
|
|
68
|
+
eventJson: JSON.stringify({ body, author }),
|
|
69
|
+
});
|
|
70
|
+
if (this.db.issueSessions.peekIssueSessionWake(issue.projectId, issue.linearIssueId)) {
|
|
71
|
+
this.enqueueIssue(issue.projectId, issue.linearIssueId);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export function getRelevantGitHubCiSnapshot(db, issue, event) {
|
|
2
|
+
const snapshot = db.issues.getLatestGitHubCiSnapshot(issue.projectId, issue.linearIssueId);
|
|
3
|
+
if (!snapshot)
|
|
4
|
+
return undefined;
|
|
5
|
+
if (snapshot.headSha !== event.headSha)
|
|
6
|
+
return undefined;
|
|
7
|
+
return snapshot;
|
|
8
|
+
}
|
|
9
|
+
export function pickPrimaryFailedCheck(snapshot) {
|
|
10
|
+
const gateName = snapshot.gateCheckName?.trim().toLowerCase();
|
|
11
|
+
return snapshot.failedChecks.find((entry) => entry.name.trim().toLowerCase() !== gateName)
|
|
12
|
+
?? snapshot.failedChecks[0];
|
|
13
|
+
}
|
|
14
|
+
export async function resolveGitHubBranchFailureContext(params) {
|
|
15
|
+
const repoFullName = params.project?.github?.repoFullName ?? params.event.repoFullName;
|
|
16
|
+
const snapshot = getRelevantGitHubCiSnapshot(params.db, params.issue, params.event);
|
|
17
|
+
const primaryFailedCheck = snapshot ? pickPrimaryFailedCheck(snapshot) : undefined;
|
|
18
|
+
const context = await params.failureContextResolver.resolve({
|
|
19
|
+
source: "branch_ci",
|
|
20
|
+
repoFullName,
|
|
21
|
+
event: primaryFailedCheck
|
|
22
|
+
? {
|
|
23
|
+
...params.event,
|
|
24
|
+
checkName: primaryFailedCheck.name,
|
|
25
|
+
checkUrl: primaryFailedCheck.detailsUrl ?? params.event.checkUrl,
|
|
26
|
+
checkDetailsUrl: primaryFailedCheck.detailsUrl ?? params.event.checkDetailsUrl,
|
|
27
|
+
}
|
|
28
|
+
: params.event,
|
|
29
|
+
});
|
|
30
|
+
return {
|
|
31
|
+
...(context ? context : {}),
|
|
32
|
+
...(context?.headSha || params.event.headSha ? { failureHeadSha: context?.headSha ?? params.event.headSha } : {}),
|
|
33
|
+
...(context?.failureSignature ? { failureSignature: context.failureSignature } : {}),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export function buildGitHubQueueFailureContext(event, project, queueRepairContext) {
|
|
37
|
+
const repoFullName = event.repoFullName || project?.github?.repoFullName || "";
|
|
38
|
+
const incident = queueRepairContext && typeof queueRepairContext === "object"
|
|
39
|
+
? queueRepairContext
|
|
40
|
+
: undefined;
|
|
41
|
+
const summary = typeof incident?.incidentSummary === "string"
|
|
42
|
+
? incident.incidentSummary
|
|
43
|
+
: event.checkOutputSummary ?? event.checkOutputTitle;
|
|
44
|
+
const failureHeadSha = event.headSha;
|
|
45
|
+
const failureSignature = [
|
|
46
|
+
"queue_eviction",
|
|
47
|
+
failureHeadSha ?? "unknown-sha",
|
|
48
|
+
event.checkName ?? "merge-steward/queue",
|
|
49
|
+
].join("::");
|
|
50
|
+
return {
|
|
51
|
+
source: "queue_eviction",
|
|
52
|
+
repoFullName,
|
|
53
|
+
capturedAt: new Date().toISOString(),
|
|
54
|
+
...(failureHeadSha ? { headSha: failureHeadSha, failureHeadSha } : {}),
|
|
55
|
+
...(event.checkName ? { checkName: event.checkName } : {}),
|
|
56
|
+
...(event.checkUrl ? { checkUrl: event.checkUrl } : {}),
|
|
57
|
+
...(event.checkDetailsUrl ? { checkDetailsUrl: event.checkDetailsUrl } : {}),
|
|
58
|
+
...(summary ? { summary } : {}),
|
|
59
|
+
failureSignature,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function resolveGitHubCheckClass(checkName, project) {
|
|
63
|
+
if (!checkName || !project)
|
|
64
|
+
return "code";
|
|
65
|
+
if ((project.reviewChecks ?? []).some((name) => checkName.includes(name)))
|
|
66
|
+
return "review";
|
|
67
|
+
if ((project.gateChecks ?? []).some((name) => checkName.includes(name)))
|
|
68
|
+
return "gate";
|
|
69
|
+
return "code";
|
|
70
|
+
}
|