Files
claude-code/src/bridge/peerSessions.ts
unraid ee0d788e58 fix: harden bounded agent communication review fixes
CodeRabbit and Codecov surfaced real gaps in UDS framing, peer discovery, mailbox retention, and summary context coverage. This tightens those paths without suppressing review or coverage signals.

Constraint: PR #369 must address CodeRabbit and Codecov findings without warning suppression or fake fallbacks

Rejected: Suppress Codecov or CodeRabbit warnings | leaves real receive-path and test-isolation gaps

Rejected: Add unreachable feature-gated tests | bun:bundle keeps those branches compile-time gated in local tests

Confidence: high

Scope-risk: moderate

Directive: Keep UDS auth-token rejection outside feature flags; do not reintroduce inline token fallbacks

Tested: bun test --coverage --coverage-reporter lcov --coverage-dir coverage; bun run test:all; bun run lint; bun run build; bun run build:vite; bun audit; git diff --cached --check

Not-tested: Remote Codecov/CodeRabbit refreshed reports until pushed
2026-04-27 10:32:18 +08:00

117 lines
3.8 KiB
TypeScript

import axios from 'axios'
import { logForDebugging } from '../utils/debug.js'
import { errorMessage } from '../utils/errors.js'
import { validateBridgeId } from './bridgeApi.js'
import { getBridgeAccessToken } from './bridgeConfig.js'
import { getReplBridgeHandle } from './replBridgeHandle.js'
import { toCompatSessionId } from './sessionIdCompat.js'
export type BridgePeerSession = {
address: string
name?: string
cwd?: string
pid?: number
}
/**
* List locally registered sessions that have published a Remote Control
* session ID. The PID registry is the local source of truth for bridge peers
* already known to this machine; SendMessage can use these bridge:<id>
* addresses when the current process has an active bridge handle.
*/
export async function listBridgePeers(): Promise<BridgePeerSession[]> {
const { listAllLiveSessions } = await import('../utils/udsClient.js')
const sessions = await listAllLiveSessions()
const peers: BridgePeerSession[] = []
for (const session of sessions) {
if (session.pid === process.pid || !session.bridgeSessionId) continue
const compatId = toCompatSessionId(session.bridgeSessionId)
peers.push({
address: `bridge:${compatId}`,
name: session.name ?? session.kind,
cwd: session.cwd,
pid: session.pid,
})
}
return peers
}
/**
* Send a plain-text message to another Claude session via the bridge API.
*
* Called by SendMessageTool when the target address scheme is "bridge:".
* Uses the current ReplBridgeHandle to derive the sender identity and
* the session ingress URL for the POST request.
*
* @param target - Target session ID (from the "bridge:<sessionId>" address)
* @param message - Plain text message content (structured messages are rejected upstream)
* @returns { ok: true } on success, { ok: false, error } on failure. Never throws.
*/
export async function postInterClaudeMessage(
target: string,
message: string,
): Promise<{ ok: true } | { ok: false; error: string }> {
try {
const handle = getReplBridgeHandle()
if (!handle) {
return { ok: false, error: 'Bridge not connected' }
}
const normalizedTarget = target.trim()
if (!normalizedTarget) {
return { ok: false, error: 'No target session specified' }
}
const accessToken = getBridgeAccessToken()
if (!accessToken) {
return { ok: false, error: 'No access token available' }
}
const compatTarget = toCompatSessionId(normalizedTarget)
// Validate against path traversal — same allowlist as bridgeApi.ts
validateBridgeId(compatTarget, 'target sessionId')
const from = toCompatSessionId(handle.bridgeSessionId)
const baseUrl = handle.sessionIngressUrl
const url = `${baseUrl}/v1/sessions/${encodeURIComponent(compatTarget)}/messages`
const response = await axios.post(
url,
{
type: 'peer_message',
from,
content: message,
},
{
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
},
timeout: 10_000,
validateStatus: (s: number) => s < 500,
},
)
if (response.status === 200 || response.status === 204) {
logForDebugging(
`[bridge:peer] Message sent to ${compatTarget} (${response.status})`,
)
return { ok: true }
}
const detail =
typeof response.data === 'object' && response.data?.error?.message
? response.data.error.message
: `HTTP ${response.status}`
logForDebugging(`[bridge:peer] Send failed: ${detail}`)
return { ok: false, error: detail }
} catch (err: unknown) {
const msg = errorMessage(err)
logForDebugging(`[bridge:peer] postInterClaudeMessage error: ${msg}`)
return { ok: false, error: msg }
}
}