agents-relay 2.0.154 → 2.0.156
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
CHANGED
|
@@ -168,7 +168,7 @@ Review destinations are durable Job facts. `job create` and `job update` accept
|
|
|
168
168
|
`--reviewer-thread-id` and `--creator-thread-id`; a reviewer ID takes precedence
|
|
169
169
|
over the creator/origin ID. A local runtime invocation may pass
|
|
170
170
|
`--thread-id` as an explicit one-run review override. When a destination exists,
|
|
171
|
-
final-review routing requires a ChatGPT/
|
|
171
|
+
final-review routing requires a ChatGPT/webhelm worker and submits the
|
|
172
172
|
review request as a verified `new-turn` to that existing thread with the Job ID,
|
|
173
173
|
PR number, and immutable review head SHA. Failure still follows the ordinary
|
|
174
174
|
review task retry/recovery path; a transport failure is never treated as
|
|
@@ -255,7 +255,7 @@ Every model-backed task declares a durable output with `--output task-pr` or `--
|
|
|
255
255
|
|
|
256
256
|
For Codex and ChatGPT, **correlated worker events are authoritative lifecycle inputs**, not a separate durable store. 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 subscribes, validates correlation, applies graph transitions, persists results in RelayStore, and projects them to GitHub. Relay publishes `task.process.launched` only after successful adapter launch; `task.started` is worker-owned and must be a new, correlated start acknowledgement. Codex publishes `task.process.exited` when its synchronous runtime exits, while ChatGPT publishes `task.process.async_exited` after its one-shot submission runtime exits; neither substitutes for `task.completed`, `task.failed`, `task.blocked`, or `task.cancelled`. The executing agent must publish exactly one correlated terminal 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.
|
|
257
257
|
|
|
258
|
-
The ChatGPT adapter uses Relay's `agents/chatgpt-worker.agent.md` lifecycle contract and invokes Browser Workspace exactly as a one-shot platform action: `
|
|
258
|
+
The ChatGPT adapter uses Relay's `agents/chatgpt-worker.agent.md` lifecycle contract and invokes Browser Workspace exactly as a one-shot platform action: `webhelm platform run chatgpt temporary-submit --auto-session --config <file>`. Browser Workspace owns creation/release of the fresh Temporary Chat session, selectors, attachments, and submit verification; Relay does not self-heal browser failures and instead lets delivery failures enter its normal retry/recovery lifecycle. Legacy `--chatgpt-tab-close-policy` values remain accepted for compatibility around Relay's event barriers, but they do not control the Browser Workspace auto-session lifetime. The adapter never uses a pre-existing user tab, polls for the assistant response, resumes/reopens a thread, or treats conversation text as the result channel. Any observed ChatGPT thread ID is diagnostic only.
|
|
259
259
|
|
|
260
260
|
~~~sh
|
|
261
261
|
npx agents-relay task create --repo OWNER/REPO --pr 5 --id job-1 \
|
|
@@ -306,7 +306,7 @@ For autonomous Jobs, `UNBLOCKED` is historical: Relay does not retry that old Ta
|
|
|
306
306
|
|
|
307
307
|
### Browser Workspace product boundary
|
|
308
308
|
|
|
309
|
-
Agents Relay owns the persistent `Relay` browser workspace as product infrastructure. At daemon startup it runs `
|
|
309
|
+
Agents Relay owns the persistent `Relay` browser workspace as product infrastructure. At daemon startup it runs `webhelm status`, dynamically imports the returned absolute `admin_helper`, and calls `ensureWorkspace("Relay", 6)`. This bootstrap is best-effort: Browser Workspace discovery/helper failures are logged and skipped so they do not prevent `agents-relayd` from starting; ChatGPT browser work may remain unavailable until Browser Workspace recovers. Ordinary ChatGPT worker activity uses the public Browser Workspace one-shot `platform run ... --auto-session` command; orchestrator-owned self-healing flows may still use explicit session commands. Relay does not create or delete workspaces through CLI lifecycle commands.
|
|
310
310
|
|
|
311
311
|
## Shared publication reporting
|
|
312
312
|
|
package/dist/adapters.js
CHANGED
|
@@ -171,8 +171,8 @@ export function parseDriverJson(output) {
|
|
|
171
171
|
}
|
|
172
172
|
throw new Error('browser-worker driver returned no submitted receipt');
|
|
173
173
|
}
|
|
174
|
-
function browserWorkspaceCli(override = process.env.
|
|
175
|
-
return override ? { command: override, prefix: [] } : { command: 'uvx', prefix: ['browser-workspace'] };
|
|
174
|
+
function browserWorkspaceCli(override = process.env.AGENTS_RELAY_WEBHELM_COMMAND) {
|
|
175
|
+
return override ? { command: override, prefix: [] } : { command: 'uvx', prefix: ['--from', 'browser-workspace', 'webhelm'] };
|
|
176
176
|
}
|
|
177
177
|
const browserWorkspaceInjection = new AsyncLocalStorage();
|
|
178
178
|
function browserWorkspaceCommand(cli, args, stdin, signal) {
|
|
@@ -181,7 +181,7 @@ function browserWorkspaceCommand(cli, args, stdin, signal) {
|
|
|
181
181
|
function rawBrowserWorkspaceCommand(cli, args, stdin, signal) {
|
|
182
182
|
return new Promise((resolve, reject) => {
|
|
183
183
|
const child = spawnOwnedProcess(cli.command, [...cli.prefix, ...args], {
|
|
184
|
-
invocation: '
|
|
184
|
+
invocation: 'webhelm.command',
|
|
185
185
|
signal,
|
|
186
186
|
spawn: { shell: false, stdio: [stdin === undefined ? 'ignore' : 'pipe', 'pipe', 'pipe'] },
|
|
187
187
|
});
|
|
@@ -200,7 +200,7 @@ function rawBrowserWorkspaceCommand(cli, args, stdin, signal) {
|
|
|
200
200
|
else {
|
|
201
201
|
// Browser Workspace reports a compact code/message JSON on nonzero exit.
|
|
202
202
|
// Preserve existing Relay failure behavior; only simplify the error text.
|
|
203
|
-
let reason = stderr.trim() || stdout.trim() || `
|
|
203
|
+
let reason = stderr.trim() || stdout.trim() || `webhelm exited ${code}`;
|
|
204
204
|
try {
|
|
205
205
|
const envelope = parseWorkspaceJson(stdout);
|
|
206
206
|
const result = envelope.result;
|
|
@@ -234,7 +234,7 @@ function parseWorkspaceJson(output) {
|
|
|
234
234
|
}
|
|
235
235
|
catch { /* diagnostics may precede JSON */ }
|
|
236
236
|
}
|
|
237
|
-
throw new Error('
|
|
237
|
+
throw new Error('webhelm returned no JSON result');
|
|
238
238
|
}
|
|
239
239
|
/** Parse only the trusted manifest emitted before the explicit task-data boundary. */
|
|
240
240
|
function declaredPromptReferences(prompt) {
|
|
@@ -274,14 +274,14 @@ async function accessiblePromptReferences(prompt) {
|
|
|
274
274
|
}
|
|
275
275
|
return readable;
|
|
276
276
|
}
|
|
277
|
-
export async function ensureChatGptBrowserWorkspace(command = process.env.
|
|
277
|
+
export async function ensureChatGptBrowserWorkspace(command = process.env.AGENTS_RELAY_WEBHELM_COMMAND) {
|
|
278
278
|
const status = parseWorkspaceJson(await browserWorkspaceCommand(browserWorkspaceCli(command), ['status']));
|
|
279
279
|
if (typeof status.admin_helper !== 'string' || !status.admin_helper.startsWith('/')) {
|
|
280
|
-
throw new Error('
|
|
280
|
+
throw new Error('webhelm status returned no absolute admin_helper path');
|
|
281
281
|
}
|
|
282
282
|
const admin = await import(pathToFileURL(status.admin_helper).href);
|
|
283
283
|
if (typeof admin.ensureWorkspace !== 'function') {
|
|
284
|
-
throw new Error('
|
|
284
|
+
throw new Error('webhelm admin helper does not export ensureWorkspace');
|
|
285
285
|
}
|
|
286
286
|
await admin.ensureWorkspace(CHATGPT_BROWSER_WORKSPACE, CHATGPT_BROWSER_WORKSPACE_POOL_SIZE);
|
|
287
287
|
}
|
|
@@ -289,7 +289,7 @@ async function runBrowserWorker(cli, configFile, signal, action = 'temporary-sub
|
|
|
289
289
|
const session = parseWorkspaceJson(await browserWorkspaceCommand(cli, ['session', 'start', '--workspace', CHATGPT_BROWSER_WORKSPACE], undefined, signal));
|
|
290
290
|
const sessionId = session.session_id;
|
|
291
291
|
if (typeof sessionId !== 'string' || !sessionId)
|
|
292
|
-
throw new Error('
|
|
292
|
+
throw new Error('webhelm session start returned no session_id');
|
|
293
293
|
try {
|
|
294
294
|
return await browserWorkspaceCommand(cli, ['platform', 'run', 'chatgpt', action, '--session-id', sessionId, '--config', configFile], undefined, signal);
|
|
295
295
|
}
|
|
@@ -347,7 +347,7 @@ function parseBrowserWorkerResponse(output) {
|
|
|
347
347
|
export class ChatGptAdapter {
|
|
348
348
|
name = 'chatgpt';
|
|
349
349
|
id = 'chatgpt/browser-worker';
|
|
350
|
-
capabilities = ['model', 'chatgpt', '
|
|
350
|
+
capabilities = ['model', 'chatgpt', 'webhelm'];
|
|
351
351
|
runner;
|
|
352
352
|
tabClosePolicy;
|
|
353
353
|
constructor(options = {}) {
|
|
@@ -259,7 +259,7 @@ export class ReconcilerRuntimeBase {
|
|
|
259
259
|
const currentTask = currentJob.tasks.find(task => task.id === taskId);
|
|
260
260
|
const platformFailure = currentTask?.adapter === 'chatgpt' ? classifyChatGptPlatformFailure(failure, phase) : undefined;
|
|
261
261
|
const failureReason = phase === 'start-ack' ? 'worker_start_timeout'
|
|
262
|
-
: /
|
|
262
|
+
: /WEBHELM_UNAVAILABLE|webhelm runtime dependency unavailable|webhelm session start returned no session_id|no close frame received or sent/i.test(message)
|
|
263
263
|
? 'browser_workspace_unavailable'
|
|
264
264
|
: undefined;
|
|
265
265
|
const claim = executionId ?? `${jobId}:${taskId}`;
|
|
@@ -305,7 +305,7 @@ export class ReconcilerRuntimeBase {
|
|
|
305
305
|
this.detach(this.emit(event), jobId, taskId, 'worker failure event');
|
|
306
306
|
if (runtimeRetry) {
|
|
307
307
|
this.detach(this.emit(eventFor(jobId, taskId, runtimeRetry.parentTaskId, 'task.rerouting', 'queued', 'ChatGPT platform rejection recorded; routing the same Task through Execution Router', 'orchestrator', { previousFailure: transition.task.platformFailure?.classification, executionId, attempt: runtimeRetry.attempt, maxAttempts: runtimeRetry.maxAttempts })), jobId, taskId, 'runtime retry event');
|
|
308
|
-
this.detach(this.eventCoordinator.accept({ jobId, reason: '
|
|
308
|
+
this.detach(this.eventCoordinator.accept({ jobId, reason: 'webhelm-runtime-retry' }), jobId, taskId, 'runtime retry reconcile wake');
|
|
309
309
|
return;
|
|
310
310
|
}
|
|
311
311
|
this.detach(this.eventCoordinator.accept({ jobId, reason: `worker-${phase}-failure` }), jobId, taskId, 'worker failure reconcile wake');
|
package/dist/relayd.js
CHANGED
|
@@ -465,7 +465,7 @@ export async function runDaemon(argv) {
|
|
|
465
465
|
const runtime = summarizeRuntime(cachedOverviews.map(item => item.job));
|
|
466
466
|
const workers = workerCandidates([
|
|
467
467
|
{ name: 'codex', id: 'codex', capabilities: ['model', 'codex'] },
|
|
468
|
-
{ name: 'chatgpt', id: 'chatgpt/browser-worker', capabilities: ['model', 'chatgpt', '
|
|
468
|
+
{ name: 'chatgpt', id: 'chatgpt/browser-worker', capabilities: ['model', 'chatgpt', 'webhelm'] },
|
|
469
469
|
], [], usage);
|
|
470
470
|
const result = await routeExecution(workers, task.workerRequirements ?? { capabilities: task.capabilities ?? [] }, {
|
|
471
471
|
decisionEngine: executionDecisionEngine(),
|
package/dist/reviewer-thread.js
CHANGED
|
@@ -50,7 +50,7 @@ export function prepareReviewerThreadDelivery(job, task, override) {
|
|
|
50
50
|
}
|
|
51
51
|
export function chatgptThreadRequirements(configured) {
|
|
52
52
|
const base = configured ?? { capabilities: ['model'] };
|
|
53
|
-
const capabilities = new Set([...base.capabilities, 'model', 'chatgpt', '
|
|
53
|
+
const capabilities = new Set([...base.capabilities, 'model', 'chatgpt', 'webhelm']);
|
|
54
54
|
return { ...base, capabilities: [...capabilities], runtime: 'chatgpt' };
|
|
55
55
|
}
|
|
56
56
|
export function runtimeReviewer(args = []) {
|
package/package.json
CHANGED
|
@@ -27,7 +27,7 @@ Read `skills/agents-relay/troubleshooter-qa.md` for user-editable best practices
|
|
|
27
27
|
- shared worker/runtime/tool/Agents Relay defect;
|
|
28
28
|
- genuine external/human blocker.
|
|
29
29
|
4. Diagnose the smallest root cause from the combined durable Task + event sequence + logs. The final `task.error` is only one piece of evidence. Never retry first and investigate later.
|
|
30
|
-
5. For ChatGPT browser failures, run `
|
|
30
|
+
5. For ChatGPT browser failures, run `webhelm platform run chatgpt validate --session-id SESSION_ID` in a caller-owned `Relay` session and close the session afterward. Parse the action JSON from the platform-run `result.stdout` (the outer `result.ok` only reports command execution). The action returns exactly JSON `true` on validation success or one failure ID: `PAGE_NOT_READY`, `COMPOSER_NOT_READY`, or `DELIVERY_ERROR`. Treat any unexpected output or runner error as validation unavailable. These are UI-readiness results, NOT authentication results. None permits a human login escalation. Never independently interpret DOM snapshots, sign-in buttons, or disabled Send; only confirmed authentication failure from the actual Worker authentication protocol can justify login escalation. Inspect original task delivery/start-ack evidence separately.
|
|
31
31
|
|
|
32
32
|
6. Repair:
|
|
33
33
|
- For a transient failure, verify the transient condition is gone.
|