agents-relay 1.0.6 → 1.0.8
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/README.md +1 -1
- package/dist/cli.js +1 -1
- package/dist/events.js +27 -9
- package/dist/reconciler.js +5 -1
- package/dist/relayd.js +1 -1
- package/package.json +1 -1
- package/skills/agents-relay/SKILL.md +3 -1
package/README.md
CHANGED
|
@@ -93,7 +93,7 @@ Managed tasks are descriptive work for agents, not shell commands. Use `codex` o
|
|
|
93
93
|
|
|
94
94
|
Every model-backed task declares a durable output with `--output task-pr` or `--output file --output-path PATH`. Agents Relay enriches the prompt with the PR URL, job/task identity, output contract, and mandatory terminal-event contract.
|
|
95
95
|
|
|
96
|
-
For Codex and ChatGPT, **events are authoritative task state**. A process exit or browser submission receipt does not complete a task. The executing agent must publish exactly one correlated `task.completed`, `task.failed`, or `task.blocked` event. Relay persists the matching durable state only from that terminal event. Model-backed tasks require an event bus and time out explicitly if no terminal event arrives.
|
|
96
|
+
For Codex and ChatGPT, **events are authoritative task state**. Event publication uses the canonical Neo `events-bus` surface (`events__publish` for sandboxed/hosted workers, or `NEO_EVENTS_EMIT` for direct local workers); Agents Relay only subscribes and reconciles. A process exit or browser submission receipt does not complete a task. The executing agent must publish exactly one correlated `task.completed`, `task.failed`, or `task.blocked` event. Relay persists the matching durable state only from that terminal event. Model-backed tasks require an event bus and time out explicitly if no terminal event arrives.
|
|
97
97
|
|
|
98
98
|
The ChatGPT adapter uses the packaged `chatgpt-browser-worker` as a one-shot submitter. It opens a fresh Temporary Chat tab, uses the account defaults, submits the prompt, verifies acceptance, closes the owned tab, and returns. It does not poll for the assistant response, resume/reopen a thread, or use conversation text as the result channel. Any observed ChatGPT thread ID is diagnostic only.
|
|
99
99
|
|
package/dist/cli.js
CHANGED
|
@@ -211,7 +211,7 @@ async function storeFor(args) { const repo = arg(args, '--repo'); const pr = arg
|
|
|
211
211
|
async function saveLocal(file, job) { if (file)
|
|
212
212
|
await writeFile(file, JSON.stringify(job, null, 2)); }
|
|
213
213
|
export function eventBus(args) { if (arg(args, '--events') !== 'nats')
|
|
214
|
-
return undefined; return new NatsEventBus(arg(args, '--nats-url', 'nats://127.0.0.1:4222'), arg(args, '--subject-prefix', '
|
|
214
|
+
return undefined; return new NatsEventBus(arg(args, '--nats-url', 'nats://127.0.0.1:4222'), arg(args, '--subject-prefix', 'neo.events.job')); }
|
|
215
215
|
async function publishWake(bus, job, taskId, message) { if (!bus)
|
|
216
216
|
return; try {
|
|
217
217
|
await bus.publish(eventFor(job.id, taskId, null, 'job.wake', 'queued', message, 'orchestrator', { repository: job.repository, prNumber: job.prNumber }));
|
package/dist/events.js
CHANGED
|
@@ -13,7 +13,7 @@ export class NatsEventBus {
|
|
|
13
13
|
subjectPrefix;
|
|
14
14
|
connection = null;
|
|
15
15
|
module = null;
|
|
16
|
-
constructor(url = 'nats://127.0.0.1:4222', subjectPrefix = '
|
|
16
|
+
constructor(url = 'nats://127.0.0.1:4222', subjectPrefix = 'neo.events.job') {
|
|
17
17
|
this.url = url;
|
|
18
18
|
this.subjectPrefix = subjectPrefix;
|
|
19
19
|
if (!/^[A-Za-z0-9_.-]+$/.test(subjectPrefix))
|
|
@@ -24,13 +24,31 @@ export class NatsEventBus {
|
|
|
24
24
|
async publish(event) { const nats = await this.moduleLoaded(); const subject = `${this.subjectPrefix}.${subjectToken(event.job_id)}.${event.type}`; (await this.client()).publish(subject, nats.StringCodec().encode(JSON.stringify(event))); }
|
|
25
25
|
async subscribe(jobId, wake) { return this.subscribeSubject(`${this.subjectPrefix}.${subjectToken(jobId)}.>`, wake); }
|
|
26
26
|
async subscribeAll(wake) { return this.subscribeSubject(`${this.subjectPrefix}.>`, wake); }
|
|
27
|
-
async subscribeSubject(subject, wake) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
async subscribeSubject(subject, wake) {
|
|
28
|
+
const nats = await this.moduleLoaded();
|
|
29
|
+
const codec = nats.StringCodec();
|
|
30
|
+
const subscription = (await this.client()).subscribe(subject);
|
|
31
|
+
let active = true;
|
|
32
|
+
void (async () => {
|
|
33
|
+
for await (const message of subscription) {
|
|
34
|
+
if (!active)
|
|
35
|
+
break;
|
|
36
|
+
let event;
|
|
37
|
+
try {
|
|
38
|
+
event = JSON.parse(codec.decode(message.data));
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await wake(event);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
process.stderr.write(`warning: event handler failed for ${subject}: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
return async () => { active = false; subscription.unsubscribe(); };
|
|
52
|
+
}
|
|
35
53
|
}
|
|
36
54
|
export function eventFor(jobId, taskId, parentTaskId, type, status, message, visibility = 'orchestrator', data) { return { version: 1, event_id: randomUUID(), job_id: jobId, task_id: taskId, parent_task_id: parentTaskId, type, status, timestamp: new Date().toISOString(), visibility, level: status === 'failed' || status === 'blocked' ? 'error' : 'info', message, data }; }
|
package/dist/reconciler.js
CHANGED
|
@@ -26,7 +26,11 @@ ${task.input}
|
|
|
26
26
|
${output}
|
|
27
27
|
|
|
28
28
|
[Execution event contract]
|
|
29
|
-
Use the
|
|
29
|
+
Use the canonical Neo events-bus with exactly Job ${job.id} and Task ${task.id}.
|
|
30
|
+
Follow the events-bus skill/protocol. For sandboxed Codex or hosted ChatGPT,
|
|
31
|
+
publish through the federated MCP tool events__publish; do not open NATS directly.
|
|
32
|
+
For a direct non-sandbox local worker, use NEO_EVENTS_EMIT when the caller provides it.
|
|
33
|
+
|
|
30
34
|
You MAY publish progress events while working.
|
|
31
35
|
Before stopping, you MUST publish exactly one terminal event:
|
|
32
36
|
- task.completed only after the declared output is durable;
|
package/dist/relayd.js
CHANGED
|
@@ -63,7 +63,7 @@ export async function runDaemon(argv) {
|
|
|
63
63
|
const client = auth.client;
|
|
64
64
|
const trusted = auth.trustedAuthors;
|
|
65
65
|
const concurrency = Number(value(argv, '--concurrency', '4'));
|
|
66
|
-
const bus = value(argv, '--events') === 'nats' ? new NatsEventBus(value(argv, '--nats-url', 'nats://127.0.0.1:4222'), value(argv, '--subject-prefix', '
|
|
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
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 });
|
|
68
68
|
const repositories = normalized.repository
|
|
69
69
|
? [normalized.repository]
|
package/package.json
CHANGED
|
@@ -31,11 +31,13 @@ Events are authoritative for model-backed task state.
|
|
|
31
31
|
|
|
32
32
|
Agents Relay may launch a Codex process or submit a one-shot ChatGPT browser task, but runtime/process completion is only delivery/runtime evidence. It never means the task completed.
|
|
33
33
|
|
|
34
|
-
A running agent MUST publish exactly one correlated terminal event before stopping:
|
|
34
|
+
A running agent MUST use the canonical Neo `events-bus` protocol and publish exactly one correlated terminal event before stopping:
|
|
35
35
|
- `task.completed` after the declared output is durable;
|
|
36
36
|
- `task.failed` when the task cannot complete;
|
|
37
37
|
- `task.blocked` when external or human action is required.
|
|
38
38
|
|
|
39
|
+
Event publishing is not an Agents Relay CLI responsibility. Sandboxed Codex and hosted ChatGPT workers use the federated `events__publish` MCP tool from `events-bus`; direct non-sandbox local workers may use the caller-provided `NEO_EVENTS_EMIT`. Agents Relay subscribes to the canonical `neo.events.job.<job_id>.>` stream and maps terminal events into durable task state.
|
|
40
|
+
|
|
39
41
|
The event must carry the exact durable `job_id` and `task_id`. Progress events are optional.
|
|
40
42
|
|
|
41
43
|
Agents Relay maps those terminal events to durable state:
|