patchrelay 0.56.1 → 0.57.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
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
const ACTIVITY_RECOVERY_LIMIT = 20;
|
|
2
|
+
const MAX_CONTEXT_ACTIVITIES = 8;
|
|
3
|
+
const MAX_ACTIVITY_TEXT_LENGTH = 500;
|
|
4
|
+
function trimBounded(value, maxLength = MAX_ACTIVITY_TEXT_LENGTH) {
|
|
5
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
6
|
+
if (normalized.length <= maxLength)
|
|
7
|
+
return normalized;
|
|
8
|
+
return `${normalized.slice(0, maxLength - 1).trimEnd()}...`;
|
|
9
|
+
}
|
|
10
|
+
function hasRecoveredContext(context) {
|
|
11
|
+
return typeof context?.linearAgentActivityContext === "string" && context.linearAgentActivityContext.trim().length > 0;
|
|
12
|
+
}
|
|
13
|
+
function hasLocalHumanContext(context) {
|
|
14
|
+
if (hasRecoveredContext(context))
|
|
15
|
+
return true;
|
|
16
|
+
for (const key of ["promptContext", "promptBody", "operatorPrompt", "userComment"]) {
|
|
17
|
+
const value = context?.[key];
|
|
18
|
+
if (typeof value === "string" && value.trim().length > 0)
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (!Array.isArray(context?.followUps))
|
|
22
|
+
return false;
|
|
23
|
+
return context.followUps.some((entry) => {
|
|
24
|
+
if (!entry || typeof entry !== "object")
|
|
25
|
+
return false;
|
|
26
|
+
const text = entry.text;
|
|
27
|
+
return typeof text === "string" && text.trim().length > 0;
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function activitySortKey(activity) {
|
|
31
|
+
const parsed = activity.updatedAt ? Date.parse(activity.updatedAt) : NaN;
|
|
32
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
33
|
+
}
|
|
34
|
+
function describeActivity(activity) {
|
|
35
|
+
const type = activity.type?.trim() || "activity";
|
|
36
|
+
const body = typeof activity.body === "string" ? trimBounded(activity.body) : "";
|
|
37
|
+
if (body) {
|
|
38
|
+
return `${type}: ${body}`;
|
|
39
|
+
}
|
|
40
|
+
if (activity.action || activity.parameter || activity.result) {
|
|
41
|
+
const action = activity.action ? trimBounded(activity.action, 120) : "action";
|
|
42
|
+
const parameter = activity.parameter ? ` ${trimBounded(activity.parameter, 180)}` : "";
|
|
43
|
+
const result = activity.result ? ` -> ${trimBounded(activity.result, 180)}` : "";
|
|
44
|
+
return `${type}: ${action}${parameter}${result}`;
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
export function summarizeLinearAgentActivities(activities) {
|
|
49
|
+
const lines = [...activities]
|
|
50
|
+
.sort((left, right) => activitySortKey(left) - activitySortKey(right))
|
|
51
|
+
.map(describeActivity)
|
|
52
|
+
.filter((line) => Boolean(line))
|
|
53
|
+
.slice(-MAX_CONTEXT_ACTIVITIES);
|
|
54
|
+
if (lines.length === 0)
|
|
55
|
+
return undefined;
|
|
56
|
+
return {
|
|
57
|
+
linearAgentActivityContext: lines.map((line) => `- ${line}`).join("\n"),
|
|
58
|
+
linearAgentActivityCount: lines.length,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export async function recoverLinearAgentActivityContext(params) {
|
|
62
|
+
if (!params.agentSessionId || hasLocalHumanContext(params.context)) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const linear = await params.linearProvider.forProject(params.projectId);
|
|
67
|
+
if (!linear?.listAgentSessionActivities) {
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
const activities = await linear.listAgentSessionActivities(params.agentSessionId, {
|
|
71
|
+
first: ACTIVITY_RECOVERY_LIMIT,
|
|
72
|
+
});
|
|
73
|
+
return summarizeLinearAgentActivities(activities);
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
params.logger.warn({
|
|
77
|
+
issueKey: params.issueKey,
|
|
78
|
+
agentSessionId: params.agentSessionId,
|
|
79
|
+
error: error instanceof Error ? error.message : String(error),
|
|
80
|
+
}, "Failed to recover Linear agent activity context");
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
package/dist/linear-client.js
CHANGED
|
@@ -241,6 +241,55 @@ export class LinearGraphqlClient {
|
|
|
241
241
|
}
|
|
242
242
|
return response.agentSessionUpdate.agentSession;
|
|
243
243
|
}
|
|
244
|
+
async listAgentSessionActivities(agentSessionId, options) {
|
|
245
|
+
const response = await this.request(`
|
|
246
|
+
query PatchRelayAgentSessionActivities($id: String!, $first: Int!) {
|
|
247
|
+
agentSession(id: $id) {
|
|
248
|
+
activities(first: $first) {
|
|
249
|
+
edges {
|
|
250
|
+
node {
|
|
251
|
+
id
|
|
252
|
+
updatedAt
|
|
253
|
+
content {
|
|
254
|
+
__typename
|
|
255
|
+
... on AgentActivityThoughtContent {
|
|
256
|
+
body
|
|
257
|
+
}
|
|
258
|
+
... on AgentActivityActionContent {
|
|
259
|
+
action
|
|
260
|
+
parameter
|
|
261
|
+
result
|
|
262
|
+
}
|
|
263
|
+
... on AgentActivityElicitationContent {
|
|
264
|
+
body
|
|
265
|
+
}
|
|
266
|
+
... on AgentActivityResponseContent {
|
|
267
|
+
body
|
|
268
|
+
}
|
|
269
|
+
... on AgentActivityErrorContent {
|
|
270
|
+
body
|
|
271
|
+
}
|
|
272
|
+
... on AgentActivityPromptContent {
|
|
273
|
+
body
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
`, {
|
|
282
|
+
id: agentSessionId,
|
|
283
|
+
first: Math.max(1, Math.min(options?.first ?? 20, 50)),
|
|
284
|
+
});
|
|
285
|
+
const activityEdges = response.agentSession?.activities?.edges;
|
|
286
|
+
const rawActivities = activityEdges
|
|
287
|
+
? activityEdges.map((edge) => edge?.node)
|
|
288
|
+
: response.agentSession?.activities?.nodes ?? [];
|
|
289
|
+
return rawActivities
|
|
290
|
+
.filter((activity) => Boolean(activity))
|
|
291
|
+
.map(mapAgentActivity);
|
|
292
|
+
}
|
|
244
293
|
async updateIssueLabels(params) {
|
|
245
294
|
const issue = await this.getIssue(params.issueId);
|
|
246
295
|
const addIds = this.resolveLabelIds(issue, params.addNames ?? []);
|
|
@@ -439,6 +488,25 @@ function mapIssueRelation(raw) {
|
|
|
439
488
|
...(raw.state?.type ? { stateType: raw.state.type } : {}),
|
|
440
489
|
};
|
|
441
490
|
}
|
|
491
|
+
function mapAgentActivity(raw) {
|
|
492
|
+
const content = raw.content ?? {};
|
|
493
|
+
return {
|
|
494
|
+
id: raw.id,
|
|
495
|
+
...(content.__typename ? { type: normalizeAgentActivityType(content.__typename) } : {}),
|
|
496
|
+
...(content.body ? { body: content.body } : {}),
|
|
497
|
+
...(content.action ? { action: content.action } : {}),
|
|
498
|
+
...(content.parameter ? { parameter: content.parameter } : {}),
|
|
499
|
+
...(content.result ? { result: content.result } : {}),
|
|
500
|
+
...(raw.updatedAt ? { updatedAt: raw.updatedAt } : {}),
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function normalizeAgentActivityType(typename) {
|
|
504
|
+
return typename
|
|
505
|
+
.replace(/^AgentActivity/, "")
|
|
506
|
+
.replace(/Content$/, "")
|
|
507
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1_$2")
|
|
508
|
+
.toLowerCase();
|
|
509
|
+
}
|
|
442
510
|
export class DatabaseBackedLinearClientProvider {
|
|
443
511
|
config;
|
|
444
512
|
db;
|
|
@@ -197,6 +197,9 @@ function buildHumanContextLines(context) {
|
|
|
197
197
|
const latestPrompt = typeof context?.promptBody === "string" ? context.promptBody.trim() : "";
|
|
198
198
|
const operatorPrompt = typeof context?.operatorPrompt === "string" ? context.operatorPrompt.trim() : "";
|
|
199
199
|
const userComment = typeof context?.userComment === "string" ? context.userComment.trim() : "";
|
|
200
|
+
const linearAgentActivityContext = typeof context?.linearAgentActivityContext === "string"
|
|
201
|
+
? context.linearAgentActivityContext.trim()
|
|
202
|
+
: "";
|
|
200
203
|
const lines = [];
|
|
201
204
|
if (promptContext) {
|
|
202
205
|
lines.push("Linear session context:", promptContext, "");
|
|
@@ -210,6 +213,9 @@ function buildHumanContextLines(context) {
|
|
|
210
213
|
if (userComment) {
|
|
211
214
|
lines.push("Human follow-up comment:", userComment, "");
|
|
212
215
|
}
|
|
216
|
+
if (linearAgentActivityContext) {
|
|
217
|
+
lines.push("Recovered Linear agent activity context:", linearAgentActivityContext, "");
|
|
218
|
+
}
|
|
213
219
|
return lines;
|
|
214
220
|
}
|
|
215
221
|
function resolveRequestedChangesMode(runType, context) {
|
package/dist/run-orchestrator.js
CHANGED
|
@@ -8,6 +8,7 @@ import { MainBranchHealthMonitor } from "./main-branch-health-monitor.js";
|
|
|
8
8
|
import { QueueHealthMonitor } from "./queue-health-monitor.js";
|
|
9
9
|
import { IdleIssueReconciler } from "./idle-reconciliation.js";
|
|
10
10
|
import { LinearSessionSync } from "./linear-session-sync.js";
|
|
11
|
+
import { recoverLinearAgentActivityContext } from "./linear-agent-activity-recovery.js";
|
|
11
12
|
import { IssueSessionLeaseService } from "./issue-session-lease-service.js";
|
|
12
13
|
import { InterruptedRunRecovery } from "./interrupted-run-recovery.js";
|
|
13
14
|
import { RunCompletionPolicy } from "./run-completion-policy.js";
|
|
@@ -234,12 +235,23 @@ export class RunOrchestrator {
|
|
|
234
235
|
const baseContext = isRequestedChangesRunType(runType)
|
|
235
236
|
? await this.runCompletionPolicy.resolveRequestedChangesWakeContext(issue, runType, context)
|
|
236
237
|
: context;
|
|
238
|
+
const recoveredLinearActivityContext = await recoverLinearAgentActivityContext({
|
|
239
|
+
linearProvider: this.linearProvider,
|
|
240
|
+
projectId: issue.projectId,
|
|
241
|
+
agentSessionId: issue.agentSessionId,
|
|
242
|
+
context: baseContext,
|
|
243
|
+
issueKey: issue.issueKey,
|
|
244
|
+
logger: this.logger,
|
|
245
|
+
});
|
|
246
|
+
const baseContextWithRecoveredActivity = recoveredLinearActivityContext
|
|
247
|
+
? { ...baseContext, ...recoveredLinearActivityContext }
|
|
248
|
+
: baseContext;
|
|
237
249
|
const coordinationContext = runType === "implementation"
|
|
238
250
|
? this.buildRelatedIssueContext(issue)
|
|
239
251
|
: undefined;
|
|
240
252
|
const effectiveContext = coordinationContext
|
|
241
|
-
? { ...coordinationContext, ...
|
|
242
|
-
:
|
|
253
|
+
? { ...coordinationContext, ...baseContextWithRecoveredActivity }
|
|
254
|
+
: baseContextWithRecoveredActivity;
|
|
243
255
|
const sourceHeadSha = typeof effectiveContext?.failureHeadSha === "string"
|
|
244
256
|
? effectiveContext.failureHeadSha
|
|
245
257
|
: typeof effectiveContext?.headSha === "string"
|