patchrelay 0.8.9 → 0.9.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.
Files changed (60) hide show
  1. package/README.md +64 -62
  2. package/dist/agent-session-plan.js +17 -17
  3. package/dist/build-info.json +3 -3
  4. package/dist/cli/args.js +1 -1
  5. package/dist/cli/commands/issues.js +18 -18
  6. package/dist/cli/data.js +109 -298
  7. package/dist/cli/formatters/text.js +22 -28
  8. package/dist/cli/help.js +7 -7
  9. package/dist/cli/index.js +3 -3
  10. package/dist/config.js +13 -166
  11. package/dist/db/migrations.js +46 -154
  12. package/dist/db.js +369 -45
  13. package/dist/factory-state.js +55 -0
  14. package/dist/github-webhook-handler.js +199 -0
  15. package/dist/github-webhooks.js +166 -0
  16. package/dist/hook-runner.js +28 -0
  17. package/dist/http.js +48 -22
  18. package/dist/issue-query-service.js +33 -38
  19. package/dist/linear-workflow.js +5 -118
  20. package/dist/preflight.js +1 -6
  21. package/dist/project-resolution.js +12 -1
  22. package/dist/run-orchestrator.js +446 -0
  23. package/dist/{stage-reporting.js → run-reporting.js} +11 -13
  24. package/dist/service-runtime.js +12 -61
  25. package/dist/service-webhooks.js +7 -52
  26. package/dist/service.js +39 -61
  27. package/dist/webhook-handler.js +387 -0
  28. package/dist/webhook-installation-handler.js +3 -8
  29. package/package.json +2 -1
  30. package/dist/db/authoritative-ledger-store.js +0 -536
  31. package/dist/db/issue-projection-store.js +0 -54
  32. package/dist/db/issue-workflow-coordinator.js +0 -320
  33. package/dist/db/issue-workflow-store.js +0 -194
  34. package/dist/db/run-report-store.js +0 -33
  35. package/dist/db/stage-event-store.js +0 -33
  36. package/dist/db/webhook-event-store.js +0 -59
  37. package/dist/db-ports.js +0 -5
  38. package/dist/ledger-ports.js +0 -1
  39. package/dist/reconciliation-action-applier.js +0 -68
  40. package/dist/reconciliation-actions.js +0 -1
  41. package/dist/reconciliation-engine.js +0 -350
  42. package/dist/reconciliation-snapshot-builder.js +0 -135
  43. package/dist/reconciliation-types.js +0 -1
  44. package/dist/service-stage-finalizer.js +0 -753
  45. package/dist/service-stage-runner.js +0 -336
  46. package/dist/service-webhook-processor.js +0 -411
  47. package/dist/stage-agent-activity-publisher.js +0 -59
  48. package/dist/stage-event-ports.js +0 -1
  49. package/dist/stage-failure.js +0 -92
  50. package/dist/stage-handoff.js +0 -107
  51. package/dist/stage-launch.js +0 -84
  52. package/dist/stage-lifecycle-publisher.js +0 -284
  53. package/dist/stage-turn-input-dispatcher.js +0 -104
  54. package/dist/webhook-agent-session-handler.js +0 -228
  55. package/dist/webhook-comment-handler.js +0 -141
  56. package/dist/webhook-desired-stage-recorder.js +0 -122
  57. package/dist/webhook-event-ports.js +0 -1
  58. package/dist/workflow-policy.js +0 -149
  59. package/dist/workflow-ports.js +0 -1
  60. /package/dist/{installation-ports.js → github-types.js} +0 -0
