patchrelay 0.4.1 → 0.6.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/dist/build-info.json +3 -3
- package/dist/cli/args.js +35 -7
- package/dist/cli/commands/feed.js +53 -0
- package/dist/cli/commands/issues.js +9 -7
- package/dist/cli/data.js +82 -2
- package/dist/cli/formatters/text.js +53 -4
- package/dist/cli/index.js +101 -5
- package/dist/db/migrations.js +15 -0
- package/dist/db/operator-feed-store.js +72 -0
- package/dist/db.js +3 -0
- package/dist/http.js +52 -0
- package/dist/operator-feed.js +85 -0
- package/dist/service-stage-finalizer.js +37 -2
- package/dist/service-stage-runner.js +43 -2
- package/dist/service-webhook-processor.js +105 -3
- package/dist/service.js +12 -3
- package/dist/stage-lifecycle-publisher.js +31 -1
- package/dist/stage-turn-input-dispatcher.js +5 -3
- package/dist/webhook-agent-session-handler.js +51 -1
- package/dist/webhook-comment-handler.js +55 -3
- package/package.json +3 -2
|
@@ -9,10 +9,12 @@ export class AgentSessionWebhookHandler {
|
|
|
9
9
|
stores;
|
|
10
10
|
turnInputDispatcher;
|
|
11
11
|
agentActivity;
|
|
12
|
-
|
|
12
|
+
feed;
|
|
13
|
+
constructor(stores, turnInputDispatcher, agentActivity, feed) {
|
|
13
14
|
this.stores = stores;
|
|
14
15
|
this.turnInputDispatcher = turnInputDispatcher;
|
|
15
16
|
this.agentActivity = agentActivity;
|
|
17
|
+
this.feed = feed;
|
|
16
18
|
}
|
|
17
19
|
async handle(params) {
|
|
18
20
|
const { normalized, project, issue, desiredStage, delegatedToPatchRelay } = params;
|
|
@@ -95,6 +97,13 @@ export class AgentSessionWebhookHandler {
|
|
|
95
97
|
...(issue?.issueKey ? { issueKey: issue.issueKey } : {}),
|
|
96
98
|
failureMessage: "Failed to deliver queued Linear agent prompt to active Codex turn",
|
|
97
99
|
});
|
|
100
|
+
this.publishPromptDeliveryEvent({
|
|
101
|
+
projectId: project.id,
|
|
102
|
+
issueKey: issue?.issueKey ?? normalized.issue?.identifier,
|
|
103
|
+
stage: activeRunLease.stage,
|
|
104
|
+
obligationId,
|
|
105
|
+
flushResult,
|
|
106
|
+
});
|
|
98
107
|
await this.agentActivity.publishForSession(project.id, normalized.agentSession.id, {
|
|
99
108
|
type: "thought",
|
|
100
109
|
body: obligationId !== undefined && flushResult.deliveredObligationIds.includes(obligationId)
|
|
@@ -148,6 +157,47 @@ export class AgentSessionWebhookHandler {
|
|
|
148
157
|
});
|
|
149
158
|
return obligation.id;
|
|
150
159
|
}
|
|
160
|
+
publishPromptDeliveryEvent(params) {
|
|
161
|
+
if (params.obligationId === undefined) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (params.flushResult.deliveredObligationIds.includes(params.obligationId)) {
|
|
165
|
+
this.feed?.publish({
|
|
166
|
+
level: "info",
|
|
167
|
+
kind: "agent",
|
|
168
|
+
projectId: params.projectId,
|
|
169
|
+
issueKey: params.issueKey,
|
|
170
|
+
stage: params.stage,
|
|
171
|
+
status: "delivered",
|
|
172
|
+
summary: `Delivered follow-up prompt to active ${params.stage} workflow`,
|
|
173
|
+
detail: "The active Linear agent session was routed into the running Codex turn.",
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (params.flushResult.failedObligationIds.includes(params.obligationId)) {
|
|
178
|
+
this.feed?.publish({
|
|
179
|
+
level: "warn",
|
|
180
|
+
kind: "agent",
|
|
181
|
+
projectId: params.projectId,
|
|
182
|
+
issueKey: params.issueKey,
|
|
183
|
+
stage: params.stage,
|
|
184
|
+
status: "delivery_failed",
|
|
185
|
+
summary: `Could not deliver follow-up prompt to active ${params.stage} workflow`,
|
|
186
|
+
detail: "PatchRelay kept the prompt queued and will retry delivery on the next active turn.",
|
|
187
|
+
});
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
this.feed?.publish({
|
|
191
|
+
level: "info",
|
|
192
|
+
kind: "agent",
|
|
193
|
+
projectId: params.projectId,
|
|
194
|
+
issueKey: params.issueKey,
|
|
195
|
+
stage: params.stage,
|
|
196
|
+
status: "queued",
|
|
197
|
+
summary: `Queued follow-up prompt for active ${params.stage} workflow`,
|
|
198
|
+
detail: "PatchRelay saved the prompt for the next delivery opportunity.",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
151
201
|
}
|
|
152
202
|
function buildPromptDedupeKey(agentSessionId, promptBody) {
|
|
153
203
|
return `linear-agent-prompt:${agentSessionId}:${hashBody(promptBody)}`;
|
|
@@ -4,9 +4,11 @@ import { triggerEventAllowed } from "./project-resolution.js";
|
|
|
4
4
|
export class CommentWebhookHandler {
|
|
5
5
|
stores;
|
|
6
6
|
turnInputDispatcher;
|
|
7
|
-
|
|
7
|
+
feed;
|
|
8
|
+
constructor(stores, turnInputDispatcher, feed) {
|
|
8
9
|
this.stores = stores;
|
|
9
10
|
this.turnInputDispatcher = turnInputDispatcher;
|
|
11
|
+
this.feed = feed;
|
|
10
12
|
}
|
|
11
13
|
async handle(normalized, project) {
|
|
12
14
|
if ((normalized.triggerEvent !== "commentCreated" && normalized.triggerEvent !== "commentUpdated") || !normalized.comment?.body) {
|
|
@@ -48,8 +50,8 @@ export class CommentWebhookHandler {
|
|
|
48
50
|
})) {
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
|
-
this.enqueueObligation(project.id, normalizedIssue.id, runLease.threadId, runLease.turnId, normalized.comment.id, body, dedupeKey);
|
|
52
|
-
await this.turnInputDispatcher.flush({
|
|
53
|
+
const obligationId = this.enqueueObligation(project.id, normalizedIssue.id, runLease.threadId, runLease.turnId, normalized.comment.id, body, dedupeKey);
|
|
54
|
+
const flushResult = await this.turnInputDispatcher.flush({
|
|
53
55
|
id: issueControl.activeRunLeaseId,
|
|
54
56
|
projectId: project.id,
|
|
55
57
|
linearIssueId: normalizedIssue.id,
|
|
@@ -59,6 +61,14 @@ export class CommentWebhookHandler {
|
|
|
59
61
|
...(issue?.issueKey ? { issueKey: issue.issueKey } : {}),
|
|
60
62
|
failureMessage: "Failed to deliver queued Linear comment to active Codex turn",
|
|
61
63
|
});
|
|
64
|
+
this.publishCommentDeliveryEvent({
|
|
65
|
+
projectId: project.id,
|
|
66
|
+
issueKey: issue?.issueKey ?? normalizedIssue.identifier,
|
|
67
|
+
stage: runLease.stage,
|
|
68
|
+
obligationId,
|
|
69
|
+
authorName: normalized.comment.userName,
|
|
70
|
+
flushResult,
|
|
71
|
+
});
|
|
62
72
|
}
|
|
63
73
|
enqueueObligation(projectId, linearIssueId, threadId, turnId, commentId, body, dedupeKey) {
|
|
64
74
|
const activeRunLeaseId = this.stores.issueControl.getIssueControl(projectId, linearIssueId)?.activeRunLeaseId;
|
|
@@ -80,6 +90,48 @@ export class CommentWebhookHandler {
|
|
|
80
90
|
});
|
|
81
91
|
return obligation.id;
|
|
82
92
|
}
|
|
93
|
+
publishCommentDeliveryEvent(params) {
|
|
94
|
+
if (params.obligationId === undefined) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const authorDetail = params.authorName ? `Author: ${params.authorName}.` : undefined;
|
|
98
|
+
if (params.flushResult.deliveredObligationIds.includes(params.obligationId)) {
|
|
99
|
+
this.feed?.publish({
|
|
100
|
+
level: "info",
|
|
101
|
+
kind: "comment",
|
|
102
|
+
projectId: params.projectId,
|
|
103
|
+
issueKey: params.issueKey,
|
|
104
|
+
stage: params.stage,
|
|
105
|
+
status: "delivered",
|
|
106
|
+
summary: `Delivered follow-up comment to active ${params.stage} workflow`,
|
|
107
|
+
detail: authorDetail ?? "The comment was routed into the running Codex turn.",
|
|
108
|
+
});
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (params.flushResult.failedObligationIds.includes(params.obligationId)) {
|
|
112
|
+
this.feed?.publish({
|
|
113
|
+
level: "warn",
|
|
114
|
+
kind: "comment",
|
|
115
|
+
projectId: params.projectId,
|
|
116
|
+
issueKey: params.issueKey,
|
|
117
|
+
stage: params.stage,
|
|
118
|
+
status: "delivery_failed",
|
|
119
|
+
summary: `Could not deliver follow-up comment to active ${params.stage} workflow`,
|
|
120
|
+
detail: authorDetail ?? "PatchRelay kept the comment queued and will retry delivery.",
|
|
121
|
+
});
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
this.feed?.publish({
|
|
125
|
+
level: "info",
|
|
126
|
+
kind: "comment",
|
|
127
|
+
projectId: params.projectId,
|
|
128
|
+
issueKey: params.issueKey,
|
|
129
|
+
stage: params.stage,
|
|
130
|
+
status: "queued",
|
|
131
|
+
summary: `Queued follow-up comment for active ${params.stage} workflow`,
|
|
132
|
+
detail: authorDetail ?? "PatchRelay saved the comment for the next delivery opportunity.",
|
|
133
|
+
});
|
|
134
|
+
}
|
|
83
135
|
}
|
|
84
136
|
function buildCommentDedupeKey(commentId, body) {
|
|
85
137
|
return `linear-comment:${commentId}:${hashBody(body)}`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "patchrelay",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,8 @@
|
|
|
34
34
|
"doctor": "node dist/index.js doctor",
|
|
35
35
|
"restart": "node dist/index.js restart-service",
|
|
36
36
|
"lint": "eslint .",
|
|
37
|
-
"
|
|
37
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
38
|
+
"check": "npm run typecheck",
|
|
38
39
|
"test": "node --experimental-transform-types --test test/**/*.test.ts",
|
|
39
40
|
"ci": "npm run lint && npm run check && npm test && npm run build"
|
|
40
41
|
},
|