From fbb0f24e92fb16a5916b456d6e69fb44717b00b5 Mon Sep 17 00:00:00 2001 From: khaliqgant Date: Fri, 2 Oct 2026 04:45:19 -0700 Subject: [PATCH 1/4] fix(connect): return without redundant host hello Let the Cloud join announcement notify the host once, removing the synchronous post-join send and mapping the probe's retry-safe timeout code. Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 6 +++- packages/connect/src/cli.js | 19 +---------- packages/connect/tests/connect.test.ts | 47 ++++++++++---------------- 3 files changed, 24 insertions(+), 48 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf946055d0..e47f78d1bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,11 @@ All notable changes to Agent Relay will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased - Patch] + +### Fixed + +- `@agent-relay/connect join` returns as soon as the probe admits the participant instead of waiting for a redundant host notification, and timed-out joins clearly instruct the caller to retry safely. ## [13.0.1] - 2026-10-02 diff --git a/packages/connect/src/cli.js b/packages/connect/src/cli.js index 3f5c78d6c8..9ca6ee04d9 100644 --- a/packages/connect/src/cli.js +++ b/packages/connect/src/cli.js @@ -101,6 +101,7 @@ function errorDetails(error) { connect_unavailable: ['Relay Connect cloud service is unavailable; retry once.', 8], connect_not_joined: ['This agent session has not joined a Relay Connect.', 4], connect_already_joined: ['This agent session is already in a different Relay Connect.', 5], + connect_join_timeout: ['Relay Connect join timed out; retry the same join safely.', 8], connect_unreachable: ['Relay Connect cannot reach Cloud or Relaycast; retry once.', 8], agent_token_invalid: ['Relay Connect is over; run leave once to clear the local registration.', 3], not_a_relay_session: ['Run this command from a live Claude Code or Codex session.', 6], @@ -205,24 +206,6 @@ async function run(options) { ); delete body.host_claim; - if (response.data?.role !== 'host') { - const agentName = response.data?.agent_name; - const host = response.data?.host?.agent_name; - try { - if (!agentName || !host) throw new Error('Join response did not identify this agent and its host.'); - requireOk( - await requestJson(probe.socketPath, { - method: 'POST', - path: `/connect/send?to=${encodeURIComponent(host)}`, - body: `${agentName} joined this Relay Connect and is ready to help.`, - }) - ); - } catch (error) { - const warning = errorDetails(error).message; - process.stderr.write(`Joined, but could not notify the host: ${warning}\n`); - } - } - if (options.json) printJson(response); else process.stdout.write(`${joinSummary(response)}\n`); return; diff --git a/packages/connect/tests/connect.test.ts b/packages/connect/tests/connect.test.ts index f51ddbddd9..11715738a4 100644 --- a/packages/connect/tests/connect.test.ts +++ b/packages/connect/tests/connect.test.ts @@ -102,7 +102,7 @@ async function runCli(home: string, args: string[], input = '') { } describe('@agent-relay/connect CLI', () => { - it('joins through a fake socket, keeps the host claim private, sends one hello, and is repeatable', async () => { + it('joins through a fake socket, keeps the host claim private, and makes no blocking follow-up send', async () => { const joins: Array> = []; const hellos: string[] = []; const { home } = await listen((request, response, body) => { @@ -123,9 +123,9 @@ describe('@agent-relay/connect CLI', () => { participants: [], }, }); - } else if (request.url === '/connect/send?to=host-agent') { + } else if (request.url?.startsWith('/connect/send')) { hellos.push(body); - json(response, { ok: true, data: { sent: [{ to: 'host-agent', message_id: 'message-1' }] } }); + json(response, { ok: false, error: { code: 'unexpected_send' } }, 500); } else { json(response, { ok: false, error: { code: 'unexpected', message: request.url } }, 404); } @@ -150,10 +150,7 @@ describe('@agent-relay/connect CLI', () => { { link: 'connect-1', name: 'guest-one', host_claim: claim }, { link: 'connect-1' }, ]); - expect(hellos).toEqual([ - 'guest-one joined this Relay Connect and is ready to help.', - 'guest-one joined this Relay Connect and is ready to help.', - ]); + expect(hellos).toEqual([]); }); it('wraps send, status, and leave routes', async () => { @@ -220,36 +217,28 @@ describe('@agent-relay/connect CLI', () => { expect(sendRequests).toBe(0); }); - it('keeps a completed join successful when the host hello fails', async () => { + it('maps a retry-safe join timeout', async () => { const { home } = await listen((request, response) => { if (request.url === '/setup/status') { json(response, { ok: true, data: { version: '2026.10.4' } }); } else if (request.url === '/connect/join') { - json(response, { - ok: true, - data: { - connect_id: 'connect-joined', - agent_name: 'guest-joined', - role: 'guest', - task: 'Keep the join result', - expires_at: 'later', - host: { agent_name: 'host-left' }, + json( + response, + { + ok: false, + error: { code: 'connect_join_timeout', message: 'safe to retry' }, }, - }); - } else if (request.url === '/connect/send?to=host-left') { - json(response, { - ok: false, - error: { code: 'connect_unreachable', message: 'host unavailable' }, - }); + 504 + ); } }); - const joined = await runCli(home, ['join', 'connect-joined', '--json']); - expect(joined.code).toBe(0); - expect(JSON.parse(joined.stdout).data.connect_id).toBe('connect-joined'); - expect(joined.stderr).toBe( - 'Joined, but could not notify the host: Relay Connect cannot reach Cloud or Relaycast; retry once.\n' - ); + const joined = await runCli(home, ['join', 'connect-timed-out']); + expect(joined).toEqual({ + code: 8, + stdout: '', + stderr: 'Relay Connect join timed out; retry the same join safely.\n', + }); }); it('maps socket errors and preserves their code in JSON mode', async () => { From 61500813b614654af6aea60c5b450590eef5b2fd Mon Sep 17 00:00:00 2001 From: khaliqgant Date: Fri, 2 Oct 2026 04:56:07 -0700 Subject: [PATCH 2/4] fix(connect): explain pending join retries Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 2 +- packages/connect/src/cli.js | 4 ++++ packages/connect/tests/connect.test.ts | 18 ++++++++++++++---- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e47f78d1bb..8d741aab15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `@agent-relay/connect join` returns as soon as the probe admits the participant instead of waiting for a redundant host notification, and timed-out joins clearly instruct the caller to retry safely. +- `@agent-relay/connect join` returns as soon as the probe admits the participant instead of waiting for a redundant host notification, and timed-out or still-pending joins clearly instruct the caller to retry the same request safely. ## [13.0.1] - 2026-10-02 diff --git a/packages/connect/src/cli.js b/packages/connect/src/cli.js index 9ca6ee04d9..185c77ccee 100644 --- a/packages/connect/src/cli.js +++ b/packages/connect/src/cli.js @@ -101,6 +101,10 @@ function errorDetails(error) { connect_unavailable: ['Relay Connect cloud service is unavailable; retry once.', 8], connect_not_joined: ['This agent session has not joined a Relay Connect.', 4], connect_already_joined: ['This agent session is already in a different Relay Connect.', 5], + connect_join_pending: [ + 'A previous join may still have completed; retry with the same link and options.', + 8, + ], connect_join_timeout: ['Relay Connect join timed out; retry the same join safely.', 8], connect_unreachable: ['Relay Connect cannot reach Cloud or Relaycast; retry once.', 8], agent_token_invalid: ['Relay Connect is over; run leave once to clear the local registration.', 3], diff --git a/packages/connect/tests/connect.test.ts b/packages/connect/tests/connect.test.ts index 11715738a4..34781ee2e7 100644 --- a/packages/connect/tests/connect.test.ts +++ b/packages/connect/tests/connect.test.ts @@ -217,18 +217,22 @@ describe('@agent-relay/connect CLI', () => { expect(sendRequests).toBe(0); }); - it('maps a retry-safe join timeout', async () => { - const { home } = await listen((request, response) => { + it('maps retry-safe join timeout and pending-operation errors', async () => { + const { home } = await listen((request, response, body) => { if (request.url === '/setup/status') { json(response, { ok: true, data: { version: '2026.10.4' } }); } else if (request.url === '/connect/join') { + const pending = JSON.parse(body).link === 'connect-pending'; json( response, { ok: false, - error: { code: 'connect_join_timeout', message: 'safe to retry' }, + error: { + code: pending ? 'connect_join_pending' : 'connect_join_timeout', + message: 'safe to retry', + }, }, - 504 + pending ? 409 : 504 ); } }); @@ -239,6 +243,12 @@ describe('@agent-relay/connect CLI', () => { stdout: '', stderr: 'Relay Connect join timed out; retry the same join safely.\n', }); + const pending = await runCli(home, ['join', 'connect-pending']); + expect(pending).toEqual({ + code: 8, + stdout: '', + stderr: 'A previous join may still have completed; retry with the same link and options.\n', + }); }); it('maps socket errors and preserves their code in JSON mode', async () => { From eecbfeeb305a3b4d7c30fad39ff9055d3914b76e Mon Sep 17 00:00:00 2001 From: khaliqgant Date: Fri, 2 Oct 2026 05:04:16 -0700 Subject: [PATCH 3/4] docs(connect): separate join behavior changes Co-Authored-By: Claude Opus 5.5 (1M context) --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d741aab15..0174ea8e1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed -- `@agent-relay/connect join` returns as soon as the probe admits the participant instead of waiting for a redundant host notification, and timed-out or still-pending joins clearly instruct the caller to retry the same request safely. +- `@agent-relay/connect join` returns as soon as the probe admits the participant. +- Timed-out or still-pending `@agent-relay/connect join` calls exit with status 8 and instruct the caller to retry the same request. ## [13.0.1] - 2026-10-02 From ea940b0cffa428dc3e965718bc14e4ecc7ac0c3f Mon Sep 17 00:00:00 2001 From: khaliqgant Date: Fri, 2 Oct 2026 05:07:03 -0700 Subject: [PATCH 4/4] fix(connect): clarify pending join guidance Co-Authored-By: Claude Opus 5.5 (1M context) --- packages/connect/src/cli.js | 2 +- packages/connect/tests/connect.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/connect/src/cli.js b/packages/connect/src/cli.js index 185c77ccee..23790ab88a 100644 --- a/packages/connect/src/cli.js +++ b/packages/connect/src/cli.js @@ -102,7 +102,7 @@ function errorDetails(error) { connect_not_joined: ['This agent session has not joined a Relay Connect.', 4], connect_already_joined: ['This agent session is already in a different Relay Connect.', 5], connect_join_pending: [ - 'A previous join may still have completed; retry with the same link and options.', + 'A previous join has an unknown outcome; retry with the same link and options.', 8, ], connect_join_timeout: ['Relay Connect join timed out; retry the same join safely.', 8], diff --git a/packages/connect/tests/connect.test.ts b/packages/connect/tests/connect.test.ts index 34781ee2e7..7286c8ebe1 100644 --- a/packages/connect/tests/connect.test.ts +++ b/packages/connect/tests/connect.test.ts @@ -247,7 +247,7 @@ describe('@agent-relay/connect CLI', () => { expect(pending).toEqual({ code: 8, stdout: '', - stderr: 'A previous join may still have completed; retry with the same link and options.\n', + stderr: 'A previous join has an unknown outcome; retry with the same link and options.\n', }); });