agents-relay 2.0.167 → 2.0.169

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
@@ -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: `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.
258
+ The ChatGPT adapter uses Relay's `agents/chatgpt-worker.agent.md` lifecycle contract. A new worker uses `webhelm platform run chatgpt new-thread --session-id SESSION_ID --config <file>` in a caller-owned Session. This is a regular persistent ChatGPT thread, which supports the connected Neo MCP in the verified account. Relay closes the Session after submission, but the ChatGPT thread remains in history. Existing-thread review and handoff continue through `new-turn`. File paths are included in the worker message for Neo MCP reading; they are not uploaded as browser attachments. Submission receipts retain `status: submitted`; the event bus owns task completion.
259
259
 
260
260
  ~~~sh
261
261
  npx agents-relay task create --repo OWNER/REPO --pr 5 --id job-1 \
@@ -6,17 +6,17 @@ type: worker
6
6
 
7
7
  # ChatGPT Worker Agent
8
8
 
9
- You execute one complete Relay task by submitting it to a fresh worker-owned ChatGPT Temporary Chat through Browser Workspace.
9
+ You execute one complete Relay task by submitting it to a fresh, regular ChatGPT Thread through Browser Workspace.
10
10
 
11
11
  ## Contract
12
12
 
13
- - Browser Workspace `platforms/chatgpt` is the sole owner of ChatGPT selectors, readiness checks, attachments, Temporary Chat mechanics, and submit verification.
13
+ - Browser Workspace `platforms/chatgpt` is the sole owner of ChatGPT selectors, readiness checks, new-thread mechanics and submit verification.
14
14
  - Relay owns worker lifecycle, task/event barriers, routing, and cancellation; Browser Workspace owns the one-shot auto-session lifecycle.
15
- - Execute only a one-shot submission with `platform run chatgpt temporary-submit --auto-session`. Do not expose a create/resume/status/result conversation lifecycle.
16
- - Let Browser Workspace create and release the fresh Temporary Chat session/tab for the action. Never submit through a pre-existing user tab.
15
+ - Execute one new worker submission with `platform run chatgpt new-thread --session-id SESSION_ID`. Existing-thread delivery uses `new-turn`. Never use Temporary Chat for workers that need Neo MCP.
16
+ - Relay creates and stops a caller-owned WebHelm Session for each delivery. New worker conversations persist in ChatGPT history; do not reuse a pre-existing user tab.
17
17
  - Preserve the account's current default model/thinking settings.
18
18
  - A submission is accepted only after Browser Workspace returns a verified submit receipt.
19
- - Treat observed conversation/thread identity as diagnostic only, never as a resumable handle.
20
- - Legacy ChatGPT tab-close policy values remain accepted by Relay for compatibility, but they do not control Browser Workspace's auto-session lifetime or add self-heal.
19
+ - Require the real new-thread receipt to include a durable thread ID and URL. The thread identifies delivery, not task completion.
20
+ - Legacy ChatGPT tab-close policy values remain accepted by Relay for compatibility, but they do not control the caller-owned Session lifetime or add self-heal.
21
21
  - Do not wait for the assistant response. Durable task success/failure is event-owned by Relay and the executing task.
22
22
  - Stop on authentication, MFA, consent, or ambiguous browser state rather than bypassing it.
package/dist/adapters.js CHANGED
@@ -148,7 +148,7 @@ function findSubmittedReceipt(value) {
148
148
  if (!value || typeof value !== 'object')
149
149
  return undefined;
150
150
  const record = value;
151
- if ((record.operation === 'submit' || record.operation === 'new-turn' || record.mode === 'new-turn') && record.status === 'submitted')
151
+ if ((record.operation === 'submit' || record.operation === 'new-turn' || record.mode === 'new-turn' || record.mode === 'new-thread') && record.status === 'submitted')
152
152
  return record;
153
153
  for (const key of ['result', 'output', 'data', 'stdout']) {
154
154
  const nested = findSubmittedReceipt(record[key]);
@@ -285,13 +285,13 @@ export async function ensureChatGptBrowserWorkspace(command = process.env.AGENTS
285
285
  }
286
286
  await admin.ensureWorkspace(CHATGPT_BROWSER_WORKSPACE, CHATGPT_BROWSER_WORKSPACE_POOL_SIZE);
287
287
  }
288
- async function runBrowserWorker(cli, configFile, signal, action = 'temporary-submit') {
288
+ async function runBrowserWorker(cli, configFile, signal, action = 'new-thread') {
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
292
  throw new Error('webhelm session start returned no session_id');
293
293
  try {
294
- return await browserWorkspaceCommand(cli, ['platform', 'run', 'chatgpt', action, '--session-id', sessionId, '--config', configFile], undefined, signal);
294
+ return await browserWorkspaceCommand(cli, ['platform', 'run', 'chatgpt', action, ...(action === 'new-turn' ? ['--mode', 'async'] : []), '--session-id', sessionId, '--config', configFile], undefined, signal);
295
295
  }
296
296
  finally {
297
297
  await browserWorkspaceCommand(cli, ['session', 'stop', sessionId]).catch(() => { });
@@ -304,18 +304,23 @@ function browserWorkerRunner() {
304
304
  const configFile = join(process.env.TMPDIR ?? '/tmp', `agents-relay-chatgpt-${randomUUID()}.json`);
305
305
  try {
306
306
  const delivery = request.thread;
307
+ // Temporary Chat lacks Neo MCP tools on this verified account.
308
+ // File paths remain accessible to normal workers through Neo MCP.
309
+ const message = request.files?.length
310
+ ? `${request.prompt}\n\nTask file paths (read with Neo MCP; files are not browser-uploaded):\n${request.files.map(file => `- ${JSON.stringify(file)}`).join('\n')}`
311
+ : request.prompt;
307
312
  await writeFile(configFile, JSON.stringify(delivery ? {
308
313
  project_id: delivery.projectId ?? _route.projectId ?? null,
309
314
  thread_id: delivery.threadId,
310
315
  message: `${request.prompt}\n\nRelay delivery ID: ${delivery.requestId}`,
311
316
  idempotency_key: delivery.requestId,
312
317
  } : {
313
- prompt: request.prompt,
314
- file: request.files ?? [],
318
+ message,
319
+ temporary: false,
315
320
  }));
316
321
  if (signal.aborted)
317
322
  throw new Error('browser-worker execution aborted');
318
- const action = delivery ? 'new-turn' : 'temporary-submit';
323
+ const action = delivery ? 'new-turn' : 'new-thread';
319
324
  const output = await runBrowserWorker(cli, configFile, signal, action);
320
325
  return JSON.stringify(parseDriverJson(output));
321
326
  }
@@ -338,6 +343,10 @@ function parseBrowserWorkerResponse(output) {
338
343
  if (!value || typeof value !== 'object')
339
344
  throw new Error('browser-worker returned an invalid JSON response');
340
345
  const response = value;
346
+ if (response.mode === 'new-thread' && (response.temporary !== false ||
347
+ typeof response.thread_id !== 'string' || !/^[A-Za-z0-9_-]{8,160}$/.test(response.thread_id) ||
348
+ typeof response.thread_url !== 'string' || !/^https:\/\/chatgpt\.com\/c\//.test(response.thread_url)))
349
+ throw new Error('browser-worker did not return a durable ChatGPT thread');
341
350
  const verified = response.verified === true || response.verified_by !== undefined;
342
351
  if (response.status !== 'submitted' || !verified) {
343
352
  throw new Error('browser-worker did not return a submitted receipt');
@@ -432,7 +441,7 @@ export class ChatGptAdapter {
432
441
  summary: 'ChatGPT task submitted; terminal task state is event-owned',
433
442
  data: {
434
443
  submitted: true,
435
- mode: response.mode ?? 'temporary-submit',
444
+ mode: response.mode ?? 'new-thread',
436
445
  threadId: response.thread_id ?? response.diagnostic_thread_id ?? null,
437
446
  diagnosticThreadId: response.diagnostic_thread_id ?? null,
438
447
  userMessageId: response.user_message_id ?? null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agents-relay",
3
- "version": "2.0.167",
3
+ "version": "2.0.169",
4
4
  "description": "Durable async agent jobs coordinated through GitHub pull requests",
5
5
  "repository": {
6
6
  "type": "git",
@@ -100,7 +100,7 @@ Leaf workers consume the resolved instruction/skill/agent paths, not the full to
100
100
 
101
101
  ## ChatGPT worker
102
102
 
103
- The ChatGPT adapter uses Relay's `chatgpt-worker` agent contract and Browser Workspace ChatGPT platform; it does not depend on a Neo worker skill. It opens a fresh Temporary Chat tab, submits the complete task with the account defaults, verifies acceptance, applies the configured `after-start` (default), `never`, or `after-terminal` owned-tab close policy, and returns a submission receipt.
103
+ The ChatGPT adapter uses Relay's `chatgpt-worker` agent contract and Browser Workspace ChatGPT platform; it does not depend on a Neo worker skill. It opens a fresh regular ChatGPT thread so the worker can use Neo MCP, submits the task with the account defaults, verifies the durable thread ID and submission receipt, and closes its caller-owned WebHelm Session after submission. File inputs are passed as explicit local paths for Neo MCP reading, not browser uploads.
104
104
 
105
105
  ChatGPT thread identity is diagnostic only. It is not persisted as a resumable task handle, and Relay does not poll, resume, retrieve a result from, or delete a ChatGPT conversation as part of task lifecycle.
106
106