@@ -1,68 +0,0 @@
1
- export class ReconciliationActionApplier {
2
- callbacks;
3
- constructor(callbacks) {
4
- this.callbacks = callbacks;
5
- }
6
- async apply(params) {
7
- const { snapshot, decision } = params;
8
- const threadId = snapshot.runLease.threadId;
9
- const turnId = snapshot.runLease.turnId;
10
- const obligationTargetAction = decision.actions.find((action) => action.type === "deliver_obligation" || action.type === "route_obligation");
11
- const targetThreadId = obligationTargetAction?.type === "deliver_obligation" || obligationTargetAction?.type === "route_obligation"
12
- ? obligationTargetAction.threadId
13
- : threadId;
14
- const targetTurnId = obligationTargetAction?.type === "deliver_obligation" || obligationTargetAction?.type === "route_obligation"
15
- ? obligationTargetAction.turnId
16
- : turnId;
17
- const clearAction = decision.actions.find((action) => action.type === "clear_active_run" || action.type === "release_issue_ownership");
18
- const nextLifecycleStatus = clearAction?.type === "clear_active_run" || clearAction?.type === "release_issue_ownership"
19
- ? clearAction.nextLifecycleStatus
20
- : undefined;
21
- if (decision.outcome === "launch") {
22
- this.callbacks.enqueueIssue(snapshot.runLease.projectId, snapshot.runLease.linearIssueId);
23
- return;
24
- }
25
- if (decision.outcome === "continue") {
26
- if (targetThreadId) {
27
- await this.callbacks.deliverPendingObligations(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, targetThreadId, targetTurnId);
28
- }
29
- return;
30
- }
31
- const completedAction = decision.actions.find((action) => action.type === "mark_run_completed");
32
- if (decision.outcome === "complete" || (decision.outcome === "release" && completedAction?.type === "mark_run_completed")) {
33
- const liveThread = snapshot.input.live?.codex?.status === "found" ? snapshot.input.live.codex.thread : undefined;
34
- if (!liveThread) {
35
- return;
36
- }
37
- const latestTurn = liveThread.turns.at(-1);
38
- this.callbacks.completeRun(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, liveThread, {
39
- threadId: liveThread.id,
40
- ...(latestTurn?.id ? { turnId: latestTurn.id } : {}),
41
- ...(nextLifecycleStatus ? { nextLifecycleStatus } : {}),
42
- });
43
- return;
44
- }
45
- if (decision.outcome === "fail" || decision.outcome === "release") {
46
- const failedAction = decision.actions.find((action) => action.type === "mark_run_failed");
47
- if (decision.outcome === "release" && failedAction?.type !== "mark_run_failed") {
48
- const releasedAction = decision.actions.find((action) => action.type === "release_issue_ownership");
49
- if (releasedAction?.type !== "release_issue_ownership") {
50
- return;
51
- }
52
- await this.callbacks.releaseRunDuringReconciliation(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, {
53
- runId: snapshot.runLease.id,
54
- ...(threadId ? { threadId } : {}),
55
- ...(turnId ? { turnId } : {}),
56
- ...(nextLifecycleStatus ? { nextLifecycleStatus } : {}),
57
- ...(snapshot.input.live?.linear?.status === "known" && snapshot.input.live.linear.issue?.stateName
58
- ? { currentLinearState: snapshot.input.live.linear.issue.stateName }
59
- : {}),
60
- });
61
- return;
62
- }
63
- await this.callbacks.failRunDuringReconciliation(snapshot.runLease.projectId, snapshot.runLease.linearIssueId, failedAction?.type === "mark_run_failed" && failedAction.threadId
64
- ? failedAction.threadId
65
- : threadId ?? `missing-thread-${snapshot.runLease.id}`, decision.reasons[0] ?? "Thread was not found during startup reconciliation", ...(failedAction?.type === "mark_run_failed" && failedAction.turnId ? [{ turnId: failedAction.turnId }] : []));
66
- }
67
- }
68
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,350 +0,0 @@
1
- export class ReconciliationEngine {
2
- reconcile(input) {
3
- const actions = [];
4
- const issue = input.issue;
5
- const policy = input.policy ?? {};
6
- const liveLinear = input.live?.linear ?? { status: "unknown" };
7
- const liveCodex = input.live?.codex ?? { status: "unknown" };
8
- const obligations = relevantObligations(issue, input.obligations ?? []);
9
- if (!issue.activeRun) {
10
- if (!issue.desiredStage) {
11
- return {
12
- outcome: "noop",
13
- reasons: ["issue has no active run and no desired stage"],
14
- actions,
15
- };
16
- }
17
- return {
18
- outcome: "launch",
19
- reasons: [`desired stage ${issue.desiredStage} is ready to launch`],
20
- actions: [
21
- {
22
- type: "launch_desired_stage",
23
- projectId: issue.projectId,
24
- linearIssueId: issue.linearIssueId,
25
- stage: issue.desiredStage,
26
- reason: "desired stage exists without an active run",
27
- },
28
- ],
29
- };
30
- }
31
- if (needsLinearState(issue, policy, liveLinear)) {
32
- actions.push({
33
- type: "read_linear_issue",
34
- projectId: issue.projectId,
35
- linearIssueId: issue.linearIssueId,
36
- reason: "active reconciliation needs the live Linear state",
37
- });
38
- }
39
- if (needsCodexState(issue.activeRun, liveCodex)) {
40
- actions.push({
41
- type: "read_codex_thread",
42
- projectId: issue.projectId,
43
- linearIssueId: issue.linearIssueId,
44
- runId: issue.activeRun.id,
45
- threadId: issue.activeRun.threadId,
46
- reason: "active reconciliation needs the live Codex thread",
47
- });
48
- }
49
- if (actions.length > 0) {
50
- return {
51
- outcome: "hydrate_live_state",
52
- reasons: ["reconciliation needs fresh live state before deciding"],
53
- actions,
54
- };
55
- }
56
- return reconcileActiveRun({
57
- issue,
58
- liveLinear,
59
- liveCodex,
60
- obligations,
61
- policy,
62
- });
63
- }
64
- }
65
- export function reconcileIssue(input) {
66
- return new ReconciliationEngine().reconcile(input);
67
- }
68
- function reconcileActiveRun(params) {
69
- const { issue, liveLinear, liveCodex, obligations, policy } = params;
70
- const run = issue.activeRun;
71
- const authoritativeStopState = resolveAuthoritativeStopState(liveLinear);
72
- if (authoritativeStopState) {
73
- return {
74
- outcome: "release",
75
- reasons: [`live Linear state is already ${authoritativeStopState.stateName}`],
76
- actions: [
77
- {
78
- type: "release_issue_ownership",
79
- projectId: issue.projectId,
80
- linearIssueId: issue.linearIssueId,
81
- runId: run.id,
82
- nextLifecycleStatus: authoritativeStopState.lifecycleStatus,
83
- reason: `live Linear state is already ${authoritativeStopState.stateName}`,
84
- },
85
- ],
86
- };
87
- }
88
- if (run.status === "queued") {
89
- return {
90
- outcome: "launch",
91
- reasons: ["queued run has not been materialized yet"],
92
- actions: [
93
- {
94
- type: "launch_desired_stage",
95
- projectId: issue.projectId,
96
- linearIssueId: issue.linearIssueId,
97
- stage: run.stage,
98
- runId: run.id,
99
- reason: "active run is queued and should be launched",
100
- },
101
- ],
102
- };
103
- }
104
- if (!run.threadId) {
105
- return failRun(issue, run, liveLinear, policy, "active run is missing a persisted thread id");
106
- }
107
- if (liveCodex.status === "error") {
108
- return {
109
- outcome: "continue",
110
- reasons: [liveCodex.errorMessage ?? "codex thread lookup failed"],
111
- actions: [
112
- {
113
- type: "await_codex_retry",
114
- projectId: issue.projectId,
115
- linearIssueId: issue.linearIssueId,
116
- runId: run.id,
117
- reason: liveCodex.errorMessage ?? "codex thread lookup failed",
118
- },
119
- ],
120
- };
121
- }
122
- if (liveCodex.status === "missing" || !liveCodex.thread) {
123
- return failRun(issue, run, liveLinear, policy, "thread was not found during reconciliation");
124
- }
125
- const latestTurn = latestThreadTurn(liveCodex.thread);
126
- const targetTurnId = latestTurn?.id ?? run.turnId;
127
- if (!latestTurn || latestTurn.status === "inProgress") {
128
- const actions = routePendingObligations(issue, run, obligations, liveCodex.thread.id, targetTurnId);
129
- actions.push({
130
- type: "keep_run_active",
131
- projectId: issue.projectId,
132
- linearIssueId: issue.linearIssueId,
133
- runId: run.id,
134
- reason: !latestTurn ? "thread has not produced a turn yet" : "latest turn is still in progress",
135
- });
136
- return {
137
- outcome: "continue",
138
- reasons: [!latestTurn ? "thread has no completed turns yet" : "latest turn is still in progress"],
139
- actions,
140
- };
141
- }
142
- if (latestTurn.status !== "completed") {
143
- return failRun(issue, run, liveLinear, policy, "thread completed reconciliation in a failed state", latestTurn.id);
144
- }
145
- const actions = [
146
- {
147
- type: "mark_run_completed",
148
- projectId: issue.projectId,
149
- linearIssueId: issue.linearIssueId,
150
- runId: run.id,
151
- threadId: liveCodex.thread.id,
152
- ...(latestTurn.id ? { turnId: latestTurn.id } : {}),
153
- reason: "latest turn completed successfully during reconciliation",
154
- },
155
- ];
156
- if (shouldAwaitHandoff(liveLinear, policy)) {
157
- actions.push({
158
- type: "clear_active_run",
159
- projectId: issue.projectId,
160
- linearIssueId: issue.linearIssueId,
161
- runId: run.id,
162
- nextLifecycleStatus: "paused",
163
- reason: "stage completed while the issue still matches the service-owned active Linear state",
164
- }, {
165
- type: "refresh_status_comment",
166
- projectId: issue.projectId,
167
- linearIssueId: issue.linearIssueId,
168
- runId: run.id,
169
- ...(issue.statusCommentId ? { commentId: issue.statusCommentId } : {}),
170
- mode: "awaiting_handoff",
171
- reason: "stage completed and should publish an awaiting handoff status",
172
- });
173
- return {
174
- outcome: "complete",
175
- reasons: ["stage completed and should pause for human handoff"],
176
- actions,
177
- };
178
- }
179
- actions.push({
180
- type: "release_issue_ownership",
181
- projectId: issue.projectId,
182
- linearIssueId: issue.linearIssueId,
183
- runId: run.id,
184
- nextLifecycleStatus: "completed",
185
- reason: "stage completed after the live Linear state moved on",
186
- });
187
- return {
188
- outcome: "release",
189
- reasons: ["stage completed and the live Linear state already moved on"],
190
- actions,
191
- };
192
- }
193
- function failRun(issue, run, liveLinear, policy, message, turnId) {
194
- const actions = [
195
- {
196
- type: "mark_run_failed",
197
- projectId: issue.projectId,
198
- linearIssueId: issue.linearIssueId,
199
- runId: run.id,
200
- ...(run.threadId ? { threadId: run.threadId } : {}),
201
- ...(turnId ? { turnId } : run.turnId ? { turnId: run.turnId } : {}),
202
- reason: message,
203
- },
204
- ];
205
- if (shouldFailBack(liveLinear, policy)) {
206
- actions.push({
207
- type: "sync_linear_failure",
208
- projectId: issue.projectId,
209
- linearIssueId: issue.linearIssueId,
210
- runId: run.id,
211
- ...(policy.activeLinearStateName ? { expectedStateName: policy.activeLinearStateName } : {}),
212
- ...(policy.fallbackLinearStateName ? { fallbackStateName: policy.fallbackLinearStateName } : {}),
213
- message,
214
- }, {
215
- type: "clear_active_run",
216
- projectId: issue.projectId,
217
- linearIssueId: issue.linearIssueId,
218
- runId: run.id,
219
- nextLifecycleStatus: "failed",
220
- reason: "run failed while PatchRelay still owned the expected active Linear state",
221
- });
222
- if (issue.statusCommentId) {
223
- actions.push({
224
- type: "refresh_status_comment",
225
- projectId: issue.projectId,
226
- linearIssueId: issue.linearIssueId,
227
- runId: run.id,
228
- commentId: issue.statusCommentId,
229
- mode: "failed",
230
- reason: "run failed and should refresh the service-owned status comment",
231
- });
232
- }
233
- return {
234
- outcome: "fail",
235
- reasons: [message],
236
- actions,
237
- };
238
- }
239
- actions.push({
240
- type: "release_issue_ownership",
241
- projectId: issue.projectId,
242
- linearIssueId: issue.linearIssueId,
243
- runId: run.id,
244
- nextLifecycleStatus: "failed",
245
- reason: "run failed after the live Linear state moved on",
246
- });
247
- return {
248
- outcome: "release",
249
- reasons: [message, "live Linear state no longer matches the expected service-owned active state"],
250
- actions,
251
- };
252
- }
253
- function routePendingObligations(issue, run, obligations, threadId, turnId) {
254
- if (!turnId) {
255
- return [];
256
- }
257
- const actions = [];
258
- for (const obligation of obligations) {
259
- const needsRouting = obligation.threadId !== threadId || obligation.turnId !== turnId;
260
- if (needsRouting) {
261
- actions.push({
262
- type: "route_obligation",
263
- projectId: issue.projectId,
264
- linearIssueId: issue.linearIssueId,
265
- obligationId: obligation.id,
266
- runId: run.id,
267
- threadId,
268
- turnId,
269
- reason: "pending obligation should target the latest live turn",
270
- });
271
- }
272
- actions.push({
273
- type: "deliver_obligation",
274
- projectId: issue.projectId,
275
- linearIssueId: issue.linearIssueId,
276
- obligationId: obligation.id,
277
- runId: run.id,
278
- threadId,
279
- turnId,
280
- reason: "pending obligation can be delivered to the active turn",
281
- });
282
- }
283
- return actions;
284
- }
285
- function needsLinearState(issue, policy, liveLinear) {
286
- if (!issue.activeRun) {
287
- return false;
288
- }
289
- if (!policy.activeLinearStateName && !policy.fallbackLinearStateName) {
290
- return false;
291
- }
292
- return liveLinear.status === "unknown";
293
- }
294
- function needsCodexState(run, liveCodex) {
295
- if (!run.threadId) {
296
- return false;
297
- }
298
- return liveCodex.status === "unknown";
299
- }
300
- function shouldFailBack(liveLinear, policy) {
301
- return matchesActiveLinearOwnership(liveLinear, policy);
302
- }
303
- function shouldAwaitHandoff(liveLinear, policy) {
304
- return matchesActiveLinearOwnership(liveLinear, policy);
305
- }
306
- function matchesActiveLinearOwnership(liveLinear, policy) {
307
- if (!policy.activeLinearStateName) {
308
- return true;
309
- }
310
- if (liveLinear.status !== "known") {
311
- return false;
312
- }
313
- return liveLinear.issue?.stateName === policy.activeLinearStateName;
314
- }
315
- function resolveAuthoritativeStopState(liveLinear) {
316
- if (liveLinear.status !== "known" || !liveLinear.issue?.stateName) {
317
- return undefined;
318
- }
319
- const stateName = liveLinear.issue.stateName.trim();
320
- const normalizedName = stateName.toLowerCase();
321
- const normalizedType = liveLinear.issue.stateType?.trim().toLowerCase();
322
- if (normalizedType === "completed" || normalizedName === "done" || normalizedName === "completed" || normalizedName === "complete") {
323
- return {
324
- stateName,
325
- lifecycleStatus: "completed",
326
- };
327
- }
328
- if (normalizedName === "human needed") {
329
- return {
330
- stateName,
331
- lifecycleStatus: "paused",
332
- };
333
- }
334
- return undefined;
335
- }
336
- function relevantObligations(issue, obligations) {
337
- const activeRunId = issue.activeRun?.id;
338
- return obligations.filter((obligation) => {
339
- if (obligation.status === "completed" || obligation.status === "cancelled") {
340
- return false;
341
- }
342
- if (activeRunId === undefined) {
343
- return false;
344
- }
345
- return obligation.runId === undefined || obligation.runId === activeRunId;
346
- });
347
- }
348
- function latestThreadTurn(thread) {
349
- return thread.turns.at(-1);
350
- }
@@ -1,135 +0,0 @@
1
- import { resolveWorkflowStageConfig } from "./workflow-policy.js";
2
- import { safeJsonParse } from "./utils.js";
3
- export async function buildReconciliationSnapshot(params) {
4
- const runLease = params.stores.runLeases.getRunLease(params.runLeaseId);
5
- if (!runLease) {
6
- return undefined;
7
- }
8
- const issueControl = params.stores.issueControl.getIssueControl(runLease.projectId, runLease.linearIssueId);
9
- if (!issueControl) {
10
- return undefined;
11
- }
12
- const workspaceOwnership = params.stores.workspaceOwnership.getWorkspaceOwnership(runLease.workspaceOwnershipId);
13
- const project = params.config.projects.find((candidate) => candidate.id === runLease.projectId);
14
- const workflowConfig = project ? resolveWorkflowStageConfig(project, runLease.stage, issueControl.selectedWorkflowId) : undefined;
15
- const liveLinear = project
16
- ? await params.linearProvider
17
- .forProject(runLease.projectId)
18
- .then((linear) => linear
19
- ? linear.getIssue(runLease.linearIssueId).then((issue) => issue.stateName
20
- ? {
21
- status: "known",
22
- issue: {
23
- id: issue.id,
24
- stateName: issue.stateName,
25
- ...(() => {
26
- const currentState = issue.workflowStates?.find((state) => state.name === issue.stateName);
27
- return currentState?.type ? { stateType: currentState.type } : {};
28
- })(),
29
- },
30
- }
31
- : ({ status: "unknown" }))
32
- : ({ status: "unknown" }))
33
- .catch(() => ({ status: "unknown" }))
34
- : ({ status: "unknown" });
35
- const liveCodex = runLease.threadId
36
- ? await hydrateLiveCodexState({
37
- codex: params.codex,
38
- threadId: runLease.threadId,
39
- ...(workspaceOwnership?.worktreePath ? { cwd: workspaceOwnership.worktreePath } : {}),
40
- })
41
- : ({ status: "unknown" });
42
- const obligations = params.stores.obligations
43
- .listPendingObligations({ runLeaseId: runLease.id, includeInProgress: true })
44
- .map((obligation) => {
45
- const payload = safeJsonParse(obligation.payloadJson);
46
- return {
47
- id: obligation.id,
48
- kind: obligation.kind,
49
- status: obligation.status,
50
- ...(obligation.runLeaseId !== undefined ? { runId: obligation.runLeaseId } : {}),
51
- ...(obligation.threadId ? { threadId: obligation.threadId } : {}),
52
- ...(obligation.turnId ? { turnId: obligation.turnId } : {}),
53
- ...(payload !== undefined ? { payload } : {}),
54
- };
55
- });
56
- return {
57
- issueControl,
58
- runLease,
59
- ...(workspaceOwnership ? { workspaceOwnership } : {}),
60
- input: {
61
- issue: {
62
- projectId: runLease.projectId,
63
- linearIssueId: runLease.linearIssueId,
64
- ...(issueControl.desiredStage ? { desiredStage: issueControl.desiredStage } : {}),
65
- lifecycleStatus: issueControl.lifecycleStatus,
66
- ...(issueControl.serviceOwnedCommentId ? { statusCommentId: issueControl.serviceOwnedCommentId } : {}),
67
- activeRun: {
68
- id: runLease.id,
69
- stage: runLease.stage,
70
- status: runLease.status,
71
- ...(runLease.threadId ? { threadId: runLease.threadId } : {}),
72
- ...(runLease.turnId ? { turnId: runLease.turnId } : {}),
73
- ...(runLease.parentThreadId ? { parentThreadId: runLease.parentThreadId } : {}),
74
- },
75
- },
76
- ...(obligations.length > 0 ? { obligations } : {}),
77
- ...(workflowConfig
78
- ? {
79
- policy: {
80
- ...(workflowConfig.activeState ? { activeLinearStateName: workflowConfig.activeState } : {}),
81
- ...(workflowConfig.fallbackState ? { fallbackLinearStateName: workflowConfig.fallbackState } : {}),
82
- },
83
- }
84
- : {}),
85
- live: {
86
- linear: liveLinear,
87
- codex: liveCodex,
88
- },
89
- },
90
- };
91
- }
92
- async function hydrateLiveCodexState(params) {
93
- try {
94
- const thread = await params.codex.readThread(params.threadId, true);
95
- if (latestThreadTurn(thread)?.status === "interrupted" && params.cwd) {
96
- const resumedThread = await tryResumeThread(params.codex, params.threadId, params.cwd);
97
- if (resumedThread) {
98
- return { status: "found", thread: resumedThread };
99
- }
100
- }
101
- return { status: "found", thread };
102
- }
103
- catch (error) {
104
- const mapped = mapCodexReadFailure(error);
105
- if (mapped.status === "missing" && params.cwd) {
106
- const resumedThread = await tryResumeThread(params.codex, params.threadId, params.cwd);
107
- if (resumedThread) {
108
- return { status: "found", thread: resumedThread };
109
- }
110
- }
111
- return mapped;
112
- }
113
- }
114
- async function tryResumeThread(codex, threadId, cwd) {
115
- try {
116
- return await codex.resumeThread(threadId, cwd);
117
- }
118
- catch {
119
- return undefined;
120
- }
121
- }
122
- function latestThreadTurn(thread) {
123
- return thread.turns.at(-1);
124
- }
125
- function mapCodexReadFailure(error) {
126
- const message = error instanceof Error ? error.message : String(error);
127
- const normalized = message.trim().toLowerCase();
128
- if (normalized.includes("not found") || normalized.includes("missing")) {
129
- return { status: "missing" };
130
- }
131
- return {
132
- status: "error",
133
- errorMessage: message,
134
- };
135
- }
@@ -1 +0,0 @@
1
- export {};