agents-relay 1.0.20 → 1.0.22
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 +2 -1
- package/dist/reconciler.js +23 -9
- package/dist/relayd.js +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -424,7 +424,8 @@ async function runJobCommand(action, args) {
|
|
|
424
424
|
console.log(JSON.stringify({ repository: repo, pr: result.pr.number, job: result.job.id, executionMode: result.job.executionMode, state: result.job.state, task: start ? arg(args, '--task-id', 'initial') : undefined }, null, 2));
|
|
425
425
|
}
|
|
426
426
|
export function runtimePlanner(args) { const plannerCommand = arg([...args], '--planner-command'); const modelRuntime = arg([...args], '--codex', 'codex'); return plannerCommand ? new CommandObjectivePlanner(plannerCommand) : new AgentObjectivePlanner(modelRuntime); }
|
|
427
|
-
export function
|
|
427
|
+
export function runtimeReviewer() { return { adapter: 'chatgpt', routing: { provider: 'chatgpt', model: 'account-defaults', decidedBy: 'fixed-chatgpt-reviewer', decidedAt: new Date().toISOString() }, startAckTimeoutMs: 120000 }; }
|
|
428
|
+
export function runtime(store, args, bus) { const emit = (event) => { process.stderr.write(`${JSON.stringify(event)}\n`); }; const modelRuntime = arg(args, '--codex', 'codex'); const policy = chatGptTabClosePolicy(args); const planner = runtimePlanner(args); return new Reconciler(store, { owner: arg(args, '--owner', `cli-${process.pid}`), maxConcurrent: Number(arg(args, '--concurrency', '4')), leaseMs: Number(arg(args, '--lease-ms', '300000')), adapters: [new CodexAdapter(modelRuntime), new ChatGptAdapter({ tabClosePolicy: policy })], continuations: [new CodexThreadContinuation(modelRuntime), new CommandContinuation(), new WebhookContinuation()], planner, reviewer: runtimeReviewer(), eventBus: bus, emit, chatgptTabClosePolicy: policy }); }
|
|
428
429
|
export async function createService(args) {
|
|
429
430
|
const loaded = await storeFor(args);
|
|
430
431
|
const upstream = eventBus(args);
|
package/dist/reconciler.js
CHANGED
|
@@ -26,18 +26,32 @@ ${task.input}
|
|
|
26
26
|
[Output contract]
|
|
27
27
|
${output}
|
|
28
28
|
|
|
29
|
-
[Execution event contract]
|
|
29
|
+
[Execution event contract — mandatory]
|
|
30
30
|
Use the canonical Neo events-bus with exactly Job ${job.id} and Task ${task.id}.
|
|
31
31
|
Follow the events-bus skill/protocol. For sandboxed Codex or hosted ChatGPT,
|
|
32
32
|
publish through the federated MCP tool events__publish; do not open NATS directly.
|
|
33
33
|
For a direct non-sandbox local worker, use NEO_EVENTS_EMIT when the caller provides it.
|
|
34
34
|
|
|
35
|
+
As your FIRST execution action, before reading files, editing code, running commands, or doing other work,
|
|
36
|
+
you MUST publish exactly one canonical task.started event for this exact Job/Task.
|
|
37
|
+
|
|
35
38
|
You MAY publish progress events while working.
|
|
36
|
-
Before
|
|
39
|
+
Before returning or allowing the worker process to exit, you MUST publish exactly one canonical terminal event:
|
|
37
40
|
- task.completed only after the declared output is durable;
|
|
38
|
-
- task.failed
|
|
39
|
-
- task.blocked when human/external action is required
|
|
40
|
-
|
|
41
|
+
- task.failed for implementation, tool, command, test, patch, commit, push, runtime, or other execution failure that prevents completion;
|
|
42
|
+
- task.blocked only when genuine human/external action is required;
|
|
43
|
+
- task.cancelled only when execution was cancelled.
|
|
44
|
+
|
|
45
|
+
If any execution step fails and you cannot continue, publish task.failed with the concrete reason BEFORE exiting.
|
|
46
|
+
Never exit after task.started without publishing exactly one terminal task event.
|
|
47
|
+
Do not publish duplicate terminal task events.
|
|
48
|
+
The worker owns task.started and task terminal events. Relay-owned task.process.launched, task.process.exited,
|
|
49
|
+
and task.process.async_exited are process lifecycle only and NEVER replace the worker task lifecycle.
|
|
50
|
+
The ChatGPT/Codex conversation or process exit is never the task result.
|
|
51
|
+
|
|
52
|
+
[Final event gate]
|
|
53
|
+
Before your final return, verify that a terminal event for this exact Job/Task was published.
|
|
54
|
+
If no terminal event has been published, publish task.failed before exiting.`;
|
|
41
55
|
}
|
|
42
56
|
export class Reconciler {
|
|
43
57
|
store;
|
|
@@ -183,7 +197,7 @@ export class Reconciler {
|
|
|
183
197
|
return;
|
|
184
198
|
const id = `final-review-${headSha.slice(0, 12)}`;
|
|
185
199
|
const now = new Date().toISOString();
|
|
186
|
-
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').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 verify the deterministic pre-review gates have passed
|
|
200
|
+
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').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 and do not merge.`, 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 ?? 300000, createdAt: now, updatedAt: now };
|
|
187
201
|
await this.store.appendTask(task);
|
|
188
202
|
job.review = { outcome: 'pending', headSha, taskId: id, evidence: null, updatedAt: now };
|
|
189
203
|
job.state = 'RUNNING';
|
|
@@ -306,7 +320,7 @@ export class Reconciler {
|
|
|
306
320
|
const controller = new AbortController();
|
|
307
321
|
let execution;
|
|
308
322
|
const workerTask = { ...task, input: workerInput };
|
|
309
|
-
const startAckSetup = task.adapter === 'chatgpt' ? await this.startAck(job.id, task.id, Date.now()) : null;
|
|
323
|
+
const startAckSetup = task.adapter === 'chatgpt' ? await this.startAck(job.id, task.id, Date.now(), task.agentName === 'reviewer' ? this.options.reviewer?.startAckTimeoutMs : undefined) : null;
|
|
310
324
|
const closePolicy = task.chatgptTabClosePolicy ?? this.options.chatgptTabClosePolicy ?? adapter.tabClosePolicy ?? 'after-start';
|
|
311
325
|
const terminalWait = task.adapter === 'chatgpt' && closePolicy === 'after-terminal'
|
|
312
326
|
? this.terminalWait(task.id, durableExecutionId, startedAt - 1)
|
|
@@ -338,7 +352,7 @@ export class Reconciler {
|
|
|
338
352
|
? this.failLaunch(job.id, task.id, durableExecutionId, error.message, 'delivery')
|
|
339
353
|
: this.runtimeSettled(job.id, task.id, durableExecutionId, null, error instanceof Error ? error.message : String(error))), job.id, task.id, 'worker runtime completion');
|
|
340
354
|
}
|
|
341
|
-
async startAck(jobId, taskId, launchBarrier) {
|
|
355
|
+
async startAck(jobId, taskId, launchBarrier, timeoutMs = this.options.startAckTimeoutMs ?? 60000) {
|
|
342
356
|
if (!this.options.eventBus)
|
|
343
357
|
throw new Error('Worker start acknowledgement requires an event bus');
|
|
344
358
|
let resolveAck;
|
|
@@ -367,7 +381,7 @@ export class Reconciler {
|
|
|
367
381
|
settled = true;
|
|
368
382
|
void unsubscribe();
|
|
369
383
|
rejectAck(new StartAckTimeout(`Task ${taskId} worker_start_timeout: no worker task.started start-ack`));
|
|
370
|
-
},
|
|
384
|
+
}, timeoutMs);
|
|
371
385
|
let stopped = false;
|
|
372
386
|
const stop = async () => { if (stopped)
|
|
373
387
|
return; stopped = true; if (timer)
|
package/dist/relayd.js
CHANGED
|
@@ -11,7 +11,7 @@ import { RepositoryWorkerPool, aggregateManagedGitHubJobs } from './pool.js';
|
|
|
11
11
|
import { Reconciler } from './reconciler.js';
|
|
12
12
|
import { dashboardManagedJobs, GitHubStore, InMemoryStore, loadManagedGitHubPullRequestGraphql } from './store.js';
|
|
13
13
|
import { githubAuthContext } from './github-auth.js';
|
|
14
|
-
import { isEntrypoint, runtimePlanner, startService, SERVICE_DEFAULTS } from './cli.js';
|
|
14
|
+
import { isEntrypoint, runtimePlanner, runtimeReviewer, startService, SERVICE_DEFAULTS } from './cli.js';
|
|
15
15
|
import { codexAndZaiUsageRegistry } from './usage.js';
|
|
16
16
|
import { defaultWorkspaceRoot, discoverWorkspaceRepositories } from './workspace.js';
|
|
17
17
|
export const daemonHelp = `Agents Relay daemon runs the dashboard and worker pool.
|
|
@@ -64,7 +64,7 @@ export async function runDaemon(argv) {
|
|
|
64
64
|
const trusted = auth.trustedAuthors;
|
|
65
65
|
const concurrency = Number(value(argv, '--concurrency', '4'));
|
|
66
66
|
const bus = value(argv, '--events') === 'nats' ? new NatsEventBus(value(argv, '--nats-url', 'nats://127.0.0.1:4222'), value(argv, '--subject-prefix', 'neo.events.job')) : undefined;
|
|
67
|
-
const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { owner: `relayd-${process.pid}`, maxConcurrent, leaseMs: Number(value(argv, '--lease-ms', '300000')), adapters: [new CodexAdapter(value(argv, '--codex', 'codex')), new ChatGptAdapter()], continuations: [new CodexThreadContinuation(value(argv, '--codex', 'codex')), new CommandContinuation(), new WebhookContinuation()], planner: runtimePlanner(argv), eventBus: bus });
|
|
67
|
+
const makeReconciler = (store, maxConcurrent) => new Reconciler(store, { owner: `relayd-${process.pid}`, maxConcurrent, leaseMs: Number(value(argv, '--lease-ms', '300000')), adapters: [new CodexAdapter(value(argv, '--codex', 'codex')), new ChatGptAdapter()], continuations: [new CodexThreadContinuation(value(argv, '--codex', 'codex')), new CommandContinuation(), new WebhookContinuation()], planner: runtimePlanner(argv), eventBus: bus, reviewer: runtimeReviewer() });
|
|
68
68
|
const repositories = normalized.repository
|
|
69
69
|
? [normalized.repository]
|
|
70
70
|
: (await discoverWorkspaceRepositories(normalized.workspaceRoot ?? defaultWorkspaceRoot())).map(item => item.repository);
|