From 3a7f278738854afd10220aa473d5c06c699136d1 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sat, 31 Oct 2026 15:40:51 -0700 Subject: [PATCH] feat(thread): a never-connected agent's room says 'isn't connected yet' and shows the owner the command (TASK-655) Co-Authored-By: Claude Sonnet 5.5 Co-authored-by: Ember Agent-Model: claude/claude-opus-5-5/medium --- frontend/src/i18n/locales/en.json | 5 ++-- frontend/src/i18n/locales/zh-CN.json | 3 +- .../src/v2/__tests__/V2ThreadStarter.test.tsx | 29 +++++++++++++++++++ frontend/src/v2/components/V2Thread.tsx | 8 +++++ frontend/src/v2/v2.css | 8 +++++ 5 files changed, 50 insertions(+), 3 deletions(-) diff --git a/frontend/src/i18n/locales/en.json b/frontend/src/i18n/locales/en.json index d0388f1422..f53062e346 100644 --- a/frontend/src/i18n/locales/en.json +++ b/frontend/src/i18n/locales/en.json @@ -585,11 +585,12 @@ "action": "Add an agent" }, "agentRoomLiveness": { - "neverConnected": "{{agentName}} isn't connected. Messages sent here will wait for this seat to connect.", + "neverConnected": "{{agentName}} isn't connected yet. Messages sent here will wait until it runs.", "goneDark": "{{agentName}} hasn't checked in recently. A reply may take longer.", "unknown": "{{agentName}}'s availability is unknown. A reply may take longer.", "waiting": "Waiting for {{agentName}} to reply…", - "timedOut": "{{agentName}} hasn't replied yet. Your message is still in this conversation." + "timedOut": "{{agentName}} hasn't replied yet. Your message is still in this conversation.", + "runCommand": "To connect it, run:" }, "podAgentLiveness": { "notConnected": "{{agentName}} isn't connected yet, so it won't answer here until it's running.", diff --git a/frontend/src/i18n/locales/zh-CN.json b/frontend/src/i18n/locales/zh-CN.json index f5ea3e5fe2..73ff46d6d8 100644 --- a/frontend/src/i18n/locales/zh-CN.json +++ b/frontend/src/i18n/locales/zh-CN.json @@ -589,7 +589,8 @@ "goneDark": "{{agentName}} 最近没有接入,回复可能需要更长时间。", "unknown": "{{agentName}} 的可用状态未知,回复可能需要更长时间。", "waiting": "正在等待 {{agentName}} 回复…", - "timedOut": "{{agentName}} 还没有回复。你的消息仍在此对话中。" + "timedOut": "{{agentName}} 还没有回复。你的消息仍在此对话中。", + "runCommand": "要连接它,请运行:" }, "podAgentLiveness": { "notConnected": "{{agentName}} 还没有连接,运行起来之前不会在这里回复。", diff --git a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx index 43e6c69926..adf4e8aaaf 100644 --- a/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx +++ b/frontend/src/v2/__tests__/V2ThreadStarter.test.tsx @@ -224,6 +224,35 @@ describe('V2Thread agent-room liveness', () => { ); }); + test('a never-connected direct seat says so and shows the owner the command to run', async () => { + axios.get.mockResolvedValue({ + data: { + agents: [{ + agentName: 'openclaw', instanceId: 'aria', displayName: 'Aria', state: 'never-connected', isOwner: true, fixCommand: 'commonly agent run aria', + }], + }, + }); + renderChat(makeAgentRoom()); + + const banner = await screen.findByTestId('agent-room-liveness'); + expect(banner).toHaveTextContent("Aria isn't connected yet"); + expect(screen.getByTestId('agent-room-fix-command')).toHaveTextContent('commonly agent run aria'); + }); + + test('a never-connected direct seat shows a non-owner no command', async () => { + axios.get.mockResolvedValue({ + data: { + agents: [{ + agentName: 'openclaw', instanceId: 'aria', displayName: 'Aria', state: 'never-connected', isOwner: false, + }], + }, + }); + renderChat(makeAgentRoom()); + + expect(await screen.findByTestId('agent-room-liveness')).toHaveTextContent("Aria isn't connected yet"); + expect(screen.queryByTestId('agent-room-fix-command')).not.toBeInTheDocument(); + }); + test('a live direct seat shows a wait until it replies', async () => { axios.get.mockResolvedValue({ data: { diff --git a/frontend/src/v2/components/V2Thread.tsx b/frontend/src/v2/components/V2Thread.tsx index f29bc81dec..2e0ccdaf8d 100644 --- a/frontend/src/v2/components/V2Thread.tsx +++ b/frontend/src/v2/components/V2Thread.tsx @@ -2146,6 +2146,14 @@ const V2Thread: React.FC = ({ detail, firstRunVisible = false, in {showDirectAgentLiveness && directAgent && (
{t(directAgentLivenessKey, { agentName: directAgent.displayName })} + {directAgentReach === 'never-connected' && directAgent.state?.isOwner && directAgent.state?.fixCommand && ( + <> + {' '}{t('podChat.agentRoomLiveness.runCommand')} + + {directAgent.state.fixCommand} + + + )}
)} {podAgentLiveness.map((row) => ( diff --git a/frontend/src/v2/v2.css b/frontend/src/v2/v2.css index d918c0c58f..64e1d815a6 100644 --- a/frontend/src/v2/v2.css +++ b/frontend/src/v2/v2.css @@ -2495,6 +2495,14 @@ body.modern-ui.v2-canvas { line-height: 1.4; } +.v2-chat__agent-room-command { + display: block; + margin-top: 4px; + font-family: var(--v2-font-mono, ui-monospace, monospace); + overflow-wrap: anywhere; + user-select: all; +} + .v2-chat__agent-room-status--wait { border-color: var(--v2-border); background: var(--v2-surface-hover);