agents-relay 1.0.26 → 1.0.28
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/cli.js +22 -6
- package/dist/planner.js +9 -3
- package/dist/reconciler.js +9 -4
- package/dist/store.js +12 -1
- package/dist/types.js +3 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
6
6
|
import { randomUUID } from 'node:crypto';
|
|
7
7
|
import { dashboardManagedJobs, InMemoryStore, GitHubStore, resolvePullRequest, listManagedGitHubJobs } from './store.js';
|
|
8
8
|
import { githubAuthContext } from './github-auth.js';
|
|
9
|
+
import { taskOverallTimeoutMs } from './types.js';
|
|
9
10
|
import { Reconciler } from './reconciler.js';
|
|
10
11
|
import { CodexAdapter, ChatGptAdapter } from './adapters.js';
|
|
11
12
|
import { CodexThreadContinuation, CommandContinuation, WebhookContinuation } from './continuation.js';
|
|
@@ -61,7 +62,7 @@ Example:
|
|
|
61
62
|
'job create': `Create or reuse an open PR and persist one durable job marker.
|
|
62
63
|
|
|
63
64
|
Required: --repo OWNER/REPO, --id JOB_ID, and --head BRANCH.
|
|
64
|
-
Common: --base BRANCH (default: main), --title TEXT, --body TEXT, --mode fixed|autonomous.
|
|
65
|
+
Common: --base BRANCH (default: main), --title TEXT, --body TEXT, --mode fixed|autonomous, --no-auto-merge.
|
|
65
66
|
With --start, also require --adapter codex|chatgpt, --model MODEL, --output task-pr|file,
|
|
66
67
|
and --input TEXT. The initial task ID defaults to 'initial'; use --task-id to override it.
|
|
67
68
|
|
|
@@ -70,7 +71,7 @@ Example:
|
|
|
70
71
|
'job adopt': `Adopt an existing PR as a durable job.
|
|
71
72
|
|
|
72
73
|
Required: --repo OWNER/REPO, --pr NUMBER, and --id JOB_ID.
|
|
73
|
-
Common: --title TEXT, --objective TEXT, --mode fixed|autonomous.
|
|
74
|
+
Common: --title TEXT, --objective TEXT, --mode fixed|autonomous, --no-auto-merge.
|
|
74
75
|
|
|
75
76
|
Example:
|
|
76
77
|
npx agents-relay job adopt --repo OWNER/REPO --pr 12 --id job-12 --title "Objective"`,
|
|
@@ -90,7 +91,7 @@ Actions:
|
|
|
90
91
|
'task update': `Update mutable durable task fields in place.
|
|
91
92
|
|
|
92
93
|
Required: --task-id ID and the normal job storage options.
|
|
93
|
-
Mutable while not RUNNING/SUCCEEDED/CANCELLED: --adapter, --priority, --input, --project, --agent, --parent, --deps, --capabilities, --timeout, --max-attempts, --provider, --model, --profile, --reasoning, --cwd, --chatgpt-project, --chatgpt-tab-close-policy.
|
|
94
|
+
Mutable while not RUNNING/SUCCEEDED/CANCELLED: --adapter, --priority, --story-size, --input, --project, --agent, --parent, --deps, --capabilities, --timeout, --max-attempts, --provider, --model, --profile, --reasoning, --cwd, --chatgpt-project, --chatgpt-tab-close-policy.
|
|
94
95
|
Use --clear-routing to remove routing metadata. A model update requires --model; provider defaults to the existing provider or openai.
|
|
95
96
|
|
|
96
97
|
Example:
|
|
@@ -224,6 +225,8 @@ catch (error) {
|
|
|
224
225
|
} }
|
|
225
226
|
function priority(args, fallback = 'P2') { const value = arg(args, '--priority', fallback); if (!['P0', 'P1', 'P2', 'P3'].includes(value))
|
|
226
227
|
throw new Error('--priority must be P0, P1, P2, or P3'); return value; }
|
|
228
|
+
function storySize(args, fallback = 'M') { const value = arg(args, '--story-size', fallback).toUpperCase(); if (!['S', 'M', 'L'].includes(value))
|
|
229
|
+
throw new Error('--story-size must be S, M, or L'); return value; }
|
|
227
230
|
export function executionMode(args, fallback = 'fixed') { const value = arg(args, '--mode', arg(args, '--execution-mode', fallback)); if (value !== 'fixed' && value !== 'autonomous')
|
|
228
231
|
throw new Error('--mode must be fixed or autonomous'); return value; }
|
|
229
232
|
export function chatGptTabClosePolicy(args) {
|
|
@@ -247,7 +250,7 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
|
|
|
247
250
|
if (!(error instanceof Error) || !error.message.includes('has no durable'))
|
|
248
251
|
throw error;
|
|
249
252
|
const now = new Date().toISOString();
|
|
250
|
-
job = { id: options.id, title: options.title || pr.title || 'Agents Relay job', objective: options.objective || pr.body || undefined, description: pr.body || undefined, priority: options.priority ?? 'P2', executionMode: options.executionMode ?? 'fixed', prNumber: pr.number, repository: repositoryName, state: 'OPEN', continuation: options.continuation ?? null, createdAt: now, updatedAt: now, tasks: [] };
|
|
253
|
+
job = { id: options.id, title: options.title || pr.title || 'Agents Relay job', objective: options.objective || pr.body || undefined, description: pr.body || undefined, priority: options.priority ?? 'P2', executionMode: options.executionMode ?? 'fixed', autoMerge: options.autoMerge ?? true, prNumber: pr.number, repository: repositoryName, state: 'OPEN', continuation: options.continuation ?? null, createdAt: now, updatedAt: now, tasks: [] };
|
|
251
254
|
}
|
|
252
255
|
if (!job.description && pr.body)
|
|
253
256
|
job.description = pr.body;
|
|
@@ -255,6 +258,8 @@ export async function ensureManagedGitHubJob(client, repositoryName, trustedAuth
|
|
|
255
258
|
job.objective = options.objective;
|
|
256
259
|
if (options.executionMode)
|
|
257
260
|
job.executionMode = options.executionMode;
|
|
261
|
+
if (options.autoMerge !== undefined)
|
|
262
|
+
job.autoMerge = options.autoMerge;
|
|
258
263
|
if (options.priority)
|
|
259
264
|
job.priority = options.priority;
|
|
260
265
|
else if (!job.priority)
|
|
@@ -283,10 +288,13 @@ function workerTaskFromArgs(job, args, now, requireInput = false) {
|
|
|
283
288
|
throw new Error('--output file requires --output-path');
|
|
284
289
|
const routing = { provider: arg(args, '--provider', 'openai'), model, profile: arg(args, '--profile') || undefined, reasoning: arg(args, '--reasoning') || undefined, cwd: arg(args, '--cwd') || undefined, projectId: arg(args, '--chatgpt-project') || undefined, decidedBy: 'cli', decidedAt: now };
|
|
285
290
|
const closePolicy = hasArg(args, '--chatgpt-tab-close-policy') ? chatGptTabClosePolicy(args) : undefined;
|
|
291
|
+
const size = storySize(args);
|
|
292
|
+
const timeoutMs = hasArg(args, '--timeout') ? integerArg(args, '--timeout', taskOverallTimeoutMs(size, routing.reasoning)) : taskOverallTimeoutMs(size, routing.reasoning);
|
|
286
293
|
return {
|
|
287
294
|
jobId: job.id,
|
|
288
295
|
id: arg(args, '--task-id', 'initial'),
|
|
289
296
|
priority: priority(args, job.priority ?? 'P2'),
|
|
297
|
+
storySize: size,
|
|
290
298
|
projectName: arg(args, '--project') || undefined,
|
|
291
299
|
agentName: arg(args, '--agent') || undefined,
|
|
292
300
|
parentTaskId: arg(args, '--parent') || null,
|
|
@@ -308,7 +316,7 @@ function workerTaskFromArgs(job, args, now, requireInput = false) {
|
|
|
308
316
|
threadId: null,
|
|
309
317
|
result: null,
|
|
310
318
|
error: null,
|
|
311
|
-
timeoutMs
|
|
319
|
+
timeoutMs,
|
|
312
320
|
createdAt: now,
|
|
313
321
|
updatedAt: now
|
|
314
322
|
};
|
|
@@ -333,6 +341,11 @@ async function runTaskCommand(action, args) {
|
|
|
333
341
|
}
|
|
334
342
|
if (hasArg(args, '--priority'))
|
|
335
343
|
task.priority = priority(args, task.priority ?? job.priority ?? 'P2');
|
|
344
|
+
if (hasArg(args, '--story-size')) {
|
|
345
|
+
task.storySize = storySize(args, task.storySize ?? 'M');
|
|
346
|
+
if (!hasArg(args, '--timeout'))
|
|
347
|
+
task.timeoutMs = taskOverallTimeoutMs(task.storySize, task.routing?.reasoning);
|
|
348
|
+
}
|
|
336
349
|
if (hasArg(args, '--input'))
|
|
337
350
|
task.input = arg(args, '--input');
|
|
338
351
|
if (hasArg(args, '--project'))
|
|
@@ -369,6 +382,8 @@ async function runTaskCommand(action, args) {
|
|
|
369
382
|
decidedBy: 'cli:update',
|
|
370
383
|
decidedAt: new Date().toISOString()
|
|
371
384
|
};
|
|
385
|
+
if (hasArg(args, '--reasoning') && !hasArg(args, '--timeout'))
|
|
386
|
+
task.timeoutMs = taskOverallTimeoutMs(task.storySize ?? 'M', task.routing.reasoning);
|
|
372
387
|
}
|
|
373
388
|
task.updatedAt = new Date().toISOString();
|
|
374
389
|
await store.saveTask(task);
|
|
@@ -405,6 +420,7 @@ async function runJobCommand(action, args) {
|
|
|
405
420
|
objective: arg(args, '--objective') || undefined,
|
|
406
421
|
priority: priority(args),
|
|
407
422
|
executionMode: requestedExecutionMode(args),
|
|
423
|
+
autoMerge: !hasArg(args, '--no-auto-merge'),
|
|
408
424
|
continuation: continuation(args),
|
|
409
425
|
prNumber,
|
|
410
426
|
head: arg(args, '--head') || undefined,
|
|
@@ -518,7 +534,7 @@ async function main() {
|
|
|
518
534
|
}
|
|
519
535
|
if (command === 'init') {
|
|
520
536
|
const now = new Date().toISOString();
|
|
521
|
-
const job = { id: arg(args, '--id', randomUUID()), title: arg(args, '--title', 'Agents Relay job'), objective: arg(args, '--objective') || undefined, priority: priority(args), executionMode: executionMode(args), prNumber: Number(arg(args, '--pr', '0')), repository: arg(args, '--repo'), state: 'OPEN', continuation: continuation(args), createdAt: now, updatedAt: now, tasks: [] };
|
|
537
|
+
const job = { id: arg(args, '--id', randomUUID()), title: arg(args, '--title', 'Agents Relay job'), objective: arg(args, '--objective') || undefined, priority: priority(args), executionMode: executionMode(args), autoMerge: !hasArg(args, '--no-auto-merge'), prNumber: Number(arg(args, '--pr', '0')), repository: arg(args, '--repo'), state: 'OPEN', continuation: continuation(args), createdAt: now, updatedAt: now, tasks: [] };
|
|
522
538
|
let store;
|
|
523
539
|
let file = null;
|
|
524
540
|
if (arg(args, '--repo') && arg(args, '--pr'))
|
package/dist/planner.js
CHANGED
|
@@ -2,6 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
4
|
import { dirname, join } from 'node:path';
|
|
5
|
+
import { taskOverallTimeoutMs } from './types.js';
|
|
5
6
|
function record(value) {
|
|
6
7
|
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
7
8
|
throw new Error('Planner result must be an object');
|
|
@@ -83,17 +84,22 @@ export function parsePlannerResult(value) {
|
|
|
83
84
|
if (output.kind === 'file' && (typeof output.path !== 'string' || output.path.length === 0))
|
|
84
85
|
throw new Error(`Planner next_tasks[${index}].output.path is required for file output`);
|
|
85
86
|
const priority = task.priority === undefined ? undefined : normalizePlannerPriority(task.priority, `next_tasks[${index}].priority`);
|
|
87
|
+
const sizeValue = task.storySize === undefined ? 'M' : String(task.storySize).toUpperCase();
|
|
88
|
+
if (!['S', 'M', 'L'].includes(sizeValue))
|
|
89
|
+
throw new Error('Planner next_tasks[' + index + '].storySize must be S, M, or L');
|
|
90
|
+
const storySize = sizeValue;
|
|
86
91
|
const maxAttempts = task.maxAttempts === undefined ? 3 : task.maxAttempts;
|
|
87
|
-
const
|
|
92
|
+
const route = routing(task.routing, 'next_tasks[' + index + '].routing');
|
|
93
|
+
const timeoutMs = task.timeoutMs === undefined ? taskOverallTimeoutMs(storySize, route?.reasoning) : task.timeoutMs;
|
|
88
94
|
if (typeof maxAttempts !== 'number' || !Number.isInteger(maxAttempts) || maxAttempts < 1)
|
|
89
95
|
throw new Error(`Planner next_tasks[${index}].maxAttempts is invalid`);
|
|
90
96
|
if (typeof timeoutMs !== 'number' || !Number.isFinite(timeoutMs) || timeoutMs < 0)
|
|
91
97
|
throw new Error(`Planner next_tasks[${index}].timeoutMs is invalid`);
|
|
92
98
|
return {
|
|
93
|
-
id, input: task.input, priority,
|
|
99
|
+
id, input: task.input, priority, storySize,
|
|
94
100
|
projectName: optionalString(task.projectName, 'projectName'), agentName: optionalString(task.agentName, 'agentName'),
|
|
95
101
|
dependencies: stringArray(task.dependencies, 'dependencies'), capabilities: stringArray(task.capabilities, 'capabilities'),
|
|
96
|
-
adapter: adapter, output: output, routing:
|
|
102
|
+
adapter: adapter, output: output, routing: route,
|
|
97
103
|
continuation: task.continuation, maxAttempts, timeoutMs
|
|
98
104
|
};
|
|
99
105
|
});
|
package/dist/reconciler.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { storySizeTimeoutMs, taskOverallTimeoutMs } from './types.js';
|
|
2
3
|
import { schedule, transitionTask } from './scheduler.js';
|
|
3
4
|
import { LaunchFailure, StartAckTimeout } from './adapters.js';
|
|
4
5
|
import { eventFor } from './events.js';
|
|
@@ -203,7 +204,7 @@ export class Reconciler {
|
|
|
203
204
|
return;
|
|
204
205
|
const id = `final-review-${headSha.slice(0, 12)}`;
|
|
205
206
|
const now = new Date().toISOString();
|
|
206
|
-
const task = { jobId: job.id, id, kind: 'work', priority: job.priority, projectName: job.title, agentName: 'reviewer', parentTaskId: null, dependencies: job.tasks.filter(item => item.agentName !== 'reviewer' && !this.isSupersededTask(item)).map(item => item.id), capabilities: ['review'], adapter: this.options.reviewer?.adapter ?? 'codex', input: `Review PR #${job.prNumber} at head SHA ${headSha}. Objective: ${job.objective ?? job.description ?? job.title}. First publish task.started as the worker start acknowledgement with data.worker_owned=true, then verify the deterministic pre-review gates have passed and inspect the completed work. Emit review.started, then exactly one of review.approved, review.changes_required, or review.blocked with data.review.headSha set to ${headSha}. Do not modify code
|
|
207
|
+
const task = { jobId: job.id, id, kind: 'work', priority: job.priority, storySize: 'M', projectName: job.title, agentName: 'reviewer', parentTaskId: null, dependencies: job.tasks.filter(item => item.agentName !== 'reviewer' && !this.isSupersededTask(item)).map(item => item.id), capabilities: ['review'], adapter: this.options.reviewer?.adapter ?? 'codex', input: `Review PR #${job.prNumber} at head SHA ${headSha}. Objective: ${job.objective ?? job.description ?? job.title}. First publish task.started as the worker start acknowledgement with data.worker_owned=true, then verify the deterministic pre-review gates have passed and inspect the completed work. Emit review.started, then exactly one of review.approved, review.changes_required, or review.blocked with data.review.headSha set to ${headSha}. Do not modify code. When auto-merge is enabled, Relay will merge this exact reviewed head after review.approved.`, output: { kind: 'task_pr' }, routing: this.options.reviewer?.routing, continuation: null, continuationDeliveredAt: null, reviewHeadSha: headSha, state: 'QUEUED', attempt: 0, maxAttempts: this.options.reviewer?.maxAttempts ?? 2, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: this.options.reviewer?.timeoutMs ?? storySizeTimeoutMs('M'), createdAt: now, updatedAt: now };
|
|
207
208
|
await this.store.appendTask(task);
|
|
208
209
|
job.review = { outcome: 'pending', headSha, taskId: id, evidence: null, updatedAt: now };
|
|
209
210
|
job.state = 'RUNNING';
|
|
@@ -246,6 +247,10 @@ export class Reconciler {
|
|
|
246
247
|
}
|
|
247
248
|
job.updatedAt = job.review.updatedAt;
|
|
248
249
|
await this.store.saveJob(job);
|
|
250
|
+
if (outcome === 'approved' && job.autoMerge !== false && this.store.mergePullRequest) {
|
|
251
|
+
await this.store.mergePullRequest(headSha);
|
|
252
|
+
await this.emit(eventFor(job.id, task.id, task.parentTaskId, 'review.merged', 'succeeded', 'Reviewer-approved PR #' + job.prNumber + ' merged', 'user', { headSha, autoMerge: true }, { agent: 'reviewer', component: 'agents-relay' }));
|
|
253
|
+
}
|
|
249
254
|
}
|
|
250
255
|
async recoverLostExecutions(job, now) {
|
|
251
256
|
for (const task of job.tasks.filter(item => item.state === 'RUNNING')) {
|
|
@@ -297,7 +302,7 @@ export class Reconciler {
|
|
|
297
302
|
id = `planner-${number}`;
|
|
298
303
|
}
|
|
299
304
|
const now = new Date().toISOString();
|
|
300
|
-
const task = { jobId: job.id, id, kind: 'planner', agentName: 'planner', parentTaskId: latest?.id ?? null, dependencies: [], capabilities: ['planner'], adapter: 'orchestrator', input: plannerInput({ job, tasks: job.tasks, objective: job.objective ?? job.description ?? job.title, previousPlannerTaskId: latest?.id ?? null }), continuation: null, continuationDeliveredAt: null, state: 'QUEUED', attempt: 0, maxAttempts: 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: this.options.plannerTimeoutMs ??
|
|
305
|
+
const task = { jobId: job.id, id, kind: 'planner', storySize: 'M', agentName: 'planner', parentTaskId: latest?.id ?? null, dependencies: [], capabilities: ['planner'], adapter: 'orchestrator', input: plannerInput({ job, tasks: job.tasks, objective: job.objective ?? job.description ?? job.title, previousPlannerTaskId: latest?.id ?? null }), continuation: null, continuationDeliveredAt: null, state: 'QUEUED', attempt: 0, maxAttempts: 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: this.options.plannerTimeoutMs ?? storySizeTimeoutMs('M'), createdAt: now, updatedAt: now };
|
|
301
306
|
await this.store.appendTask(task);
|
|
302
307
|
await this.emit(eventFor(job.id, task.id, task.parentTaskId, 'planner.created', 'queued', `Planner task ${task.id} created`, 'orchestrator'));
|
|
303
308
|
return this.store.load(job.id);
|
|
@@ -492,7 +497,7 @@ export class Reconciler {
|
|
|
492
497
|
}
|
|
493
498
|
plannerChild(job, planner, spec) {
|
|
494
499
|
const now = new Date().toISOString();
|
|
495
|
-
return { jobId: job.id, id: spec.id, kind: 'work', priority: spec.priority ?? job.priority, projectName: spec.projectName, agentName: spec.agentName, parentTaskId: planner.id, dependencies: spec.dependencies ?? [], capabilities: spec.capabilities ?? [], adapter: spec.adapter, input: spec.input, output: spec.output, routing: spec.routing, continuation: spec.continuation ?? null, continuationDeliveredAt: null, state: 'QUEUED', attempt: 0, maxAttempts: spec.maxAttempts ?? 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: spec.timeoutMs ??
|
|
500
|
+
return { jobId: job.id, id: spec.id, kind: 'work', priority: spec.priority ?? job.priority, storySize: spec.storySize ?? 'M', projectName: spec.projectName, agentName: spec.agentName, parentTaskId: planner.id, dependencies: spec.dependencies ?? [], capabilities: spec.capabilities ?? [], adapter: spec.adapter, input: spec.input, output: spec.output, routing: spec.routing, continuation: spec.continuation ?? null, continuationDeliveredAt: null, state: 'QUEUED', attempt: 0, maxAttempts: spec.maxAttempts ?? 3, leaseOwner: null, leaseExpiresAt: null, executionId: null, threadId: null, result: null, plannerResult: null, error: null, timeoutMs: spec.timeoutMs ?? taskOverallTimeoutMs(spec.storySize ?? 'M', spec.routing?.reasoning), createdAt: now, updatedAt: now };
|
|
496
501
|
}
|
|
497
502
|
clearExecution(taskId, executionId) {
|
|
498
503
|
const live = this.live.get(taskId);
|
|
@@ -693,5 +698,5 @@ export class Reconciler {
|
|
|
693
698
|
} }
|
|
694
699
|
}
|
|
695
700
|
function samePlannerChildDefinition(left, right) {
|
|
696
|
-
return JSON.stringify({ id: left.id, input: left.input, output: left.output, priority: left.priority, projectName: left.projectName, agentName: left.agentName, dependencies: left.dependencies, capabilities: left.capabilities, adapter: left.adapter, routing: left.routing, continuation: left.continuation ?? null, maxAttempts: left.maxAttempts, timeoutMs: left.timeoutMs }) === JSON.stringify({ id: right.id, input: right.input, output: right.output, priority: right.priority, projectName: right.projectName, agentName: right.agentName, dependencies: right.dependencies, capabilities: right.capabilities, adapter: right.adapter, routing: right.routing, continuation: right.continuation ?? null, maxAttempts: right.maxAttempts, timeoutMs: right.timeoutMs });
|
|
701
|
+
return JSON.stringify({ id: left.id, input: left.input, output: left.output, priority: left.priority, storySize: left.storySize ?? 'M', projectName: left.projectName, agentName: left.agentName, dependencies: left.dependencies, capabilities: left.capabilities, adapter: left.adapter, routing: left.routing, continuation: left.continuation ?? null, maxAttempts: left.maxAttempts, timeoutMs: left.timeoutMs }) === JSON.stringify({ id: right.id, input: right.input, output: right.output, priority: right.priority, storySize: right.storySize ?? 'M', projectName: right.projectName, agentName: right.agentName, dependencies: right.dependencies, capabilities: right.capabilities, adapter: right.adapter, routing: right.routing, continuation: right.continuation ?? null, maxAttempts: right.maxAttempts, timeoutMs: right.timeoutMs });
|
|
697
702
|
}
|
package/dist/store.js
CHANGED
|
@@ -81,7 +81,7 @@ function immutableTaskDefinition(task) {
|
|
|
81
81
|
reasoning: task.routing.reasoning, cwd: task.routing.cwd, projectId: task.routing.projectId
|
|
82
82
|
} : undefined;
|
|
83
83
|
return {
|
|
84
|
-
jobId: task.jobId, id: task.id, priority: task.priority, projectName: task.projectName, agentName: task.agentName,
|
|
84
|
+
jobId: task.jobId, id: task.id, priority: task.priority, storySize: task.storySize ?? 'M', projectName: task.projectName, agentName: task.agentName,
|
|
85
85
|
kind: task.kind ?? 'work', parentTaskId: task.parentTaskId, dependencies: task.dependencies, capabilities: task.capabilities, adapter: task.adapter,
|
|
86
86
|
input: task.input, output: task.output, routing, chatgptTabClosePolicy: task.chatgptTabClosePolicy ?? 'after-start', continuation: task.continuation ?? null, maxAttempts: task.maxAttempts, timeoutMs: task.timeoutMs
|
|
87
87
|
};
|
|
@@ -461,6 +461,17 @@ export class GitHubStore {
|
|
|
461
461
|
return 'MERGED';
|
|
462
462
|
return String(pr.state ?? '').toUpperCase() === 'CLOSED' ? 'CLOSED' : 'OPEN';
|
|
463
463
|
}
|
|
464
|
+
async mergePullRequest(expectedHeadSha) {
|
|
465
|
+
const payload = { merge_method: 'squash' };
|
|
466
|
+
if (expectedHeadSha)
|
|
467
|
+
payload.sha = expectedHeadSha;
|
|
468
|
+
const result = await this.client.request('repos/' + this.repository + '/pulls/' + this.prNumber + '/merge', {
|
|
469
|
+
method: 'PUT',
|
|
470
|
+
body: JSON.stringify(payload),
|
|
471
|
+
});
|
|
472
|
+
if (result.merged !== true)
|
|
473
|
+
throw new Error(typeof result.message === 'string' ? result.message : 'Pull request #' + this.prNumber + ' was not merged');
|
|
474
|
+
}
|
|
464
475
|
async headSha() {
|
|
465
476
|
const { owner, name } = this.repoParts();
|
|
466
477
|
const data = await this.graphql('query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){pullRequest(number:$number){headRefOid}}}', { owner, name, number: this.prNumber });
|
package/dist/types.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export const STORY_SIZE_TIMEOUT_MS = { S: 120000, M: 300000, L: 900000 };
|
|
2
|
+
export function storySizeTimeoutMs(size) { return STORY_SIZE_TIMEOUT_MS[size]; }
|
|
3
|
+
export function taskOverallTimeoutMs(size, reasoning) { return storySizeTimeoutMs(size) * (reasoning?.toLowerCase() === 'high' ? 2 : 1); }
|
|
1
4
|
const jobTransitions = { OPEN: ['RUNNING', 'WAITING', 'BLOCKED', 'COMPLETED', 'CANCELLED'], RUNNING: ['OPEN', 'WAITING', 'BLOCKED', 'COMPLETED', 'FAILED', 'CANCELLED'], WAITING: ['RUNNING', 'BLOCKED', 'COMPLETED', 'CANCELLED'], BLOCKED: ['RUNNING', 'CANCELLED'], COMPLETED: ['RUNNING', 'CANCELLED'], FAILED: ['RUNNING', 'CANCELLED'], CANCELLED: ['RUNNING'] };
|
|
2
5
|
const taskTransitions = { QUEUED: ['READY', 'BLOCKED', 'CANCELLED'], READY: ['RUNNING', 'BLOCKED', 'FAILED', 'CANCELLED'], RUNNING: ['WAITING', 'BLOCKED', 'SUCCEEDED', 'FAILED', 'CANCELLED', 'READY'], WAITING: ['READY', 'BLOCKED', 'CANCELLED'], BLOCKED: ['READY', 'CANCELLED'], SUCCEEDED: [], FAILED: ['READY', 'CANCELLED'], CANCELLED: [] };
|
|
3
6
|
export function assertJobTransition(from, to) { if (from !== to && !jobTransitions[from].includes(to))
|