patchrelay 0.38.1 → 0.39.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 +4 -0
- package/dist/cli/commands/issues.js +20 -1
- package/dist/cli/data.js +54 -7
- package/dist/cli/formatters/text.js +10 -0
- package/dist/cli/help.js +4 -0
- package/dist/cli/index.js +3 -0
- package/dist/config.js +26 -0
- package/dist/db/issue-store.js +10 -2
- package/dist/db/migrations.js +5 -0
- package/dist/factory-state.js +1 -0
- package/dist/github-webhook-handler.js +12 -0
- package/dist/github-webhook-late-publication-guard.js +94 -0
- package/dist/github-webhook-state-projector.js +15 -1
- package/dist/github-webhooks.js +39 -4
- package/dist/github-worktree-auth.js +18 -0
- package/dist/http.js +17 -0
- package/dist/idle-reconciliation.js +4 -2
- package/dist/issue-session-events.js +1 -0
- package/dist/linear-activity-key.js +11 -0
- package/dist/linear-agent-session-client.js +14 -1
- package/dist/linear-progress-facts.js +170 -0
- package/dist/linear-progress-reporter.js +21 -168
- package/dist/linear-status-comment-sync.js +3 -19
- package/dist/linear-workflow-state-sync.js +37 -18
- package/dist/manual-issue-actions.js +37 -0
- package/dist/merged-linear-completion-reconciler.js +102 -22
- package/dist/no-pr-completion-check.js +52 -0
- package/dist/presentation-text.js +11 -1
- package/dist/prompting/patchrelay.js +8 -6
- package/dist/run-budgets.js +12 -0
- package/dist/run-launcher.js +6 -6
- package/dist/run-notification-handler.js +4 -0
- package/dist/run-orchestrator.js +7 -1
- package/dist/run-wake-planner.js +11 -10
- package/dist/service-issue-actions.js +80 -27
- package/dist/service.js +3 -0
- package/dist/trusted-no-pr-completion.js +7 -0
- package/dist/webhooks/desired-stage-recorder.js +34 -10
- package/package.json +1 -1
|
@@ -19,16 +19,22 @@ export class DesiredStageRecorder {
|
|
|
19
19
|
const existingIssue = this.db.issues.getIssue(params.project.id, normalizedIssue.id);
|
|
20
20
|
const activeRun = existingIssue?.activeRunId ? this.db.runs.getRunById(existingIssue.activeRunId) : undefined;
|
|
21
21
|
const latestRun = existingIssue ? this.db.runs.getLatestRunForIssue(params.project.id, normalizedIssue.id) : undefined;
|
|
22
|
-
const delegated = this.isDelegatedToPatchRelay(params.project, params.normalized);
|
|
23
22
|
const triggerAllowed = triggerEventAllowed(params.project, params.normalized.triggerEvent);
|
|
24
23
|
const incomingAgentSessionId = params.normalized.agentSession?.id;
|
|
25
24
|
const hasPendingWake = this.db.issueSessions.peekIssueSessionWake(params.project.id, normalizedIssue.id) !== undefined;
|
|
26
|
-
if (!existingIssue && !
|
|
27
|
-
return { issue: undefined, wakeRunType: undefined, delegated };
|
|
25
|
+
if (!existingIssue && !this.isDelegatedToPatchRelay(params.project, normalizedIssue) && !incomingAgentSessionId) {
|
|
26
|
+
return { issue: undefined, wakeRunType: undefined, delegated: false };
|
|
28
27
|
}
|
|
29
28
|
const hydratedIssue = await this.syncIssueDependencies(params.project.id, normalizedIssue);
|
|
29
|
+
const delegated = this.isDelegatedToPatchRelay(params.project, hydratedIssue);
|
|
30
30
|
const unresolvedBlockers = this.db.issues.countUnresolvedBlockers(params.project.id, normalizedIssue.id);
|
|
31
31
|
const terminal = isTerminalDelegationState(existingIssue, hydratedIssue);
|
|
32
|
+
const openPrExists = existingIssue?.prNumber !== undefined
|
|
33
|
+
&& existingIssue.prState !== "closed"
|
|
34
|
+
&& existingIssue.prState !== "merged";
|
|
35
|
+
const blockerPausedImplementation = unresolvedBlockers > 0
|
|
36
|
+
&& activeRun?.runType === "implementation"
|
|
37
|
+
&& !openPrExists;
|
|
32
38
|
const desiredStage = decideRunIntent({
|
|
33
39
|
delegated,
|
|
34
40
|
triggerAllowed,
|
|
@@ -45,6 +51,9 @@ export class DesiredStageRecorder {
|
|
|
45
51
|
triggerEvent: params.normalized.triggerEvent,
|
|
46
52
|
delegated,
|
|
47
53
|
});
|
|
54
|
+
const effectiveRunRelease = blockerPausedImplementation
|
|
55
|
+
? { release: true, reason: "Issue became blocked during implementation" }
|
|
56
|
+
: runRelease;
|
|
48
57
|
const undelegation = decideUnDelegation({
|
|
49
58
|
triggerEvent: params.normalized.triggerEvent,
|
|
50
59
|
delegated,
|
|
@@ -96,11 +105,12 @@ export class DesiredStageRecorder {
|
|
|
96
105
|
...(!reDelegationResume.factoryState && desiredStage ? { pendingRunType: null, pendingRunContextJson: null, factoryState: "delegated" } : {}),
|
|
97
106
|
...(clearPending ? { pendingRunType: null, pendingRunContextJson: null } : {}),
|
|
98
107
|
...(agentSessionId !== undefined ? { agentSessionId } : {}),
|
|
99
|
-
...(
|
|
108
|
+
...(effectiveRunRelease.release ? { activeRunId: null } : {}),
|
|
109
|
+
...(blockerPausedImplementation ? { factoryState: "delegated" } : {}),
|
|
100
110
|
...(undelegation.factoryState ? { factoryState: undelegation.factoryState } : {}),
|
|
101
111
|
});
|
|
102
|
-
if (
|
|
103
|
-
this.db.runs.finishRun(activeRun.id, { status: "released", failureReason:
|
|
112
|
+
if (effectiveRunRelease.release && activeRun && effectiveRunRelease.reason) {
|
|
113
|
+
this.db.runs.finishRun(activeRun.id, { status: "released", failureReason: effectiveRunRelease.reason });
|
|
104
114
|
}
|
|
105
115
|
return record;
|
|
106
116
|
};
|
|
@@ -136,6 +146,22 @@ export class DesiredStageRecorder {
|
|
|
136
146
|
: `Issue un-delegated from PatchRelay; ${issue.factoryState} is now paused`,
|
|
137
147
|
});
|
|
138
148
|
}
|
|
149
|
+
else if (blockerPausedImplementation) {
|
|
150
|
+
if (activeRun?.threadId && activeRun.turnId) {
|
|
151
|
+
await params.stopActiveRun(activeRun, "STOP: The issue is now blocked by another task. Stop working immediately and exit without publishing.");
|
|
152
|
+
}
|
|
153
|
+
this.db.issueSessions.clearPendingIssueSessionEventsRespectingActiveLease(params.project.id, normalizedIssue.id);
|
|
154
|
+
this.db.issueSessions.releaseIssueSessionLeaseRespectingActiveLease(params.project.id, normalizedIssue.id);
|
|
155
|
+
this.feed?.publish({
|
|
156
|
+
level: "warn",
|
|
157
|
+
kind: "stage",
|
|
158
|
+
issueKey: issue.issueKey,
|
|
159
|
+
projectId: params.project.id,
|
|
160
|
+
stage: issue.factoryState,
|
|
161
|
+
status: "blocked",
|
|
162
|
+
summary: `Implementation paused because ${issue.issueKey ?? normalizedIssue.id} is now blocked`,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
139
165
|
else if (reDelegationResume.pendingRunType) {
|
|
140
166
|
this.db.issueSessions.appendIssueSessionEventRespectingActiveLease(params.project.id, normalizedIssue.id, {
|
|
141
167
|
projectId: params.project.id,
|
|
@@ -168,13 +194,11 @@ export class DesiredStageRecorder {
|
|
|
168
194
|
delegated,
|
|
169
195
|
};
|
|
170
196
|
}
|
|
171
|
-
isDelegatedToPatchRelay(project,
|
|
172
|
-
if (!normalized.issue)
|
|
173
|
-
return false;
|
|
197
|
+
isDelegatedToPatchRelay(project, issue) {
|
|
174
198
|
const installation = this.db.linearInstallations.getLinearInstallationForProject(project.id);
|
|
175
199
|
if (!installation?.actorId)
|
|
176
200
|
return false;
|
|
177
|
-
return
|
|
201
|
+
return issue.delegateId === installation.actorId;
|
|
178
202
|
}
|
|
179
203
|
async syncIssueDependencies(projectId, issue) {
|
|
180
204
|
let source = issue;
|