refactor: migrate from clientSessionKey to clientId identity

This commit is contained in:
Mohamed Boudra
2026-02-21 15:33:29 +07:00
parent e906bffbb9
commit c3c8103563
56 changed files with 3289 additions and 941 deletions
@@ -13,12 +13,24 @@ describe("relay websocket URL versioning", () => {
endpoint: "relay.paseo.sh:443",
serverId: "srv_test",
role: "client",
clientSessionKey: "clsk_test",
})
);
expect(url.searchParams.get("v")).toBe(CURRENT_RELAY_PROTOCOL_VERSION);
expect(url.searchParams.get("clientId")).toBe("clsk_test");
expect(url.searchParams.has("connectionId")).toBe(false);
});
test("includes connectionId when provided (server data sockets)", () => {
const url = new URL(
buildRelayWebSocketUrl({
endpoint: "relay.paseo.sh:443",
serverId: "srv_test",
role: "server",
connectionId: "conn_abc123",
})
);
expect(url.searchParams.get("connectionId")).toBe("conn_abc123");
});
test("allows explicitly requesting v1 relay URLs", () => {
+9 -22
View File
@@ -83,28 +83,23 @@ function shouldUseSecureWebSocket(port: number): boolean {
}
export function buildDaemonWebSocketUrl(
endpoint: string,
params?: { clientSessionKey?: string }
endpoint: string
): string {
const { host, port, isIpv6 } = parseHostPort(endpoint);
const protocol = shouldUseSecureWebSocket(port) ? "wss" : "ws";
const hostPart = isIpv6 ? `[${host}]` : host;
const url = new URL(`${protocol}://${hostPart}:${port}/ws`);
if (
typeof params?.clientSessionKey === "string" &&
params.clientSessionKey.trim().length > 0
) {
url.searchParams.set("clientSessionKey", params.clientSessionKey.trim());
}
return url.toString();
return new URL(`${protocol}://${hostPart}:${port}/ws`).toString();
}
export function buildRelayWebSocketUrl(params: {
endpoint: string;
serverId: string;
role: RelayRole;
clientId?: string;
clientSessionKey?: string;
/**
* Per-connection routing identifier used by the daemon to open server data sockets.
* Clients should NOT provide this — the relay assigns a routing ID on connect.
*/
connectionId?: string;
version?: RelayProtocolVersion | 1 | 2;
}): string {
const { host, port, isIpv6 } = parseHostPort(params.endpoint);
@@ -114,16 +109,8 @@ export function buildRelayWebSocketUrl(params: {
url.searchParams.set("serverId", params.serverId);
url.searchParams.set("role", params.role);
url.searchParams.set("v", normalizeRelayProtocolVersion(params.version));
if (
params.clientId &&
params.clientSessionKey &&
params.clientId !== params.clientSessionKey
) {
throw new Error("clientId and clientSessionKey must match when both are provided");
}
const resolvedClientId = params.clientId ?? params.clientSessionKey;
if (resolvedClientId) {
url.searchParams.set("clientId", resolvedClientId);
if (params.connectionId) {
url.searchParams.set("connectionId", params.connectionId);
}
return url.toString();
}
@@ -78,6 +78,48 @@ describe('shared messages stream parsing', () => {
}
})
it('parses representative sub_agent tool_call event', () => {
const parsed = AgentStreamMessageSchema.parse({
type: 'agent_stream',
payload: {
agentId: 'agent_live',
timestamp: '2026-02-08T20:10:00.000Z',
event: {
type: 'timeline',
provider: 'claude',
item: {
type: 'tool_call',
callId: 'call_sub_agent_live',
name: 'Task',
status: 'running',
detail: {
type: 'sub_agent',
subAgentType: 'Explore',
description: 'Inspect repository structure',
log: '[Read] README.md',
actions: [
{
index: 1,
toolName: 'Read',
summary: 'README.md',
},
],
},
error: null,
},
},
},
})
expect(parsed.payload.event.type).toBe('timeline')
if (parsed.payload.event.type === 'timeline') {
expect(parsed.payload.event.item.type).toBe('tool_call')
if (parsed.payload.event.item.type === 'tool_call') {
expect(parsed.payload.event.item.detail.type).toBe('sub_agent')
}
}
})
it('rejects removed initialize_agent_request inbound payload', () => {
const parsed = SessionInboundMessageSchema.safeParse({
type: 'initialize_agent_request',
@@ -109,4 +109,41 @@ describe("shared messages tool_call schema", () => {
expect(missingDetail.success).toBe(false);
expect(legacyStatus.success).toBe(false);
});
it("parses canonical sub_agent detail payload", () => {
const parsed = AgentTimelineItemPayloadSchema.parse({
type: "tool_call",
callId: "call_sub_agent_1",
name: "Task",
status: "running",
error: null,
detail: {
type: "sub_agent",
subAgentType: "Explore",
description: "Inspect repository structure",
log: "[Read] README.md\n[Bash] ls",
actions: [
{
index: 1,
toolName: "Read",
summary: "README.md",
},
{
index: 2,
toolName: "Bash",
summary: "ls",
},
],
},
});
expect(parsed.type).toBe("tool_call");
if (parsed.type === "tool_call") {
expect(parsed.detail.type).toBe("sub_agent");
if (parsed.detail.type === "sub_agent") {
expect(parsed.detail.subAgentType).toBe("Explore");
expect(parsed.detail.actions).toHaveLength(2);
}
}
});
});
@@ -38,24 +38,29 @@ describe("shared tool-call display mapping", () => {
});
});
it("keeps task metadata summary on unknown detail", () => {
it("uses sub-agent detail for task label and description", () => {
const display = buildToolCallDisplayModel({
name: "task",
status: "running",
error: null,
detail: {
type: "unknown",
input: null,
output: null,
},
metadata: {
subAgentActivity: "Running tests",
type: "sub_agent",
subAgentType: "Explore",
description: "Inspect repository structure",
log: "[Read] README.md",
actions: [
{
index: 1,
toolName: "Read",
summary: "README.md",
},
],
},
});
expect(display).toEqual({
displayName: "Task",
summary: "Running tests",
displayName: "Explore",
summary: "Inspect repository structure",
});
});
@@ -87,6 +87,10 @@ export function buildToolCallDisplayModel(input: ToolCallDisplayInput): ToolCall
displayName = "Worktree Setup";
summary = input.detail.branchName;
break;
case "sub_agent":
displayName = readString(input.detail.subAgentType) ?? "Task";
summary = readString(input.detail.description);
break;
case "unknown":
break;
}