Files
paseo/packages/protocol/src/messages.hub.test.ts
T
Mohamed Boudra adf80a9fe0 Keep Hub daemon access explicit (#4088)
* feat(hub): make daemon authority explicit

Connecting a daemon establishes presence without granting execution. Hub authority now uses semantic session permissions, with workflow execution granted explicitly and inspectable through the normal session protocol.

Attention frames remain part of the existing agent and terminal subscriptions, keeping session output solicited.

* test(server): match sentence-case tool labels
2026-08-30 22:46:24 +02:00

414 lines
12 KiB
TypeScript

import { describe, expect, test } from "vitest";
import { z } from "zod";
import {
HubMessageCorrelationError,
SessionInboundMessageSchema,
SessionOutboundMessageSchema,
parseHubExecutionOutboundMessage,
} from "./messages.js";
const agent = {
id: "agent-1",
provider: "codex",
cwd: "/workspace",
model: null,
createdAt: "2026-07-13T00:00:00.000Z",
updatedAt: "2026-07-13T00:00:00.000Z",
lastUserMessageAt: null,
status: "idle",
capabilities: {
supportsStreaming: true,
supportsSessionPersistence: true,
supportsDynamicModes: true,
supportsMcpServers: false,
supportsReasoningStream: true,
supportsToolInvocations: true,
supportsRewindConversation: false,
supportsRewindFiles: false,
supportsRewindBoth: false,
},
currentModeId: null,
availableModes: [],
pendingPermissions: [],
persistence: null,
title: null,
labels: {},
};
// Frozen at the Hub create request shape shipped before worktree.
const PreviousHubAgentCreateRequestSchema = z.object({
type: z.literal("hub.execution.agent.create.request"),
requestId: z.string(),
executionId: z.string(),
provider: z.string(),
cwd: z.string(),
workspaceId: z.string().optional(),
prompt: z.string(),
model: z.string().optional(),
modeId: z.string().optional(),
thinkingOptionId: z.string().optional(),
featureValues: z.record(z.string(), z.unknown()).optional(),
env: z.record(z.string(), z.string()).optional(),
});
// Frozen at the Hub create request shape that temporarily carried turn-based auto-archive policy.
const PreviousHubAgentCreateWithAutoArchiveRequestSchema =
PreviousHubAgentCreateRequestSchema.extend({
worktree: z
.object({
mode: z.literal("branch-off"),
newBranch: z.string(),
base: z.string().optional(),
})
.optional(),
autoArchive: z.boolean().optional(),
});
describe("Hub session protocol", () => {
test("round-trips named-agent validation", () => {
const request = {
type: "hub.execution.agent.validate.request" as const,
requestId: "validate-codex",
provider: "codex",
model: "gpt-5.5",
thinkingOptionId: "xhigh",
providerOptions: {
sandbox_workspace_write: {
writable_roots: ["/var/cache/npm"],
network_access: false,
},
},
};
const response = {
type: "hub.execution.agent.validate.response" as const,
payload: {
requestId: request.requestId,
valid: true,
issues: [],
error: null,
},
};
expect(SessionInboundMessageSchema.parse(request)).toEqual(request);
expect(SessionOutboundMessageSchema.parse(response)).toEqual(response);
});
test("accepts the Hub execution create request", () => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "request-1",
executionId: "execution-1",
provider: "codex",
cwd: "/workspace",
prompt: "Implement the requested change",
modeId: "code",
};
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
});
test("keeps the retired Hub workspace selector wire-compatible", () => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "request-retired-workspace",
executionId: "execution-retired-workspace",
provider: "codex",
cwd: "/workspace",
workspaceId: "caller-owned-workspace",
prompt: "Implement the requested change",
};
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
});
test("accepts an optional MCP server configuration on Hub creates", () => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "request-mcp",
executionId: "execution-mcp",
provider: "codex",
cwd: "/workspace",
prompt: "Implement the requested change",
mcpServers: {
hub: {
type: "http",
url: "https://hub.example/mcp/executions/execution-mcp",
headers: { Authorization: "Bearer hub-execution-bearer" },
},
},
};
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
expect(PreviousHubAgentCreateRequestSchema.parse(message)).toEqual({
type: "hub.execution.agent.create.request",
requestId: "request-mcp",
executionId: "execution-mcp",
provider: "codex",
cwd: "/workspace",
prompt: "Implement the requested change",
});
});
test("round-trips native provider options and structured MCP preapproval", () => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "request-policy",
executionId: "execution-policy",
provider: "codex",
cwd: "/workspace",
prompt: "Classify and finish",
providerOptions: {
sandbox_mode: "workspace-write",
sandbox_workspace_write: { writable_roots: ["/var/cache/npm"] },
},
mcpServers: {
hub: { type: "http", url: "https://hub.example/executions/policy" },
},
toolPolicy: {
preapproved: [{ kind: "mcp", server: "hub", tool: "finish_execution" }],
},
};
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
});
test.each(["Bash", "Edit", "Write"])("cannot encode native %s tool preapproval", (tool) => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "request-native-tool",
executionId: "execution-native-tool",
provider: "claude",
cwd: "/workspace",
prompt: "Do work",
toolPolicy: { preapproved: [{ kind: "native", server: "claude", tool }] },
};
expect(SessionInboundMessageSchema.safeParse(message).success).toBe(false);
});
test.each([
undefined,
{ mode: "branch-off", newBranch: "hub-work", base: "main" },
{ mode: "checkout-branch", branch: "existing-work" },
{ mode: "checkout-pr", prNumber: 42 },
])("accepts Hub create worktree target %#", (worktree) => {
const message = {
type: "hub.execution.agent.create.request",
requestId: "hub-worktree",
executionId: "execution-worktree",
provider: "codex",
cwd: "/repo",
prompt: "Work in the requested target",
...(worktree ? { worktree } : {}),
};
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
});
test("accepts old Hub creates while ignoring their removed auto-archive policy", () => {
const oldRequest = {
type: "hub.execution.agent.create.request" as const,
requestId: "hub-old-policy",
executionId: "execution-old-policy",
provider: "codex",
cwd: "/repo",
prompt: "Work in the requested target",
worktree: { mode: "branch-off" as const, newBranch: "hub-work", base: "main" },
autoArchive: true,
};
expect(PreviousHubAgentCreateWithAutoArchiveRequestSchema.parse(oldRequest)).toEqual(
oldRequest,
);
expect(SessionInboundMessageSchema.parse(oldRequest)).toEqual({
type: "hub.execution.agent.create.request",
requestId: "hub-old-policy",
executionId: "execution-old-policy",
provider: "codex",
cwd: "/repo",
prompt: "Work in the requested target",
worktree: { mode: "branch-off", newBranch: "hub-work", base: "main" },
});
});
test.each(["interrupt", "archive"] as const)(
"round-trips the Hub execution %s command",
(action) => {
const request = {
type: "hub.execution.control.request" as const,
requestId: `control-${action}`,
executionId: "execution-1",
action,
};
const response = {
type: "hub.execution.control.response" as const,
payload: {
requestId: request.requestId,
executionId: request.executionId,
action,
success: true,
error: null,
},
};
expect(SessionInboundMessageSchema.parse(request)).toEqual(request);
expect(SessionOutboundMessageSchema.parse(response)).toEqual(response);
expect(parseHubExecutionOutboundMessage(response)).toEqual(response);
},
);
test("the previous Hub create parser ignores additive worktree and auto-archive fields", () => {
const newRequest = {
type: "hub.execution.agent.create.request" as const,
requestId: "hub-worktree",
executionId: "execution-worktree",
provider: "codex",
cwd: "/repo",
prompt: "Work in the requested target",
worktree: { mode: "branch-off", newBranch: "hub-work", base: "main" },
autoArchive: true,
};
expect(PreviousHubAgentCreateRequestSchema.parse(newRequest)).toEqual({
type: "hub.execution.agent.create.request",
requestId: "hub-worktree",
executionId: "execution-worktree",
provider: "codex",
cwd: "/repo",
prompt: "Work in the requested target",
});
});
test.each([
{
type: "hub.execution.agent.create.response",
payload: {
requestId: "request-1",
executionId: "execution-1",
agentId: "agent-1",
agent,
success: true,
error: null,
},
},
{
type: "hub.execution.control.response",
payload: {
requestId: "control-1",
executionId: "execution-1",
action: "archive",
success: false,
error: "Execution not found",
},
},
{
type: "hub.execution.agent.update",
payload: { executionId: "execution-1", agentId: "agent-1", agent },
},
{
type: "hub.execution.agent.stream",
payload: {
executionId: "execution-1",
agentId: "agent-1",
event: { type: "turn_started", provider: "codex" },
},
},
])("accepts outbound variant $type", (message) => {
expect(SessionOutboundMessageSchema.parse(message)).toEqual(message);
expect(parseHubExecutionOutboundMessage(message)).toEqual(message);
});
test("rejects a Hub update whose correlated agent ids disagree", () => {
const malformed = {
type: "hub.execution.agent.update",
payload: { executionId: "execution-1", agentId: "agent-2", agent },
};
expect(SessionOutboundMessageSchema.safeParse(malformed).success).toBe(true);
expect(() => parseHubExecutionOutboundMessage(malformed)).toThrow(HubMessageCorrelationError);
});
test.each([
{
type: "hub.management.daemon.connect.request",
requestId: "r1",
hubUrl: "https://hub.example",
token: "token",
permissions: [],
},
{ type: "hub.management.daemon.get_status.request", requestId: "r2" },
{ type: "hub.management.daemon.disconnect.request", requestId: "r3", force: true },
{
type: "hub.management.daemon.permissions.update.request",
requestId: "r4",
grant: ["hub.execute"],
revoke: [],
},
])("accepts trusted management request $type", (message) => {
expect(SessionInboundMessageSchema.parse(message)).toEqual(message);
});
test.each([
{
type: "hub.management.daemon.connect.response",
payload: {
requestId: "r1",
status: {
state: "connected",
daemonId: "daemon-1",
hubOrigin: "https://hub.example",
permissions: ["hub.execute"],
connectedAt: "2026-07-13T00:00:00.000Z",
lastError: null,
},
},
},
{
type: "hub.management.daemon.get_status.response",
payload: {
requestId: "r2",
status: {
state: "not_connected",
daemonId: null,
hubOrigin: null,
permissions: [],
connectedAt: null,
lastError: null,
},
},
},
{
type: "hub.management.daemon.disconnect.response",
payload: {
requestId: "r3",
status: {
state: "disconnecting",
daemonId: "daemon-1",
hubOrigin: "https://hub.example",
permissions: ["hub.execute"],
connectedAt: null,
lastError: "offline",
},
warning: "pending",
},
},
{
type: "hub.management.daemon.permissions.update.response",
payload: {
requestId: "r4",
status: {
state: "connected",
daemonId: "daemon-1",
hubOrigin: "https://hub.example",
permissions: ["hub.execute"],
connectedAt: "2026-07-13T00:00:00.000Z",
lastError: null,
},
},
},
])("accepts trusted management response $type", (message) => {
expect(SessionOutboundMessageSchema.parse(message)).toEqual(message);
});
});