patchrelay 0.68.6 → 0.69.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/data.js +1 -0
- package/dist/codex-app-server.js +18 -0
- package/dist/codex-conversation-adapter.js +270 -0
- package/dist/followup-intent.js +167 -54
- package/dist/issue-session-events.js +12 -0
- package/dist/linear-progress-reporter.js +81 -2
- package/dist/linear-session-reporting.js +74 -14
- package/dist/manual-issue-actions.js +5 -0
- package/dist/operator-retry-event.js +7 -0
- package/dist/prompting/patchrelay.js +39 -7
- package/dist/queue-health-monitor.js +16 -1
- package/dist/reactive-run-policy.js +12 -25
- package/dist/run-finalizer.js +23 -0
- package/dist/run-orchestrator.js +11 -2
- package/dist/service-issue-actions.js +1 -0
- package/dist/webhook-handler.js +8 -4
- package/dist/webhooks/agent-session-handler.js +18 -93
- package/dist/webhooks/comment-policy.js +19 -1
- package/dist/webhooks/comment-wake-handler.js +25 -161
- package/package.json +1 -1
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { buildAgentSessionPlanForIssue, } from "../agent-session-plan.js";
|
|
2
2
|
import { buildAgentSessionExternalUrls } from "../agent-session-presentation.js";
|
|
3
|
-
import {
|
|
4
|
-
import { extractLatestAssistantSummary } from "../issue-session-events.js";
|
|
5
|
-
import { buildAlreadyRunningThought, buildAgentSessionAcknowledgementThought, buildBlockedDelegationActivity, buildDelegationThought, buildFollowupStatusActivity, buildNonActionableFollowupActivity, buildPromptDeliveredThought, buildStopConfirmationActivity, } from "../linear-session-reporting.js";
|
|
3
|
+
import { buildAlreadyRunningThought, buildAgentSessionAcknowledgementThought, buildBlockedDelegationActivity, buildDelegationThought, buildStopConfirmationActivity, } from "../linear-session-reporting.js";
|
|
6
4
|
import { resolveProject, triggerEventAllowed } from "../project-resolution.js";
|
|
7
|
-
import { deriveIssueStatusNote } from "../status-note.js";
|
|
8
5
|
const PATCHRELAY_AGENT_ACTIVITY_TYPES = new Set([
|
|
9
6
|
"action",
|
|
10
7
|
"elicitation",
|
|
@@ -20,7 +17,8 @@ export class AgentSessionHandler {
|
|
|
20
17
|
wakeDispatcher;
|
|
21
18
|
logger;
|
|
22
19
|
feed;
|
|
23
|
-
|
|
20
|
+
conversationAdapter;
|
|
21
|
+
constructor(config, db, linearProvider, codex, wakeDispatcher, logger, feed, conversationAdapter) {
|
|
24
22
|
this.config = config;
|
|
25
23
|
this.db = db;
|
|
26
24
|
this.linearProvider = linearProvider;
|
|
@@ -28,6 +26,7 @@ export class AgentSessionHandler {
|
|
|
28
26
|
this.wakeDispatcher = wakeDispatcher;
|
|
29
27
|
this.logger = logger;
|
|
30
28
|
this.feed = feed;
|
|
29
|
+
this.conversationAdapter = conversationAdapter;
|
|
31
30
|
}
|
|
32
31
|
async acknowledgeCreated(normalized) {
|
|
33
32
|
if (normalized.triggerEvent !== "agentSessionCreated" || !normalized.agentSession?.id || !normalized.issue) {
|
|
@@ -65,7 +64,6 @@ export class AgentSessionHandler {
|
|
|
65
64
|
return;
|
|
66
65
|
const existingIssue = this.db.issues.getIssue(project.id, normalized.issue.id);
|
|
67
66
|
const activeRun = existingIssue?.activeRunId ? this.db.runs.getRunById(existingIssue.activeRunId) : undefined;
|
|
68
|
-
const automationEnabled = delegated || existingIssue?.delegatedToPatchRelay === true;
|
|
69
67
|
if (normalized.triggerEvent === "agentSessionCreated") {
|
|
70
68
|
if (!delegated) {
|
|
71
69
|
const latestIssue = this.db.issues.getIssue(project.id, normalized.issue.id);
|
|
@@ -128,78 +126,23 @@ export class AgentSessionHandler {
|
|
|
128
126
|
}
|
|
129
127
|
const promptBody = normalized.agentSession.promptBody?.trim();
|
|
130
128
|
const directReply = promptBody && existingIssue ? params.isDirectReplyToOutstandingQuestion(existingIssue) : false;
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
await this.handleStopSignal({
|
|
134
|
-
normalized,
|
|
129
|
+
if (promptBody && existingIssue && this.conversationAdapter) {
|
|
130
|
+
const result = await this.conversationAdapter.deliverAgentInput({
|
|
135
131
|
project,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
if (promptBody && existingIssue && promptIntent === "status" && !directReply) {
|
|
144
|
-
await this.publishAgentActivity(linear, normalized.agentSession.id, this.buildStatusActivity(existingIssue, activeRun, params.peekPendingSessionWakeRunType));
|
|
145
|
-
await this.syncAgentSession(linear, normalized.agentSession.id, existingIssue ?? trackedIssue, params.peekPendingSessionWakeRunType, activeRun ? { activeRunType: activeRun.runType } : undefined);
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
if (promptBody && promptIntent && followupIntentIsNonActionable(promptIntent) && !directReply) {
|
|
149
|
-
await this.publishAgentActivity(linear, normalized.agentSession.id, buildNonActionableFollowupActivity(promptIntent));
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
if (!automationEnabled && promptBody && existingIssue) {
|
|
153
|
-
await this.publishAgentActivity(linear, normalized.agentSession.id, {
|
|
154
|
-
type: "thought",
|
|
155
|
-
body: "PatchRelay is paused because the issue is undelegated.",
|
|
156
|
-
}, { ephemeral: true });
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
159
|
-
if (activeRun && promptBody && activeRun.threadId && activeRun.turnId) {
|
|
160
|
-
const input = `New Linear agent prompt received while you are working.\n\n${promptBody}`;
|
|
161
|
-
try {
|
|
162
|
-
await this.codex.steerTurn({ threadId: activeRun.threadId, turnId: activeRun.turnId, input });
|
|
163
|
-
this.feed?.publish({
|
|
164
|
-
level: "info",
|
|
165
|
-
kind: "agent",
|
|
166
|
-
projectId: project.id,
|
|
167
|
-
issueKey: trackedIssue?.issueKey,
|
|
168
|
-
stage: activeRun.runType,
|
|
169
|
-
status: "delivered",
|
|
170
|
-
summary: `Delivered follow-up prompt to active ${activeRun.runType} workflow`,
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
this.logger.warn({ issueKey: trackedIssue?.issueKey, error: error instanceof Error ? error.message : String(error) }, "Failed to deliver follow-up prompt");
|
|
175
|
-
this.feed?.publish({
|
|
176
|
-
level: "warn",
|
|
177
|
-
kind: "agent",
|
|
178
|
-
projectId: project.id,
|
|
179
|
-
issueKey: trackedIssue?.issueKey,
|
|
180
|
-
stage: activeRun.runType,
|
|
181
|
-
status: "delivery_failed",
|
|
182
|
-
summary: `Could not deliver follow-up prompt to active ${activeRun.runType} workflow`,
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
await this.publishAgentActivity(linear, normalized.agentSession.id, buildPromptDeliveredThought(activeRun.runType), { ephemeral: true });
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (promptBody && existingIssue && automationEnabled) {
|
|
189
|
-
if (!directReply && promptIntent && followupIntentIsNonActionable(promptIntent)) {
|
|
190
|
-
await this.publishAgentActivity(linear, normalized.agentSession.id, buildNonActionableFollowupActivity(promptIntent));
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
const queuedRunType = this.wakeDispatcher.recordEventAndDispatch(project.id, normalized.issue.id, {
|
|
194
|
-
eventType: directReply ? "direct_reply" : "followup_prompt",
|
|
195
|
-
eventJson: JSON.stringify({
|
|
196
|
-
text: promptBody,
|
|
197
|
-
source: "linear_agent_prompt",
|
|
198
|
-
}),
|
|
132
|
+
issue: existingIssue,
|
|
133
|
+
source: "agent_session_prompt",
|
|
134
|
+
body: promptBody,
|
|
135
|
+
directReply,
|
|
136
|
+
emitActivity: (content, options) => this.publishAgentActivity(linear, normalized.agentSession.id, content, options),
|
|
137
|
+
peekPendingSessionWakeRunType: params.peekPendingSessionWakeRunType,
|
|
199
138
|
});
|
|
200
139
|
const latestIssue = this.db.issues.getIssue(project.id, normalized.issue.id);
|
|
201
|
-
|
|
202
|
-
|
|
140
|
+
const syncOptions = result.activeRunType
|
|
141
|
+
? { activeRunType: result.activeRunType }
|
|
142
|
+
: result.queuedRunType ? { pendingRunType: result.queuedRunType }
|
|
143
|
+
: wakeRunType ? { pendingRunType: wakeRunType }
|
|
144
|
+
: undefined;
|
|
145
|
+
await this.syncAgentSession(linear, normalized.agentSession.id, latestIssue ?? trackedIssue, params.peekPendingSessionWakeRunType, syncOptions);
|
|
203
146
|
return;
|
|
204
147
|
}
|
|
205
148
|
if (wakeRunType) {
|
|
@@ -208,24 +151,6 @@ export class AgentSessionHandler {
|
|
|
208
151
|
await this.publishAgentActivity(linear, normalized.agentSession.id, buildDelegationThought(wakeRunType, "prompt"), { ephemeral: true });
|
|
209
152
|
}
|
|
210
153
|
}
|
|
211
|
-
buildStatusActivity(issue, activeRun, peekPendingSessionWakeRunType) {
|
|
212
|
-
const latestRun = activeRun ?? this.db.runs.getLatestRunForIssue(issue.projectId, issue.linearIssueId);
|
|
213
|
-
const latestEvent = this.db.issueSessions.listIssueSessionEvents(issue.projectId, issue.linearIssueId).at(-1);
|
|
214
|
-
const statusNote = deriveIssueStatusNote({
|
|
215
|
-
issue,
|
|
216
|
-
latestRun,
|
|
217
|
-
latestEvent,
|
|
218
|
-
sessionSummary: extractLatestAssistantSummary(latestRun),
|
|
219
|
-
waitingReason: undefined,
|
|
220
|
-
});
|
|
221
|
-
const pendingRunType = peekPendingSessionWakeRunType(issue.projectId, issue.linearIssueId);
|
|
222
|
-
return buildFollowupStatusActivity({
|
|
223
|
-
issue,
|
|
224
|
-
...(statusNote ? { statusNote } : {}),
|
|
225
|
-
...(activeRun?.runType ? { activeRunType: activeRun.runType } : {}),
|
|
226
|
-
...(pendingRunType ? { pendingRunType } : {}),
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
154
|
async handleStopSignal(params) {
|
|
230
155
|
const issueId = params.normalized.issue.id;
|
|
231
156
|
const sessionId = params.normalized.agentSession.id;
|
|
@@ -31,6 +31,7 @@ export function isPatchRelayGeneratedActivityComment(body) {
|
|
|
31
31
|
|| body.startsWith("PatchRelay is already working on ")
|
|
32
32
|
|| body.startsWith("PatchRelay received the ")
|
|
33
33
|
|| body.startsWith("PatchRelay routed your latest instructions into ")
|
|
34
|
+
|| body.startsWith("PatchRelay could not route your latest instructions into ")
|
|
34
35
|
|| body.startsWith("PatchRelay status:")
|
|
35
36
|
|| body.startsWith("PatchRelay did not start implementation ")
|
|
36
37
|
|| body.startsWith("PatchRelay has stopped work as requested.")
|
|
@@ -38,5 +39,22 @@ export function isPatchRelayGeneratedActivityComment(body) {
|
|
|
38
39
|
|| body === "This thread is for an agent session with patchrelay.";
|
|
39
40
|
}
|
|
40
41
|
export function hasExplicitPatchRelayWakeIntent(body) {
|
|
41
|
-
return
|
|
42
|
+
return extractPatchRelayAddressedText(body) !== undefined;
|
|
43
|
+
}
|
|
44
|
+
export function extractPatchRelayAddressedText(body) {
|
|
45
|
+
const trimmed = body.trim();
|
|
46
|
+
if (!trimmed)
|
|
47
|
+
return undefined;
|
|
48
|
+
const patterns = [
|
|
49
|
+
/^@?patchrelay\b[\s,:;-]*/i,
|
|
50
|
+
/^\bhey\s+@?patchrelay\b[\s,:;-]*/i,
|
|
51
|
+
];
|
|
52
|
+
for (const pattern of patterns) {
|
|
53
|
+
const match = trimmed.match(pattern);
|
|
54
|
+
if (!match)
|
|
55
|
+
continue;
|
|
56
|
+
const rest = trimmed.slice(match[0].length).trim();
|
|
57
|
+
return rest || trimmed;
|
|
58
|
+
}
|
|
59
|
+
return undefined;
|
|
42
60
|
}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import { classifyFollowupIntent, followupIntentIsNonActionable, followupIntentQueuesWork } from "../followup-intent.js";
|
|
2
1
|
import { triggerEventAllowed } from "../project-resolution.js";
|
|
3
|
-
import {
|
|
4
|
-
import { classifyIssue } from "../issue-class.js";
|
|
5
|
-
const ENQUEUEABLE_STATES = new Set(["pr_open", "changes_requested", "implementing", "delegated", "awaiting_input"]);
|
|
2
|
+
import { extractPatchRelayAddressedText, isInertPatchRelayComment, isPatchRelayManagedCommentAuthor, } from "./comment-policy.js";
|
|
6
3
|
export class CommentWakeHandler {
|
|
7
4
|
db;
|
|
8
|
-
codex;
|
|
9
5
|
wakeDispatcher;
|
|
10
|
-
logger;
|
|
11
6
|
feed;
|
|
12
|
-
|
|
7
|
+
conversationAdapter;
|
|
8
|
+
emitLinearActivity;
|
|
9
|
+
constructor(db, wakeDispatcher, feed, conversationAdapter, emitLinearActivity) {
|
|
13
10
|
this.db = db;
|
|
14
|
-
this.codex = codex;
|
|
15
11
|
this.wakeDispatcher = wakeDispatcher;
|
|
16
|
-
this.logger = logger;
|
|
17
12
|
this.feed = feed;
|
|
13
|
+
this.conversationAdapter = conversationAdapter;
|
|
14
|
+
this.emitLinearActivity = emitLinearActivity;
|
|
18
15
|
}
|
|
19
16
|
async handle(params) {
|
|
20
17
|
const { normalized, project, trackedIssue } = params;
|
|
@@ -28,10 +25,6 @@ export class CommentWakeHandler {
|
|
|
28
25
|
const issue = this.db.issues.getIssue(project.id, normalized.issue.id);
|
|
29
26
|
if (!issue)
|
|
30
27
|
return;
|
|
31
|
-
const issueClass = classifyIssue({
|
|
32
|
-
issue,
|
|
33
|
-
childIssueCount: this.db.issues.listCanonicalChildIssues(project.id, normalized.issue.id).length,
|
|
34
|
-
}).issueClass;
|
|
35
28
|
const trimmedBody = normalized.comment.body.trim();
|
|
36
29
|
const installation = this.db.linearInstallations.getLinearInstallationForProject(project.id);
|
|
37
30
|
const selfAuthored = isPatchRelayManagedCommentAuthor(installation, normalized.actor, normalized.comment.userName);
|
|
@@ -58,168 +51,39 @@ export class CommentWakeHandler {
|
|
|
58
51
|
});
|
|
59
52
|
return;
|
|
60
53
|
}
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
if (!issue.activeRunId) {
|
|
64
|
-
if (ENQUEUEABLE_STATES.has(issue.factoryState)) {
|
|
65
|
-
const wakeIntent = issueClass === "orchestration" || directReply || hasExplicitPatchRelayWakeIntent(trimmedBody);
|
|
66
|
-
if (!wakeIntent) {
|
|
67
|
-
this.feed?.publish({
|
|
68
|
-
level: "info",
|
|
69
|
-
kind: "comment",
|
|
70
|
-
projectId: project.id,
|
|
71
|
-
issueKey: trackedIssue?.issueKey,
|
|
72
|
-
status: "ignored",
|
|
73
|
-
summary: "Ignored comment with no explicit PatchRelay wake intent",
|
|
74
|
-
detail: trimmedBody.slice(0, 200),
|
|
75
|
-
});
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
if (intent === "stop") {
|
|
79
|
-
this.wakeDispatcher.recordEventAndDispatch(project.id, normalized.issue.id, {
|
|
80
|
-
eventType: "stop_requested",
|
|
81
|
-
eventJson: JSON.stringify({
|
|
82
|
-
body: trimmedBody,
|
|
83
|
-
author: normalized.comment.userName,
|
|
84
|
-
}),
|
|
85
|
-
});
|
|
86
|
-
this.db.issueSessions.clearPendingIssueSessionEventsRespectingActiveLease(project.id, normalized.issue.id);
|
|
87
|
-
this.feed?.publish({
|
|
88
|
-
level: "info",
|
|
89
|
-
kind: "comment",
|
|
90
|
-
projectId: project.id,
|
|
91
|
-
issueKey: trackedIssue?.issueKey,
|
|
92
|
-
status: "stopped",
|
|
93
|
-
summary: "Stop request recorded from Linear comment",
|
|
94
|
-
detail: trimmedBody.slice(0, 200),
|
|
95
|
-
});
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (!directReply && !followupIntentQueuesWork(intent)) {
|
|
99
|
-
this.feed?.publish({
|
|
100
|
-
level: "info",
|
|
101
|
-
kind: "comment",
|
|
102
|
-
projectId: project.id,
|
|
103
|
-
issueKey: trackedIssue?.issueKey,
|
|
104
|
-
status: intent === "status" ? "status_requested" : "ignored",
|
|
105
|
-
summary: intent === "status"
|
|
106
|
-
? "Ignored status comment without queueing work"
|
|
107
|
-
: "Ignored non-actionable follow-up comment",
|
|
108
|
-
detail: trimmedBody.slice(0, 200),
|
|
109
|
-
});
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
const runType = issue.prReviewState === "changes_requested" ? "review_fix" : "implementation";
|
|
113
|
-
const queuedRunType = this.wakeDispatcher.recordEventAndDispatch(project.id, normalized.issue.id, {
|
|
114
|
-
eventType: directReply ? "direct_reply" : "followup_comment",
|
|
115
|
-
eventJson: JSON.stringify({
|
|
116
|
-
body: trimmedBody,
|
|
117
|
-
author: normalized.comment.userName,
|
|
118
|
-
}),
|
|
119
|
-
});
|
|
120
|
-
this.feed?.publish({
|
|
121
|
-
level: "info",
|
|
122
|
-
kind: "comment",
|
|
123
|
-
projectId: project.id,
|
|
124
|
-
issueKey: trackedIssue?.issueKey,
|
|
125
|
-
status: "enqueued",
|
|
126
|
-
summary: `Comment enqueued ${(queuedRunType ?? runType)} run`,
|
|
127
|
-
detail: trimmedBody.slice(0, 200),
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
const run = this.db.runs.getRunById(issue.activeRunId);
|
|
133
|
-
if (!run?.threadId || !run.turnId)
|
|
134
|
-
return;
|
|
135
|
-
if (intent === "stop") {
|
|
136
|
-
try {
|
|
137
|
-
await this.codex.steerTurn({
|
|
138
|
-
threadId: run.threadId,
|
|
139
|
-
turnId: run.turnId,
|
|
140
|
-
input: "STOP: The user has requested you stop working immediately. Do not make further changes. Wrap up and exit.",
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
this.logger.warn({ issueKey: trackedIssue?.issueKey, error: error instanceof Error ? error.message : String(error) }, "Failed to steer Codex turn for comment stop request");
|
|
145
|
-
}
|
|
146
|
-
this.db.runs.finishRun(run.id, { status: "released", threadId: run.threadId, turnId: run.turnId });
|
|
147
|
-
this.db.issueSessions.upsertIssueRespectingActiveLease(project.id, normalized.issue.id, {
|
|
148
|
-
projectId: project.id,
|
|
149
|
-
linearIssueId: normalized.issue.id,
|
|
150
|
-
activeRunId: null,
|
|
151
|
-
factoryState: "awaiting_input",
|
|
152
|
-
});
|
|
153
|
-
this.wakeDispatcher.recordEventAndDispatch(project.id, normalized.issue.id, {
|
|
154
|
-
eventType: "stop_requested",
|
|
155
|
-
eventJson: JSON.stringify({
|
|
156
|
-
body: trimmedBody,
|
|
157
|
-
author: normalized.comment.userName,
|
|
158
|
-
}),
|
|
159
|
-
});
|
|
160
|
-
this.db.issueSessions.clearPendingIssueSessionEventsRespectingActiveLease(project.id, normalized.issue.id);
|
|
54
|
+
const addressedText = extractPatchRelayAddressedText(trimmedBody);
|
|
55
|
+
if (!addressedText) {
|
|
161
56
|
this.feed?.publish({
|
|
162
57
|
level: "info",
|
|
163
58
|
kind: "comment",
|
|
164
59
|
projectId: project.id,
|
|
165
60
|
issueKey: trackedIssue?.issueKey,
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
summary: "Stop request delivered to active workflow",
|
|
169
|
-
});
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
if (!directReply && followupIntentIsNonActionable(intent)) {
|
|
173
|
-
this.feed?.publish({
|
|
174
|
-
level: "info",
|
|
175
|
-
kind: "comment",
|
|
176
|
-
projectId: project.id,
|
|
177
|
-
issueKey: trackedIssue?.issueKey,
|
|
178
|
-
stage: run.runType,
|
|
179
|
-
status: intent === "status" ? "status_requested" : "ignored",
|
|
180
|
-
summary: intent === "status"
|
|
181
|
-
? "Ignored status comment without steering active workflow"
|
|
182
|
-
: "Ignored non-actionable follow-up comment",
|
|
61
|
+
status: "ignored",
|
|
62
|
+
summary: "Ignored issue comment because it did not address PatchRelay",
|
|
183
63
|
detail: trimmedBody.slice(0, 200),
|
|
184
64
|
});
|
|
185
65
|
return;
|
|
186
66
|
}
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
"",
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
67
|
+
const result = await this.conversationAdapter?.deliverAgentInput({
|
|
68
|
+
project,
|
|
69
|
+
issue,
|
|
70
|
+
source: "addressed_issue_comment",
|
|
71
|
+
body: addressedText,
|
|
72
|
+
author: normalized.comment.userName,
|
|
73
|
+
directReply: params.isDirectReplyToOutstandingQuestion(issue),
|
|
74
|
+
emitActivity: this.emitLinearActivity
|
|
75
|
+
? (content, options) => this.emitLinearActivity(issue, content, options)
|
|
76
|
+
: undefined,
|
|
77
|
+
});
|
|
78
|
+
if (result?.queuedRunType) {
|
|
195
79
|
this.feed?.publish({
|
|
196
80
|
level: "info",
|
|
197
81
|
kind: "comment",
|
|
198
82
|
projectId: project.id,
|
|
199
83
|
issueKey: trackedIssue?.issueKey,
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
catch (error) {
|
|
206
|
-
this.logger.warn({ issueKey: trackedIssue?.issueKey, error: error instanceof Error ? error.message : String(error) }, "Failed to deliver follow-up comment");
|
|
207
|
-
const directReply = params.isDirectReplyToOutstandingQuestion(issue);
|
|
208
|
-
this.wakeDispatcher.recordEventAndDispatch(project.id, normalized.issue.id, {
|
|
209
|
-
eventType: directReply ? "direct_reply" : "followup_comment",
|
|
210
|
-
eventJson: JSON.stringify({
|
|
211
|
-
body: trimmedBody,
|
|
212
|
-
author: normalized.comment.userName,
|
|
213
|
-
}),
|
|
214
|
-
});
|
|
215
|
-
this.feed?.publish({
|
|
216
|
-
level: "warn",
|
|
217
|
-
kind: "comment",
|
|
218
|
-
projectId: project.id,
|
|
219
|
-
issueKey: trackedIssue?.issueKey,
|
|
220
|
-
stage: run.runType,
|
|
221
|
-
status: "delivery_failed",
|
|
222
|
-
summary: `Could not deliver follow-up comment to active ${run.runType} workflow`,
|
|
84
|
+
status: "enqueued",
|
|
85
|
+
summary: `Comment enqueued ${result.queuedRunType} run`,
|
|
86
|
+
detail: addressedText.slice(0, 200),
|
|
223
87
|
});
|
|
224
88
|
}
|
|
225
89
|
}
|